From ca4633fd683527097451ca1398c90c87bb5c14fc Mon Sep 17 00:00:00 2001
From: Stavros Aronis
Date: Sat, 2 Apr 2011 18:57:42 +0300
Subject: Rename suite data directories
---
.../test/callgraph_SUITE_data/dialyzer_options | 1 +
.../results/test_missing_functions | 3 +
.../src/test_missing_functions/t1.erl | 16 +
.../src/test_missing_functions/t2.erl | 16 +
.../callgraph_tests_SUITE_data/dialyzer_options | 1 -
.../results/test_missing_functions | 3 -
.../src/test_missing_functions/t1.erl | 16 -
.../src/test_missing_functions/t2.erl | 16 -
lib/dialyzer/test/dialyzer_common.erl | 2 +-
.../test/opaque_SUITE_data/dialyzer_options | 1 +
lib/dialyzer/test/opaque_SUITE_data/results/array | 3 +
lib/dialyzer/test/opaque_SUITE_data/results/crash | 7 +
lib/dialyzer/test/opaque_SUITE_data/results/dict | 15 +
lib/dialyzer/test/opaque_SUITE_data/results/ets | 3 +
.../test/opaque_SUITE_data/results/gb_sets | 0
.../test/opaque_SUITE_data/results/inf_loop1 | 5 +
lib/dialyzer/test/opaque_SUITE_data/results/int | 3 +
.../test/opaque_SUITE_data/results/mixed_opaque | 2 +
.../test/opaque_SUITE_data/results/my_digraph | 0
.../test/opaque_SUITE_data/results/my_queue | 7 +
lib/dialyzer/test/opaque_SUITE_data/results/opaque | 2 +
lib/dialyzer/test/opaque_SUITE_data/results/queue | 11 +
lib/dialyzer/test/opaque_SUITE_data/results/rec | 6 +
lib/dialyzer/test/opaque_SUITE_data/results/timer | 4 +
lib/dialyzer/test/opaque_SUITE_data/results/union | 5 +
lib/dialyzer/test/opaque_SUITE_data/results/wings | 11 +
.../test/opaque_SUITE_data/src/array/array_use.erl | 15 +
.../test/opaque_SUITE_data/src/crash/crash_1.erl | 55 +
.../test/opaque_SUITE_data/src/dict/dict_use.erl | 82 +
.../test/opaque_SUITE_data/src/ets/ets_use.erl | 16 +
.../opaque_SUITE_data/src/gb_sets/gb_sets_rec.erl | 23 +
.../test/opaque_SUITE_data/src/inf_loop1.erl | 172 +
.../test/opaque_SUITE_data/src/int/int_adt.erl | 33 +
.../test/opaque_SUITE_data/src/int/int_use.erl | 11 +
.../src/mixed_opaque/mixed_opaque_queue_adt.erl | 26 +
.../src/mixed_opaque/mixed_opaque_rec_adt.erl | 25 +
.../src/mixed_opaque/mixed_opaque_use.erl | 31 +
.../src/my_digraph/my_digraph_adt.erl | 51 +
.../src/my_queue/my_queue_adt.erl | 23 +
.../src/my_queue/my_queue_use.erl | 35 +
.../opaque_SUITE_data/src/opaque/opaque_adt.erl | 9 +
.../opaque_SUITE_data/src/opaque/opaque_bug1.erl | 16 +
.../opaque_SUITE_data/src/opaque/opaque_bug2.erl | 13 +
.../opaque_SUITE_data/src/opaque/opaque_bug3.erl | 19 +
.../opaque_SUITE_data/src/opaque/opaque_bug4.erl | 21 +
.../test/opaque_SUITE_data/src/queue/queue_use.erl | 65 +
.../test/opaque_SUITE_data/src/rec/rec_adt.erl | 22 +
.../test/opaque_SUITE_data/src/rec/rec_use.erl | 30 +
.../test/opaque_SUITE_data/src/timer/timer_use.erl | 20 +
.../test/opaque_SUITE_data/src/union/union_adt.erl | 19 +
.../test/opaque_SUITE_data/src/union/union_use.erl | 16 +
.../test/opaque_SUITE_data/src/wings/wings.hrl | 204 +
.../opaque_SUITE_data/src/wings/wings_dissolve.erl | 375 ++
.../opaque_SUITE_data/src/wings/wings_edge.erl | 243 +
.../opaque_SUITE_data/src/wings/wings_edge_cmd.erl | 90 +
.../opaque_SUITE_data/src/wings/wings_face.erl | 127 +
.../opaque_SUITE_data/src/wings/wings_facemat.erl | 299 ++
.../opaque_SUITE_data/src/wings/wings_intl.hrl | 15 +
.../test/opaque_SUITE_data/src/wings/wings_io.erl | 37 +
.../test/opaque_SUITE_data/src/wings/wings_sel.erl | 68 +
.../opaque_SUITE_data/src/wings/wings_shape.erl | 69 +
.../opaque_SUITE_data/src/wings/wings_util.erl | 38 +
.../test/opaque_SUITE_data/src/wings/wings_we.erl | 250 +
.../test/opaque_SUITE_data/src/zoltan_kis1.erl | 14 +
.../test/opaque_SUITE_data/src/zoltan_kis2.erl | 14 +
.../test/opaque_SUITE_data/src/zoltan_kis3.erl | 14 +
.../test/opaque_SUITE_data/src/zoltan_kis4.erl | 14 +
.../test/opaque_SUITE_data/src/zoltan_kis5.erl | 14 +
.../test/opaque_SUITE_data/src/zoltan_kis6.erl | 14 +
.../test/opaque_tests_SUITE_data/dialyzer_options | 1 -
.../test/opaque_tests_SUITE_data/results/array | 3 -
.../test/opaque_tests_SUITE_data/results/crash | 7 -
.../test/opaque_tests_SUITE_data/results/dict | 15 -
.../test/opaque_tests_SUITE_data/results/ets | 3 -
.../test/opaque_tests_SUITE_data/results/gb_sets | 0
.../test/opaque_tests_SUITE_data/results/inf_loop1 | 5 -
.../test/opaque_tests_SUITE_data/results/int | 3 -
.../opaque_tests_SUITE_data/results/mixed_opaque | 2 -
.../opaque_tests_SUITE_data/results/my_digraph | 0
.../test/opaque_tests_SUITE_data/results/my_queue | 7 -
.../test/opaque_tests_SUITE_data/results/opaque | 2 -
.../test/opaque_tests_SUITE_data/results/queue | 11 -
.../test/opaque_tests_SUITE_data/results/rec | 6 -
.../test/opaque_tests_SUITE_data/results/timer | 4 -
.../test/opaque_tests_SUITE_data/results/union | 5 -
.../test/opaque_tests_SUITE_data/results/wings | 11 -
.../src/array/array_use.erl | 15 -
.../opaque_tests_SUITE_data/src/crash/crash_1.erl | 55 -
.../opaque_tests_SUITE_data/src/dict/dict_use.erl | 83 -
.../opaque_tests_SUITE_data/src/ets/ets_use.erl | 17 -
.../src/gb_sets/gb_sets_rec.erl | 23 -
.../test/opaque_tests_SUITE_data/src/inf_loop1.erl | 172 -
.../opaque_tests_SUITE_data/src/int/int_adt.erl | 33 -
.../opaque_tests_SUITE_data/src/int/int_use.erl | 11 -
.../src/mixed_opaque/mixed_opaque_queue_adt.erl | 26 -
.../src/mixed_opaque/mixed_opaque_rec_adt.erl | 25 -
.../src/mixed_opaque/mixed_opaque_use.erl | 31 -
.../src/my_digraph/my_digraph_adt.erl | 51 -
.../src/my_queue/my_queue_adt.erl | 23 -
.../src/my_queue/my_queue_use.erl | 35 -
.../src/opaque/opaque_adt.erl | 9 -
.../src/opaque/opaque_bug1.erl | 17 -
.../src/opaque/opaque_bug2.erl | 13 -
.../src/opaque/opaque_bug3.erl | 19 -
.../src/opaque/opaque_bug4.erl | 21 -
.../src/queue/queue_use.erl | 66 -
.../opaque_tests_SUITE_data/src/rec/rec_adt.erl | 22 -
.../opaque_tests_SUITE_data/src/rec/rec_use.erl | 30 -
.../src/timer/timer_use.erl | 20 -
.../src/union/union_adt.erl | 19 -
.../src/union/union_use.erl | 16 -
.../opaque_tests_SUITE_data/src/wings/wings.hrl | 205 -
.../src/wings/wings_dissolve.erl | 375 --
.../src/wings/wings_edge.erl | 243 -
.../src/wings/wings_edge_cmd.erl | 91 -
.../src/wings/wings_face.erl | 127 -
.../src/wings/wings_facemat.erl | 299 --
.../src/wings/wings_intl.hrl | 15 -
.../opaque_tests_SUITE_data/src/wings/wings_io.erl | 37 -
.../src/wings/wings_sel.erl | 68 -
.../src/wings/wings_shape.erl | 69 -
.../src/wings/wings_util.erl | 39 -
.../opaque_tests_SUITE_data/src/wings/wings_we.erl | 250 -
.../opaque_tests_SUITE_data/src/zoltan_kis1.erl | 14 -
.../opaque_tests_SUITE_data/src/zoltan_kis2.erl | 14 -
.../opaque_tests_SUITE_data/src/zoltan_kis3.erl | 14 -
.../opaque_tests_SUITE_data/src/zoltan_kis4.erl | 14 -
.../opaque_tests_SUITE_data/src/zoltan_kis5.erl | 14 -
.../opaque_tests_SUITE_data/src/zoltan_kis6.erl | 14 -
.../test/options1_SUITE_data/dialyzer_options | 2 +
.../options1_SUITE_data/my_include/CVS/Entries | 3 +
.../options1_SUITE_data/my_include/CVS/Repository | 1 +
.../test/options1_SUITE_data/my_include/CVS/Root | 1 +
.../options1_SUITE_data/my_include/erl_bits.hrl | 43 +
.../options1_SUITE_data/my_include/erl_compile.hrl | 41 +
.../test/options1_SUITE_data/results/compiler | 35 +
.../options1_SUITE_data/src/compiler/beam_asm.erl | 358 ++
.../src/compiler/beam_block.erl | 601 +++
.../options1_SUITE_data/src/compiler/beam_bool.erl | 617 +++
.../src/compiler/beam_clean.erl | 232 +
.../options1_SUITE_data/src/compiler/beam_dict.erl | 196 +
.../src/compiler/beam_disasm.erl | 964 ++++
.../src/compiler/beam_flatten.erl | 137 +
.../options1_SUITE_data/src/compiler/beam_jump.erl | 477 ++
.../src/compiler/beam_listing.erl | 117 +
.../src/compiler/beam_opcodes.erl | 240 +
.../src/compiler/beam_opcodes.hrl | 11 +
.../options1_SUITE_data/src/compiler/beam_type.erl | 551 ++
.../src/compiler/beam_validator.erl | 1022 ++++
.../test/options1_SUITE_data/src/compiler/cerl.erl | 4169 +++++++++++++++
.../src/compiler/cerl_clauses.erl | 409 ++
.../src/compiler/cerl_inline.erl | 2762 ++++++++++
.../src/compiler/cerl_trees.erl | 801 +++
.../options1_SUITE_data/src/compiler/compile.erl | 1109 ++++
.../options1_SUITE_data/src/compiler/core_lib.erl | 509 ++
.../options1_SUITE_data/src/compiler/core_lint.erl | 515 ++
.../src/compiler/core_parse.erl | 4909 +++++++++++++++++
.../src/compiler/core_parse.hrl | 111 +
.../options1_SUITE_data/src/compiler/core_pp.erl | 430 ++
.../options1_SUITE_data/src/compiler/core_scan.erl | 495 ++
.../options1_SUITE_data/src/compiler/erl_bifs.erl | 486 ++
.../options1_SUITE_data/src/compiler/rec_env.erl | 611 +++
.../src/compiler/sys_expand_pmod.erl | 425 ++
.../src/compiler/sys_pre_attributes.erl | 212 +
.../src/compiler/sys_pre_expand.erl | 1026 ++++
.../src/compiler/v3_codegen.erl | 1755 ++++++
.../options1_SUITE_data/src/compiler/v3_core.erl | 1319 +++++
.../options1_SUITE_data/src/compiler/v3_kernel.erl | 1567 ++++++
.../options1_SUITE_data/src/compiler/v3_kernel.hrl | 77 +
.../src/compiler/v3_kernel_pp.erl | 444 ++
.../options1_SUITE_data/src/compiler/v3_life.erl | 448 ++
.../options1_SUITE_data/src/compiler/v3_life.hrl | 24 +
.../options1_tests_SUITE_data/dialyzer_options | 2 -
.../my_include/CVS/Entries | 3 -
.../my_include/CVS/Repository | 1 -
.../options1_tests_SUITE_data/my_include/CVS/Root | 1 -
.../my_include/erl_bits.hrl | 43 -
.../my_include/erl_compile.hrl | 42 -
.../options1_tests_SUITE_data/results/compiler | 35 -
.../src/compiler/beam_asm.erl | 358 --
.../src/compiler/beam_block.erl | 601 ---
.../src/compiler/beam_bool.erl | 617 ---
.../src/compiler/beam_clean.erl | 232 -
.../src/compiler/beam_dict.erl | 196 -
.../src/compiler/beam_disasm.erl | 964 ----
.../src/compiler/beam_flatten.erl | 137 -
.../src/compiler/beam_jump.erl | 477 --
.../src/compiler/beam_listing.erl | 117 -
.../src/compiler/beam_opcodes.erl | 240 -
.../src/compiler/beam_opcodes.hrl | 12 -
.../src/compiler/beam_type.erl | 551 --
.../src/compiler/beam_validator.erl | 1022 ----
.../src/compiler/cerl.erl | 4169 ---------------
.../src/compiler/cerl_clauses.erl | 409 --
.../src/compiler/cerl_inline.erl | 2762 ----------
.../src/compiler/cerl_trees.erl | 801 ---
.../src/compiler/compile.erl | 1109 ----
.../src/compiler/core_lib.erl | 509 --
.../src/compiler/core_lint.erl | 515 --
.../src/compiler/core_parse.erl | 4911 -----------------
.../src/compiler/core_parse.hrl | 111 -
.../src/compiler/core_pp.erl | 430 --
.../src/compiler/core_scan.erl | 495 --
.../src/compiler/erl_bifs.erl | 486 --
.../src/compiler/rec_env.erl | 611 ---
.../src/compiler/sys_expand_pmod.erl | 425 --
.../src/compiler/sys_pre_attributes.erl | 212 -
.../src/compiler/sys_pre_expand.erl | 1026 ----
.../src/compiler/v3_codegen.erl | 1755 ------
.../src/compiler/v3_core.erl | 1320 -----
.../src/compiler/v3_kernel.erl | 1568 ------
.../src/compiler/v3_kernel.hrl | 77 -
.../src/compiler/v3_kernel_pp.erl | 444 --
.../src/compiler/v3_life.erl | 448 --
.../src/compiler/v3_life.hrl | 25 -
.../test/options2_SUITE_data/dialyzer_options | 1 +
.../test/options2_SUITE_data/results/kernel | 0
.../test/options2_SUITE_data/src/kernel/global.erl | 1999 +++++++
.../options2_tests_SUITE_data/dialyzer_options | 1 -
.../test/options2_tests_SUITE_data/results/kernel | 0
.../src/kernel/global.erl | 1999 -------
lib/dialyzer/test/plt_SUITE.erl | 21 +
lib/dialyzer/test/plt_tests_SUITE.erl | 21 -
lib/dialyzer/test/r9c_SUITE_data/dialyzer_options | 2 +
lib/dialyzer/test/r9c_SUITE_data/results/asn1 | 106 +
lib/dialyzer/test/r9c_SUITE_data/results/inets | 59 +
lib/dialyzer/test/r9c_SUITE_data/results/mnesia | 34 +
lib/dialyzer/test/r9c_SUITE_data/src/asn1/Makefile | 142 +
.../test/r9c_SUITE_data/src/asn1/Restrictions.txt | 55 +
.../test/r9c_SUITE_data/src/asn1/asn1.app.src | 20 +
.../test/r9c_SUITE_data/src/asn1/asn1.appup.src | 162 +
.../test/r9c_SUITE_data/src/asn1/asn1_db.erl | 160 +
.../test/r9c_SUITE_data/src/asn1/asn1_records.hrl | 96 +
.../test/r9c_SUITE_data/src/asn1/asn1ct.erl | 1904 +++++++
.../test/r9c_SUITE_data/src/asn1/asn1ct_check.erl | 5566 +++++++++++++++++++
.../src/asn1/asn1ct_constructed_ber.erl | 1468 ++++++
.../src/asn1/asn1ct_constructed_ber_bin_v2.erl | 1357 +++++
.../src/asn1/asn1ct_constructed_per.erl | 1234 +++++
.../test/r9c_SUITE_data/src/asn1/asn1ct_gen.erl | 1664 ++++++
.../r9c_SUITE_data/src/asn1/asn1ct_gen_ber.erl | 1525 ++++++
.../src/asn1/asn1ct_gen_ber_bin_v2.erl | 1562 ++++++
.../r9c_SUITE_data/src/asn1/asn1ct_gen_per.erl | 1189 +++++
.../src/asn1/asn1ct_gen_per_rt2ct.erl | 1811 +++++++
.../test/r9c_SUITE_data/src/asn1/asn1ct_name.erl | 225 +
.../test/r9c_SUITE_data/src/asn1/asn1ct_parser.yrl | 1162 ++++
.../r9c_SUITE_data/src/asn1/asn1ct_parser2.erl | 2763 ++++++++++
.../src/asn1/asn1ct_pretty_format.erl | 197 +
.../test/r9c_SUITE_data/src/asn1/asn1ct_tok.erl | 351 ++
.../test/r9c_SUITE_data/src/asn1/asn1ct_value.erl | 330 ++
.../test/r9c_SUITE_data/src/asn1/asn1rt.erl | 69 +
.../r9c_SUITE_data/src/asn1/asn1rt_ber_bin.erl | 2310 ++++++++
.../r9c_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl | 1849 +++++++
.../test/r9c_SUITE_data/src/asn1/asn1rt_check.erl | 333 ++
.../src/asn1/asn1rt_driver_handler.erl | 108 +
.../test/r9c_SUITE_data/src/asn1/asn1rt_per.erl | 1593 ++++++
.../r9c_SUITE_data/src/asn1/asn1rt_per_bin.erl | 2176 ++++++++
.../src/asn1/asn1rt_per_bin_rt2ct.erl | 2102 ++++++++
.../test/r9c_SUITE_data/src/asn1/asn1rt_per_v1.erl | 1827 +++++++
.../r9c_SUITE_data/src/asn1/notes_history.sgml | 97 +
.../test/r9c_SUITE_data/src/asn1/notes_latest.sgml | 97 +
.../test/r9c_SUITE_data/src/inets/Makefile | 178 +
lib/dialyzer/test/r9c_SUITE_data/src/inets/ftp.erl | 1582 ++++++
.../test/r9c_SUITE_data/src/inets/http.erl | 260 +
.../test/r9c_SUITE_data/src/inets/http.hrl | 127 +
.../test/r9c_SUITE_data/src/inets/http_lib.erl | 745 +++
.../r9c_SUITE_data/src/inets/httpc_handler.erl | 724 +++
.../r9c_SUITE_data/src/inets/httpc_manager.erl | 542 ++
.../test/r9c_SUITE_data/src/inets/httpd.erl | 594 +++
.../test/r9c_SUITE_data/src/inets/httpd.hrl | 77 +
.../r9c_SUITE_data/src/inets/httpd_acceptor.erl | 174 +
.../src/inets/httpd_acceptor_sup.erl | 116 +
.../test/r9c_SUITE_data/src/inets/httpd_conf.erl | 688 +++
.../r9c_SUITE_data/src/inets/httpd_example.erl | 134 +
.../r9c_SUITE_data/src/inets/httpd_manager.erl | 1029 ++++
.../r9c_SUITE_data/src/inets/httpd_misc_sup.erl | 113 +
.../test/r9c_SUITE_data/src/inets/httpd_parse.erl | 344 ++
.../src/inets/httpd_request_handler.erl | 994 ++++
.../r9c_SUITE_data/src/inets/httpd_response.erl | 437 ++
.../test/r9c_SUITE_data/src/inets/httpd_socket.erl | 381 ++
.../test/r9c_SUITE_data/src/inets/httpd_sup.erl | 202 +
.../test/r9c_SUITE_data/src/inets/httpd_util.erl | 773 +++
.../r9c_SUITE_data/src/inets/httpd_verbosity.erl | 93 +
.../r9c_SUITE_data/src/inets/httpd_verbosity.hrl | 62 +
.../test/r9c_SUITE_data/src/inets/inets.app.src | 56 +
.../test/r9c_SUITE_data/src/inets/inets.appup.src | 133 +
.../test/r9c_SUITE_data/src/inets/inets.config | 2 +
.../test/r9c_SUITE_data/src/inets/inets_sup.erl | 158 +
.../test/r9c_SUITE_data/src/inets/jnets_httpd.hrl | 138 +
.../test/r9c_SUITE_data/src/inets/mod_actions.erl | 92 +
.../test/r9c_SUITE_data/src/inets/mod_alias.erl | 175 +
.../test/r9c_SUITE_data/src/inets/mod_auth.erl | 748 +++
.../test/r9c_SUITE_data/src/inets/mod_auth.hrl | 26 +
.../r9c_SUITE_data/src/inets/mod_auth_dets.erl | 222 +
.../r9c_SUITE_data/src/inets/mod_auth_mnesia.erl | 269 +
.../r9c_SUITE_data/src/inets/mod_auth_plain.erl | 338 ++
.../r9c_SUITE_data/src/inets/mod_auth_server.erl | 422 ++
.../test/r9c_SUITE_data/src/inets/mod_browser.erl | 213 +
.../test/r9c_SUITE_data/src/inets/mod_cgi.erl | 692 +++
.../test/r9c_SUITE_data/src/inets/mod_dir.erl | 266 +
.../test/r9c_SUITE_data/src/inets/mod_disk_log.erl | 404 ++
.../test/r9c_SUITE_data/src/inets/mod_esi.erl | 481 ++
.../test/r9c_SUITE_data/src/inets/mod_get.erl | 151 +
.../test/r9c_SUITE_data/src/inets/mod_head.erl | 89 +
.../test/r9c_SUITE_data/src/inets/mod_htaccess.erl | 1136 ++++
.../test/r9c_SUITE_data/src/inets/mod_include.erl | 722 +++
.../test/r9c_SUITE_data/src/inets/mod_log.erl | 250 +
.../test/r9c_SUITE_data/src/inets/mod_range.erl | 380 ++
.../src/inets/mod_responsecontrol.erl | 320 ++
.../test/r9c_SUITE_data/src/inets/mod_security.erl | 307 ++
.../src/inets/mod_security_server.erl | 727 +++
.../test/r9c_SUITE_data/src/inets/mod_trace.erl | 64 +
lib/dialyzer/test/r9c_SUITE_data/src/inets/uri.erl | 349 ++
.../test/r9c_SUITE_data/src/mnesia/Makefile | 136 +
.../test/r9c_SUITE_data/src/mnesia/mnesia.app.src | 50 +
.../r9c_SUITE_data/src/mnesia/mnesia.appup.src | 6 +
.../test/r9c_SUITE_data/src/mnesia/mnesia.erl | 2191 ++++++++
.../test/r9c_SUITE_data/src/mnesia/mnesia.hrl | 117 +
.../r9c_SUITE_data/src/mnesia/mnesia_backup.erl | 194 +
.../test/r9c_SUITE_data/src/mnesia/mnesia_bup.erl | 1168 ++++
.../src/mnesia/mnesia_checkpoint.erl | 1283 +++++
.../src/mnesia/mnesia_checkpoint_sup.erl | 39 +
.../src/mnesia/mnesia_controller.erl | 2010 +++++++
.../r9c_SUITE_data/src/mnesia/mnesia_dumper.erl | 1092 ++++
.../r9c_SUITE_data/src/mnesia/mnesia_event.erl | 260 +
.../test/r9c_SUITE_data/src/mnesia/mnesia_frag.erl | 1201 +++++
.../r9c_SUITE_data/src/mnesia/mnesia_frag_hash.erl | 118 +
.../src/mnesia/mnesia_frag_old_hash.erl | 127 +
.../r9c_SUITE_data/src/mnesia/mnesia_index.erl | 379 ++
.../src/mnesia/mnesia_kernel_sup.erl | 60 +
.../src/mnesia/mnesia_late_loader.erl | 95 +
.../test/r9c_SUITE_data/src/mnesia/mnesia_lib.erl | 1276 +++++
.../r9c_SUITE_data/src/mnesia/mnesia_loader.erl | 805 +++
.../r9c_SUITE_data/src/mnesia/mnesia_locker.erl | 1021 ++++
.../test/r9c_SUITE_data/src/mnesia/mnesia_log.erl | 1019 ++++
.../r9c_SUITE_data/src/mnesia/mnesia_monitor.erl | 776 +++
.../r9c_SUITE_data/src/mnesia/mnesia_recover.erl | 1174 +++++
.../r9c_SUITE_data/src/mnesia/mnesia_registry.erl | 276 +
.../r9c_SUITE_data/src/mnesia/mnesia_schema.erl | 2898 ++++++++++
.../r9c_SUITE_data/src/mnesia/mnesia_snmp_hook.erl | 271 +
.../r9c_SUITE_data/src/mnesia/mnesia_snmp_sup.erl | 39 +
.../test/r9c_SUITE_data/src/mnesia/mnesia_sp.erl | 35 +
.../r9c_SUITE_data/src/mnesia/mnesia_subscr.erl | 491 ++
.../test/r9c_SUITE_data/src/mnesia/mnesia_sup.erl | 136 +
.../test/r9c_SUITE_data/src/mnesia/mnesia_text.erl | 189 +
.../test/r9c_SUITE_data/src/mnesia/mnesia_tm.erl | 2173 ++++++++
.../test/r9c_tests_SUITE_data/dialyzer_options | 2 -
.../test/r9c_tests_SUITE_data/results/asn1 | 106 -
.../test/r9c_tests_SUITE_data/results/inets | 59 -
.../test/r9c_tests_SUITE_data/results/mnesia | 34 -
.../test/r9c_tests_SUITE_data/src/asn1/Makefile | 151 -
.../r9c_tests_SUITE_data/src/asn1/Restrictions.txt | 55 -
.../r9c_tests_SUITE_data/src/asn1/asn1.app.src | 20 -
.../r9c_tests_SUITE_data/src/asn1/asn1.appup.src | 166 -
.../test/r9c_tests_SUITE_data/src/asn1/asn1_db.erl | 162 -
.../r9c_tests_SUITE_data/src/asn1/asn1_records.hrl | 96 -
.../test/r9c_tests_SUITE_data/src/asn1/asn1ct.erl | 1904 -------
.../r9c_tests_SUITE_data/src/asn1/asn1ct_check.erl | 5567 --------------------
.../src/asn1/asn1ct_constructed_ber.erl | 1468 ------
.../src/asn1/asn1ct_constructed_ber_bin_v2.erl | 1357 -----
.../src/asn1/asn1ct_constructed_per.erl | 1235 -----
.../r9c_tests_SUITE_data/src/asn1/asn1ct_gen.erl | 1664 ------
.../src/asn1/asn1ct_gen_ber.erl | 1525 ------
.../src/asn1/asn1ct_gen_ber_bin_v2.erl | 1568 ------
.../src/asn1/asn1ct_gen_per.erl | 1190 -----
.../src/asn1/asn1ct_gen_per_rt2ct.erl | 1811 -------
.../r9c_tests_SUITE_data/src/asn1/asn1ct_name.erl | 225 -
.../src/asn1/asn1ct_parser.yrl | 1175 -----
.../src/asn1/asn1ct_parser2.erl | 2764 ----------
.../src/asn1/asn1ct_pretty_format.erl | 199 -
.../r9c_tests_SUITE_data/src/asn1/asn1ct_tok.erl | 351 --
.../r9c_tests_SUITE_data/src/asn1/asn1ct_value.erl | 330 --
.../test/r9c_tests_SUITE_data/src/asn1/asn1rt.erl | 69 -
.../src/asn1/asn1rt_ber_bin.erl | 2310 --------
.../src/asn1/asn1rt_ber_bin_v2.erl | 1869 -------
.../r9c_tests_SUITE_data/src/asn1/asn1rt_check.erl | 333 --
.../src/asn1/asn1rt_driver_handler.erl | 108 -
.../r9c_tests_SUITE_data/src/asn1/asn1rt_per.erl | 1609 ------
.../src/asn1/asn1rt_per_bin.erl | 2182 --------
.../src/asn1/asn1rt_per_bin_rt2ct.erl | 2102 --------
.../src/asn1/asn1rt_per_v1.erl | 1843 -------
.../src/asn1/notes_history.sgml | 100 -
.../src/asn1/notes_latest.sgml | 100 -
.../test/r9c_tests_SUITE_data/src/inets/Makefile | 178 -
.../test/r9c_tests_SUITE_data/src/inets/ftp.erl | 1582 ------
.../test/r9c_tests_SUITE_data/src/inets/http.erl | 260 -
.../test/r9c_tests_SUITE_data/src/inets/http.hrl | 127 -
.../r9c_tests_SUITE_data/src/inets/http_lib.erl | 745 ---
.../src/inets/httpc_handler.erl | 724 ---
.../src/inets/httpc_manager.erl | 542 --
.../test/r9c_tests_SUITE_data/src/inets/httpd.erl | 596 ---
.../test/r9c_tests_SUITE_data/src/inets/httpd.hrl | 77 -
.../src/inets/httpd_acceptor.erl | 176 -
.../src/inets/httpd_acceptor_sup.erl | 118 -
.../r9c_tests_SUITE_data/src/inets/httpd_conf.erl | 688 ---
.../src/inets/httpd_example.erl | 134 -
.../src/inets/httpd_manager.erl | 1030 ----
.../src/inets/httpd_misc_sup.erl | 116 -
.../r9c_tests_SUITE_data/src/inets/httpd_parse.erl | 348 --
.../src/inets/httpd_request_handler.erl | 995 ----
.../src/inets/httpd_response.erl | 437 --
.../src/inets/httpd_socket.erl | 381 --
.../r9c_tests_SUITE_data/src/inets/httpd_sup.erl | 203 -
.../r9c_tests_SUITE_data/src/inets/httpd_util.erl | 777 ---
.../src/inets/httpd_verbosity.erl | 94 -
.../src/inets/httpd_verbosity.hrl | 65 -
.../r9c_tests_SUITE_data/src/inets/inets.app.src | 56 -
.../r9c_tests_SUITE_data/src/inets/inets.appup.src | 135 -
.../r9c_tests_SUITE_data/src/inets/inets.config | 2 -
.../r9c_tests_SUITE_data/src/inets/inets_sup.erl | 158 -
.../r9c_tests_SUITE_data/src/inets/jnets_httpd.hrl | 138 -
.../r9c_tests_SUITE_data/src/inets/mod_actions.erl | 92 -
.../r9c_tests_SUITE_data/src/inets/mod_alias.erl | 175 -
.../r9c_tests_SUITE_data/src/inets/mod_auth.erl | 750 ---
.../r9c_tests_SUITE_data/src/inets/mod_auth.hrl | 27 -
.../src/inets/mod_auth_dets.erl | 222 -
.../src/inets/mod_auth_mnesia.erl | 276 -
.../src/inets/mod_auth_plain.erl | 344 --
.../src/inets/mod_auth_server.erl | 424 --
.../r9c_tests_SUITE_data/src/inets/mod_browser.erl | 214 -
.../r9c_tests_SUITE_data/src/inets/mod_cgi.erl | 694 ---
.../r9c_tests_SUITE_data/src/inets/mod_dir.erl | 266 -
.../src/inets/mod_disk_log.erl | 405 --
.../r9c_tests_SUITE_data/src/inets/mod_esi.erl | 490 --
.../r9c_tests_SUITE_data/src/inets/mod_get.erl | 179 -
.../r9c_tests_SUITE_data/src/inets/mod_head.erl | 89 -
.../src/inets/mod_htaccess.erl | 1150 ----
.../r9c_tests_SUITE_data/src/inets/mod_include.erl | 726 ---
.../r9c_tests_SUITE_data/src/inets/mod_log.erl | 250 -
.../r9c_tests_SUITE_data/src/inets/mod_range.erl | 397 --
.../src/inets/mod_responsecontrol.erl | 337 --
.../src/inets/mod_security.erl | 307 --
.../src/inets/mod_security_server.erl | 728 ---
.../r9c_tests_SUITE_data/src/inets/mod_trace.erl | 69 -
.../test/r9c_tests_SUITE_data/src/inets/uri.erl | 349 --
.../test/r9c_tests_SUITE_data/src/mnesia/Makefile | 137 -
.../r9c_tests_SUITE_data/src/mnesia/mnesia.app.src | 52 -
.../src/mnesia/mnesia.appup.src | 6 -
.../r9c_tests_SUITE_data/src/mnesia/mnesia.erl | 2191 --------
.../r9c_tests_SUITE_data/src/mnesia/mnesia.hrl | 118 -
.../src/mnesia/mnesia_backup.erl | 195 -
.../r9c_tests_SUITE_data/src/mnesia/mnesia_bup.erl | 1169 ----
.../src/mnesia/mnesia_checkpoint.erl | 1284 -----
.../src/mnesia/mnesia_checkpoint_sup.erl | 39 -
.../src/mnesia/mnesia_controller.erl | 2012 -------
.../src/mnesia/mnesia_dumper.erl | 1092 ----
.../src/mnesia/mnesia_event.erl | 263 -
.../src/mnesia/mnesia_frag.erl | 1201 -----
.../src/mnesia/mnesia_frag_hash.erl | 118 -
.../src/mnesia/mnesia_frag_old_hash.erl | 127 -
.../src/mnesia/mnesia_index.erl | 380 --
.../src/mnesia/mnesia_kernel_sup.erl | 62 -
.../src/mnesia/mnesia_late_loader.erl | 95 -
.../r9c_tests_SUITE_data/src/mnesia/mnesia_lib.erl | 1278 -----
.../src/mnesia/mnesia_loader.erl | 805 ---
.../src/mnesia/mnesia_locker.erl | 1022 ----
.../r9c_tests_SUITE_data/src/mnesia/mnesia_log.erl | 1019 ----
.../src/mnesia/mnesia_monitor.erl | 776 ---
.../src/mnesia/mnesia_recover.erl | 1175 -----
.../src/mnesia/mnesia_registry.erl | 277 -
.../src/mnesia/mnesia_schema.erl | 2899 ----------
.../src/mnesia/mnesia_snmp_hook.erl | 271 -
.../src/mnesia/mnesia_snmp_sup.erl | 39 -
.../r9c_tests_SUITE_data/src/mnesia/mnesia_sp.erl | 39 -
.../src/mnesia/mnesia_subscr.erl | 492 --
.../r9c_tests_SUITE_data/src/mnesia/mnesia_sup.erl | 137 -
.../src/mnesia/mnesia_text.erl | 191 -
.../r9c_tests_SUITE_data/src/mnesia/mnesia_tm.erl | 2173 --------
lib/dialyzer/test/race_SUITE_data/dialyzer_options | 1 +
.../test/race_SUITE_data/results/ets_insert_args1 | 2 +
.../test/race_SUITE_data/results/ets_insert_args2 | 2 +
.../test/race_SUITE_data/results/ets_insert_args3 | 0
.../test/race_SUITE_data/results/ets_insert_args4 | 2 +
.../test/race_SUITE_data/results/ets_insert_args5 | 2 +
.../test/race_SUITE_data/results/ets_insert_args6 | 2 +
.../test/race_SUITE_data/results/ets_insert_args7 | 2 +
.../test/race_SUITE_data/results/ets_insert_args8 | 2 +
.../results/ets_insert_control_flow1 | 2 +
.../results/ets_insert_control_flow2 | 3 +
.../results/ets_insert_control_flow3 | 3 +
.../results/ets_insert_control_flow4 | 3 +
.../results/ets_insert_control_flow5 | 5 +
.../results/ets_insert_diff_atoms_race1 | 2 +
.../results/ets_insert_diff_atoms_race2 | 2 +
.../results/ets_insert_diff_atoms_race3 | 2 +
.../results/ets_insert_diff_atoms_race4 | 2 +
.../results/ets_insert_diff_atoms_race5 | 2 +
.../results/ets_insert_diff_atoms_race6 | 2 +
.../race_SUITE_data/results/ets_insert_double1 | 4 +
.../race_SUITE_data/results/ets_insert_double2 | 4 +
.../test/race_SUITE_data/results/ets_insert_funs1 | 2 +
.../test/race_SUITE_data/results/ets_insert_funs2 | 2 +
.../test/race_SUITE_data/results/ets_insert_new | 0
.../test/race_SUITE_data/results/ets_insert_param | 5 +
.../race_SUITE_data/results/extract_translations | 5 +
.../results/mnesia_diff_atoms_race1 | 2 +
.../results/mnesia_diff_atoms_race2 | 2 +
.../results/mnesia_dirty_read_one_write_two | 2 +
.../results/mnesia_dirty_read_two_write_one | 2 +
.../results/mnesia_dirty_read_write_double1 | 2 +
.../results/mnesia_dirty_read_write_double2 | 2 +
.../results/mnesia_dirty_read_write_double3 | 2 +
.../results/mnesia_dirty_read_write_double4 | 2 +
.../results/mnesia_dirty_read_write_one | 2 +
.../results/mnesia_dirty_read_write_two | 2 +
.../race_SUITE_data/results/whereis_control_flow1 | 2 +
.../race_SUITE_data/results/whereis_control_flow2 | 3 +
.../race_SUITE_data/results/whereis_control_flow3 | 2 +
.../race_SUITE_data/results/whereis_control_flow4 | 3 +
.../race_SUITE_data/results/whereis_control_flow5 | 2 +
.../race_SUITE_data/results/whereis_control_flow6 | 2 +
.../results/whereis_diff_atoms_no_race | 0
.../results/whereis_diff_atoms_race | 2 +
.../results/whereis_diff_functions1 | 3 +
.../results/whereis_diff_functions1_nested | 2 +
.../results/whereis_diff_functions1_pathsens | 2 +
.../results/whereis_diff_functions1_twice | 3 +
.../results/whereis_diff_functions2 | 2 +
.../results/whereis_diff_functions2_nested | 2 +
.../results/whereis_diff_functions2_pathsens | 2 +
.../results/whereis_diff_functions2_twice | 3 +
.../results/whereis_diff_functions3 | 2 +
.../results/whereis_diff_functions3_nested | 2 +
.../results/whereis_diff_functions3_pathsens | 2 +
.../results/whereis_diff_functions4 | 2 +
.../results/whereis_diff_functions5 | 2 +
.../results/whereis_diff_functions6 | 2 +
.../race_SUITE_data/results/whereis_diff_modules1 | 2 +
.../results/whereis_diff_modules1_pathsens | 2 +
.../results/whereis_diff_modules1_rec | 2 +
.../race_SUITE_data/results/whereis_diff_modules2 | 2 +
.../results/whereis_diff_modules2_pathsens | 2 +
.../results/whereis_diff_modules2_rec | 2 +
.../race_SUITE_data/results/whereis_diff_modules3 | 2 +
.../results/whereis_diff_modules_nested | 2 +
.../results/whereis_diff_modules_twice | 3 +
.../results/whereis_diff_vars_no_race | 0
.../race_SUITE_data/results/whereis_diff_vars_race | 2 +
.../results/whereis_intra_inter_module1 | 2 +
.../results/whereis_intra_inter_module2 | 2 +
.../results/whereis_intra_inter_module3 | 2 +
.../results/whereis_intra_inter_module4 | 2 +
.../results/whereis_intra_inter_module5 | 2 +
.../results/whereis_intra_inter_module6 | 2 +
.../results/whereis_intra_inter_module7 | 2 +
.../results/whereis_intra_inter_module8 | 2 +
.../test/race_SUITE_data/results/whereis_param | 2 +
.../results/whereis_param_inter_module | 2 +
.../race_SUITE_data/results/whereis_rec_function1 | 2 +
.../race_SUITE_data/results/whereis_rec_function2 | 2 +
.../race_SUITE_data/results/whereis_rec_function3 | 2 +
.../race_SUITE_data/results/whereis_rec_function4 | 2 +
.../race_SUITE_data/results/whereis_rec_function5 | 2 +
.../race_SUITE_data/results/whereis_rec_function6 | 2 +
.../race_SUITE_data/results/whereis_rec_function7 | 2 +
.../race_SUITE_data/results/whereis_rec_function8 | 2 +
.../test/race_SUITE_data/results/whereis_try_catch | 3 +
.../test/race_SUITE_data/results/whereis_vars1 | 0
.../test/race_SUITE_data/results/whereis_vars10 | 2 +
.../test/race_SUITE_data/results/whereis_vars11 | 0
.../test/race_SUITE_data/results/whereis_vars12 | 2 +
.../test/race_SUITE_data/results/whereis_vars13 | 2 +
.../test/race_SUITE_data/results/whereis_vars14 | 2 +
.../test/race_SUITE_data/results/whereis_vars15 | 2 +
.../test/race_SUITE_data/results/whereis_vars16 | 2 +
.../test/race_SUITE_data/results/whereis_vars17 | 2 +
.../test/race_SUITE_data/results/whereis_vars18 | 0
.../test/race_SUITE_data/results/whereis_vars19 | 0
.../test/race_SUITE_data/results/whereis_vars2 | 2 +
.../test/race_SUITE_data/results/whereis_vars20 | 0
.../test/race_SUITE_data/results/whereis_vars21 | 0
.../test/race_SUITE_data/results/whereis_vars22 | 2 +
.../test/race_SUITE_data/results/whereis_vars3 | 2 +
.../test/race_SUITE_data/results/whereis_vars4 | 2 +
.../test/race_SUITE_data/results/whereis_vars5 | 2 +
.../test/race_SUITE_data/results/whereis_vars6 | 2 +
.../test/race_SUITE_data/results/whereis_vars7 | 2 +
.../test/race_SUITE_data/results/whereis_vars8 | 2 +
.../test/race_SUITE_data/results/whereis_vars9 | 2 +
.../test/race_SUITE_data/src/ets_insert_args1.erl | 17 +
.../test/race_SUITE_data/src/ets_insert_args2.erl | 17 +
.../test/race_SUITE_data/src/ets_insert_args3.erl | 17 +
.../test/race_SUITE_data/src/ets_insert_args4.erl | 17 +
.../test/race_SUITE_data/src/ets_insert_args5.erl | 17 +
.../test/race_SUITE_data/src/ets_insert_args6.erl | 17 +
.../test/race_SUITE_data/src/ets_insert_args7.erl | 17 +
.../test/race_SUITE_data/src/ets_insert_args8.erl | 16 +
.../src/ets_insert_control_flow1.erl | 20 +
.../src/ets_insert_control_flow2.erl | 26 +
.../src/ets_insert_control_flow3.erl | 31 +
.../src/ets_insert_control_flow4.erl | 31 +
.../src/ets_insert_control_flow5.erl | 34 +
.../src/ets_insert_diff_atoms_race1.erl | 22 +
.../src/ets_insert_diff_atoms_race2.erl | 22 +
.../src/ets_insert_diff_atoms_race3.erl | 22 +
.../src/ets_insert_diff_atoms_race4.erl | 22 +
.../src/ets_insert_diff_atoms_race5.erl | 22 +
.../src/ets_insert_diff_atoms_race6.erl | 22 +
.../race_SUITE_data/src/ets_insert_double1.erl | 28 +
.../race_SUITE_data/src/ets_insert_double2.erl | 28 +
.../test/race_SUITE_data/src/ets_insert_funs1.erl | 18 +
.../test/race_SUITE_data/src/ets_insert_funs2.erl | 18 +
.../test/race_SUITE_data/src/ets_insert_new.erl | 15 +
.../test/race_SUITE_data/src/ets_insert_param.erl | 26 +
.../race_SUITE_data/src/extract_translations.erl | 293 ++
.../src/mnesia_diff_atoms_race1.erl | 33 +
.../src/mnesia_diff_atoms_race2.erl | 37 +
.../src/mnesia_dirty_read_one_write_two.erl | 20 +
.../src/mnesia_dirty_read_two_write_one.erl | 20 +
.../src/mnesia_dirty_read_write_double1.erl | 25 +
.../src/mnesia_dirty_read_write_double2.erl | 25 +
.../src/mnesia_dirty_read_write_double3.erl | 25 +
.../src/mnesia_dirty_read_write_double4.erl | 25 +
.../src/mnesia_dirty_read_write_one.erl | 20 +
.../src/mnesia_dirty_read_write_two.erl | 20 +
.../race_SUITE_data/src/whereis_control_flow1.erl | 17 +
.../race_SUITE_data/src/whereis_control_flow2.erl | 19 +
.../race_SUITE_data/src/whereis_control_flow3.erl | 25 +
.../race_SUITE_data/src/whereis_control_flow4.erl | 29 +
.../race_SUITE_data/src/whereis_control_flow5.erl | 12 +
.../race_SUITE_data/src/whereis_control_flow6.erl | 12 +
.../src/whereis_diff_atoms_no_race.erl | 23 +
.../src/whereis_diff_atoms_race.erl | 34 +
.../src/whereis_diff_functions1.erl | 22 +
.../src/whereis_diff_functions1_nested.erl | 23 +
.../src/whereis_diff_functions1_pathsens.erl | 32 +
.../src/whereis_diff_functions1_twice.erl | 30 +
.../src/whereis_diff_functions2.erl | 25 +
.../src/whereis_diff_functions2_nested.erl | 20 +
.../src/whereis_diff_functions2_pathsens.erl | 29 +
.../src/whereis_diff_functions2_twice.erl | 27 +
.../src/whereis_diff_functions3.erl | 11 +
.../src/whereis_diff_functions3_nested.erl | 21 +
.../src/whereis_diff_functions3_pathsens.erl | 29 +
.../src/whereis_diff_functions4.erl | 32 +
.../src/whereis_diff_functions5.erl | 22 +
.../src/whereis_diff_functions6.erl | 29 +
.../whereis_diff_modules1.erl | 16 +
.../whereis_diff_modules2.erl | 11 +
.../whereis_diff_modules1_pathsens.erl | 26 +
.../whereis_diff_modules2_pathsens.erl | 12 +
.../whereis_diff_modules1_rec.erl | 22 +
.../whereis_diff_modules2_rec.erl | 8 +
.../whereis_diff_modules3.erl | 8 +
.../whereis_diff_modules4.erl | 11 +
.../whereis_diff_modules3_pathsens.erl | 25 +
.../whereis_diff_modules4_pathsens.erl | 13 +
.../whereis_diff_modules3_rec.erl | 25 +
.../whereis_diff_modules4_rec.erl | 8 +
.../whereis_diff_modules5.erl | 23 +
.../whereis_diff_modules6.erl | 11 +
.../whereis_diff_modules1_nested.erl | 14 +
.../whereis_diff_modules2_nested.erl | 11 +
.../whereis_diff_modules3_nested.erl | 11 +
.../whereis_diff_modules1_twice.erl | 21 +
.../whereis_diff_modules2_twice.erl | 11 +
.../src/whereis_diff_vars_no_race.erl | 13 +
.../race_SUITE_data/src/whereis_diff_vars_race.erl | 19 +
.../whereis_intra_inter_module1.erl | 19 +
.../whereis_intra_inter_module2.erl | 11 +
.../whereis_intra_inter_module3.erl | 16 +
.../whereis_intra_inter_module4.erl | 14 +
.../whereis_intra_inter_module5.erl | 19 +
.../whereis_intra_inter_module6.erl | 14 +
.../whereis_intra_inter_module7.erl | 11 +
.../whereis_intra_inter_module8.erl | 13 +
.../whereis_intra_inter_module10.erl | 16 +
.../whereis_intra_inter_module9.erl | 11 +
.../whereis_intra_inter_module11.erl | 27 +
.../whereis_intra_inter_module12.erl | 14 +
.../whereis_intra_inter_module13.erl | 19 +
.../whereis_intra_inter_module14.erl | 22 +
.../whereis_intra_inter_module15.erl | 19 +
.../whereis_intra_inter_module16.erl | 22 +
.../test/race_SUITE_data/src/whereis_param.erl | 16 +
.../whereis_param_inter_module1.erl | 8 +
.../whereis_param_inter_module2.erl | 13 +
.../race_SUITE_data/src/whereis_rec_function1.erl | 19 +
.../race_SUITE_data/src/whereis_rec_function2.erl | 24 +
.../race_SUITE_data/src/whereis_rec_function3.erl | 27 +
.../race_SUITE_data/src/whereis_rec_function4.erl | 27 +
.../race_SUITE_data/src/whereis_rec_function5.erl | 21 +
.../race_SUITE_data/src/whereis_rec_function6.erl | 24 +
.../race_SUITE_data/src/whereis_rec_function7.erl | 19 +
.../race_SUITE_data/src/whereis_rec_function8.erl | 22 +
.../test/race_SUITE_data/src/whereis_try_catch.erl | 25 +
.../test/race_SUITE_data/src/whereis_vars1.erl | 17 +
.../test/race_SUITE_data/src/whereis_vars10.erl | 22 +
.../test/race_SUITE_data/src/whereis_vars11.erl | 22 +
.../test/race_SUITE_data/src/whereis_vars12.erl | 22 +
.../test/race_SUITE_data/src/whereis_vars13.erl | 22 +
.../test/race_SUITE_data/src/whereis_vars14.erl | 22 +
.../test/race_SUITE_data/src/whereis_vars15.erl | 23 +
.../test/race_SUITE_data/src/whereis_vars16.erl | 23 +
.../test/race_SUITE_data/src/whereis_vars17.erl | 23 +
.../test/race_SUITE_data/src/whereis_vars18.erl | 22 +
.../test/race_SUITE_data/src/whereis_vars19.erl | 23 +
.../test/race_SUITE_data/src/whereis_vars2.erl | 18 +
.../test/race_SUITE_data/src/whereis_vars20.erl | 22 +
.../test/race_SUITE_data/src/whereis_vars21.erl | 23 +
.../test/race_SUITE_data/src/whereis_vars22.erl | 27 +
.../test/race_SUITE_data/src/whereis_vars3.erl | 18 +
.../test/race_SUITE_data/src/whereis_vars4.erl | 18 +
.../test/race_SUITE_data/src/whereis_vars5.erl | 22 +
.../test/race_SUITE_data/src/whereis_vars6.erl | 22 +
.../test/race_SUITE_data/src/whereis_vars7.erl | 22 +
.../test/race_SUITE_data/src/whereis_vars8.erl | 22 +
.../test/race_SUITE_data/src/whereis_vars9.erl | 22 +
.../test/race_tests_SUITE_data/dialyzer_options | 1 -
.../race_tests_SUITE_data/results/ets_insert_args1 | 2 -
.../race_tests_SUITE_data/results/ets_insert_args2 | 2 -
.../race_tests_SUITE_data/results/ets_insert_args3 | 0
.../race_tests_SUITE_data/results/ets_insert_args4 | 2 -
.../race_tests_SUITE_data/results/ets_insert_args5 | 2 -
.../race_tests_SUITE_data/results/ets_insert_args6 | 2 -
.../race_tests_SUITE_data/results/ets_insert_args7 | 2 -
.../race_tests_SUITE_data/results/ets_insert_args8 | 2 -
.../results/ets_insert_control_flow1 | 2 -
.../results/ets_insert_control_flow2 | 3 -
.../results/ets_insert_control_flow3 | 3 -
.../results/ets_insert_control_flow4 | 3 -
.../results/ets_insert_control_flow5 | 5 -
.../results/ets_insert_diff_atoms_race1 | 2 -
.../results/ets_insert_diff_atoms_race2 | 2 -
.../results/ets_insert_diff_atoms_race3 | 2 -
.../results/ets_insert_diff_atoms_race4 | 2 -
.../results/ets_insert_diff_atoms_race5 | 2 -
.../results/ets_insert_diff_atoms_race6 | 2 -
.../results/ets_insert_double1 | 4 -
.../results/ets_insert_double2 | 4 -
.../race_tests_SUITE_data/results/ets_insert_funs1 | 2 -
.../race_tests_SUITE_data/results/ets_insert_funs2 | 2 -
.../race_tests_SUITE_data/results/ets_insert_new | 0
.../race_tests_SUITE_data/results/ets_insert_param | 5 -
.../results/extract_translations | 5 -
.../results/mnesia_diff_atoms_race1 | 2 -
.../results/mnesia_diff_atoms_race2 | 2 -
.../results/mnesia_dirty_read_one_write_two | 2 -
.../results/mnesia_dirty_read_two_write_one | 2 -
.../results/mnesia_dirty_read_write_double1 | 2 -
.../results/mnesia_dirty_read_write_double2 | 2 -
.../results/mnesia_dirty_read_write_double3 | 2 -
.../results/mnesia_dirty_read_write_double4 | 2 -
.../results/mnesia_dirty_read_write_one | 2 -
.../results/mnesia_dirty_read_write_two | 2 -
.../results/whereis_control_flow1 | 2 -
.../results/whereis_control_flow2 | 3 -
.../results/whereis_control_flow3 | 2 -
.../results/whereis_control_flow4 | 3 -
.../results/whereis_control_flow5 | 2 -
.../results/whereis_control_flow6 | 2 -
.../results/whereis_diff_atoms_no_race | 0
.../results/whereis_diff_atoms_race | 2 -
.../results/whereis_diff_functions1 | 3 -
.../results/whereis_diff_functions1_nested | 2 -
.../results/whereis_diff_functions1_pathsens | 2 -
.../results/whereis_diff_functions1_twice | 3 -
.../results/whereis_diff_functions2 | 2 -
.../results/whereis_diff_functions2_nested | 2 -
.../results/whereis_diff_functions2_pathsens | 2 -
.../results/whereis_diff_functions2_twice | 3 -
.../results/whereis_diff_functions3 | 2 -
.../results/whereis_diff_functions3_nested | 2 -
.../results/whereis_diff_functions3_pathsens | 2 -
.../results/whereis_diff_functions4 | 2 -
.../results/whereis_diff_functions5 | 2 -
.../results/whereis_diff_functions6 | 2 -
.../results/whereis_diff_modules1 | 2 -
.../results/whereis_diff_modules1_pathsens | 2 -
.../results/whereis_diff_modules1_rec | 2 -
.../results/whereis_diff_modules2 | 2 -
.../results/whereis_diff_modules2_pathsens | 2 -
.../results/whereis_diff_modules2_rec | 2 -
.../results/whereis_diff_modules3 | 2 -
.../results/whereis_diff_modules_nested | 2 -
.../results/whereis_diff_modules_twice | 3 -
.../results/whereis_diff_vars_no_race | 0
.../results/whereis_diff_vars_race | 2 -
.../results/whereis_intra_inter_module1 | 2 -
.../results/whereis_intra_inter_module2 | 2 -
.../results/whereis_intra_inter_module3 | 2 -
.../results/whereis_intra_inter_module4 | 2 -
.../results/whereis_intra_inter_module5 | 2 -
.../results/whereis_intra_inter_module6 | 2 -
.../results/whereis_intra_inter_module7 | 2 -
.../results/whereis_intra_inter_module8 | 2 -
.../race_tests_SUITE_data/results/whereis_param | 2 -
.../results/whereis_param_inter_module | 2 -
.../results/whereis_rec_function1 | 2 -
.../results/whereis_rec_function2 | 2 -
.../results/whereis_rec_function3 | 2 -
.../results/whereis_rec_function4 | 2 -
.../results/whereis_rec_function5 | 2 -
.../results/whereis_rec_function6 | 2 -
.../results/whereis_rec_function7 | 2 -
.../results/whereis_rec_function8 | 2 -
.../results/whereis_try_catch | 3 -
.../race_tests_SUITE_data/results/whereis_vars1 | 0
.../race_tests_SUITE_data/results/whereis_vars10 | 2 -
.../race_tests_SUITE_data/results/whereis_vars11 | 0
.../race_tests_SUITE_data/results/whereis_vars12 | 2 -
.../race_tests_SUITE_data/results/whereis_vars13 | 2 -
.../race_tests_SUITE_data/results/whereis_vars14 | 2 -
.../race_tests_SUITE_data/results/whereis_vars15 | 2 -
.../race_tests_SUITE_data/results/whereis_vars16 | 2 -
.../race_tests_SUITE_data/results/whereis_vars17 | 2 -
.../race_tests_SUITE_data/results/whereis_vars18 | 0
.../race_tests_SUITE_data/results/whereis_vars19 | 0
.../race_tests_SUITE_data/results/whereis_vars2 | 2 -
.../race_tests_SUITE_data/results/whereis_vars20 | 0
.../race_tests_SUITE_data/results/whereis_vars21 | 0
.../race_tests_SUITE_data/results/whereis_vars22 | 2 -
.../race_tests_SUITE_data/results/whereis_vars3 | 2 -
.../race_tests_SUITE_data/results/whereis_vars4 | 2 -
.../race_tests_SUITE_data/results/whereis_vars5 | 2 -
.../race_tests_SUITE_data/results/whereis_vars6 | 2 -
.../race_tests_SUITE_data/results/whereis_vars7 | 2 -
.../race_tests_SUITE_data/results/whereis_vars8 | 2 -
.../race_tests_SUITE_data/results/whereis_vars9 | 2 -
.../race_tests_SUITE_data/src/ets_insert_args1.erl | 17 -
.../race_tests_SUITE_data/src/ets_insert_args2.erl | 17 -
.../race_tests_SUITE_data/src/ets_insert_args3.erl | 17 -
.../race_tests_SUITE_data/src/ets_insert_args4.erl | 17 -
.../race_tests_SUITE_data/src/ets_insert_args5.erl | 17 -
.../race_tests_SUITE_data/src/ets_insert_args6.erl | 17 -
.../race_tests_SUITE_data/src/ets_insert_args7.erl | 17 -
.../race_tests_SUITE_data/src/ets_insert_args8.erl | 16 -
.../src/ets_insert_control_flow1.erl | 20 -
.../src/ets_insert_control_flow2.erl | 26 -
.../src/ets_insert_control_flow3.erl | 31 -
.../src/ets_insert_control_flow4.erl | 31 -
.../src/ets_insert_control_flow5.erl | 34 -
.../src/ets_insert_diff_atoms_race1.erl | 22 -
.../src/ets_insert_diff_atoms_race2.erl | 22 -
.../src/ets_insert_diff_atoms_race3.erl | 22 -
.../src/ets_insert_diff_atoms_race4.erl | 22 -
.../src/ets_insert_diff_atoms_race5.erl | 22 -
.../src/ets_insert_diff_atoms_race6.erl | 22 -
.../src/ets_insert_double1.erl | 28 -
.../src/ets_insert_double2.erl | 28 -
.../race_tests_SUITE_data/src/ets_insert_funs1.erl | 18 -
.../race_tests_SUITE_data/src/ets_insert_funs2.erl | 18 -
.../race_tests_SUITE_data/src/ets_insert_new.erl | 15 -
.../race_tests_SUITE_data/src/ets_insert_param.erl | 26 -
.../src/extract_translations.erl | 294 --
.../src/mnesia_diff_atoms_race1.erl | 33 -
.../src/mnesia_diff_atoms_race2.erl | 37 -
.../src/mnesia_dirty_read_one_write_two.erl | 22 -
.../src/mnesia_dirty_read_two_write_one.erl | 22 -
.../src/mnesia_dirty_read_write_double1.erl | 25 -
.../src/mnesia_dirty_read_write_double2.erl | 25 -
.../src/mnesia_dirty_read_write_double3.erl | 25 -
.../src/mnesia_dirty_read_write_double4.erl | 25 -
.../src/mnesia_dirty_read_write_one.erl | 22 -
.../src/mnesia_dirty_read_write_two.erl | 22 -
.../src/whereis_control_flow1.erl | 17 -
.../src/whereis_control_flow2.erl | 19 -
.../src/whereis_control_flow3.erl | 25 -
.../src/whereis_control_flow4.erl | 29 -
.../src/whereis_control_flow5.erl | 12 -
.../src/whereis_control_flow6.erl | 12 -
.../src/whereis_diff_atoms_no_race.erl | 24 -
.../src/whereis_diff_atoms_race.erl | 35 -
.../src/whereis_diff_functions1.erl | 22 -
.../src/whereis_diff_functions1_nested.erl | 23 -
.../src/whereis_diff_functions1_pathsens.erl | 32 -
.../src/whereis_diff_functions1_twice.erl | 30 -
.../src/whereis_diff_functions2.erl | 25 -
.../src/whereis_diff_functions2_nested.erl | 20 -
.../src/whereis_diff_functions2_pathsens.erl | 29 -
.../src/whereis_diff_functions2_twice.erl | 27 -
.../src/whereis_diff_functions3.erl | 11 -
.../src/whereis_diff_functions3_nested.erl | 21 -
.../src/whereis_diff_functions3_pathsens.erl | 29 -
.../src/whereis_diff_functions4.erl | 32 -
.../src/whereis_diff_functions5.erl | 22 -
.../src/whereis_diff_functions6.erl | 29 -
.../whereis_diff_modules1.erl | 16 -
.../whereis_diff_modules2.erl | 11 -
.../whereis_diff_modules1_pathsens.erl | 26 -
.../whereis_diff_modules2_pathsens.erl | 12 -
.../whereis_diff_modules1_rec.erl | 22 -
.../whereis_diff_modules2_rec.erl | 8 -
.../whereis_diff_modules3.erl | 8 -
.../whereis_diff_modules4.erl | 11 -
.../whereis_diff_modules3_pathsens.erl | 25 -
.../whereis_diff_modules4_pathsens.erl | 13 -
.../whereis_diff_modules3_rec.erl | 25 -
.../whereis_diff_modules4_rec.erl | 8 -
.../whereis_diff_modules5.erl | 23 -
.../whereis_diff_modules6.erl | 11 -
.../whereis_diff_modules1_nested.erl | 14 -
.../whereis_diff_modules2_nested.erl | 11 -
.../whereis_diff_modules3_nested.erl | 11 -
.../whereis_diff_modules1_twice.erl | 21 -
.../whereis_diff_modules2_twice.erl | 11 -
.../src/whereis_diff_vars_no_race.erl | 13 -
.../src/whereis_diff_vars_race.erl | 19 -
.../whereis_intra_inter_module1.erl | 19 -
.../whereis_intra_inter_module2.erl | 11 -
.../whereis_intra_inter_module3.erl | 16 -
.../whereis_intra_inter_module4.erl | 14 -
.../whereis_intra_inter_module5.erl | 19 -
.../whereis_intra_inter_module6.erl | 14 -
.../whereis_intra_inter_module7.erl | 11 -
.../whereis_intra_inter_module8.erl | 13 -
.../whereis_intra_inter_module10.erl | 16 -
.../whereis_intra_inter_module9.erl | 11 -
.../whereis_intra_inter_module11.erl | 27 -
.../whereis_intra_inter_module12.erl | 14 -
.../whereis_intra_inter_module13.erl | 19 -
.../whereis_intra_inter_module14.erl | 23 -
.../whereis_intra_inter_module15.erl | 19 -
.../whereis_intra_inter_module16.erl | 23 -
.../race_tests_SUITE_data/src/whereis_param.erl | 16 -
.../whereis_param_inter_module1.erl | 9 -
.../whereis_param_inter_module2.erl | 13 -
.../src/whereis_rec_function1.erl | 19 -
.../src/whereis_rec_function2.erl | 24 -
.../src/whereis_rec_function3.erl | 27 -
.../src/whereis_rec_function4.erl | 27 -
.../src/whereis_rec_function5.erl | 21 -
.../src/whereis_rec_function6.erl | 24 -
.../src/whereis_rec_function7.erl | 19 -
.../src/whereis_rec_function8.erl | 22 -
.../src/whereis_try_catch.erl | 25 -
.../race_tests_SUITE_data/src/whereis_vars1.erl | 17 -
.../race_tests_SUITE_data/src/whereis_vars10.erl | 22 -
.../race_tests_SUITE_data/src/whereis_vars11.erl | 22 -
.../race_tests_SUITE_data/src/whereis_vars12.erl | 22 -
.../race_tests_SUITE_data/src/whereis_vars13.erl | 22 -
.../race_tests_SUITE_data/src/whereis_vars14.erl | 22 -
.../race_tests_SUITE_data/src/whereis_vars15.erl | 23 -
.../race_tests_SUITE_data/src/whereis_vars16.erl | 23 -
.../race_tests_SUITE_data/src/whereis_vars17.erl | 23 -
.../race_tests_SUITE_data/src/whereis_vars18.erl | 22 -
.../race_tests_SUITE_data/src/whereis_vars19.erl | 23 -
.../race_tests_SUITE_data/src/whereis_vars2.erl | 18 -
.../race_tests_SUITE_data/src/whereis_vars20.erl | 22 -
.../race_tests_SUITE_data/src/whereis_vars21.erl | 23 -
.../race_tests_SUITE_data/src/whereis_vars22.erl | 27 -
.../race_tests_SUITE_data/src/whereis_vars3.erl | 18 -
.../race_tests_SUITE_data/src/whereis_vars4.erl | 18 -
.../race_tests_SUITE_data/src/whereis_vars5.erl | 22 -
.../race_tests_SUITE_data/src/whereis_vars6.erl | 22 -
.../race_tests_SUITE_data/src/whereis_vars7.erl | 22 -
.../race_tests_SUITE_data/src/whereis_vars8.erl | 22 -
.../race_tests_SUITE_data/src/whereis_vars9.erl | 22 -
.../test/small_SUITE_data/dialyzer_options | 1 +
.../test/small_SUITE_data/results/andalso_test | 0
.../test/small_SUITE_data/results/app_call | 3 +
.../test/small_SUITE_data/results/appmon_place | 0
lib/dialyzer/test/small_SUITE_data/results/areq | 2 +
.../test/small_SUITE_data/results/atom_call | 3 +
.../test/small_SUITE_data/results/atom_widen | 3 +
.../test/small_SUITE_data/results/bs_fail_constr | 9 +
lib/dialyzer/test/small_SUITE_data/results/bs_utf8 | 0
.../test/small_SUITE_data/results/cerl_hipeify | 4 +
.../test/small_SUITE_data/results/comm_layer | 2 +
.../test/small_SUITE_data/results/compare1 | 4 +
.../small_SUITE_data/results/confusing_warning | 2 +
.../test/small_SUITE_data/results/contract1 | 3 +
.../test/small_SUITE_data/results/contract2 | 2 +
.../test/small_SUITE_data/results/contract3 | 3 +
.../test/small_SUITE_data/results/contract5 | 2 +
lib/dialyzer/test/small_SUITE_data/results/eqeq | 2 +
.../test/small_SUITE_data/results/ets_select | 0
.../test/small_SUITE_data/results/exhaust_case | 3 +
.../test/small_SUITE_data/results/failing_guard1 | 4 +
lib/dialyzer/test/small_SUITE_data/results/flatten | 2 +
lib/dialyzer/test/small_SUITE_data/results/fun_app | 7 +
.../test/small_SUITE_data/results/fun_ref_match | 2 +
lib/dialyzer/test/small_SUITE_data/results/gencall | 4 +
lib/dialyzer/test/small_SUITE_data/results/gs_make | 0
.../test/small_SUITE_data/results/inf_loop2 | 4 +
.../test/small_SUITE_data/results/invalid_specs | 3 +
lib/dialyzer/test/small_SUITE_data/results/letrec1 | 0
.../test/small_SUITE_data/results/list_match | 2 +
lib/dialyzer/test/small_SUITE_data/results/lzip | 0
.../test/small_SUITE_data/results/make_tuple | 3 +
.../test/small_SUITE_data/results/minus_minus | 0
.../test/small_SUITE_data/results/mod_info | 0
.../test/small_SUITE_data/results/my_filter | 0
lib/dialyzer/test/small_SUITE_data/results/my_sofs | 3 +
.../test/small_SUITE_data/results/no_match | 4 +
.../test/small_SUITE_data/results/no_unused_fun | 0
.../test/small_SUITE_data/results/no_unused_fun2 | 0
.../test/small_SUITE_data/results/non_existing | 2 +
.../test/small_SUITE_data/results/not_guard_crash | 0
lib/dialyzer/test/small_SUITE_data/results/or_bug | 0
.../test/small_SUITE_data/results/orelsebug | 0
.../test/small_SUITE_data/results/orelsebug2 | 0
.../test/small_SUITE_data/results/overloaded1 | 3 +
.../test/small_SUITE_data/results/port_info_test | 6 +
.../small_SUITE_data/results/process_info_test | 0
lib/dialyzer/test/small_SUITE_data/results/pubsub | 0
.../test/small_SUITE_data/results/receive1 | 2 +
.../test/small_SUITE_data/results/record_construct | 7 +
.../test/small_SUITE_data/results/record_pat | 2 +
.../test/small_SUITE_data/results/record_send_test | 2 +
.../test/small_SUITE_data/results/record_test | 3 +
.../test/small_SUITE_data/results/recursive_types1 | 0
.../test/small_SUITE_data/results/recursive_types2 | 0
.../test/small_SUITE_data/results/recursive_types3 | 0
.../test/small_SUITE_data/results/recursive_types4 | 0
.../test/small_SUITE_data/results/recursive_types5 | 0
.../test/small_SUITE_data/results/recursive_types6 | 0
.../test/small_SUITE_data/results/recursive_types7 | 0
lib/dialyzer/test/small_SUITE_data/results/toth | 0
lib/dialyzer/test/small_SUITE_data/results/trec | 7 +
lib/dialyzer/test/small_SUITE_data/results/try1 | 0
lib/dialyzer/test/small_SUITE_data/results/tuple1 | 5 +
.../small_SUITE_data/results/unsafe_beamcode_bug | 0
.../test/small_SUITE_data/results/unused_cases | 4 +
.../test/small_SUITE_data/results/unused_clauses | 3 +
.../test/small_SUITE_data/results/zero_tuple | 5 +
.../test/small_SUITE_data/src/app_call.erl | 17 +
.../test/small_SUITE_data/src/appmon_place.erl | 70 +
lib/dialyzer/test/small_SUITE_data/src/areq.erl | 11 +
.../test/small_SUITE_data/src/atom_call.erl | 14 +
.../test/small_SUITE_data/src/atom_guard.erl | 8 +
.../test/small_SUITE_data/src/atom_widen.erl | 24 +
.../test/small_SUITE_data/src/bs_fail_constr.erl | 15 +
lib/dialyzer/test/small_SUITE_data/src/bs_utf8.erl | 27 +
.../test/small_SUITE_data/src/cerl_hipeify.erl | 684 +++
.../src/comm_layer/comm_acceptor.erl | 119 +
.../src/comm_layer/comm_connection.erl | 206 +
.../small_SUITE_data/src/comm_layer/comm_layer.erl | 83 +
.../small_SUITE_data/src/comm_layer/comm_layer.hrl | 29 +
.../src/comm_layer/comm_logger.erl | 143 +
.../small_SUITE_data/src/comm_layer/comm_port.erl | 240 +
.../src/comm_layer/comm_port_sup.erl | 88 +
.../test/small_SUITE_data/src/compare1.erl | 21 +
.../small_SUITE_data/src/confusing_warning.erl | 22 +
.../test/small_SUITE_data/src/contract2.erl | 18 +
.../test/small_SUITE_data/src/contract3.erl | 33 +
.../test/small_SUITE_data/src/contract5.erl | 15 +
.../test/small_SUITE_data/src/disj_norm_form.erl | 23 +
lib/dialyzer/test/small_SUITE_data/src/eqeq.erl | 15 +
.../test/small_SUITE_data/src/ets_select.erl | 11 +
.../test/small_SUITE_data/src/exhaust_case.erl | 23 +
.../test/small_SUITE_data/src/failing_guard1.erl | 15 +
lib/dialyzer/test/small_SUITE_data/src/flatten.erl | 18 +
lib/dialyzer/test/small_SUITE_data/src/fun_app.erl | 41 +
.../test/small_SUITE_data/src/fun_ref_match.erl | 21 +
.../test/small_SUITE_data/src/fun_ref_record.erl | 17 +
lib/dialyzer/test/small_SUITE_data/src/gencall.erl | 12 +
lib/dialyzer/test/small_SUITE_data/src/gs_make.erl | 260 +
.../test/small_SUITE_data/src/inf_loop2.erl | 23 +
.../src/invalid_specs/invalid_spec1.erl | 28 +
.../src/invalid_specs/invalid_spec2.erl | 11 +
lib/dialyzer/test/small_SUITE_data/src/letrec1.erl | 13 +
.../test/small_SUITE_data/src/list_match.erl | 20 +
lib/dialyzer/test/small_SUITE_data/src/lzip.erl | 8 +
.../test/small_SUITE_data/src/make_tuple.erl | 5 +
.../test/small_SUITE_data/src/minus_minus.erl | 8 +
.../test/small_SUITE_data/src/mod_info.erl | 5 +
.../test/small_SUITE_data/src/my_filter.erl | 17 +
lib/dialyzer/test/small_SUITE_data/src/my_sofs.erl | 83 +
.../test/small_SUITE_data/src/no_match.erl | 9 +
.../test/small_SUITE_data/src/no_unused_fun.erl | 20 +
.../test/small_SUITE_data/src/no_unused_fun2.erl | 20 +
.../test/small_SUITE_data/src/non_existing.erl | 13 +
.../test/small_SUITE_data/src/not_guard_crash.erl | 49 +
lib/dialyzer/test/small_SUITE_data/src/or_bug.erl | 24 +
.../test/small_SUITE_data/src/orelsebug.erl | 16 +
.../test/small_SUITE_data/src/orelsebug2.erl | 23 +
.../test/small_SUITE_data/src/overloaded1.erl | 31 +
.../test/small_SUITE_data/src/port_info_test.erl | 33 +
.../small_SUITE_data/src/process_info_test.erl | 20 +
.../small_SUITE_data/src/pubsub/pubsub_api.erl | 99 +
.../small_SUITE_data/src/pubsub/pubsub_publish.erl | 49 +
.../test/small_SUITE_data/src/receive1.erl | 16 +
.../test/small_SUITE_data/src/record_construct.erl | 21 +
.../test/small_SUITE_data/src/record_pat.erl | 15 +
.../test/small_SUITE_data/src/record_send_test.erl | 32 +
.../test/small_SUITE_data/src/record_test.erl | 22 +
.../test/small_SUITE_data/src/recursive_types1.erl | 10 +
.../test/small_SUITE_data/src/recursive_types2.erl | 12 +
.../test/small_SUITE_data/src/recursive_types3.erl | 15 +
.../test/small_SUITE_data/src/recursive_types4.erl | 13 +
.../test/small_SUITE_data/src/recursive_types5.erl | 13 +
.../test/small_SUITE_data/src/recursive_types6.erl | 17 +
.../test/small_SUITE_data/src/recursive_types7.erl | 13 +
.../test/small_SUITE_data/src/refine_bug1.erl | 11 +
lib/dialyzer/test/small_SUITE_data/src/toth.erl | 99 +
lib/dialyzer/test/small_SUITE_data/src/trec.erl | 37 +
lib/dialyzer/test/small_SUITE_data/src/try1.erl | 26 +
lib/dialyzer/test/small_SUITE_data/src/tuple1.erl | 29 +
.../small_SUITE_data/src/unsafe_beamcode_bug.erl | 14 +
.../test/small_SUITE_data/src/unused_cases.erl | 41 +
.../test/small_SUITE_data/src/unused_clauses.erl | 18 +
.../test/small_SUITE_data/src/zero_tuple.erl | 12 +
.../test/small_tests_SUITE_data/dialyzer_options | 1 -
.../small_tests_SUITE_data/results/andalso_test | 0
.../test/small_tests_SUITE_data/results/app_call | 3 -
.../small_tests_SUITE_data/results/appmon_place | 0
.../test/small_tests_SUITE_data/results/areq | 2 -
.../test/small_tests_SUITE_data/results/atom_call | 3 -
.../test/small_tests_SUITE_data/results/atom_widen | 3 -
.../small_tests_SUITE_data/results/bs_fail_constr | 9 -
.../test/small_tests_SUITE_data/results/bs_utf8 | 0
.../small_tests_SUITE_data/results/cerl_hipeify | 4 -
.../test/small_tests_SUITE_data/results/comm_layer | 2 -
.../test/small_tests_SUITE_data/results/compare1 | 4 -
.../results/confusing_warning | 2 -
.../test/small_tests_SUITE_data/results/contract1 | 3 -
.../test/small_tests_SUITE_data/results/contract2 | 2 -
.../test/small_tests_SUITE_data/results/contract3 | 3 -
.../test/small_tests_SUITE_data/results/contract5 | 2 -
.../test/small_tests_SUITE_data/results/eqeq | 2 -
.../test/small_tests_SUITE_data/results/ets_select | 0
.../small_tests_SUITE_data/results/exhaust_case | 3 -
.../small_tests_SUITE_data/results/failing_guard1 | 4 -
.../test/small_tests_SUITE_data/results/flatten | 2 -
.../test/small_tests_SUITE_data/results/fun_app | 7 -
.../small_tests_SUITE_data/results/fun_ref_match | 2 -
.../test/small_tests_SUITE_data/results/gencall | 4 -
.../test/small_tests_SUITE_data/results/gs_make | 0
.../test/small_tests_SUITE_data/results/inf_loop2 | 4 -
.../small_tests_SUITE_data/results/invalid_specs | 3 -
.../test/small_tests_SUITE_data/results/letrec1 | 0
.../test/small_tests_SUITE_data/results/list_match | 2 -
.../test/small_tests_SUITE_data/results/lzip | 0
.../test/small_tests_SUITE_data/results/make_tuple | 3 -
.../small_tests_SUITE_data/results/minus_minus | 0
.../test/small_tests_SUITE_data/results/mod_info | 0
.../test/small_tests_SUITE_data/results/my_filter | 0
.../test/small_tests_SUITE_data/results/my_sofs | 3 -
.../test/small_tests_SUITE_data/results/no_match | 4 -
.../small_tests_SUITE_data/results/no_unused_fun | 0
.../small_tests_SUITE_data/results/no_unused_fun2 | 0
.../small_tests_SUITE_data/results/non_existing | 2 -
.../small_tests_SUITE_data/results/not_guard_crash | 0
.../test/small_tests_SUITE_data/results/or_bug | 0
.../test/small_tests_SUITE_data/results/orelsebug | 0
.../test/small_tests_SUITE_data/results/orelsebug2 | 0
.../small_tests_SUITE_data/results/overloaded1 | 3 -
.../small_tests_SUITE_data/results/port_info_test | 6 -
.../results/process_info_test | 0
.../test/small_tests_SUITE_data/results/pubsub | 0
.../test/small_tests_SUITE_data/results/receive1 | 2 -
.../results/record_construct | 7 -
.../test/small_tests_SUITE_data/results/record_pat | 2 -
.../results/record_send_test | 2 -
.../small_tests_SUITE_data/results/record_test | 3 -
.../results/recursive_types1 | 0
.../results/recursive_types2 | 0
.../results/recursive_types3 | 0
.../results/recursive_types4 | 0
.../results/recursive_types5 | 0
.../results/recursive_types6 | 0
.../results/recursive_types7 | 0
.../test/small_tests_SUITE_data/results/toth | 0
.../test/small_tests_SUITE_data/results/trec | 7 -
.../test/small_tests_SUITE_data/results/try1 | 0
.../test/small_tests_SUITE_data/results/tuple1 | 5 -
.../results/unsafe_beamcode_bug | 0
.../small_tests_SUITE_data/results/unused_cases | 4 -
.../small_tests_SUITE_data/results/unused_clauses | 3 -
.../test/small_tests_SUITE_data/results/zero_tuple | 5 -
.../test/small_tests_SUITE_data/src/app_call.erl | 17 -
.../small_tests_SUITE_data/src/appmon_place.erl | 71 -
.../test/small_tests_SUITE_data/src/areq.erl | 12 -
.../test/small_tests_SUITE_data/src/atom_call.erl | 14 -
.../test/small_tests_SUITE_data/src/atom_guard.erl | 9 -
.../test/small_tests_SUITE_data/src/atom_widen.erl | 24 -
.../small_tests_SUITE_data/src/bs_fail_constr.erl | 16 -
.../test/small_tests_SUITE_data/src/bs_utf8.erl | 27 -
.../small_tests_SUITE_data/src/cerl_hipeify.erl | 684 ---
.../src/comm_layer/comm_acceptor.erl | 120 -
.../src/comm_layer/comm_connection.erl | 206 -
.../src/comm_layer/comm_layer.erl | 83 -
.../src/comm_layer/comm_layer.hrl | 30 -
.../src/comm_layer/comm_logger.erl | 143 -
.../src/comm_layer/comm_port.erl | 240 -
.../src/comm_layer/comm_port_sup.erl | 90 -
.../test/small_tests_SUITE_data/src/compare1.erl | 21 -
.../src/confusing_warning.erl | 22 -
.../test/small_tests_SUITE_data/src/contract2.erl | 18 -
.../test/small_tests_SUITE_data/src/contract3.erl | 34 -
.../test/small_tests_SUITE_data/src/contract5.erl | 15 -
.../small_tests_SUITE_data/src/disj_norm_form.erl | 23 -
.../test/small_tests_SUITE_data/src/eqeq.erl | 16 -
.../test/small_tests_SUITE_data/src/ets_select.erl | 12 -
.../small_tests_SUITE_data/src/exhaust_case.erl | 24 -
.../small_tests_SUITE_data/src/failing_guard1.erl | 16 -
.../test/small_tests_SUITE_data/src/flatten.erl | 18 -
.../test/small_tests_SUITE_data/src/fun_app.erl | 42 -
.../small_tests_SUITE_data/src/fun_ref_match.erl | 21 -
.../small_tests_SUITE_data/src/fun_ref_record.erl | 17 -
.../test/small_tests_SUITE_data/src/gencall.erl | 12 -
.../test/small_tests_SUITE_data/src/gs_make.erl | 261 -
.../test/small_tests_SUITE_data/src/inf_loop2.erl | 23 -
.../src/invalid_specs/invalid_spec1.erl | 28 -
.../src/invalid_specs/invalid_spec2.erl | 11 -
.../test/small_tests_SUITE_data/src/letrec1.erl | 13 -
.../test/small_tests_SUITE_data/src/list_match.erl | 20 -
.../test/small_tests_SUITE_data/src/lzip.erl | 8 -
.../test/small_tests_SUITE_data/src/make_tuple.erl | 5 -
.../small_tests_SUITE_data/src/minus_minus.erl | 8 -
.../test/small_tests_SUITE_data/src/mod_info.erl | 5 -
.../test/small_tests_SUITE_data/src/my_filter.erl | 17 -
.../test/small_tests_SUITE_data/src/my_sofs.erl | 83 -
.../test/small_tests_SUITE_data/src/no_match.erl | 9 -
.../small_tests_SUITE_data/src/no_unused_fun.erl | 20 -
.../small_tests_SUITE_data/src/no_unused_fun2.erl | 20 -
.../small_tests_SUITE_data/src/non_existing.erl | 13 -
.../small_tests_SUITE_data/src/not_guard_crash.erl | 49 -
.../test/small_tests_SUITE_data/src/or_bug.erl | 24 -
.../test/small_tests_SUITE_data/src/orelsebug.erl | 17 -
.../test/small_tests_SUITE_data/src/orelsebug2.erl | 23 -
.../small_tests_SUITE_data/src/overloaded1.erl | 31 -
.../small_tests_SUITE_data/src/port_info_test.erl | 34 -
.../src/process_info_test.erl | 21 -
.../src/pubsub/pubsub_api.erl | 99 -
.../src/pubsub/pubsub_publish.erl | 50 -
.../test/small_tests_SUITE_data/src/receive1.erl | 17 -
.../src/record_construct.erl | 22 -
.../test/small_tests_SUITE_data/src/record_pat.erl | 19 -
.../src/record_send_test.erl | 33 -
.../small_tests_SUITE_data/src/record_test.erl | 24 -
.../src/recursive_types1.erl | 10 -
.../src/recursive_types2.erl | 12 -
.../src/recursive_types3.erl | 15 -
.../src/recursive_types4.erl | 13 -
.../src/recursive_types5.erl | 13 -
.../src/recursive_types6.erl | 17 -
.../src/recursive_types7.erl | 13 -
.../small_tests_SUITE_data/src/refine_bug1.erl | 11 -
.../test/small_tests_SUITE_data/src/toth.erl | 99 -
.../test/small_tests_SUITE_data/src/trec.erl | 37 -
.../test/small_tests_SUITE_data/src/try1.erl | 27 -
.../test/small_tests_SUITE_data/src/tuple1.erl | 29 -
.../src/unsafe_beamcode_bug.erl | 15 -
.../small_tests_SUITE_data/src/unused_cases.erl | 41 -
.../small_tests_SUITE_data/src/unused_clauses.erl | 18 -
.../test/small_tests_SUITE_data/src/zero_tuple.erl | 13 -
lib/dialyzer/test/user_SUITE_data/dialyzer_options | 2 +
.../test/user_SUITE_data/results/broken_dialyzer | 0
.../test/user_SUITE_data/results/gcpFlowControl | 2 +
.../test/user_SUITE_data/results/qlc_error | 0
lib/dialyzer/test/user_SUITE_data/results/spvcOrig | 193 +
lib/dialyzer/test/user_SUITE_data/results/wsp_pdu | 25 +
.../test/user_SUITE_data/src/broken_dialyzer.erl | 130 +
lib/dialyzer/test/user_SUITE_data/src/gcp.hrl | 166 +
.../test/user_SUITE_data/src/gcpFlowControl.erl | 397 ++
.../test/user_SUITE_data/src/qlc_error.erl | 15 +
lib/dialyzer/test/user_SUITE_data/src/spvcOrig.erl | 3520 +++++++++++++
lib/dialyzer/test/user_SUITE_data/src/wdp.hrl | 96 +
lib/dialyzer/test/user_SUITE_data/src/wsp.hrl | 239 +
lib/dialyzer/test/user_SUITE_data/src/wsp_pdu.erl | 5423 +++++++++++++++++++
.../test/user_tests_SUITE_data/dialyzer_options | 2 -
.../user_tests_SUITE_data/results/broken_dialyzer | 0
.../user_tests_SUITE_data/results/gcpFlowControl | 2 -
.../test/user_tests_SUITE_data/results/qlc_error | 0
.../test/user_tests_SUITE_data/results/spvcOrig | 193 -
.../test/user_tests_SUITE_data/results/wsp_pdu | 25 -
.../user_tests_SUITE_data/src/broken_dialyzer.erl | 130 -
.../test/user_tests_SUITE_data/src/gcp.hrl | 166 -
.../user_tests_SUITE_data/src/gcpFlowControl.erl | 397 --
.../test/user_tests_SUITE_data/src/qlc_error.erl | 15 -
.../test/user_tests_SUITE_data/src/spvcOrig.erl | 3523 -------------
.../test/user_tests_SUITE_data/src/wdp.hrl | 97 -
.../test/user_tests_SUITE_data/src/wsp.hrl | 242 -
.../test/user_tests_SUITE_data/src/wsp_pdu.erl | 5423 -------------------
1265 files changed, 132602 insertions(+), 132944 deletions(-)
create mode 100644 lib/dialyzer/test/callgraph_SUITE_data/dialyzer_options
create mode 100644 lib/dialyzer/test/callgraph_SUITE_data/results/test_missing_functions
create mode 100644 lib/dialyzer/test/callgraph_SUITE_data/src/test_missing_functions/t1.erl
create mode 100644 lib/dialyzer/test/callgraph_SUITE_data/src/test_missing_functions/t2.erl
delete mode 100644 lib/dialyzer/test/callgraph_tests_SUITE_data/dialyzer_options
delete mode 100644 lib/dialyzer/test/callgraph_tests_SUITE_data/results/test_missing_functions
delete mode 100644 lib/dialyzer/test/callgraph_tests_SUITE_data/src/test_missing_functions/t1.erl
delete mode 100644 lib/dialyzer/test/callgraph_tests_SUITE_data/src/test_missing_functions/t2.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/dialyzer_options
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/results/array
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/results/crash
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/results/dict
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/results/ets
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/results/gb_sets
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/results/inf_loop1
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/results/int
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/results/mixed_opaque
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/results/my_digraph
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/results/my_queue
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/results/opaque
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/results/queue
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/results/rec
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/results/timer
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/results/union
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/results/wings
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/array/array_use.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/crash/crash_1.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/dict/dict_use.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/ets/ets_use.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/gb_sets/gb_sets_rec.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/inf_loop1.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/int/int_adt.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/int/int_use.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/mixed_opaque/mixed_opaque_queue_adt.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/mixed_opaque/mixed_opaque_rec_adt.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/mixed_opaque/mixed_opaque_use.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/my_digraph/my_digraph_adt.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/my_queue/my_queue_adt.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/my_queue/my_queue_use.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_adt.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug1.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug2.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug3.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug4.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/queue/queue_use.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/rec/rec_adt.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/rec/rec_use.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/timer/timer_use.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/union/union_adt.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/union/union_use.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/wings/wings.hrl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_dissolve.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_edge.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_edge_cmd.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_face.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_facemat.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_intl.hrl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_io.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_sel.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_shape.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_util.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_we.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis1.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis2.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis3.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis4.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis5.erl
create mode 100644 lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis6.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/dialyzer_options
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/results/array
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/results/crash
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/results/dict
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/results/ets
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/results/gb_sets
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/results/inf_loop1
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/results/int
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/results/mixed_opaque
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/results/my_digraph
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/results/my_queue
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/results/opaque
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/results/queue
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/results/rec
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/results/timer
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/results/union
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/results/wings
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/array/array_use.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/crash/crash_1.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/dict/dict_use.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/ets/ets_use.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/gb_sets/gb_sets_rec.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/inf_loop1.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/int/int_adt.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/int/int_use.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_queue_adt.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_rec_adt.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_use.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/my_digraph/my_digraph_adt.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/my_queue/my_queue_adt.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/my_queue/my_queue_use.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_adt.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug1.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug2.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug3.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug4.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/queue/queue_use.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/rec/rec_adt.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/rec/rec_use.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/timer/timer_use.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/union/union_adt.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/union/union_use.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings.hrl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_dissolve.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_edge.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_edge_cmd.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_face.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_facemat.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_intl.hrl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_io.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_sel.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_shape.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_util.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_we.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis1.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis2.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis3.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis4.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis5.erl
delete mode 100644 lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis6.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/dialyzer_options
create mode 100644 lib/dialyzer/test/options1_SUITE_data/my_include/CVS/Entries
create mode 100644 lib/dialyzer/test/options1_SUITE_data/my_include/CVS/Repository
create mode 100644 lib/dialyzer/test/options1_SUITE_data/my_include/CVS/Root
create mode 100644 lib/dialyzer/test/options1_SUITE_data/my_include/erl_bits.hrl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/my_include/erl_compile.hrl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/results/compiler
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_asm.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_block.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_bool.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_clean.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_dict.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_disasm.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_flatten.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_jump.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_listing.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_opcodes.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_opcodes.hrl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_type.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_validator.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/cerl.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/cerl_clauses.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/cerl_inline.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/cerl_trees.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/compile.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/core_lib.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/core_lint.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/core_parse.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/core_parse.hrl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/core_pp.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/core_scan.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/erl_bifs.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/rec_env.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/sys_expand_pmod.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/sys_pre_attributes.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/sys_pre_expand.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_codegen.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_core.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_kernel.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_kernel.hrl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_kernel_pp.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_life.erl
create mode 100644 lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_life.hrl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/dialyzer_options
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Entries
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Repository
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Root
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/my_include/erl_bits.hrl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/my_include/erl_compile.hrl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/results/compiler
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_asm.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_block.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_bool.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_clean.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_dict.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_disasm.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_flatten.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_jump.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_listing.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_opcodes.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_opcodes.hrl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_type.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_validator.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_clauses.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_inline.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_trees.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/compile.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_lib.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_lint.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_parse.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_parse.hrl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_pp.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_scan.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/erl_bifs.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/rec_env.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_expand_pmod.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_pre_attributes.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_pre_expand.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_codegen.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_core.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel.hrl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel_pp.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_life.erl
delete mode 100644 lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_life.hrl
create mode 100644 lib/dialyzer/test/options2_SUITE_data/dialyzer_options
create mode 100644 lib/dialyzer/test/options2_SUITE_data/results/kernel
create mode 100644 lib/dialyzer/test/options2_SUITE_data/src/kernel/global.erl
delete mode 100644 lib/dialyzer/test/options2_tests_SUITE_data/dialyzer_options
delete mode 100644 lib/dialyzer/test/options2_tests_SUITE_data/results/kernel
delete mode 100644 lib/dialyzer/test/options2_tests_SUITE_data/src/kernel/global.erl
create mode 100644 lib/dialyzer/test/plt_SUITE.erl
delete mode 100644 lib/dialyzer/test/plt_tests_SUITE.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/dialyzer_options
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/results/asn1
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/results/inets
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/results/mnesia
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/Makefile
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/Restrictions.txt
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1.app.src
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1.appup.src
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1_db.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1_records.hrl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_check.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_constructed_ber.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_constructed_ber_bin_v2.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_constructed_per.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen_ber.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen_ber_bin_v2.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen_per.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen_per_rt2ct.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_name.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_parser.yrl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_parser2.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_pretty_format.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_tok.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_value.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_check.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_driver_handler.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per_bin.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per_bin_rt2ct.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per_v1.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/notes_history.sgml
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/asn1/notes_latest.sgml
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/Makefile
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/ftp.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/http.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/http.hrl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/http_lib.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/httpc_handler.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/httpc_manager.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/httpd.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/httpd.hrl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/httpd_acceptor.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/httpd_acceptor_sup.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/httpd_conf.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/httpd_example.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/httpd_manager.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/httpd_misc_sup.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/httpd_parse.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/httpd_request_handler.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/httpd_response.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/httpd_socket.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/httpd_sup.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/httpd_util.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/httpd_verbosity.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/httpd_verbosity.hrl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/inets.app.src
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/inets.appup.src
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/inets.config
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/inets_sup.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/jnets_httpd.hrl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_actions.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_alias.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_auth.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_auth.hrl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_auth_dets.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_auth_mnesia.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_auth_plain.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_auth_server.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_browser.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_cgi.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_dir.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_disk_log.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_esi.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_get.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_head.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_htaccess.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_include.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_log.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_range.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_responsecontrol.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_security.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_security_server.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_trace.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/inets/uri.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/Makefile
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia.app.src
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia.appup.src
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia.hrl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_backup.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_bup.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_checkpoint.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_checkpoint_sup.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_controller.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_dumper.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_event.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_frag.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_frag_hash.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_frag_old_hash.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_index.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_kernel_sup.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_late_loader.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_lib.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_loader.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_locker.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_log.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_monitor.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_recover.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_registry.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_schema.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_snmp_hook.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_snmp_sup.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_sp.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_subscr.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_sup.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_text.erl
create mode 100644 lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_tm.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/dialyzer_options
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/results/asn1
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/results/inets
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/results/mnesia
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/Makefile
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/Restrictions.txt
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1.app.src
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1.appup.src
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1_db.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1_records.hrl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_check.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_constructed_ber.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_constructed_ber_bin_v2.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_constructed_per.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_ber.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_ber_bin_v2.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_per.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_per_rt2ct.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_name.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_parser.yrl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_parser2.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_pretty_format.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_tok.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_value.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_ber_bin.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_check.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_driver_handler.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per_bin.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per_bin_rt2ct.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per_v1.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/notes_history.sgml
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/notes_latest.sgml
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/Makefile
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/ftp.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/http.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/http.hrl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/http_lib.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpc_handler.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpc_manager.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd.hrl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_acceptor.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_acceptor_sup.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_conf.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_example.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_manager.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_misc_sup.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_parse.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_request_handler.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_response.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_socket.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_sup.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_util.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_verbosity.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_verbosity.hrl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets.app.src
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets.appup.src
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets.config
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets_sup.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/jnets_httpd.hrl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_actions.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_alias.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth.hrl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_dets.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_mnesia.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_plain.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_server.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_browser.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_cgi.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_dir.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_disk_log.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_esi.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_get.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_head.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_htaccess.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_include.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_log.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_range.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_responsecontrol.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_security.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_security_server.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_trace.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/uri.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/Makefile
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.app.src
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.appup.src
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.hrl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_backup.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_bup.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_checkpoint.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_checkpoint_sup.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_controller.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_dumper.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_event.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_frag.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_frag_hash.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_frag_old_hash.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_index.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_kernel_sup.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_late_loader.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_lib.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_loader.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_locker.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_log.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_monitor.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_recover.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_registry.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_schema.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_snmp_hook.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_snmp_sup.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_sp.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_subscr.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_sup.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_text.erl
delete mode 100644 lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_tm.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/dialyzer_options
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_args1
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_args2
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_args3
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_args4
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_args5
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_args6
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_args7
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_args8
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow1
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow2
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow3
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow4
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_control_flow5
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_diff_atoms_race1
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_diff_atoms_race2
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_diff_atoms_race3
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_diff_atoms_race4
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_diff_atoms_race5
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_diff_atoms_race6
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_double1
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_double2
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_funs1
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_funs2
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_new
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/ets_insert_param
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/extract_translations
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/mnesia_diff_atoms_race1
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/mnesia_diff_atoms_race2
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/mnesia_dirty_read_one_write_two
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/mnesia_dirty_read_two_write_one
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/mnesia_dirty_read_write_double1
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/mnesia_dirty_read_write_double2
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/mnesia_dirty_read_write_double3
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/mnesia_dirty_read_write_double4
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/mnesia_dirty_read_write_one
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/mnesia_dirty_read_write_two
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_control_flow1
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_control_flow2
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_control_flow3
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_control_flow4
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_control_flow5
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_control_flow6
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_atoms_no_race
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_atoms_race
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_functions1
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_functions1_nested
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_functions1_pathsens
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_functions1_twice
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_functions2
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_functions2_nested
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_functions2_pathsens
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_functions2_twice
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_functions3
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_functions3_nested
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_functions3_pathsens
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_functions4
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_functions5
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_functions6
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_modules1
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_modules1_pathsens
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_modules1_rec
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_modules2
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_modules2_pathsens
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_modules2_rec
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_modules3
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_modules_nested
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_modules_twice
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_vars_no_race
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_diff_vars_race
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_intra_inter_module1
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_intra_inter_module2
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_intra_inter_module3
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_intra_inter_module4
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_intra_inter_module5
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_intra_inter_module6
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_intra_inter_module7
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_intra_inter_module8
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_param
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_param_inter_module
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_rec_function1
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_rec_function2
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_rec_function3
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_rec_function4
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_rec_function5
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_rec_function6
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_rec_function7
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_rec_function8
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_try_catch
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars1
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars10
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars11
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars12
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars13
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars14
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars15
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars16
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars17
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars18
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars19
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars2
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars20
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars21
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars22
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars3
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars4
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars5
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars6
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars7
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars8
create mode 100644 lib/dialyzer/test/race_SUITE_data/results/whereis_vars9
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_args1.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_args2.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_args3.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_args4.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_args5.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_args6.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_args7.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_args8.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_control_flow1.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_control_flow2.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_control_flow3.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_control_flow4.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_control_flow5.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_diff_atoms_race1.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_diff_atoms_race2.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_diff_atoms_race3.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_diff_atoms_race4.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_diff_atoms_race5.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_diff_atoms_race6.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_double1.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_double2.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_funs1.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_funs2.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_new.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/ets_insert_param.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/extract_translations.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/mnesia_diff_atoms_race1.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/mnesia_diff_atoms_race2.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/mnesia_dirty_read_one_write_two.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/mnesia_dirty_read_two_write_one.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/mnesia_dirty_read_write_double1.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/mnesia_dirty_read_write_double2.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/mnesia_dirty_read_write_double3.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/mnesia_dirty_read_write_double4.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/mnesia_dirty_read_write_one.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/mnesia_dirty_read_write_two.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_control_flow1.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_control_flow2.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_control_flow3.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_control_flow4.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_control_flow5.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_control_flow6.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_atoms_no_race.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_atoms_race.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_functions1.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_functions1_nested.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_functions1_pathsens.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_functions1_twice.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_functions2.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_functions2_nested.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_functions2_pathsens.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_functions2_twice.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_functions3.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_functions3_nested.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_functions3_pathsens.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_functions4.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_functions5.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_functions6.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules1/whereis_diff_modules1.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules1/whereis_diff_modules2.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules1_pathsens/whereis_diff_modules1_pathsens.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules1_pathsens/whereis_diff_modules2_pathsens.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules1_rec/whereis_diff_modules1_rec.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules1_rec/whereis_diff_modules2_rec.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules2/whereis_diff_modules3.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules2/whereis_diff_modules4.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules2_pathsens/whereis_diff_modules3_pathsens.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules2_pathsens/whereis_diff_modules4_pathsens.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules2_rec/whereis_diff_modules3_rec.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules2_rec/whereis_diff_modules4_rec.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules3/whereis_diff_modules5.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules3/whereis_diff_modules6.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules_nested/whereis_diff_modules1_nested.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules_nested/whereis_diff_modules2_nested.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules_nested/whereis_diff_modules3_nested.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules_twice/whereis_diff_modules1_twice.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_modules_twice/whereis_diff_modules2_twice.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_vars_no_race.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_diff_vars_race.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_intra_inter_module1/whereis_intra_inter_module1.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_intra_inter_module1/whereis_intra_inter_module2.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_intra_inter_module2/whereis_intra_inter_module3.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_intra_inter_module2/whereis_intra_inter_module4.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_intra_inter_module3/whereis_intra_inter_module5.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_intra_inter_module3/whereis_intra_inter_module6.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_intra_inter_module4/whereis_intra_inter_module7.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_intra_inter_module4/whereis_intra_inter_module8.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_intra_inter_module5/whereis_intra_inter_module10.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_intra_inter_module5/whereis_intra_inter_module9.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_intra_inter_module6/whereis_intra_inter_module11.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_intra_inter_module6/whereis_intra_inter_module12.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_intra_inter_module7/whereis_intra_inter_module13.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_intra_inter_module7/whereis_intra_inter_module14.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_intra_inter_module8/whereis_intra_inter_module15.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_intra_inter_module8/whereis_intra_inter_module16.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_param.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_param_inter_module/whereis_param_inter_module1.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_param_inter_module/whereis_param_inter_module2.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_rec_function1.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_rec_function2.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_rec_function3.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_rec_function4.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_rec_function5.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_rec_function6.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_rec_function7.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_rec_function8.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_try_catch.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars1.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars10.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars11.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars12.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars13.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars14.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars15.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars16.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars17.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars18.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars19.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars2.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars20.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars21.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars22.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars3.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars4.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars5.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars6.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars7.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars8.erl
create mode 100644 lib/dialyzer/test/race_SUITE_data/src/whereis_vars9.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/dialyzer_options
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args1
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args2
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args3
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args4
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args5
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args6
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args7
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args8
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow1
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow2
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow3
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow4
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow5
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race1
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race2
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race3
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race4
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race5
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race6
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_double1
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_double2
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_funs1
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_funs2
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_new
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_param
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/extract_translations
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_diff_atoms_race1
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_diff_atoms_race2
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_one_write_two
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_two_write_one
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double1
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double2
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double3
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double4
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_one
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_two
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow1
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow2
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow3
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow4
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow5
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow6
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_atoms_no_race
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_atoms_race
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1_nested
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1_pathsens
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1_twice
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2_nested
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2_pathsens
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2_twice
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions3
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions3_nested
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions3_pathsens
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions4
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions5
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions6
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules1
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules1_pathsens
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules1_rec
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules2
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules2_pathsens
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules2_rec
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules3
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules_nested
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules_twice
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_vars_no_race
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_vars_race
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module1
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module2
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module3
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module4
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module5
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module6
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module7
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module8
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_param
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_param_inter_module
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function1
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function2
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function3
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function4
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function5
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function6
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function7
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function8
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_try_catch
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars1
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars10
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars11
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars12
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars13
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars14
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars15
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars16
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars17
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars18
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars19
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars2
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars20
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars21
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars22
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars3
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars4
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars5
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars6
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars7
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars8
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars9
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args1.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args2.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args3.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args4.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args5.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args6.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args7.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args8.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow1.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow2.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow3.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow4.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow5.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race1.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race2.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race3.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race4.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race5.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race6.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_double1.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_double2.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_funs1.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_funs2.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_new.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_param.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/extract_translations.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_diff_atoms_race1.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_diff_atoms_race2.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_one_write_two.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_two_write_one.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double1.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double2.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double3.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double4.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_one.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_two.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow1.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow2.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow3.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow4.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow5.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow6.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_atoms_no_race.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_atoms_race.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1_nested.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1_pathsens.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1_twice.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2_nested.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2_pathsens.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2_twice.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions3.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions3_nested.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions3_pathsens.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions4.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions5.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions6.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1/whereis_diff_modules1.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1/whereis_diff_modules2.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_pathsens/whereis_diff_modules1_pathsens.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_pathsens/whereis_diff_modules2_pathsens.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_rec/whereis_diff_modules1_rec.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_rec/whereis_diff_modules2_rec.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2/whereis_diff_modules3.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2/whereis_diff_modules4.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_pathsens/whereis_diff_modules3_pathsens.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_pathsens/whereis_diff_modules4_pathsens.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_rec/whereis_diff_modules3_rec.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_rec/whereis_diff_modules4_rec.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules3/whereis_diff_modules5.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules3/whereis_diff_modules6.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_nested/whereis_diff_modules1_nested.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_nested/whereis_diff_modules2_nested.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_nested/whereis_diff_modules3_nested.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_twice/whereis_diff_modules1_twice.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_twice/whereis_diff_modules2_twice.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_vars_no_race.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_vars_race.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module1/whereis_intra_inter_module1.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module1/whereis_intra_inter_module2.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module2/whereis_intra_inter_module3.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module2/whereis_intra_inter_module4.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module3/whereis_intra_inter_module5.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module3/whereis_intra_inter_module6.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module4/whereis_intra_inter_module7.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module4/whereis_intra_inter_module8.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module5/whereis_intra_inter_module10.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module5/whereis_intra_inter_module9.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module6/whereis_intra_inter_module11.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module6/whereis_intra_inter_module12.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module7/whereis_intra_inter_module13.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module7/whereis_intra_inter_module14.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module8/whereis_intra_inter_module15.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module8/whereis_intra_inter_module16.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_param.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_param_inter_module/whereis_param_inter_module1.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_param_inter_module/whereis_param_inter_module2.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function1.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function2.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function3.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function4.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function5.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function6.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function7.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function8.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_try_catch.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars1.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars10.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars11.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars12.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars13.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars14.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars15.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars16.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars17.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars18.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars19.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars2.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars20.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars21.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars22.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars3.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars4.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars5.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars6.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars7.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars8.erl
delete mode 100644 lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars9.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/dialyzer_options
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/andalso_test
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/app_call
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/appmon_place
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/areq
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/atom_call
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/atom_widen
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/bs_fail_constr
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/bs_utf8
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/cerl_hipeify
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/comm_layer
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/compare1
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/confusing_warning
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/contract1
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/contract2
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/contract3
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/contract5
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/eqeq
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/ets_select
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/exhaust_case
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/failing_guard1
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/flatten
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/fun_app
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/fun_ref_match
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/gencall
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/gs_make
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/inf_loop2
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/invalid_specs
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/letrec1
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/list_match
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/lzip
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/make_tuple
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/minus_minus
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/mod_info
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/my_filter
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/my_sofs
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/no_match
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/no_unused_fun
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/no_unused_fun2
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/non_existing
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/not_guard_crash
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/or_bug
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/orelsebug
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/orelsebug2
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/overloaded1
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/port_info_test
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/process_info_test
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/pubsub
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/receive1
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/record_construct
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/record_pat
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/record_send_test
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/record_test
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/recursive_types1
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/recursive_types2
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/recursive_types3
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/recursive_types4
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/recursive_types5
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/recursive_types6
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/recursive_types7
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/toth
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/trec
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/try1
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/tuple1
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/unsafe_beamcode_bug
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/unused_cases
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/unused_clauses
create mode 100644 lib/dialyzer/test/small_SUITE_data/results/zero_tuple
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/app_call.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/appmon_place.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/areq.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/atom_call.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/atom_guard.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/atom_widen.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/bs_fail_constr.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/bs_utf8.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/cerl_hipeify.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_acceptor.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_connection.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_layer.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_layer.hrl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_logger.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port_sup.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/compare1.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/confusing_warning.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/contract2.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/contract3.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/contract5.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/disj_norm_form.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/eqeq.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/ets_select.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/exhaust_case.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/failing_guard1.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/flatten.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/fun_app.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/fun_ref_match.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/fun_ref_record.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/gencall.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/gs_make.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/inf_loop2.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/invalid_specs/invalid_spec1.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/invalid_specs/invalid_spec2.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/letrec1.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/list_match.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/lzip.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/make_tuple.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/minus_minus.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/mod_info.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/my_filter.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/my_sofs.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/no_match.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/no_unused_fun.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/no_unused_fun2.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/non_existing.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/not_guard_crash.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/or_bug.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/orelsebug.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/orelsebug2.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/overloaded1.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/port_info_test.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/process_info_test.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/pubsub/pubsub_api.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/pubsub/pubsub_publish.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/receive1.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/record_construct.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/record_pat.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/record_send_test.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/record_test.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/recursive_types1.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/recursive_types2.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/recursive_types3.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/recursive_types4.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/recursive_types5.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/recursive_types6.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/recursive_types7.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/refine_bug1.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/toth.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/trec.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/try1.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/tuple1.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/unsafe_beamcode_bug.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/unused_cases.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/unused_clauses.erl
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/zero_tuple.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/dialyzer_options
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/andalso_test
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/app_call
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/appmon_place
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/areq
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/atom_call
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/atom_widen
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/bs_fail_constr
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/bs_utf8
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/cerl_hipeify
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/comm_layer
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/compare1
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/confusing_warning
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/contract1
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/contract2
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/contract3
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/contract5
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/eqeq
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/ets_select
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/exhaust_case
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/failing_guard1
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/flatten
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/fun_app
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/fun_ref_match
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/gencall
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/gs_make
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/inf_loop2
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/invalid_specs
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/letrec1
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/list_match
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/lzip
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/make_tuple
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/minus_minus
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/mod_info
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/my_filter
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/my_sofs
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/no_match
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/no_unused_fun
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/no_unused_fun2
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/non_existing
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/not_guard_crash
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/or_bug
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/orelsebug
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/orelsebug2
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/overloaded1
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/port_info_test
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/process_info_test
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/pubsub
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/receive1
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/record_construct
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/record_pat
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/record_send_test
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/record_test
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types1
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types2
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types3
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types4
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types5
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types6
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types7
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/toth
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/trec
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/try1
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/tuple1
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/unsafe_beamcode_bug
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/unused_cases
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/unused_clauses
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/results/zero_tuple
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/app_call.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/appmon_place.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/areq.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/atom_call.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/atom_guard.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/atom_widen.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/bs_fail_constr.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/bs_utf8.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/cerl_hipeify.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_acceptor.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_connection.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_layer.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_layer.hrl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_logger.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_port.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_port_sup.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/compare1.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/confusing_warning.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/contract2.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/contract3.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/contract5.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/disj_norm_form.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/eqeq.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/ets_select.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/exhaust_case.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/failing_guard1.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/flatten.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/fun_app.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/fun_ref_match.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/fun_ref_record.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/gencall.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/gs_make.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/inf_loop2.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/invalid_specs/invalid_spec1.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/invalid_specs/invalid_spec2.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/letrec1.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/list_match.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/lzip.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/make_tuple.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/minus_minus.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/mod_info.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/my_filter.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/my_sofs.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/no_match.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/no_unused_fun.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/no_unused_fun2.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/non_existing.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/not_guard_crash.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/or_bug.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/orelsebug.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/orelsebug2.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/overloaded1.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/port_info_test.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/process_info_test.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/pubsub/pubsub_api.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/pubsub/pubsub_publish.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/receive1.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/record_construct.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/record_pat.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/record_send_test.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/record_test.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types1.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types2.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types3.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types4.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types5.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types6.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types7.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/refine_bug1.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/toth.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/trec.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/try1.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/tuple1.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/unsafe_beamcode_bug.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/unused_cases.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/unused_clauses.erl
delete mode 100644 lib/dialyzer/test/small_tests_SUITE_data/src/zero_tuple.erl
create mode 100644 lib/dialyzer/test/user_SUITE_data/dialyzer_options
create mode 100644 lib/dialyzer/test/user_SUITE_data/results/broken_dialyzer
create mode 100644 lib/dialyzer/test/user_SUITE_data/results/gcpFlowControl
create mode 100644 lib/dialyzer/test/user_SUITE_data/results/qlc_error
create mode 100644 lib/dialyzer/test/user_SUITE_data/results/spvcOrig
create mode 100644 lib/dialyzer/test/user_SUITE_data/results/wsp_pdu
create mode 100644 lib/dialyzer/test/user_SUITE_data/src/broken_dialyzer.erl
create mode 100644 lib/dialyzer/test/user_SUITE_data/src/gcp.hrl
create mode 100644 lib/dialyzer/test/user_SUITE_data/src/gcpFlowControl.erl
create mode 100644 lib/dialyzer/test/user_SUITE_data/src/qlc_error.erl
create mode 100644 lib/dialyzer/test/user_SUITE_data/src/spvcOrig.erl
create mode 100644 lib/dialyzer/test/user_SUITE_data/src/wdp.hrl
create mode 100644 lib/dialyzer/test/user_SUITE_data/src/wsp.hrl
create mode 100644 lib/dialyzer/test/user_SUITE_data/src/wsp_pdu.erl
delete mode 100644 lib/dialyzer/test/user_tests_SUITE_data/dialyzer_options
delete mode 100644 lib/dialyzer/test/user_tests_SUITE_data/results/broken_dialyzer
delete mode 100644 lib/dialyzer/test/user_tests_SUITE_data/results/gcpFlowControl
delete mode 100644 lib/dialyzer/test/user_tests_SUITE_data/results/qlc_error
delete mode 100644 lib/dialyzer/test/user_tests_SUITE_data/results/spvcOrig
delete mode 100644 lib/dialyzer/test/user_tests_SUITE_data/results/wsp_pdu
delete mode 100644 lib/dialyzer/test/user_tests_SUITE_data/src/broken_dialyzer.erl
delete mode 100644 lib/dialyzer/test/user_tests_SUITE_data/src/gcp.hrl
delete mode 100644 lib/dialyzer/test/user_tests_SUITE_data/src/gcpFlowControl.erl
delete mode 100644 lib/dialyzer/test/user_tests_SUITE_data/src/qlc_error.erl
delete mode 100644 lib/dialyzer/test/user_tests_SUITE_data/src/spvcOrig.erl
delete mode 100644 lib/dialyzer/test/user_tests_SUITE_data/src/wdp.hrl
delete mode 100644 lib/dialyzer/test/user_tests_SUITE_data/src/wsp.hrl
delete mode 100644 lib/dialyzer/test/user_tests_SUITE_data/src/wsp_pdu.erl
(limited to 'lib/dialyzer/test')
diff --git a/lib/dialyzer/test/callgraph_SUITE_data/dialyzer_options b/lib/dialyzer/test/callgraph_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..50991c9bc5
--- /dev/null
+++ b/lib/dialyzer/test/callgraph_SUITE_data/dialyzer_options
@@ -0,0 +1 @@
+{dialyzer_options, []}.
diff --git a/lib/dialyzer/test/callgraph_SUITE_data/results/test_missing_functions b/lib/dialyzer/test/callgraph_SUITE_data/results/test_missing_functions
new file mode 100644
index 0000000000..4150bdb7c0
--- /dev/null
+++ b/lib/dialyzer/test/callgraph_SUITE_data/results/test_missing_functions
@@ -0,0 +1,3 @@
+
+t1.erl:16: Call to missing or unexported function t2:t2/1
+t2.erl:13: Call to missing or unexported function t1:t3/1
diff --git a/lib/dialyzer/test/callgraph_SUITE_data/src/test_missing_functions/t1.erl b/lib/dialyzer/test/callgraph_SUITE_data/src/test_missing_functions/t1.erl
new file mode 100644
index 0000000000..05ba9b0f93
--- /dev/null
+++ b/lib/dialyzer/test/callgraph_SUITE_data/src/test_missing_functions/t1.erl
@@ -0,0 +1,16 @@
+%%%-------------------------------------------------------------------
+%%% File : t1.erl
+%%% Author : Tobias Lindahl
+%%% Description :
+%%%
+%%% Created : 26 Jul 2006 by Tobias Lindahl
+%%%-------------------------------------------------------------------
+-module(t1).
+
+-export([t1/1, t2/1]).
+
+t1(X) ->
+ t2:t1(X).
+
+t2(X) ->
+ t2:t2(X).
diff --git a/lib/dialyzer/test/callgraph_SUITE_data/src/test_missing_functions/t2.erl b/lib/dialyzer/test/callgraph_SUITE_data/src/test_missing_functions/t2.erl
new file mode 100644
index 0000000000..bf940fd181
--- /dev/null
+++ b/lib/dialyzer/test/callgraph_SUITE_data/src/test_missing_functions/t2.erl
@@ -0,0 +1,16 @@
+%%%-------------------------------------------------------------------
+%%% File : t2.erl
+%%% Author : Tobias Lindahl
+%%% Description :
+%%%
+%%% Created : 26 Jul 2006 by Tobias Lindahl
+%%%-------------------------------------------------------------------
+-module(t2).
+
+-export([t1/1]).
+
+t1(X) ->
+ t1:t3(X) + t2(X).
+
+t2(X) ->
+ X + 1.
diff --git a/lib/dialyzer/test/callgraph_tests_SUITE_data/dialyzer_options b/lib/dialyzer/test/callgraph_tests_SUITE_data/dialyzer_options
deleted file mode 100644
index 50991c9bc5..0000000000
--- a/lib/dialyzer/test/callgraph_tests_SUITE_data/dialyzer_options
+++ /dev/null
@@ -1 +0,0 @@
-{dialyzer_options, []}.
diff --git a/lib/dialyzer/test/callgraph_tests_SUITE_data/results/test_missing_functions b/lib/dialyzer/test/callgraph_tests_SUITE_data/results/test_missing_functions
deleted file mode 100644
index 4150bdb7c0..0000000000
--- a/lib/dialyzer/test/callgraph_tests_SUITE_data/results/test_missing_functions
+++ /dev/null
@@ -1,3 +0,0 @@
-
-t1.erl:16: Call to missing or unexported function t2:t2/1
-t2.erl:13: Call to missing or unexported function t1:t3/1
diff --git a/lib/dialyzer/test/callgraph_tests_SUITE_data/src/test_missing_functions/t1.erl b/lib/dialyzer/test/callgraph_tests_SUITE_data/src/test_missing_functions/t1.erl
deleted file mode 100644
index 3b320e1ed4..0000000000
--- a/lib/dialyzer/test/callgraph_tests_SUITE_data/src/test_missing_functions/t1.erl
+++ /dev/null
@@ -1,16 +0,0 @@
-%%%-------------------------------------------------------------------
-%%% File : t1.erl
-%%% Author : Tobias Lindahl
-%%% Description :
-%%%
-%%% Created : 26 Jul 2006 by Tobias Lindahl
-%%%-------------------------------------------------------------------
--module(t1).
-
--export([t1/1, t2/1]).
-
-t1(X) ->
- t2:t1(X).
-
-t2(X) ->
- t2:t2(X).
diff --git a/lib/dialyzer/test/callgraph_tests_SUITE_data/src/test_missing_functions/t2.erl b/lib/dialyzer/test/callgraph_tests_SUITE_data/src/test_missing_functions/t2.erl
deleted file mode 100644
index 5ac8aa328c..0000000000
--- a/lib/dialyzer/test/callgraph_tests_SUITE_data/src/test_missing_functions/t2.erl
+++ /dev/null
@@ -1,16 +0,0 @@
-%%%-------------------------------------------------------------------
-%%% File : t2.erl
-%%% Author : Tobias Lindahl
-%%% Description :
-%%%
-%%% Created : 26 Jul 2006 by Tobias Lindahl
-%%%-------------------------------------------------------------------
--module(t2).
-
--export([t1/1]).
-
-t1(X) ->
- t1:t3(X) + t2(X).
-
-t2(X) ->
- X + 1.
diff --git a/lib/dialyzer/test/dialyzer_common.erl b/lib/dialyzer/test/dialyzer_common.erl
index a24ac96bd7..51766a4604 100644
--- a/lib/dialyzer/test/dialyzer_common.erl
+++ b/lib/dialyzer/test/dialyzer_common.erl
@@ -11,7 +11,7 @@
-include_lib("kernel/include/file.hrl").
--define(suite_suffix, "_tests_SUITE").
+-define(suite_suffix, "_SUITE").
-define(data_folder, "_data").
-define(suite_data, ?suite_suffix ++ ?data_folder).
-define(erlang_extension, ".erl").
diff --git a/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options b/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..3ff26b87db
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options
@@ -0,0 +1 @@
+{dialyzer_options, [{warnings, [no_unused, no_return]}]}.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/array b/lib/dialyzer/test/opaque_SUITE_data/results/array
new file mode 100644
index 0000000000..b05d088a03
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/array
@@ -0,0 +1,3 @@
+
+array_use.erl:12: The type test is_tuple(array()) breaks the opaqueness of the term array()
+array_use.erl:9: The attempt to match a term of type array() against the pattern {'array', _, _, 'undefined', _} breaks the opaqueness of the term
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/crash b/lib/dialyzer/test/opaque_SUITE_data/results/crash
new file mode 100644
index 0000000000..6bdd934169
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/crash
@@ -0,0 +1,7 @@
+
+crash_1.erl:42: The specification for crash_1:empty/0 states that the function might also return crash_1:targetlist() but the inferred return is none()
+crash_1.erl:45: Record construction #targetlist{list::[]} violates the declared type of field list::'undefined' | crash_1:target()
+crash_1.erl:48: The call crash_1:get_using_branch2(Branch::maybe_improper_list(),L::'undefined' | crash_1:target()) contains an opaque term as 2nd argument when terms of different types are expected in these positions
+crash_1.erl:50: The pattern <_Branch, []> can never match the type
+crash_1.erl:52: The pattern can never match the type
+crash_1.erl:54: The pattern can never match the type
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/dict b/lib/dialyzer/test/opaque_SUITE_data/results/dict
new file mode 100644
index 0000000000..5c6bf6a927
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/dict
@@ -0,0 +1,15 @@
+
+dict_use.erl:41: The attempt to match a term of type dict() against the pattern 'gazonk' breaks the opaqueness of the term
+dict_use.erl:45: The attempt to match a term of type dict() against the pattern [] breaks the opaqueness of the term
+dict_use.erl:46: The attempt to match a term of type dict() against the pattern 42 breaks the opaqueness of the term
+dict_use.erl:51: The attempt to match a term of type dict() against the pattern [] breaks the opaqueness of the term
+dict_use.erl:52: The attempt to match a term of type dict() against the pattern 42 breaks the opaqueness of the term
+dict_use.erl:58: Attempt to test for equality between a term of type maybe_improper_list() and a term of opaque type dict()
+dict_use.erl:60: Attempt to test for inequality between a term of type atom() and a term of opaque type dict()
+dict_use.erl:64: Guard test length(D::dict()) breaks the opaqueness of its argument
+dict_use.erl:65: Guard test is_atom(D::dict()) breaks the opaqueness of its argument
+dict_use.erl:66: Guard test is_list(D::dict()) breaks the opaqueness of its argument
+dict_use.erl:70: The type test is_list(dict()) breaks the opaqueness of the term dict()
+dict_use.erl:73: The call dict:fetch('foo',[1 | 2 | 3,...]) does not have an opaque term of type dict() as 2nd argument
+dict_use.erl:76: The call dict:merge(Fun::any(),42,[1 | 2,...]) does not have opaque terms as 2nd and 3rd arguments
+dict_use.erl:79: The call dict:store(42,'elli',{'dict',0,16,16,8,80,48,{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},{{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}}}) does not have an opaque term of type dict() as 3rd argument
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/ets b/lib/dialyzer/test/opaque_SUITE_data/results/ets
new file mode 100644
index 0000000000..5498ba1538
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/ets
@@ -0,0 +1,3 @@
+
+ets_use.erl:12: Guard test is_integer(T::atom() | tid()) breaks the opaqueness of its argument
+ets_use.erl:7: Guard test is_integer(T::tid()) breaks the opaqueness of its argument
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/gb_sets b/lib/dialyzer/test/opaque_SUITE_data/results/gb_sets
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop1 b/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop1
new file mode 100644
index 0000000000..eb8f304905
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/inf_loop1
@@ -0,0 +1,5 @@
+
+inf_loop1.erl:119: The pattern [{_, LNorms}] can never match the type []
+inf_loop1.erl:121: The pattern [{LinksA, LNormA}, {LinksB, LNormB}] can never match the type []
+inf_loop1.erl:129: The pattern [{_, Norm} | _] can never match the type []
+inf_loop1.erl:71: The call gb_trees:get(Edge::any(),Etab::array()) contains an opaque term as 2nd argument when terms of different types are expected in these positions
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/int b/lib/dialyzer/test/opaque_SUITE_data/results/int
new file mode 100644
index 0000000000..3ee4def34b
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/int
@@ -0,0 +1,3 @@
+
+int_adt.erl:28: Invalid type specification for function int_adt:add_f/2. The success typing is (number(),float()) -> number()
+int_adt.erl:32: Invalid type specification for function int_adt:div_f/2. The success typing is (number(),number()) -> float()
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/mixed_opaque b/lib/dialyzer/test/opaque_SUITE_data/results/mixed_opaque
new file mode 100644
index 0000000000..ab850b613e
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/mixed_opaque
@@ -0,0 +1,2 @@
+
+mixed_opaque_use.erl:31: The call mixed_opaque_rec_adt:get_a(Q::mixed_opaque_queue_adt:my_queue()) contains an opaque term as 1st argument when an opaque term of type mixed_opaque_rec_adt:rec() is expected
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/my_digraph b/lib/dialyzer/test/opaque_SUITE_data/results/my_digraph
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/my_queue b/lib/dialyzer/test/opaque_SUITE_data/results/my_queue
new file mode 100644
index 0000000000..2860b91084
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/my_queue
@@ -0,0 +1,7 @@
+
+my_queue_use.erl:15: The call my_queue_adt:is_empty([]) does not have an opaque term of type my_queue_adt:my_queue() as 1st argument
+my_queue_use.erl:19: The call my_queue_adt:add(42,Q0::[]) does not have an opaque term of type my_queue_adt:my_queue() as 2nd argument
+my_queue_use.erl:24: The attempt to match a term of type my_queue_adt:my_queue() against the pattern [42 | Q2] breaks the opaqueness of the term
+my_queue_use.erl:30: Attempt to test for equality between a term of type [] and a term of opaque type my_queue_adt:my_queue()
+my_queue_use.erl:34: Cons will produce an improper list since its 2nd argument is my_queue_adt:my_queue()
+my_queue_use.erl:34: The call my_queue_adt:dequeue(nonempty_improper_list(42,my_queue_adt:my_queue())) does not have an opaque term of type my_queue_adt:my_queue() as 1st argument
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/opaque b/lib/dialyzer/test/opaque_SUITE_data/results/opaque
new file mode 100644
index 0000000000..ca76f57b54
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/opaque
@@ -0,0 +1,2 @@
+
+opaque_bug4.erl:20: The attempt to match a term of type opaque_adt:abc() against the pattern 'a' breaks the opaqueness of the term
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/queue b/lib/dialyzer/test/opaque_SUITE_data/results/queue
new file mode 100644
index 0000000000..59ce33f098
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/queue
@@ -0,0 +1,11 @@
+
+queue_use.erl:18: The call queue:is_empty({[],[]}) does not have an opaque term of type queue() as 1st argument
+queue_use.erl:22: The call queue:in(42,Q0::{[],[]}) does not have an opaque term of type queue() as 2nd argument
+queue_use.erl:27: The attempt to match a term of type queue() against the pattern {"*", Q2} breaks the opaqueness of the term
+queue_use.erl:33: Attempt to test for equality between a term of type {[42,...],[]} and a term of opaque type queue()
+queue_use.erl:36: The attempt to match a term of type queue() against the pattern {F, _R} breaks the opaqueness of the term
+queue_use.erl:40: The call queue:out({[42,...],[]}) does not have an opaque term of type queue() as 1st argument
+queue_use.erl:51: The call queue_use:is_in_queue(E::42,DB::#db{p::[],q::queue()}) contains an opaque term as 2nd argument when terms of different types are expected in these positions
+queue_use.erl:56: The attempt to match a term of type #db{p::[],q::queue()} against the pattern {'db', _, {L1, L2}} breaks the opaqueness of queue()
+queue_use.erl:62: The call queue_use:tuple_queue({42,'gazonk'}) does not have a term of type {_,queue()} (with opaque subterms) as 1st argument
+queue_use.erl:65: The call queue:in(F::42,Q::'gazonk') does not have an opaque term of type queue() as 2nd argument
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/rec b/lib/dialyzer/test/opaque_SUITE_data/results/rec
new file mode 100644
index 0000000000..72736b3b3c
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/rec
@@ -0,0 +1,6 @@
+
+rec_use.erl:17: The attempt to match a term of type rec_adt:rec() against the pattern {'rec', _, 42} breaks the opaqueness of the term
+rec_use.erl:18: Guard test tuple_size(R::rec_adt:rec()) breaks the opaqueness of its argument
+rec_use.erl:23: The call rec_adt:get_a(R::tuple()) does not have an opaque term of type rec_adt:rec() as 1st argument
+rec_use.erl:27: Attempt to test for equality between a term of type {'rec','gazonk',42} and a term of opaque type rec_adt:rec()
+rec_use.erl:30: The call erlang:tuple_size(rec_adt:rec()) contains an opaque term as 1st argument when a structured term of type tuple() is expected
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/timer b/lib/dialyzer/test/opaque_SUITE_data/results/timer
new file mode 100644
index 0000000000..e917b76b08
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/timer
@@ -0,0 +1,4 @@
+
+timer_use.erl:16: The pattern 'gazonk' can never match the type {'error',_} | {'ok',timer:tref()}
+timer_use.erl:17: The attempt to match a term of type {'ok',timer:tref()} against the pattern {'ok', 42} breaks the opaqueness of timer:tref()
+timer_use.erl:18: The attempt to match a term of type {'error',_} | {'ok',timer:tref()} against the pattern {Tag, 'gazonk'} breaks the opaqueness of timer:tref()
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/union b/lib/dialyzer/test/opaque_SUITE_data/results/union
new file mode 100644
index 0000000000..98829b424a
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/union
@@ -0,0 +1,5 @@
+
+union_use.erl:12: The attempt to match a term of type union_adt:u() against the pattern 'aaa' breaks the opaqueness of the term
+union_use.erl:16: The type test is_tuple(union_adt:u()) breaks the opaqueness of the term union_adt:u()
+union_use.erl:7: Guard test is_atom(A::union_adt:u()) breaks the opaqueness of its argument
+union_use.erl:8: Guard test is_tuple(T::union_adt:u()) breaks the opaqueness of its argument
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/wings b/lib/dialyzer/test/opaque_SUITE_data/results/wings
new file mode 100644
index 0000000000..a9571441f8
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/wings
@@ -0,0 +1,11 @@
+
+wings_dissolve.erl:103: Guard test is_list(List::gb_set()) breaks the opaqueness of its argument
+wings_dissolve.erl:19: Guard test is_list(Faces::gb_set()) breaks the opaqueness of its argument
+wings_dissolve.erl:272: Guard test is_list(Faces::gb_set()) breaks the opaqueness of its argument
+wings_dissolve.erl:31: The call gb_sets:is_empty(Faces::[any(),...]) does not have an opaque term of type gb_set() as 1st argument
+wings_edge.erl:205: The pattern can never match the type <_,'soft',_>
+wings_edge_cmd.erl:30: The call gb_trees:size(P::gb_set()) contains an opaque term as 1st argument when an opaque term of type gb_tree() is expected
+wings_edge_cmd.erl:32: The pattern [_ | Parts] can never match the type []
+wings_edge_cmd.erl:32: The pattern [{_, P} | _] can never match the type []
+wings_io.erl:30: The attempt to match a term of type {'empty',queue()} against the pattern {'empty', {In, Out}} breaks the opaqueness of queue()
+wings_we.erl:155: The call wings_util:gb_trees_largest_key(Etab::gb_tree()) contains an opaque term as 1st argument when a structured term of type {_,{_,_,_,'nil' | {_,_,_,'nil' | {_,_,_,_}}}} is expected
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/array/array_use.erl b/lib/dialyzer/test/opaque_SUITE_data/src/array/array_use.erl
new file mode 100644
index 0000000000..1702dc8f03
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/array/array_use.erl
@@ -0,0 +1,15 @@
+-module(array_use).
+
+-export([ok1/0, wrong1/0, wrong2/0]).
+
+ok1() ->
+ array:set(17, gazonk, array:new()).
+
+wrong1() ->
+ {array, _, _, undefined, _} = array:new(42).
+
+wrong2() ->
+ case is_tuple(array:new(42)) of
+ true -> structure_is_exposed;
+ false -> cannot_possibly_be
+ end.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/crash/crash_1.erl b/lib/dialyzer/test/opaque_SUITE_data/src/crash/crash_1.erl
new file mode 100644
index 0000000000..eebeed15af
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/crash/crash_1.erl
@@ -0,0 +1,55 @@
+%%%-------------------------------------------------------------------
+%%% From : Fredrik Thulin
+%%%
+%%% A module with an erroneous record field declaration which mixes up
+%%% structured and opaque terms and causes a crash in dialyzer.
+%%%
+%%% In addition, it revealed that the compiler produced extraneous
+%%% warnings about unused record definitions when in fact they are
+%%% needed for type declarations. This is now fixed.
+%%%-------------------------------------------------------------------
+-module(crash_1).
+
+-export([add/3, empty/0]).
+
+%%--------------------------------------------------------------------
+
+-record(sipurl, {proto = "sip" :: string(), host :: string()}).
+-record(keylist, {list = [] :: [_]}).
+-type sip_headers() :: #keylist{}.
+-record(request, {uri :: #sipurl{}, header :: sip_headers()}).
+-type sip_request() :: #request{}.
+
+%%--------------------------------------------------------------------
+
+-record(target, {branch :: string(), request :: sip_request()}).
+-opaque target() :: #target{}.
+
+-record(targetlist, {list :: target()}). % XXX: THIS ONE SHOULD READ [target()]
+-opaque targetlist() :: #targetlist{}.
+
+%%====================================================================
+
+add(Branch, #request{} = Request, #targetlist{list = L} = TargetList) ->
+ case get_using_branch(Branch, TargetList) of
+ none ->
+ NewTarget = #target{branch = Branch, request = Request},
+ #targetlist{list = L ++ [NewTarget]};
+ #target{} ->
+ TargetList
+ end.
+
+-spec empty() -> targetlist().
+
+empty() ->
+ #targetlist{list = []}.
+
+get_using_branch(Branch, #targetlist{list = L}) when is_list(Branch) ->
+ get_using_branch2(Branch, L).
+
+get_using_branch2(_Branch, []) ->
+ none;
+get_using_branch2(Branch, [#target{branch=Branch}=H | _T]) ->
+ H;
+get_using_branch2(Branch, [#target{} | T]) ->
+ get_using_branch2(Branch, T).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/dict/dict_use.erl b/lib/dialyzer/test/opaque_SUITE_data/src/dict/dict_use.erl
new file mode 100644
index 0000000000..8a2cd86f43
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/dict/dict_use.erl
@@ -0,0 +1,82 @@
+-module(dict_use).
+
+-export([ok1/0, ok2/0, ok3/0, ok4/0, ok5/0, ok6/0]).
+-export([middle/0]).
+-export([w1/0, w2/0, w3/0, w4/1, w5/0, w6/0, w7/0, w8/1, w9/0]).
+
+-define(DICT, dict).
+
+%%---------------------------------------------------------------------
+%% Cases that are OK
+%%---------------------------------------------------------------------
+
+ok1() ->
+ dict:new().
+
+ok2() ->
+ case dict:new() of X -> X end.
+
+ok3() ->
+ Dict1 = dict:new(),
+ Dict2 = dict:new(),
+ Dict1 =:= Dict2.
+
+ok4() ->
+ dict:fetch(foo, dict:new()).
+
+ok5() -> % this is OK since some_mod:new/0 might be returning a dict()
+ dict:fetch(foo, some_mod:new()).
+
+ok6() ->
+ dict:store(42, elli, dict:new()).
+
+middle() ->
+ {w1(), w2()}.
+
+%%---------------------------------------------------------------------
+%% Cases that are problematic w.r.t. opaqueness of types
+%%---------------------------------------------------------------------
+
+w1() ->
+ gazonk = dict:new().
+
+w2() ->
+ case dict:new() of
+ [] -> nil;
+ 42 -> weird
+ end.
+
+w3() ->
+ try dict:new() of
+ [] -> nil;
+ 42 -> weird
+ catch
+ _:_ -> exception
+ end.
+
+w4(Dict) when is_list(Dict) ->
+ Dict =:= dict:new();
+w4(Dict) when is_atom(Dict) ->
+ Dict =/= dict:new().
+
+w5() ->
+ case dict:new() of
+ D when length(D) =/= 42 -> weird;
+ D when is_atom(D) -> weirder;
+ D when is_list(D) -> gazonk
+ end.
+
+w6() ->
+ is_list(dict:new()).
+
+w7() ->
+ dict:fetch(foo, [1,2,3]).
+
+w8(Fun) ->
+ dict:merge(Fun, 42, [1,2]).
+
+w9() ->
+ dict:store(42, elli,
+ {dict,0,16,16,8,80,48,
+ {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
+ {{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}}}).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/ets/ets_use.erl b/lib/dialyzer/test/opaque_SUITE_data/src/ets/ets_use.erl
new file mode 100644
index 0000000000..d65af0af4e
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/ets/ets_use.erl
@@ -0,0 +1,16 @@
+-module(ets_use).
+-export([t1/0, t2/0]).
+
+t1() ->
+ case n() of
+ T when is_atom(T) -> atm;
+ T when is_integer(T) -> int
+ end.
+
+t2() ->
+ case n() of
+ T when is_integer(T) -> int;
+ T when is_atom(T) -> atm
+ end.
+
+n() -> ets:new(n, [named_table]).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/gb_sets/gb_sets_rec.erl b/lib/dialyzer/test/opaque_SUITE_data/src/gb_sets/gb_sets_rec.erl
new file mode 100644
index 0000000000..008b0a486a
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/gb_sets/gb_sets_rec.erl
@@ -0,0 +1,23 @@
+%%---------------------------------------------------------------------
+%% This module does not test gb_sets. Instead it tests that we can
+%% create records whose fields are declared with an opaque type and
+%% retrieve these fields without problems. Unitialized record fields
+%% used to cause trouble for the analysis due to the implicit
+%% 'undefined' value that record fields contain. The problem was the
+%% strange interaction of ?opaque() and ?union() in the definition of
+%% erl_types:t_inf/3. This was fixed 18/1/2009.
+%% --------------------------------------------------------------------
+
+-module(gb_sets_rec).
+
+-export([new/0, get_g/1]).
+
+-record(rec, {g :: gb_set()}).
+
+-spec new() -> #rec{}.
+new() ->
+ #rec{g = gb_sets:empty()}.
+
+-spec get_g(#rec{}) -> gb_set().
+get_g(R) ->
+ R#rec.g.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop1.erl b/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop1.erl
new file mode 100644
index 0000000000..0dff16cf14
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/inf_loop1.erl
@@ -0,0 +1,172 @@
+%% -*- erlang-indent-level: 2 -*-
+%%----------------------------------------------------------------------------
+%% Non-sensical (i.e., stripped-down) program that sends the analysis
+%% into an infinite loop. The #we.es field was originally a gb_tree()
+%% but the programmer declared it as an array in order to change it to
+%% that data type instead. In the file, there are two calls to function
+%% gb_trees:get/2 which seem to be the ones responsible for sending the
+%% analysis into an infinite loop. Currently, these calls are marked and
+%% have been changed to gbee_trees:get/2 in order to be able to see that
+%% the analysis works if these two calls are taken out of the picture.
+%%----------------------------------------------------------------------------
+-module(inf_loop1).
+
+-export([command/1]).
+
+-record(we, {id,
+ es = array:new() :: array(),
+ vp,
+ mirror = none}).
+-record(edge, {vs,ve,a = none,b = none,lf,rf,ltpr,ltsu,rtpr,rtsu}).
+
+command(St) ->
+ State = drag_mode(offset_region),
+ SetupSt = wings_sel_conv:more(St),
+ Tvs = wings_sel:fold(fun(Faces, #we{id = Id} = We, Acc) ->
+ FaceRegions = wings_sel:face_regions(Faces, We),
+ {AllVs0,VsData} =
+ collect_offset_regions_data(FaceRegions, We, [], []),
+ AllVs = ordsets:from_list(AllVs0),
+ [{Id,{AllVs,offset_regions_fun(VsData, State)}}|Acc]
+ end,
+ [],
+ SetupSt),
+ wings_drag:setup(Tvs, 42, [], St).
+
+drag_mode(Type) ->
+ {Mode,Norm} = wings_pref:get_value(Type, {average,loop}),
+ {Type,Mode,Norm}.
+
+collect_offset_regions_data([Faces|Regions], We, AllVs, VsData) ->
+ {FaceNormTab,OuterEdges,RegVs} =
+ some_fake_module:faces_data_0(Faces, We, [], [], []),
+ {LoopNorm,LoopVsData,LoopVs} =
+ offset_regions_loop_data(OuterEdges, Faces, We, FaceNormTab),
+ Vs = RegVs -- LoopVs,
+ RegVsData = vertex_normals(Vs, FaceNormTab, We, LoopVsData),
+ collect_offset_regions_data(Regions, We, RegVs ++ AllVs,
+ [{LoopNorm,RegVsData}|VsData]);
+collect_offset_regions_data([], _, AllVs, VsData) ->
+ {AllVs,VsData}.
+
+offset_regions_loop_data(Edges, Faces, We, FNtab) ->
+ EdgeSet = gb_sets:from_list(Edges),
+ offset_loop_data_0(EdgeSet, Faces, We, FNtab, [], [], []).
+
+offset_loop_data_0(EdgeSet0, Faces, We, FNtab, LNorms, VData0, Vs0) ->
+ case gb_sets:is_empty(EdgeSet0) of
+ false ->
+ {Edge,EdgeSet1} = gb_sets:take_smallest(EdgeSet0),
+ {EdgeSet,VData,Links,LoopNorm,Vs} =
+ offset_loop_data_1(Edge, EdgeSet1, Faces, We, FNtab, VData0, Vs0),
+ offset_loop_data_0(EdgeSet, Faces, We, FNtab,
+ [{Links,LoopNorm}|LNorms], VData, Vs);
+ true ->
+ AvgLoopNorm = average_loop_norm(LNorms),
+ {AvgLoopNorm,VData0,Vs0}
+ end.
+
+offset_loop_data_1(Edge, EdgeSet, _Faces,
+ #we{es = Etab, vp = Vtab} = We, FNtab, VData, Vs) ->
+ #edge{vs = Va, ve = Vb, lf = Lf, ltsu = NextLeft} = gb_trees:get(Edge, Etab),
+ VposA = gb_trees:get(Va, Vtab),
+ VposB = gb_trees:get(Vb, Vtab),
+ VDir = e3d_vec:sub(VposB, VposA),
+ FNorm = wings_face:normal(Lf, We),
+ EdgeData = gb_trees:get(NextLeft, Etab),
+ offset_loop_data_2(NextLeft, EdgeData, Va, VposA, Lf, Edge, We, FNtab,
+ EdgeSet, VDir, [], [FNorm], VData, [], Vs, 0).
+
+offset_loop_data_2(CurE, #edge{vs = Va, ve = Vb, lf = PrevFace,
+ rtsu = NextEdge, ltsu = IfCurIsMember},
+ Vb, VposB, PrevFace, LastE,
+ #we{mirror = M} = We,
+ FNtab, EdgeSet0, VDir, EDir0, VNorms0, VData0, VPs0, Vs0,
+ Links) ->
+ Mirror = M == PrevFace,
+ offset_loop_is_member(Mirror, Vb, Va, VposB, CurE, IfCurIsMember, VNorms0,
+ NextEdge, EdgeSet0, VDir, EDir0, FNtab, PrevFace,
+ LastE, We, VData0, VPs0, Vs0, Links).
+
+offset_loop_is_member(Mirror, V1, V2, Vpos1, CurE, NextE, VNorms0, NEdge,
+ EdgeSet0, VDir, EDir0, FNtab, PFace, LastE, We,
+ VData0, VPs0, Vs0, Links) ->
+ #we{es = Etab, vp = Vtab} = We,
+ Vpos2 = gb_trees:get(V2, Vtab),
+ Dir = e3d_vec:sub(Vpos2, Vpos1),
+ NextVDir = e3d_vec:neg(Dir),
+ EdgeSet = gb_sets:delete(CurE, EdgeSet0),
+ EdgeData = gbee_trees:get(NextE, Etab), %% HERE
+ [FNorm|_] = VNorms0,
+ VData = offset_loop_data_3(Mirror, V1, Vpos1, VNorms0, NEdge, VDir,
+ Dir, EDir0, FNtab, We, VData0),
+ VPs = [Vpos1|VPs0],
+ Vs = [V1|Vs0],
+ offset_loop_data_2(NextE, EdgeData, V2, Vpos2, PFace, LastE, We, FNtab,
+ EdgeSet, NextVDir, [], [FNorm], VData, VPs, Vs, Links + 1).
+
+offset_loop_data_3(false, V, Vpos, VNorms0, NextEdge,
+ VDir, Dir, EDir0, FNtab, We, VData0) ->
+ #we{es = Etab} = We,
+ VNorm = e3d_vec:norm(e3d_vec:add(VNorms0)),
+ NV = wings_vertex:other(V, gbee_trees:get(NextEdge, Etab)), %% HERE
+ ANorm = vertex_normal(NV, FNtab, We),
+ EDir = some_fake_module:average_edge_dir(VNorm, VDir, Dir, EDir0),
+ AvgDir = some_fake_module:evaluate_vdata(VDir, Dir, VNorm),
+ ScaledDir = some_fake_module:along_edge_scale_factor(VDir, Dir, EDir, ANorm),
+ [{V,{Vpos,AvgDir,EDir,ScaledDir}}|VData0].
+
+average_loop_norm([{_,LNorms}]) ->
+ e3d_vec:norm(LNorms);
+average_loop_norm([{LinksA,LNormA},{LinksB,LNormB}]) ->
+ case LinksA < LinksB of
+ true ->
+ e3d_vec:norm(e3d_vec:add(e3d_vec:neg(LNormA), LNormB));
+ false ->
+ e3d_vec:norm(e3d_vec:add(e3d_vec:neg(LNormB), LNormA))
+ end;
+average_loop_norm(LNorms) ->
+ LoopNorms = [Norm || {_,Norm} <- LNorms],
+ e3d_vec:norm(e3d_vec:neg(e3d_vec:add(LoopNorms))).
+
+vertex_normals([V|Vs], FaceNormTab, #we{vp = Vtab, mirror = M} = We, Acc) ->
+ FaceNorms =
+ wings_vertex:fold(fun(_, Face, _, A) when Face == M ->
+ [e3d_vec:neg(wings_face:normal(M, We))|A];
+ (_, Face, _, A) ->
+ [gb_trees:get(Face, FaceNormTab)|A]
+ end, [], V, We),
+ VNorm = e3d_vec:norm(e3d_vec:add(FaceNorms)),
+ Vpos = gb_trees:get(V, Vtab),
+ vertex_normals(Vs, FaceNormTab, We, [{V,{Vpos,VNorm}}|Acc]);
+vertex_normals([], _, _, Acc) ->
+ Acc.
+
+vertex_normal(V, FaceNormTab, #we{mirror = M} = We) ->
+ wings_vertex:fold(fun(_, Face, _, A) when Face == M ->
+ [e3d_vec:neg(wings_face:normal(Face, We))|A];
+ (_, Face, _, A) ->
+ N = gb_trees:get(Face, FaceNormTab),
+ case e3d_vec:is_zero(N) of
+ true -> A;
+ false -> [N|A]
+ end
+ end, [], V, We).
+
+offset_regions_fun(OffsetData, {_,Solution,_} = State) ->
+ fun(new_mode_data, {NewState,_}) ->
+ offset_regions_fun(OffsetData, NewState);
+ ([Dist,_,_,Bump|_], A) ->
+ lists:foldl(fun({LoopNormal,VsData}, VsAcc0) ->
+ lists:foldl(fun({V,{Vpos0,VNorm}}, VsAcc) ->
+ [{V,Vpos0}|VsAcc];
+ ({V,{Vpos0,Dir,EDir,ScaledEDir}}, VsAcc) ->
+ Vec = case Solution of
+ average -> Dir;
+ along_edges -> EDir;
+ scaled -> ScaledEDir
+ end,
+ [{V,Vpos0}|VsAcc]
+ end, VsAcc0, VsData)
+ end, A, OffsetData)
+ end.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/int/int_adt.erl b/lib/dialyzer/test/opaque_SUITE_data/src/int/int_adt.erl
new file mode 100644
index 0000000000..99f8cbdc4a
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/int/int_adt.erl
@@ -0,0 +1,33 @@
+%%----------------------------------------------------------------------------
+%% Module that tests consistency of spec declarations in the presence of
+%% opaque types. Contains both valid and invalid contracts with opaque types.
+%%----------------------------------------------------------------------------
+
+-module(int_adt).
+
+-export([new_i/0, add_i/2, div_i/2, add_f/2, div_f/2]).
+
+-export_type([int/0]).
+
+-opaque int() :: integer().
+
+%% the user has declared the return to be an opaque type, but the success
+%% typing inference is too strong and finds a subtype as a return: this is OK
+-spec new_i() -> int().
+new_i() -> 42.
+
+%% the success typing is more general than the contract: this is OK
+-spec add_i(int(), int()) -> int().
+add_i(X, Y) -> X + Y.
+
+%% the success typing coincides with the contract: this is OK, of course
+-spec div_i(int(), int()) -> int().
+div_i(X, Y) -> X div Y.
+
+%% the success typing has an incompatible domain element: this is invalid
+-spec add_f(int(), int()) -> int().
+add_f(X, Y) when is_float(Y) -> X + trunc(Y).
+
+%% the success typing has an incompatible range: this is invalid
+-spec div_f(int(), int()) -> int().
+div_f(X, Y) -> X / Y.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/int/int_use.erl b/lib/dialyzer/test/opaque_SUITE_data/src/int/int_use.erl
new file mode 100644
index 0000000000..b4471e1cee
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/int/int_use.erl
@@ -0,0 +1,11 @@
+%%---------------------------------------------------------------------------
+%% Module that uses the opaque types of int_adt.
+%% TODO: Should be extended with invalid contracts.
+%%---------------------------------------------------------------------------
+-module(int_use).
+
+-export([test/0]).
+
+-spec test() -> int_adt:int().
+test() ->
+ int_adt:new_i().
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/mixed_opaque/mixed_opaque_queue_adt.erl b/lib/dialyzer/test/opaque_SUITE_data/src/mixed_opaque/mixed_opaque_queue_adt.erl
new file mode 100644
index 0000000000..ac59f19cd3
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/mixed_opaque/mixed_opaque_queue_adt.erl
@@ -0,0 +1,26 @@
+%%---------------------------------------------------------------------------
+%% A clone of 'queue_adt' so as to test its combination with 'rec_adt'
+%%---------------------------------------------------------------------------
+-module(mixed_opaque_queue_adt).
+
+-export([new/0, add/2, dequeue/1, is_empty/1]).
+
+-opaque my_queue() :: list().
+
+-spec new() -> my_queue().
+new() ->
+ [].
+
+-spec add(term(), my_queue()) -> my_queue().
+add(E, Q) ->
+ Q ++ [E].
+
+-spec dequeue(my_queue()) -> {term(), my_queue()}.
+dequeue([H|T]) ->
+ {H, T}.
+
+-spec is_empty(my_queue()) -> boolean().
+is_empty([]) ->
+ true;
+is_empty([_|_]) ->
+ false.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/mixed_opaque/mixed_opaque_rec_adt.erl b/lib/dialyzer/test/opaque_SUITE_data/src/mixed_opaque/mixed_opaque_rec_adt.erl
new file mode 100644
index 0000000000..61bae5110d
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/mixed_opaque/mixed_opaque_rec_adt.erl
@@ -0,0 +1,25 @@
+%%---------------------------------------------------------------------------
+%% A clone of 'rec_adt' so as to test its combination with 'queue_adt'
+%%---------------------------------------------------------------------------
+-module(mixed_opaque_rec_adt).
+
+-export([new/0, get_a/1, get_b/1, set_a/2, set_b/2]).
+
+-record(rec, {a :: atom(), b = 0 :: integer()}).
+
+-opaque rec() :: #rec{}.
+
+-spec new() -> rec().
+new() -> #rec{a = gazonk, b = 42}.
+
+-spec get_a(rec()) -> atom().
+get_a(#rec{a = A}) -> A.
+
+-spec get_b(rec()) -> integer().
+get_b(#rec{b = B}) -> B.
+
+-spec set_a(rec(), atom()) -> rec().
+set_a(R, A) -> R#rec{a = A}.
+
+-spec set_b(rec(), integer()) -> rec().
+set_b(R, B) -> R#rec{b = B}.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/mixed_opaque/mixed_opaque_use.erl b/lib/dialyzer/test/opaque_SUITE_data/src/mixed_opaque/mixed_opaque_use.erl
new file mode 100644
index 0000000000..e82dcd5f38
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/mixed_opaque/mixed_opaque_use.erl
@@ -0,0 +1,31 @@
+%%---------------------------------------------------------------------------
+%% Test that tries some combinations of using more than one opaque data type
+%% in the same function(s).
+%%----------------------------------------------------------------------------
+-module(mixed_opaque_use).
+
+-export([ok1/1, ok2/0, wrong1/0]).
+
+-define(REC, mixed_opaque_rec_adt).
+-define(QUEUE, mixed_opaque_queue_adt).
+
+%% Currently returning unions of opaque types is considered OK
+ok1(Type) ->
+ case Type of
+ queue -> ?QUEUE:new();
+ rec -> ?REC:new()
+ end.
+
+%% Constructing a queue of records is OK
+ok2() ->
+ Q0 = ?QUEUE:new(),
+ R0 = ?REC:new(),
+ Q1 = ?QUEUE:add(R0, Q0),
+ {R1,_Q2} = ?QUEUE:dequeue(Q1),
+ ?REC:get_a(R1).
+
+%% But of course calling a function expecting some opaque type
+%% with some other opaque typs is not OK
+wrong1() ->
+ Q = ?QUEUE:new(),
+ ?REC:get_a(Q).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/my_digraph/my_digraph_adt.erl b/lib/dialyzer/test/opaque_SUITE_data/src/my_digraph/my_digraph_adt.erl
new file mode 100644
index 0000000000..82159d6a8d
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/my_digraph/my_digraph_adt.erl
@@ -0,0 +1,51 @@
+-module(my_digraph_adt).
+
+-export([new/0, new/1]).
+
+-record(my_digraph, {vtab = notable,
+ etab = notable,
+ ntab = notable,
+ cyclic = true :: boolean()}).
+
+-opaque my_digraph() :: #my_digraph{}.
+
+-type d_protection() :: 'private' | 'protected'.
+-type d_cyclicity() :: 'acyclic' | 'cyclic'.
+-type d_type() :: d_cyclicity() | d_protection().
+
+-spec new() -> my_digraph().
+new() -> new([]).
+
+-spec new([atom()]) -> my_digraph().
+new(Type) ->
+ try check_type(Type, protected, []) of
+ {Access, Ts} ->
+ V = ets:new(vertices, [set, Access]),
+ E = ets:new(edges, [set, Access]),
+ N = ets:new(neighbours, [bag, Access]),
+ ets:insert(N, [{'$vid', 0}, {'$eid', 0}]),
+ set_type(Ts, #my_digraph{vtab=V, etab=E, ntab=N})
+ catch
+ throw:Error -> throw(Error)
+ end.
+
+-spec check_type([atom()], d_protection(), [{'cyclic', boolean()}]) ->
+ {d_protection(), [{'cyclic', boolean()}]}.
+
+check_type([acyclic|Ts], A, L) ->
+ check_type(Ts, A,[{cyclic,false} | L]);
+check_type([cyclic | Ts], A, L) ->
+ check_type(Ts, A, [{cyclic,true} | L]);
+check_type([protected | Ts], _, L) ->
+ check_type(Ts, protected, L);
+check_type([private | Ts], _, L) ->
+ check_type(Ts, private, L);
+check_type([T | _], _, _) ->
+ throw({error, {unknown_type, T}});
+check_type([], A, L) -> {A, L}.
+
+-spec set_type([{'cyclic', boolean()}], my_digraph()) -> my_digraph().
+
+set_type([{cyclic,V} | Ks], G) ->
+ set_type(Ks, G#my_digraph{cyclic = V});
+set_type([], G) -> G.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/my_queue/my_queue_adt.erl b/lib/dialyzer/test/opaque_SUITE_data/src/my_queue/my_queue_adt.erl
new file mode 100644
index 0000000000..52688062ce
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/my_queue/my_queue_adt.erl
@@ -0,0 +1,23 @@
+-module(my_queue_adt).
+
+-export([new/0, add/2, dequeue/1, is_empty/1]).
+
+-opaque my_queue() :: list().
+
+-spec new() -> my_queue().
+new() ->
+ [].
+
+-spec add(term(), my_queue()) -> my_queue().
+add(E, Q) ->
+ Q ++ [E].
+
+-spec dequeue(my_queue()) -> {term(), my_queue()}.
+dequeue([H|T]) ->
+ {H, T}.
+
+-spec is_empty(my_queue()) -> boolean().
+is_empty([]) ->
+ true;
+is_empty([_|_]) ->
+ false.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/my_queue/my_queue_use.erl b/lib/dialyzer/test/opaque_SUITE_data/src/my_queue/my_queue_use.erl
new file mode 100644
index 0000000000..98f9972c1e
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/my_queue/my_queue_use.erl
@@ -0,0 +1,35 @@
+-module(my_queue_use).
+
+-export([ok1/0, ok2/0, wrong1/0, wrong2/0, wrong3/0, wrong4/0, wrong5/0]).
+
+ok1() ->
+ my_queue_adt:is_empty(my_queue_adt:new()).
+
+ok2() ->
+ Q0 = my_queue_adt:new(),
+ Q1 = my_queue_adt:add(42, Q0),
+ {42, Q2} = my_queue_adt:dequeue(Q1),
+ my_queue_adt:is_empty(Q2).
+
+wrong1() ->
+ my_queue_adt:is_empty([]).
+
+wrong2() ->
+ Q0 = [],
+ my_queue_adt:add(42, Q0).
+
+wrong3() ->
+ Q0 = my_queue_adt:new(),
+ Q1 = my_queue_adt:add(42, Q0),
+ [42|Q2] = Q1,
+ Q2.
+
+wrong4() ->
+ Q0 = my_queue_adt:new(),
+ Q1 = my_queue_adt:add(42, Q0),
+ Q1 =:= [].
+
+wrong5() ->
+ Q0 = my_queue_adt:new(),
+ {42, Q2} = my_queue_adt:dequeue([42|Q0]),
+ Q2.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_adt.erl b/lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_adt.erl
new file mode 100644
index 0000000000..3456f0e9c6
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_adt.erl
@@ -0,0 +1,9 @@
+-module(opaque_adt).
+-export([atom_or_list/1]).
+
+-opaque abc() :: 'a' | 'b' | 'c'.
+
+atom_or_list(1) -> a;
+atom_or_list(2) -> b;
+atom_or_list(3) -> c;
+atom_or_list(N) -> lists:duplicate(N, a).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug1.erl b/lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug1.erl
new file mode 100644
index 0000000000..5a03989853
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug1.erl
@@ -0,0 +1,16 @@
+%%---------------------------------------------------------------------
+%% A test for which the analysis went into an infinite loop due to
+%% specialization using structured type instead of the opaque one.
+%%---------------------------------------------------------------------
+
+-module(opaque_bug1).
+
+-export([test/1]).
+
+-record(c, {a::atom()}).
+
+-opaque erl_type() :: 'any' | #c{}.
+
+test(#c{a=foo} = T) -> local(T).
+
+local(#c{a=foo}) -> any.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug2.erl b/lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug2.erl
new file mode 100644
index 0000000000..f193a58f59
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug2.erl
@@ -0,0 +1,13 @@
+%%---------------------------------------------------------------------
+%% A test for which the analysis gave a bogus warning due to
+%% considering the function call name to be of opaque type...
+%%---------------------------------------------------------------------
+
+-module(opaque_bug2).
+
+-export([test/0]).
+
+-opaque o() :: 'map'.
+
+test() ->
+ lists:map(fun(X) -> X+1 end, [1,2]).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug3.erl b/lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug3.erl
new file mode 100644
index 0000000000..71da82a1f6
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug3.erl
@@ -0,0 +1,19 @@
+%%---------------------------------------------------------------------
+%% A test for which the analysis gave wrong results because it did not
+%% handle the is_tuple/1 guard properly.
+%%---------------------------------------------------------------------
+
+-module(opaque_bug3).
+
+-export([test/1]).
+
+-record(c, {}).
+
+-opaque o() :: 'a' | #c{}.
+
+-spec test(o()) -> 42.
+
+test(#c{} = O) -> t(O).
+
+t(T) when is_tuple(T) -> 42;
+t(a) -> gazonk.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug4.erl b/lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug4.erl
new file mode 100644
index 0000000000..a7ddc80fe8
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug4.erl
@@ -0,0 +1,21 @@
+%%---------------------------------------------------------------------
+%% A test for which the analysis gave wrong results due to erroneous
+%% specialization and incorrect handling of unions.
+%%---------------------------------------------------------------------
+
+-module(opaque_bug4).
+
+-export([ok/0, wrong/0]).
+
+%-spec ok() -> 'ok'.
+ok() ->
+ L = opaque_adt:atom_or_list(42),
+ foo(L).
+
+%-spec wrong() -> 'not_ok'.
+wrong() ->
+ A = opaque_adt:atom_or_list(1),
+ foo(A).
+
+foo(a) -> not_ok;
+foo([_|_]) -> ok.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/queue/queue_use.erl b/lib/dialyzer/test/opaque_SUITE_data/src/queue/queue_use.erl
new file mode 100644
index 0000000000..8d46bdb989
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/queue/queue_use.erl
@@ -0,0 +1,65 @@
+-module(queue_use).
+
+-export([ok1/0, ok2/0]).
+-export([wrong1/0, wrong2/0, wrong3/0, wrong4/0, wrong5/0, wrong6/0, wrong7/0, wrong8/0]).
+
+ok1() ->
+ queue:is_empty(queue:new()).
+
+ok2() ->
+ Q0 = queue:new(),
+ Q1 = queue:in(42, Q0),
+ {{value, 42}, Q2} = queue:out(Q1),
+ queue:is_empty(Q2).
+
+%%--------------------------------------------------
+
+wrong1() ->
+ queue:is_empty({[],[]}).
+
+wrong2() ->
+ Q0 = {[],[]},
+ queue:in(42, Q0).
+
+wrong3() ->
+ Q0 = queue:new(),
+ Q1 = queue:in(42, Q0),
+ {[42],Q2} = Q1,
+ Q2.
+
+wrong4() ->
+ Q0 = queue:new(),
+ Q1 = queue:in(42, Q0),
+ Q1 =:= {[42],[]}.
+
+wrong5() ->
+ {F, _R} = queue:new(),
+ F.
+
+wrong6() ->
+ {{value, 42}, Q2} = queue:out({[42],[]}),
+ Q2.
+
+%%--------------------------------------------------
+
+-record(db, {p, q}).
+
+wrong7() ->
+ add_unique(42, #db{p = [], q = queue:new()}).
+
+add_unique(E, DB) ->
+ case is_in_queue(E, DB) of
+ true -> DB;
+ false -> DB#db{q = queue:in(E, DB#db.q)}
+ end.
+
+is_in_queue(P, #db{q = {L1,L2}}) ->
+ lists:member(P, L1) orelse lists:member(P, L2).
+
+%%--------------------------------------------------
+
+wrong8() ->
+ tuple_queue({42, gazonk}).
+
+tuple_queue({F, Q}) ->
+ queue:in(F, Q).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/rec/rec_adt.erl b/lib/dialyzer/test/opaque_SUITE_data/src/rec/rec_adt.erl
new file mode 100644
index 0000000000..f01cc5e519
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/rec/rec_adt.erl
@@ -0,0 +1,22 @@
+-module(rec_adt).
+
+-export([new/0, get_a/1, get_b/1, set_a/2, set_b/2]).
+
+-record(rec, {a :: atom(), b = 0 :: integer()}).
+
+-opaque rec() :: #rec{}.
+
+-spec new() -> rec().
+new() -> #rec{a = gazonk, b = 42}.
+
+-spec get_a(rec()) -> atom().
+get_a(#rec{a = A}) -> A.
+
+-spec get_b(rec()) -> integer().
+get_b(#rec{b = B}) -> B.
+
+-spec set_a(rec(), atom()) -> rec().
+set_a(R, A) -> R#rec{a = A}.
+
+-spec set_b(rec(), integer()) -> rec().
+set_b(R, B) -> R#rec{b = B}.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/rec/rec_use.erl b/lib/dialyzer/test/opaque_SUITE_data/src/rec/rec_use.erl
new file mode 100644
index 0000000000..358e9f918c
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/rec/rec_use.erl
@@ -0,0 +1,30 @@
+-module(rec_use).
+
+-export([ok1/0, ok2/0, wrong1/0, wrong2/0, wrong3/0, wrong4/0]).
+
+ok1() ->
+ rec_adt:set_a(rec_adt:new(), foo).
+
+ok2() ->
+ R1 = rec_adt:new(),
+ B1 = rec_adt:get_b(R1),
+ R2 = rec_adt:set_b(R1, 42),
+ B2 = rec_adt:get_b(R2),
+ B1 =:= B2.
+
+wrong1() ->
+ case rec_adt:new() of
+ {rec, _, 42} -> weird1;
+ R when tuple_size(R) =:= 3 -> weird2
+ end.
+
+wrong2() ->
+ R = list_to_tuple([rec, a, 42]),
+ rec_adt:get_a(R).
+
+wrong3() ->
+ R = rec_adt:new(),
+ R =:= {rec, gazonk, 42}.
+
+wrong4() ->
+ tuple_size(rec_adt:new()).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/timer/timer_use.erl b/lib/dialyzer/test/opaque_SUITE_data/src/timer/timer_use.erl
new file mode 100644
index 0000000000..9c8ea0af1c
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/timer/timer_use.erl
@@ -0,0 +1,20 @@
+%%---------------------------------------------------------------------------
+%% A test case with:
+%% - a genuine matching error -- 1st branch
+%% - a violation of the opaqueness of timer:tref() -- 2nd branch
+%% - a subtle violation of the opaqueness of timer:tref() -- 3rd branch
+%% The test is supposed to check that these cases are treated properly.
+%%---------------------------------------------------------------------------
+
+-module(timer_use).
+-export([wrong/0]).
+
+-spec wrong() -> error.
+
+wrong() ->
+ case timer:kill_after(42, self()) of
+ gazonk -> weird;
+ {ok, 42} -> weirder;
+ {Tag, gazonk} when Tag =/= error -> weirdest;
+ {error, _} -> error
+ end.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/union/union_adt.erl b/lib/dialyzer/test/opaque_SUITE_data/src/union/union_adt.erl
new file mode 100644
index 0000000000..5ca3202bba
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/union/union_adt.erl
@@ -0,0 +1,19 @@
+-module(union_adt).
+-export([new/1, new_a/1, new_rec/1]).
+
+-record(rec, {x = 42 :: integer()}).
+
+-opaque u() :: 'aaa' | 'bbb' | #rec{}.
+
+new(a) -> aaa;
+new(b) -> bbb;
+new(X) when is_integer(X) ->
+ #rec{x = X}.
+
+%% the following two functions (and their uses in union_use.erl) test
+%% that the return type is the opaque one and not just a subtype of it
+
+new_a(a) -> aaa.
+
+new_rec(X) when is_integer(X) ->
+ #rec{x = X}.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/union/union_use.erl b/lib/dialyzer/test/opaque_SUITE_data/src/union/union_use.erl
new file mode 100644
index 0000000000..6a103279cd
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/union/union_use.erl
@@ -0,0 +1,16 @@
+-module(union_use).
+
+-export([test/1, wrong_a/0, wrong_rec/0]).
+
+test(X) ->
+ case union_adt:new(X) of
+ A when is_atom(A) -> atom;
+ T when is_tuple(T) -> tuple
+ end.
+
+wrong_a() ->
+ aaa = union_adt:new_a(a),
+ ok.
+
+wrong_rec() ->
+ is_tuple(union_adt:new_rec(42)).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings.hrl b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings.hrl
new file mode 100644
index 0000000000..b815be5e1d
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings.hrl
@@ -0,0 +1,204 @@
+%%
+%% wings.hrl --
+%%
+%% Global record definition and defines.
+%%
+%% Copyright (c) 2001-2005 Bjorn Gustavsson
+%%
+%% See the file "license.terms" for information on usage and redistribution
+%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+%%
+%% $Id: wings.hrl,v 1.1 2009/01/25 18:55:33 kostis Exp $
+%%
+
+-include("wings_intl.hrl").
+
+-ifdef(NEED_ESDL).
+-include_lib("esdl/include/sdl.hrl").
+-include_lib("esdl/include/sdl_events.hrl").
+-include_lib("esdl/include/sdl_video.hrl").
+-include_lib("esdl/include/sdl_keyboard.hrl").
+-include_lib("esdl/include/sdl_mouse.hrl").
+-include_lib("esdl/src/sdl_util.hrl").
+-define(CTRL_BITS, ?KMOD_CTRL).
+-define(ALT_BITS, ?KMOD_ALT).
+-define(SHIFT_BITS, ?KMOD_SHIFT).
+-define(META_BITS, ?KMOD_META).
+-endif.
+
+-define(WINGS_VERSION, ?wings_version).
+
+-define(CHAR_HEIGHT, wings_text:height()).
+-define(CHAR_WIDTH, wings_text:width()).
+
+-define(LINE_HEIGHT, (?CHAR_HEIGHT+2)).
+-define(GROUND_GRID_SIZE, 1).
+-define(CAMERA_DIST, (8.0*?GROUND_GRID_SIZE)).
+-define(NORMAL_LINEWIDTH, 1.0).
+-define(DEGREE, 176). %Degree character.
+
+-define(HIT_BUF_SIZE, (1024*1024)).
+
+-define(PANE_COLOR, {0.52,0.52,0.52}).
+-define(BEVEL_HIGHLIGHT, {0.9,0.9,0.9}).
+-define(BEVEL_LOWLIGHT, {0.3,0.3,0.3}).
+-define(BEVEL_HIGHLIGHT_MIX, 0.5).
+-define(BEVEL_LOWLIGHT_MIX, 0.5).
+
+-define(SLOW(Cmd), begin wings_io:hourglass(), Cmd end).
+-define(TC(Cmd), wings_util:tc(fun() -> Cmd end, ?MODULE, ?LINE)).
+
+-ifdef(DEBUG).
+-define(ASSERT(E), case E of
+ true -> ok;
+ _ ->
+ erlang:error({assertion_failed,?MODULE,?LINE})
+ end).
+-define(CHECK_ERROR(), wings_gl:check_error(?MODULE, ?LINE)).
+-else.
+-define(ASSERT(E),ok).
+-define(CHECK_ERROR(), ok).
+-endif.
+
+%% Display lists per object.
+%% Important: Plain integers and integers in lists will be assumed to
+%% be display lists. Arbitrary integers must be stored inside a tuple
+%% or record to not be interpreted as a display list.
+-record(dlo,
+ {work=none, %Workmode faces.
+ smooth=none, %Smooth-shaded faces.
+ edges=none, %Edges and wire-frame.
+ vs=none, %Unselected vertices.
+ hard=none, %Hard edges.
+ sel=none, %Selected items.
+ orig_sel=none, %Original selection.
+ normals=none, %Normals.
+ pick=none, %For picking.
+ proxy_faces=none, %Smooth proxy faces.
+ proxy_edges=none, %Smooth proxy edges.
+
+ %% Miscellanous.
+ hilite=none, %Hilite display list.
+ mirror=none, %Virtual mirror data.
+ ns=none, %Normals/positions per face.
+
+ %% Source for display lists.
+ src_we=none, %Source object.
+ src_sel=none, %Source selection.
+ orig_mode=none, %Original selection mode.
+ split=none, %Split data.
+ drag=none, %For dragging.
+ transparent=false, %Object includes transparancy.
+ proxy_data=none, %Data for smooth proxy.
+ open=false, %Open (has hole).
+
+ %% List of display lists known to be needed only based
+ %% on display modes, not whether the lists themselves exist.
+ %% Example: [work,edges]
+ needed=[]
+ }).
+
+%% Main state record containing all objects and other important state.
+-record(st,
+ {shapes, %All visible shapes
+ selmode, %Selection mode:
+ % vertex, edge, face, body
+ sh=false, %Smart highlight active: true|false
+ sel=[], %Current sel: [{Id,GbSet}]
+ ssels=[], %Saved selections:
+ % [{Name,Mode,GbSet}]
+ temp_sel=none, %Selection only temporary?
+
+ mat, %Defined materials (GbTree).
+ pal=[], %Palette
+ file, %Current filename.
+ saved, %True if model has been saved.
+ onext, %Next object id to use.
+ bb=none, %Saved bounding box.
+ edge_loop=none, %Previous edge loop.
+ views={0,{}}, %{Current,TupleOfViews}
+ pst=gb_trees:empty(), %Plugin State Info
+ % gb_tree where key is plugin module
+
+ %% Previous commands.
+ repeatable, %Last repeatable command.
+ ask_args, %Ask arguments.
+ drag_args, %Drag arguments for command.
+ def, %Default operations.
+
+ %% Undo information.
+ top, %Top of stack.
+ bottom, %Bottom of stack.
+ next_is_undo, %State of undo/redo toggle.
+ undone %States that were undone.
+ }).
+
+%% The Winged-Edge data structure.
+%% See http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/model/winged-e.html
+-record(we,
+ {id, %Shape id.
+ perm=0, %Permissions:
+ % 0 - Everything allowed.
+ % 1 - Visible, can't select.
+ % [] or {Mode,GbSet} -
+ % Invisible, can't select.
+ % The GbSet contains the
+ % object's selection.
+ name, %Name.
+ es, %gb_tree containing edges
+ fs, %gb_tree containing faces
+ he, %gb_sets containing hard edges
+ vc, %Connection info (=incident edge)
+ % for vertices.
+ vp, %Vertex positions.
+ pst=gb_trees:empty(), %Plugin State Info,
+ % gb_tree where key is plugin module
+ mat=default, %Materials.
+ next_id, %Next free ID for vertices,
+ % edges, and faces.
+ % (Needed because we never re-use
+ % IDs.)
+ mode, %'vertex'/'material'/'uv'
+ mirror=none, %Mirror: none|Face
+ light=none, %Light data: none|Light
+ has_shape=true %true|false
+ }).
+
+-define(IS_VISIBLE(Perm), (Perm =< 1)).
+-define(IS_NOT_VISIBLE(Perm), (Perm > 1)).
+-define(IS_SELECTABLE(Perm), (Perm == 0)).
+-define(IS_NOT_SELECTABLE(Perm), (Perm =/= 0)).
+
+-define(IS_LIGHT(We), ((We#we.light =/= none) and (not We#we.has_shape))).
+-define(IS_ANY_LIGHT(We), (We#we.light =/= none)).
+-define(HAS_SHAPE(We), (We#we.has_shape)).
+%-define(IS_LIGHT(We), (We#we.light =/= none)).
+%-define(IS_NOT_LIGHT(We), (We#we.light =:= none)).
+
+%% Edge in a winged-edge shape.
+-record(edge,
+ {vs, %Start vertex for edge
+ ve, %End vertex for edge
+ a=none, %Color or UV coordinate.
+ b=none, %Color or UV coordinate.
+ lf, %Left face
+ rf, %Right face
+ ltpr, %Left traversal predecessor
+ ltsu, %Left traversal successor
+ rtpr, %Right traversal predecessor
+ rtsu %Right traversal successor
+ }).
+
+%% The current view/camera.
+-record(view,
+ {origin,
+ distance, % From origo.
+ azimuth,
+ elevation,
+ pan_x, %Panning in X direction.
+ pan_y, %Panning in Y direction.
+ along_axis=none, %Which axis viewed along.
+ fov, %Field of view.
+ hither, %Near clipping plane.
+ yon %Far clipping plane.
+ }).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_dissolve.erl b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_dissolve.erl
new file mode 100644
index 0000000000..c469f0a45d
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_dissolve.erl
@@ -0,0 +1,375 @@
+%%
+%% wings_dissolve.erl --
+%%
+%% This module implements dissolve of faces.
+%%
+
+-module(wings_dissolve).
+
+-export([faces/2, complement/2]).
+
+-include("wings.hrl").
+
+%% faces([Face], We) -> We'
+%% Dissolve the given faces.
+faces([], We) -> We;
+faces(Faces, #we{fs=Ftab0}=We) ->
+ case gb_sets:is_empty(Faces) of
+ true -> We;
+ false when is_list(Faces) ->
+ Complement = ordsets:subtract(gb_trees:keys(Ftab0),
+ ordsets:from_list(Faces)),
+ dissolve_1(Faces, Complement, We);
+ false ->
+ Complement = ordsets:subtract(gb_trees:keys(Ftab0),
+ gb_sets:to_list(Faces)),
+ dissolve_1(Faces, Complement, We)
+ end.
+
+faces([], _, We) -> We;
+faces(Faces,Complement,We) ->
+ case gb_sets:is_empty(Faces) of
+ true -> We;
+ false -> dissolve_1(Faces, Complement,We)
+ end.
+
+dissolve_1(Faces, Complement, We0) ->
+ We1 = optimistic_dissolve(Faces,Complement,We0#we{vc=undefined}),
+ NewFaces = wings_we:new_items_as_ordset(face, We0, We1),
+ We2 = wings_face:delete_bad_faces(NewFaces, We1),
+ We = wings_we:rebuild(We2),
+ case wings_we:is_consistent(We) of
+ true ->
+ We;
+ false ->
+ io:format("Dissolving would cause an inconsistent object structure.")
+ end.
+
+%% complement([Face], We) -> We'
+%% Dissolve all faces BUT the given faces. Also invalidate the
+%% mirror face if it existed and was dissolved.
+complement(Fs0, #we{fs=Ftab0}=We0) when is_list(Fs0) ->
+ Fs = ordsets:subtract(gb_trees:keys(Ftab0), ordsets:from_list(Fs0)),
+ case faces(Fs, Fs0, We0) of
+ #we{mirror=none}=We -> We;
+ #we{mirror=Face,fs=Ftab}=We ->
+ case gb_trees:is_defined(Face, Ftab) of
+ false -> We;
+ true -> We#we{mirror=none}
+ end
+ end;
+complement(Fs, We) -> complement(gb_sets:to_list(Fs), We).
+
+optimistic_dissolve(Faces0, Compl, We0) ->
+ %% Optimistically assume that we have a simple region without
+ %% any holes.
+ case outer_edge_loop(Faces0, We0) of
+ error ->
+ %% Assumption was wrong. We need to partition the selection
+ %% and dissolve each partition in turn.
+ Parts = wings_sel:face_regions(Faces0, We0),
+ complex_dissolve(Parts, We0);
+ [_|_]=Loop ->
+ %% Assumption was correct.
+ simple_dissolve(Faces0, Compl, Loop, We0)
+ end.
+
+%% simple_dissolve(Faces, Loop, We0) -> We
+%% Dissolve a region of faces with no holes and no
+%% repeated vertices in the outer edge loop.
+
+simple_dissolve(Faces0, Compl, Loop, We0) ->
+ Faces = to_gb_set(Faces0),
+ OldFace = gb_sets:smallest(Faces),
+ Mat = wings_facemat:face(OldFace, We0),
+ We1 = fix_materials(Faces, Compl, We0),
+ #we{es=Etab0,fs=Ftab0,he=Htab0} = We1,
+ {Ftab1,Etab1,Htab} = simple_del(Faces, Ftab0, Etab0, Htab0, We1),
+ {NewFace,We2} = wings_we:new_id(We1),
+ Ftab = gb_trees:insert(NewFace, hd(Loop), Ftab1),
+ Last = lists:last(Loop),
+ Etab = update_outer([Last|Loop], Loop, NewFace, Ftab, Etab1),
+ We = We2#we{es=Etab,fs=Ftab,he=Htab},
+ wings_facemat:assign(Mat, [NewFace], We).
+
+fix_materials(Del,Keep,We) ->
+ case gb_sets:size(Del) < length(Keep) of
+ true ->
+ wings_facemat:delete_faces(Del,We);
+ false ->
+ wings_facemat:keep_faces(Keep,We)
+ end.
+
+to_gb_set(List) when is_list(List) ->
+ gb_sets:from_list(List);
+to_gb_set(S) -> S.
+
+%% Delete faces and inner edges for a simple region.
+simple_del(Faces, Ftab0, Etab0, Htab0, We) ->
+ case {gb_trees:size(Ftab0),gb_sets:size(Faces)} of
+ {AllSz,FaceSz} when AllSz < 2*FaceSz ->
+ %% At least half of the faces are selected.
+ %% It is faster to find the edges for the
+ %% unselected faces.
+ UnselFaces = ordsets:subtract(gb_trees:keys(Ftab0),
+ gb_sets:to_list(Faces)),
+
+ UnselSet = sofs:from_external(UnselFaces, [face]),
+ Ftab1 = sofs:from_external(gb_trees:to_list(Ftab0),
+ [{face,edge}]),
+ Ftab2 = sofs:restriction(Ftab1, UnselSet),
+ Ftab = gb_trees:from_orddict(sofs:to_external(Ftab2)),
+
+ Keep0 = wings_face:to_edges(UnselFaces, We),
+ Keep = sofs:set(Keep0, [edge]),
+ Etab1 = sofs:from_external(gb_trees:to_list(Etab0),
+ [{edge,info}]),
+ Etab2 = sofs:restriction(Etab1, Keep),
+ Etab = gb_trees:from_orddict(sofs:to_external(Etab2)),
+
+ Htab = simple_del_hard(Htab0, sofs:to_external(Keep), undefined),
+ {Ftab,Etab,Htab};
+ {_,_} ->
+ Ftab = lists:foldl(fun(Face, Ft) ->
+ gb_trees:delete(Face, Ft)
+ end, Ftab0, gb_sets:to_list(Faces)),
+ Inner = wings_face:inner_edges(Faces, We),
+ Etab = lists:foldl(fun(Edge, Et) ->
+ gb_trees:delete(Edge, Et)
+ end, Etab0, Inner),
+ Htab = simple_del_hard(Htab0, undefined, Inner),
+ {Ftab,Etab,Htab}
+ end.
+
+simple_del_hard(Htab, Keep, Remove) ->
+ case gb_sets:is_empty(Htab) of
+ true -> Htab;
+ false -> simple_del_hard_1(Htab, Keep, Remove)
+ end.
+
+simple_del_hard_1(Htab, Keep, undefined) ->
+ gb_sets:intersection(Htab, gb_sets:from_ordset(Keep));
+simple_del_hard_1(Htab, undefined, Remove) ->
+ gb_sets:difference(Htab, gb_sets:from_ordset(Remove)).
+
+%% complex([Partition], We0) -> We0
+%% The general dissolve.
+
+complex_dissolve([Faces|T], We0) ->
+ Face = gb_sets:smallest(Faces),
+ Mat = wings_facemat:face(Face, We0),
+ We1 = wings_facemat:delete_faces(Faces, We0),
+ Parts = outer_edge_partition(Faces, We1),
+ We = do_dissolve(Faces, Parts, Mat, We0, We1),
+ complex_dissolve(T, We);
+complex_dissolve([], We) -> We.
+
+do_dissolve(Faces, Ess, Mat, WeOrig, We0) ->
+ We1 = do_dissolve_faces(Faces, We0),
+ Inner = wings_face:inner_edges(Faces, WeOrig),
+ We2 = delete_inner(Inner, We1),
+ #we{he=Htab0} = We = do_dissolve_1(Ess, Mat, We2),
+ Htab = gb_sets:difference(Htab0, gb_sets:from_list(Inner)),
+ We#we{he=Htab}.
+
+do_dissolve_1([EdgeList|Ess], Mat, #we{es=Etab0,fs=Ftab0}=We0) ->
+ {Face,We1} = wings_we:new_id(We0),
+ Ftab = gb_trees:insert(Face, hd(EdgeList), Ftab0),
+ Last = lists:last(EdgeList),
+ Etab = update_outer([Last|EdgeList], EdgeList, Face, Ftab, Etab0),
+ We2 = We1#we{es=Etab,fs=Ftab},
+ We = wings_facemat:assign(Mat, [Face], We2),
+ do_dissolve_1(Ess, Mat, We);
+do_dissolve_1([], _Mat, We) -> We.
+
+do_dissolve_faces(Faces, #we{fs=Ftab0}=We) ->
+ Ftab = lists:foldl(fun(Face, Ft) ->
+ gb_trees:delete(Face, Ft)
+ end, Ftab0, gb_sets:to_list(Faces)),
+ We#we{fs=Ftab}.
+
+delete_inner(Inner, #we{es=Etab0}=We) ->
+ Etab = lists:foldl(fun(Edge, Et) ->
+ gb_trees:delete(Edge, Et)
+ end, Etab0, Inner),
+ We#we{es=Etab}.
+
+update_outer([Pred|[Edge|Succ]=T], More, Face, Ftab, Etab0) ->
+ #edge{rf=Rf} = R0 = gb_trees:get(Edge, Etab0),
+ Rec = case gb_trees:is_defined(Rf, Ftab) of
+ true ->
+ ?ASSERT(false == gb_trees:is_defined(R0#edge.lf, Ftab)),
+ LS = succ(Succ, More),
+ R0#edge{lf=Face,ltpr=Pred,ltsu=LS};
+ false ->
+ ?ASSERT(true == gb_trees:is_defined(R0#edge.lf, Ftab)),
+ RS = succ(Succ, More),
+ R0#edge{rf=Face,rtpr=Pred,rtsu=RS}
+ end,
+ Etab = gb_trees:update(Edge, Rec, Etab0),
+ update_outer(T, More, Face, Ftab, Etab);
+update_outer([_], _More, _Face, _Ftab, Etab) -> Etab.
+
+succ([Succ|_], _More) -> Succ;
+succ([], [Succ|_]) -> Succ.
+
+%% outer_edge_loop(FaceSet,WingedEdge) -> [Edge] | error.
+%% Partition the outer edges of the FaceSet into a single closed loop.
+%% Return 'error' if the faces in FaceSet does not form a
+%% simple region without holes.
+%%
+%% Equvivalent to
+%% case outer_edge_partition(FaceSet,WingedEdge) of
+%% [Loop] -> Loop;
+%% [_|_] -> error
+%% end.
+%% but faster.
+
+outer_edge_loop(Faces, We) ->
+ case lists:sort(collect_outer_edges(Faces, We)) of
+ [] -> error;
+ [{Key,Val}|Es0] ->
+ case any_duplicates(Es0, Key) of
+ false ->
+ Es = gb_trees:from_orddict(Es0),
+ N = gb_trees:size(Es),
+ outer_edge_loop_1(Val, Es, Key, N, []);
+ true -> error
+ end
+ end.
+
+outer_edge_loop_1({Edge,V}, _, V, 0, Acc) ->
+ %% This edge completes the loop, and we have used all possible edges.
+ [Edge|Acc];
+outer_edge_loop_1({_,V}, _, V, _N, _) ->
+ %% Loop is complete, but we haven't used all edges.
+ error;
+outer_edge_loop_1({_,_}, _, _, 0, _) ->
+ %% We have used all possible edges, but somehow the loop
+ %% is not complete. I can't see how this is possible.
+ erlang:error(internal_error);
+outer_edge_loop_1({Edge,Vb}, Es, EndV, N, Acc0) ->
+ Acc = [Edge|Acc0],
+ outer_edge_loop_1(gb_trees:get(Vb, Es), Es, EndV, N-1, Acc).
+
+any_duplicates([{V,_}|_], V) -> true;
+any_duplicates([_], _) -> false;
+any_duplicates([{V,_}|Es], _) -> any_duplicates(Es, V).
+
+%% outer_edge_partition(FaceSet, WingedEdge) -> [[Edge]].
+%% Partition the outer edges of the FaceSet. Each partion
+%% of edges form a closed loop with no repeated vertices.
+%% Outer edges are edges that have one face in FaceSet
+%% and one outside.
+%% It is assumed that FaceSet consists of one region returned by
+%% wings_sel:face_regions/2.
+
+outer_edge_partition(Faces, We) ->
+ F0 = collect_outer_edges(Faces, We),
+ F = gb_trees:from_orddict(wings_util:rel2fam(F0)),
+ partition_edges(F, []).
+
+collect_outer_edges(Faces, We) when is_list(Faces) ->
+ collect_outer_edges_1(Faces, gb_sets:from_list(Faces), We);
+collect_outer_edges(Faces, We) ->
+ collect_outer_edges_1(gb_sets:to_list(Faces), Faces, We).
+
+collect_outer_edges_1(Fs0, Faces0, #we{fs=Ftab}=We) ->
+ case {gb_trees:size(Ftab),gb_sets:size(Faces0)} of
+ {AllSz,FaceSz} when AllSz < 2*FaceSz ->
+ Fs = ordsets:subtract(gb_trees:keys(Ftab), Fs0),
+ Faces = gb_sets:from_ordset(Fs),
+ Coll = collect_outer_edges_a(Faces),
+ wings_face:fold_faces(Coll, [], Fs, We);
+ {_,_} ->
+ Coll = collect_outer_edges_b(Faces0),
+ wings_face:fold_faces(Coll, [], Fs0, We)
+ end.
+
+collect_outer_edges_a(Faces) ->
+ fun(Face, _, Edge, #edge{ve=V,vs=OtherV,lf=Face,rf=Other}, Acc) ->
+ case gb_sets:is_member(Other, Faces) of
+ false -> [{V,{Edge,OtherV}}|Acc];
+ true -> Acc
+ end;
+ (Face, _, Edge, #edge{ve=OtherV,vs=V,rf=Face,lf=Other}, Acc) ->
+ case gb_sets:is_member(Other, Faces) of
+ false -> [{V,{Edge,OtherV}}|Acc];
+ true -> Acc
+ end
+ end.
+
+collect_outer_edges_b(Faces) ->
+ fun(Face, _, Edge, #edge{vs=V,ve=OtherV,lf=Face,rf=Other}, Acc) ->
+ case gb_sets:is_member(Other, Faces) of
+ false -> [{V,{Edge,OtherV}}|Acc];
+ true -> Acc
+ end;
+ (Face, _, Edge, #edge{vs=OtherV,ve=V,rf=Face,lf=Other}, Acc) ->
+ case gb_sets:is_member(Other, Faces) of
+ false -> [{V,{Edge,OtherV}}|Acc];
+ true -> Acc
+ end
+ end.
+
+partition_edges(Es0, Acc) ->
+ case gb_trees:is_empty(Es0) of
+ true -> Acc;
+ false ->
+ {Key,Val,Es1} = gb_trees:take_smallest(Es0),
+ {Cycle,Es} = part_collect_cycle(Key, Val, Es1, []),
+ partition_edges(Es, [Cycle|Acc])
+ end.
+
+%% part_collect_cycle(Vertex, VertexInfo, EdgeInfo, Acc0) ->
+%% none | {[Edge],EdgeInfo}
+%% Collect the cycle starting with Vertex.
+%%
+%% Note: This function can only return 'none' when called
+%% recursively.
+
+part_collect_cycle(_, repeated, _, _) ->
+ %% Repeated vertex - we are not allowed to go this way.
+ %% Can only happen if we were called recursively because
+ %% a fork was encountered.
+ none;
+part_collect_cycle(_Va, [{Edge,Vb}], Es0, Acc0) ->
+ %% Basic case. Only one way to go.
+ Acc = [Edge|Acc0],
+ case gb_trees:lookup(Vb, Es0) of
+ none ->
+ {Acc,Es0};
+ {value,Val} ->
+ Es = gb_trees:delete(Vb, Es0),
+ part_collect_cycle(Vb, Val, Es, Acc)
+ end;
+part_collect_cycle(Va, [Val|More], Es0, []) ->
+ %% No cycle started yet and we have multiple choice of
+ %% edges out from this vertex. It doesn't matter which
+ %% edge we follow, so we'll follow the first one.
+ {Cycle,Es} = part_collect_cycle(Va, [Val], Es0, []),
+ {Cycle,gb_trees:insert(Va, More, Es)};
+part_collect_cycle(Va, Edges, Es0, Acc) ->
+ %% We have a partially collected cycle and we have a
+ %% fork (multiple choice of edges). Here we must choose
+ %% an edge that closes the cycle without passing Va
+ %% again (because repeated vertices are not allowed).
+ Es = gb_trees:insert(Va, repeated, Es0),
+ part_fork(Va, Edges, Es, Acc, []).
+
+part_fork(Va, [Val|More], Es0, Acc, Tried) ->
+ %% Try to complete the cycle by following this edge.
+ case part_collect_cycle(Va, [Val], Es0, Acc) of
+ none ->
+ %% Failure - try the next edge.
+ part_fork(Va, More, Es0, Acc, [Val|Tried]);
+ {Cycle,Es} ->
+ %% Found a cycle. Update the vertex information
+ %% with all edges remaining.
+ {Cycle,gb_trees:update(Va, lists:reverse(Tried, More), Es)}
+ end;
+part_fork(_, [], _, _, _) ->
+ %% None of edges were possible. Can only happen if this function
+ %% was called recursively (i.e. if we hit another fork while
+ %% processing a fork).
+ none.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_edge.erl b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_edge.erl
new file mode 100644
index 0000000000..3483acb711
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_edge.erl
@@ -0,0 +1,243 @@
+%%
+%% wings_edge.erl --
+%%
+%% This module contains most edge command and edge utility functions.
+%%
+%% Copyright (c) 2001-2008 Bjorn Gustavsson.
+%%
+%% See the file "license.terms" for information on usage and redistribution
+%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+%%
+%% $Id: wings_edge.erl,v 1.1 2009/01/25 18:55:33 kostis Exp $
+%%
+
+-module(wings_edge).
+
+-export([dissolve_edges/2]).
+
+-include("wings.hrl").
+
+%%%
+%%% Dissolve.
+%%%
+
+dissolve_edges(Edges0, We0) when is_list(Edges0) ->
+ #we{es=Etab} = We1 = lists:foldl(fun internal_dissolve_edge/2, We0, Edges0),
+ case [E || E <- Edges0, gb_trees:is_defined(E, Etab)] of
+ Edges0 ->
+ %% No edge was deleted in the last pass. We are done.
+ We = wings_we:rebuild(We0#we{vc=undefined}),
+ wings_we:validate_mirror(We);
+ Edges ->
+ dissolve_edges(Edges, We1)
+ end;
+dissolve_edges(Edges, We) ->
+ dissolve_edges(gb_sets:to_list(Edges), We).
+
+internal_dissolve_edge(Edge, #we{es=Etab}=We0) ->
+ case gb_trees:lookup(Edge, Etab) of
+ none -> We0;
+ {value,#edge{ltpr=Same,ltsu=Same,rtpr=Same,rtsu=Same}} ->
+ Empty = gb_trees:empty(),
+ We0#we{vc=Empty,vp=Empty,es=Empty,fs=Empty,he=gb_sets:empty()};
+ {value,#edge{rtpr=Back,ltsu=Back}=Rec} ->
+ merge_edges(backward, Edge, Rec, We0);
+ {value,#edge{rtsu=Forward,ltpr=Forward}=Rec} ->
+ merge_edges(forward, Edge, Rec, We0);
+ {value,Rec} ->
+ try dissolve_edge_1(Edge, Rec, We0) of
+ We -> We
+ catch
+ throw:hole -> We0
+ end
+ end.
+
+%% dissolve_edge_1(Edge, EdgeRecord, We) -> We
+%% Remove an edge and a face. If one of the faces is degenerated
+%% (only consists of two edges), remove that one. Otherwise, it
+%% doesn't matter which face we remove.
+dissolve_edge_1(Edge, #edge{lf=Remove,rf=Keep,ltpr=Same,ltsu=Same}=Rec, We) ->
+ dissolve_edge_2(Edge, Remove, Keep, Rec, We);
+dissolve_edge_1(Edge, #edge{lf=Keep,rf=Remove}=Rec, We) ->
+ dissolve_edge_2(Edge, Remove, Keep, Rec, We).
+
+dissolve_edge_2(Edge, FaceRemove, FaceKeep,
+ #edge{ltpr=LP,ltsu=LS,rtpr=RP,rtsu=RS},
+ #we{fs=Ftab0,es=Etab0,he=Htab0}=We0) ->
+ %% First change face for all edges surrounding the face we will remove.
+ Etab1 = wings_face:fold(
+ fun (_, E, _, IntEtab) when E =:= Edge -> IntEtab;
+ (_, E, R, IntEtab) ->
+ case R of
+ #edge{lf=FaceRemove,rf=FaceKeep} ->
+ throw(hole);
+ #edge{rf=FaceRemove,lf=FaceKeep} ->
+ throw(hole);
+ #edge{lf=FaceRemove} ->
+ gb_trees:update(E, R#edge{lf=FaceKeep}, IntEtab);
+ #edge{rf=FaceRemove} ->
+ gb_trees:update(E, R#edge{rf=FaceKeep}, IntEtab)
+ end
+ end, Etab0, FaceRemove, We0),
+
+ %% Patch all predecessors and successor of the edge we will remove.
+ Etab2 = patch_edge(LP, RS, Edge, Etab1),
+ Etab3 = patch_edge(LS, RP, Edge, Etab2),
+ Etab4 = patch_edge(RP, LS, Edge, Etab3),
+ Etab5 = patch_edge(RS, LP, Edge, Etab4),
+
+ %% Remove the edge.
+ Etab = gb_trees:delete(Edge, Etab5),
+ Htab = hardness(Edge, soft, Htab0),
+
+ %% Remove the face. Patch the face entry for the remaining face.
+ Ftab1 = gb_trees:delete(FaceRemove, Ftab0),
+ We1 = wings_facemat:delete_face(FaceRemove, We0),
+ Ftab = gb_trees:update(FaceKeep, LP, Ftab1),
+
+ %% Return result.
+ We = We1#we{es=Etab,fs=Ftab,vc=undefined,he=Htab},
+ AnEdge = gb_trees:get(FaceKeep, Ftab),
+ case gb_trees:get(AnEdge, Etab) of
+ #edge{lf=FaceKeep,ltpr=Same,ltsu=Same} ->
+ internal_dissolve_edge(AnEdge, We);
+ #edge{rf=FaceKeep,rtpr=Same,rtsu=Same} ->
+ internal_dissolve_edge(AnEdge, We);
+ _Other ->
+ case wings_we:is_face_consistent(FaceKeep, We) of
+ true ->
+ We;
+ false ->
+ io:format("Dissolving would cause a badly formed face.")
+ end
+ end.
+
+%%
+%% We like winged edges, but not winged vertices (a vertex with
+%% only two edges connected to it). We will remove the winged vertex
+%% by joining the two edges connected to it.
+%%
+
+merge_edges(Dir, Edge, Rec, #we{es=Etab}=We) ->
+ {Va,Vb,_,_,_,_,To,To} = half_edge(Dir, Rec),
+ case gb_trees:get(To, Etab) of
+ #edge{vs=Va,ve=Vb} ->
+ del_2edge_face(Dir, Edge, Rec, To, We);
+ #edge{vs=Vb,ve=Va} ->
+ del_2edge_face(Dir, Edge, Rec, To, We);
+ _Other ->
+ merge_1(Dir, Edge, Rec, To, We)
+ end.
+
+merge_1(Dir, Edge, Rec, To, #we{es=Etab0,fs=Ftab0,he=Htab0}=We) ->
+ OtherDir = reverse_dir(Dir),
+ {Vkeep,Vdelete,Lf,Rf,A,B,L,R} = half_edge(OtherDir, Rec),
+ Etab1 = patch_edge(L, To, Edge, Etab0),
+ Etab2 = patch_edge(R, To, Edge, Etab1),
+ Etab3 = patch_half_edge(To, Vkeep, Lf, A, L, Rf, B, R, Vdelete, Etab2),
+ Htab = hardness(Edge, soft, Htab0),
+ Etab = gb_trees:delete(Edge, Etab3),
+ #edge{lf=Lf,rf=Rf} = Rec,
+ Ftab1 = update_face(Lf, To, Edge, Ftab0),
+ Ftab = update_face(Rf, To, Edge, Ftab1),
+ merge_2(To, We#we{es=Etab,fs=Ftab,he=Htab,vc=undefined}).
+
+merge_2(Edge, #we{es=Etab}=We) ->
+ %% If the merged edge is part of a two-edge face, we must
+ %% remove that edge too.
+ case gb_trees:get(Edge, Etab) of
+ #edge{ltpr=Same,ltsu=Same} ->
+ internal_dissolve_edge(Edge, We);
+ #edge{rtpr=Same,rtsu=Same} ->
+ internal_dissolve_edge(Edge, We);
+ _Other -> We
+ end.
+
+update_face(Face, Edge, OldEdge, Ftab) ->
+ case gb_trees:get(Face, Ftab) of
+ OldEdge -> gb_trees:update(Face, Edge, Ftab);
+ _Other -> Ftab
+ end.
+
+del_2edge_face(Dir, EdgeA, RecA, EdgeB,
+ #we{es=Etab0,fs=Ftab0,he=Htab0}=We) ->
+ {_,_,Lf,Rf,_,_,_,_} = half_edge(reverse_dir(Dir), RecA),
+ RecB = gb_trees:get(EdgeB, Etab0),
+ Del = gb_sets:from_list([EdgeA,EdgeB]),
+ EdgeANear = stabile_neighbor(RecA, Del),
+ EdgeBNear = stabile_neighbor(RecB, Del),
+ Etab1 = patch_edge(EdgeANear, EdgeBNear, EdgeA, Etab0),
+ Etab2 = patch_edge(EdgeBNear, EdgeANear, EdgeB, Etab1),
+ Etab3 = gb_trees:delete(EdgeA, Etab2),
+ Etab = gb_trees:delete(EdgeB, Etab3),
+
+ %% Patch hardness table.
+ Htab1 = hardness(EdgeA, soft, Htab0),
+ Htab = hardness(EdgeB, soft, Htab1),
+
+ %% Patch the face table.
+ #edge{lf=Klf,rf=Krf} = gb_trees:get(EdgeANear, Etab),
+ KeepFaces = ordsets:from_list([Klf,Krf]),
+ EdgeAFaces = ordsets:from_list([Lf,Rf]),
+ [DelFace] = ordsets:subtract(EdgeAFaces, KeepFaces),
+ Ftab1 = gb_trees:delete(DelFace, Ftab0),
+ [KeepFace] = ordsets:intersection(KeepFaces, EdgeAFaces),
+ Ftab2 = update_face(KeepFace, EdgeANear, EdgeA, Ftab1),
+ Ftab = update_face(KeepFace, EdgeBNear, EdgeB, Ftab2),
+
+ %% Return result.
+ We#we{vc=undefined,es=Etab,fs=Ftab,he=Htab}.
+
+stabile_neighbor(#edge{ltpr=Ea,ltsu=Eb,rtpr=Ec,rtsu=Ed}, Del) ->
+ [Edge] = lists:foldl(fun(E, A) ->
+ case gb_sets:is_member(E, Del) of
+ true -> A;
+ false -> [E|A]
+ end
+ end, [], [Ea,Eb,Ec,Ed]),
+ Edge.
+
+%%%
+%%% Setting hard/soft edges.
+%%%
+
+hardness(Edge, soft, Htab) -> gb_sets:delete_any(Edge, Htab);
+hardness(Edge, hard, Htab) -> gb_sets:add(Edge, Htab).
+
+%%%
+%%% Utilities.
+%%%
+
+reverse_dir(forward) -> backward;
+reverse_dir(backward) -> forward.
+
+half_edge(backward, #edge{vs=Va,ve=Vb,lf=Lf,rf=Rf,a=A,b=B,ltsu=L,rtpr=R}) ->
+ {Va,Vb,Lf,Rf,A,B,L,R};
+half_edge(forward, #edge{ve=Va,vs=Vb,lf=Lf,rf=Rf,a=A,b=B,ltpr=L,rtsu=R}) ->
+ {Va,Vb,Lf,Rf,A,B,L,R}.
+
+patch_half_edge(Edge, V, FaceA, A, Ea, FaceB, B, Eb, OrigV, Etab) ->
+ New = case gb_trees:get(Edge, Etab) of
+ #edge{vs=OrigV,lf=FaceA,rf=FaceB}=Rec ->
+ Rec#edge{a=A,vs=V,ltsu=Ea,rtpr=Eb};
+ #edge{vs=OrigV,lf=FaceB,rf=FaceA}=Rec ->
+ Rec#edge{a=B,vs=V,ltsu=Eb,rtpr=Ea};
+ #edge{ve=OrigV,lf=FaceA,rf=FaceB}=Rec ->
+ Rec#edge{b=B,ve=V,ltpr=Ea,rtsu=Eb};
+ #edge{ve=OrigV,lf=FaceB,rf=FaceA}=Rec ->
+ Rec#edge{b=A,ve=V,ltpr=Eb,rtsu=Ea}
+ end,
+ gb_trees:update(Edge, New, Etab).
+
+patch_edge(Edge, ToEdge, OrigEdge, Etab) ->
+ New = case gb_trees:get(Edge, Etab) of
+ #edge{ltsu=OrigEdge}=R ->
+ R#edge{ltsu=ToEdge};
+ #edge{ltpr=OrigEdge}=R ->
+ R#edge{ltpr=ToEdge};
+ #edge{rtsu=OrigEdge}=R ->
+ R#edge{rtsu=ToEdge};
+ #edge{rtpr=OrigEdge}=R ->
+ R#edge{rtpr=ToEdge}
+ end,
+ gb_trees:update(Edge, New, Etab).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_edge_cmd.erl b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_edge_cmd.erl
new file mode 100644
index 0000000000..91fa5b2a39
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_edge_cmd.erl
@@ -0,0 +1,90 @@
+%%
+%% wings_edge.erl --
+%%
+%% This module contains most edge command and edge utility functions.
+%%
+
+-module(wings_edge_cmd).
+
+-export([loop_cut/1]).
+
+-include("wings.hrl").
+
+%%%
+%%% The Loop Cut command.
+%%%
+
+loop_cut(St0) ->
+ {Sel,St} = wings_sel:fold(fun loop_cut/3, {[],St0}, St0),
+ wings_sel:set(body, Sel, St).
+
+loop_cut(Edges, #we{name=Name,id=Id,fs=Ftab}=We0, {Sel,St0}) ->
+ AdjFaces = wings_face:from_edges(Edges, We0),
+ case loop_cut_partition(AdjFaces, Edges, We0, []) of
+ [_] ->
+ io:format("Edge loop doesn't divide ~p into two parts.", [Name]);
+ Parts0 ->
+ %% We arbitrarily decide that the largest part of the object
+ %% will be left unselected and will keep the name of the object.
+
+ Parts1 = [{gb_trees:size(P),P} || P <- Parts0],
+ Parts2 = lists:reverse(lists:sort(Parts1)),
+ [_|Parts] = [gb_sets:to_list(P) || {_,P} <- Parts2],
+
+ %% Also, this first part will also contain any sub-object
+ %% that was not reachable from any of the edges. Therefore,
+ %% we calculate the first part as the complement of the union
+ %% of all other parts.
+
+ FirstComplement = ordsets:union(Parts),
+ First = ordsets:subtract(gb_trees:keys(Ftab), FirstComplement),
+
+ We = wings_dissolve:complement(First, We0),
+ Shs = St0#st.shapes,
+ St = St0#st{shapes=gb_trees:update(Id, We, Shs)},
+ loop_cut_make_copies(Parts, We0, Sel, St)
+ end.
+
+loop_cut_make_copies([P|Parts], We0, Sel0, #st{onext=Id}=St0) ->
+ Sel = [{Id,gb_sets:singleton(0)}|Sel0],
+ We = wings_dissolve:complement(P, We0),
+ St = wings_shape:insert(We, cut, St0),
+ loop_cut_make_copies(Parts, We0, Sel, St);
+loop_cut_make_copies([], _, Sel, St) -> {Sel,St}.
+
+loop_cut_partition(Faces0, Edges, We, Acc) ->
+ case gb_sets:is_empty(Faces0) of
+ true -> Acc;
+ false ->
+ {AFace,Faces1} = gb_sets:take_smallest(Faces0),
+ Reachable = collect_faces(AFace, Edges, We),
+ Faces = gb_sets:difference(Faces1, Reachable),
+ loop_cut_partition(Faces, Edges, We, [Reachable|Acc])
+ end.
+
+collect_faces(Face, Edges, We) ->
+ collect_faces(gb_sets:singleton(Face), We, Edges, gb_sets:empty()).
+
+collect_faces(Work0, We, Edges, Acc0) ->
+ case gb_sets:is_empty(Work0) of
+ true -> Acc0;
+ false ->
+ {Face,Work1} = gb_sets:take_smallest(Work0),
+ Acc = gb_sets:insert(Face, Acc0),
+ Work = collect_maybe_add(Work1, Face, Edges, We, Acc),
+ collect_faces(Work, We, Edges, Acc)
+ end.
+
+collect_maybe_add(Work, Face, Edges, We, Res) ->
+ wings_face:fold(
+ fun(_, Edge, Rec, A) ->
+ case gb_sets:is_member(Edge, Edges) of
+ true -> A;
+ false ->
+ Of = wings_face:other(Face, Rec),
+ case gb_sets:is_member(Of, Res) of
+ true -> A;
+ false -> gb_sets:add(Of, A)
+ end
+ end
+ end, Work, Face, We).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_face.erl b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_face.erl
new file mode 100644
index 0000000000..487c05aa58
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_face.erl
@@ -0,0 +1,127 @@
+%%
+%% wings_face.erl --
+%%
+%% This module contains help routines for faces, such as fold functions
+%% face iterators.
+%%
+
+-module(wings_face).
+
+-export([delete_bad_faces/2, fold/4, fold_faces/4, from_edges/2,
+ inner_edges/2, to_edges/2, other/2]).
+
+-include("wings.hrl").
+
+from_edges(Es, #we{es=Etab}) when is_list(Es) ->
+ from_edges_1(Es, Etab, []);
+from_edges(Es, We) ->
+ from_edges(gb_sets:to_list(Es), We).
+
+from_edges_1([E|Es], Etab, Acc) ->
+ #edge{lf=Lf,rf=Rf} = gb_trees:get(E, Etab),
+ from_edges_1(Es, Etab, [Lf,Rf|Acc]);
+from_edges_1([], _, Acc) -> gb_sets:from_list(Acc).
+
+%% other(Face, EdgeRecord) -> OtherFace
+%% Pick up the "other face" from an edge record.
+other(Face, #edge{lf=Face,rf=Other}) -> Other;
+other(Face, #edge{rf=Face,lf=Other}) -> Other.
+
+%% to_edges(Faces, We) -> [Edge]
+%% Convert a set or list of faces to a list of edges.
+to_edges(Fs, We) ->
+ ordsets:from_list(to_edges_raw(Fs, We)).
+
+%% inner_edges(Faces, We) -> [Edge]
+%% Given a set of faces, return all inner edges.
+inner_edges(Faces, We) ->
+ S = to_edges_raw(Faces, We),
+ inner_edges_1(lists:sort(S), []).
+
+inner_edges_1([E,E|T], In) ->
+ inner_edges_1(T, [E|In]);
+inner_edges_1([_|T], In) ->
+ inner_edges_1(T, In);
+inner_edges_1([], In) -> lists:reverse(In).
+
+%% Fold over all edges surrounding a face.
+
+fold(F, Acc, Face, #we{es=Etab,fs=Ftab}) ->
+ Edge = gb_trees:get(Face, Ftab),
+ fold(Edge, Etab, F, Acc, Face, Edge, not_done).
+
+fold(LastEdge, _, _, Acc, _, LastEdge, done) -> Acc;
+fold(Edge, Etab, F, Acc0, Face, LastEdge, _) ->
+ case gb_trees:get(Edge, Etab) of
+ #edge{ve=V,lf=Face,ltsu=NextEdge}=E ->
+ Acc = F(V, Edge, E, Acc0),
+ fold(NextEdge, Etab, F, Acc, Face, LastEdge, done);
+ #edge{vs=V,rf=Face,rtsu=NextEdge}=E ->
+ Acc = F(V, Edge, E, Acc0),
+ fold(NextEdge, Etab, F, Acc, Face, LastEdge, done)
+ end.
+
+%% Fold over a set of faces.
+
+fold_faces(F, Acc0, [Face|Faces], #we{es=Etab,fs=Ftab}=We) ->
+ Edge = gb_trees:get(Face, Ftab),
+ Acc = fold_faces_1(Edge, Etab, F, Acc0, Face, Edge, not_done),
+ fold_faces(F, Acc, Faces, We);
+fold_faces(_F, Acc, [], _We) -> Acc;
+fold_faces(F, Acc, Faces, We) ->
+ fold_faces(F, Acc, gb_sets:to_list(Faces), We).
+
+fold_faces_1(LastEdge, _, _, Acc, _, LastEdge, done) -> Acc;
+fold_faces_1(Edge, Etab, F, Acc0, Face, LastEdge, _) ->
+ case gb_trees:get(Edge, Etab) of
+ #edge{ve=V,lf=Face,ltsu=NextEdge}=E ->
+ Acc = F(Face, V, Edge, E, Acc0),
+ fold_faces_1(NextEdge, Etab, F, Acc, Face, LastEdge, done);
+ #edge{vs=V,rf=Face,rtsu=NextEdge}=E ->
+ Acc = F(Face, V, Edge, E, Acc0),
+ fold_faces_1(NextEdge, Etab, F, Acc, Face, LastEdge, done)
+ end.
+
+%% Return an unsorted list of edges for the faces (with duplicates).
+
+to_edges_raw(Faces, #we{es=Etab,fs=Ftab}) when is_list(Faces) ->
+ to_edges_raw(Faces, Ftab, Etab, []);
+to_edges_raw(Faces, We) ->
+ to_edges_raw(gb_sets:to_list(Faces), We).
+
+to_edges_raw([Face|Faces], Ftab, Etab, Acc0) ->
+ Edge = gb_trees:get(Face, Ftab),
+ Acc = to_edges_raw_1(Edge, Etab, Acc0, Face, Edge, not_done),
+ to_edges_raw(Faces, Ftab, Etab, Acc);
+to_edges_raw([], _, _, Acc) -> Acc.
+
+to_edges_raw_1(LastEdge, _, Acc, _, LastEdge, done) -> Acc;
+to_edges_raw_1(Edge, Etab, Acc, Face, LastEdge, _) ->
+ case gb_trees:get(Edge, Etab) of
+ #edge{lf=Face,ltsu=NextEdge} ->
+ to_edges_raw_1(NextEdge, Etab, [Edge|Acc], Face, LastEdge, done);
+ #edge{rf=Face,rtsu=NextEdge} ->
+ to_edges_raw_1(NextEdge, Etab, [Edge|Acc], Face, LastEdge, done)
+ end.
+
+delete_bad_faces(Fs, #we{fs=Ftab,es=Etab}=We) when is_list(Fs) ->
+ Es = bad_edges(Fs, Ftab, Etab, []),
+ wings_edge:dissolve_edges(Es, We);
+delete_bad_faces(Fs, We) ->
+ delete_bad_faces(gb_sets:to_list(Fs), We).
+
+bad_edges([F|Fs], Ftab, Etab, Acc) ->
+ case gb_trees:lookup(F, Ftab) of
+ {value,Edge} ->
+ case gb_trees:get(Edge, Etab) of
+ #edge{ltpr=Same,ltsu=Same,rtpr=Same,rtsu=Same} ->
+ erlang:error({internal_error,one_edged_face,F});
+ #edge{ltpr=Same,ltsu=Same} ->
+ bad_edges(Fs, Ftab, Etab, [Edge|Acc]);
+ #edge{rtpr=Same,rtsu=Same} ->
+ bad_edges(Fs, Ftab, Etab, [Edge|Acc]);
+ _ -> bad_edges(Fs, Ftab, Etab, Acc)
+ end;
+ none -> bad_edges(Fs, Ftab, Etab, Acc)
+ end;
+bad_edges([], _, _, Acc) -> Acc.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_facemat.erl b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_facemat.erl
new file mode 100644
index 0000000000..a3fa5e3508
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_facemat.erl
@@ -0,0 +1,299 @@
+%%
+%% wings_facemat.erl --
+%%
+%% This module keeps tracks of the mapping from a face number
+%% to its material name.
+%%
+%% Copyright (c) 2001-2005 Bjorn Gustavsson
+%%
+%% See the file "license.terms" for information on usage and redistribution
+%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+%%
+%% $Id: wings_facemat.erl,v 1.1 2009/01/25 18:55:33 kostis Exp $
+%%
+%%
+%%
+
+-module(wings_facemat).
+-export([all/1,face/2,used_materials/1,mat_faces/2,
+ assign/2,assign/3,
+ delete_face/2,delete_faces/2,keep_faces/2,
+ hide_faces/1,show_faces/1,
+ renumber/2,gc/1,merge/1]).
+
+-include("wings.hrl").
+-import(lists, [keysearch/3,reverse/1,reverse/2,sort/1]).
+
+%%%
+%%% API functions for retrieving information.
+%%%
+
+%% all(We) -> [{Face,MaterialName}]
+%% Return materials for all faces as an ordered list.
+all(#we{mat=M}=We) when is_atom(M) ->
+ Vis = visible_faces(We),
+ make_tab(Vis, M);
+all(#we{mat=L}) when is_list(L) ->
+ remove_invisible(L).
+
+%% face(Face, We) -> MaterialName
+%% Return the material for the face Face.
+face(_, #we{mat=M}) when is_atom(M) -> M;
+face(Face, #we{mat=Tab}) ->
+ {value,{_,Mat}} = keysearch(Face, 1, Tab),
+ Mat.
+
+%% used_materials(We) -> [MaterialName]
+%% Return an ordered list of all materials used in the We.
+used_materials(#we{mat=M}) when is_atom(M) -> [M];
+used_materials(#we{mat=L}) when is_list(L) ->
+ used_materials_1(L, []).
+
+%% mat_faces([{Face,Info}], We) -> [{Mat,[{Face,Info}]}]
+%% Group face tab into groups based on material.
+%% Used for displaying objects.
+mat_faces(Ftab, #we{mat=AtomMat}) when is_atom(AtomMat) ->
+ [{AtomMat,Ftab}];
+mat_faces(Ftab, #we{mat=MatTab}) ->
+ mat_faces_1(Ftab, remove_invisible(MatTab), []).
+
+%%%
+%%% API functions for updating material name mapping.
+%%%
+
+%% assign([{Face,MaterialName}], We) -> We'
+%% Assign materials.
+assign([], We) -> We;
+assign([{F,M}|_]=FaceMs, We) when is_atom(M), is_integer(F) ->
+ Tab = ordsets:from_list(FaceMs),
+ assign_face_ms(Tab, We).
+
+%% assign(MaterialName, Faces, We) -> We'
+%% Assign MaterialName to all faces Faces.
+assign(Mat, _, #we{mat=Mat}=We) when is_atom(Mat) -> We;
+assign(Mat, Fs, We) when is_atom(Mat), is_list(Fs) ->
+ assign_1(Mat, Fs, We);
+assign(Mat, Fs, We) when is_atom(Mat) ->
+ assign_1(Mat, gb_sets:to_list(Fs), We).
+
+%% delete_face(Face, We) -> We'
+%% Delete the material name mapping for the face Face.
+delete_face(_, #we{mat=AtomMat}=We) when is_atom(AtomMat) -> We;
+delete_face(Face, #we{mat=MatTab0}=We) ->
+ MatTab = orddict:erase(Face, MatTab0),
+ We#we{mat=MatTab}.
+
+%% delete_face(Faces, We) -> We'
+%% Delete the material name mapping for all faces Faces.
+delete_faces(_, #we{mat=AtomMat}=We) when is_atom(AtomMat) -> We;
+delete_faces(Faces0, #we{mat=MatTab0}=We) when is_list(Faces0) ->
+ Faces = sofs:from_external(Faces0, [face]),
+ MatTab1 = sofs:from_external(MatTab0, [{face,mat}]),
+ MatTab2 = sofs:drestriction(MatTab1, Faces),
+ MatTab = sofs:to_external(MatTab2),
+ We#we{mat=MatTab};
+delete_faces(Faces, We) ->
+ delete_faces(gb_sets:to_list(Faces), We).
+
+%% keep_faces(Faces, We) -> We'
+%% Delete all the other material names mapping for all faces other Faces.
+keep_faces(_, #we{mat=AtomMat}=We) when is_atom(AtomMat) -> We;
+keep_faces([Face], We) ->
+ Mat = face(Face,We),
+ We#we{mat=[{Face,Mat}]};
+keep_faces(Faces0, #we{mat=MatTab0}=We) when is_list(Faces0) ->
+ Faces = sofs:from_external(Faces0, [face]),
+ MatTab1 = sofs:from_external(MatTab0, [{face,mat}]),
+ MatTab2 = sofs:restriction(MatTab1, Faces),
+ MatTab = sofs:to_external(MatTab2),
+ We#we{mat=MatTab};
+keep_faces(Faces, We) ->
+ keep_faces(gb_sets:to_list(Faces), We).
+
+%% hide_faces(We) -> We'
+%% Update the material name mapping in the We to reflect
+%% the newly hidden faces in the face tab.
+hide_faces(#we{mat=M}=We) when is_atom(M) -> We;
+hide_faces(#we{mat=L0,fs=Ftab}=We) ->
+ L = hide_faces_1(L0, Ftab, []),
+ We#we{mat=L}.
+
+%% show_faces(We) -> We'
+%% Update the material name mapping in the We to reflect
+%% that all faces are again visible.
+show_faces(#we{mat=M}=We) when is_atom(M) -> We;
+show_faces(#we{mat=L0}=We) ->
+ L = show_faces_1(L0, []),
+ We#we{mat=L}.
+
+%% renumber(MaterialMapping, FaceOldToNew) -> MaterialMapping.
+%% Renumber face number in material name mapping.
+renumber(Mat, _) when is_atom(Mat) -> Mat;
+renumber(L, Fmap) when is_list(L) -> renumber_1(L, Fmap, []).
+
+%% gc(We) -> We'
+%% Garbage collect the material mapping information, removing
+%% the mapping for any face no longer present in the face table.
+gc(#we{mat=Mat}=We) when is_atom(Mat) -> We;
+gc(#we{mat=Tab0,fs=Ftab}=We) ->
+ Fs = sofs:from_external(gb_trees:keys(Ftab), [face]),
+ Tab1 = sofs:from_external(Tab0, [{face,material}]),
+ Tab2 = sofs:restriction(Tab1, Fs),
+ Tab = sofs:to_external(Tab2),
+ We#we{mat=compress(Tab)}.
+
+%% merge([We]) -> [{Face,MaterialName}] | MaterialName.
+%% Merge materials for several objects.
+merge([#we{mat=M}|Wes]=L) when is_atom(M) ->
+ case merge_all_same(Wes, M) of
+ true -> M;
+ false -> merge_1(L, [])
+ end;
+merge(L) -> merge_1(L, []).
+
+merge_1([#we{mat=M,es=Etab}|T], Acc) when is_atom(M) ->
+ FsM = merge_2(gb_trees:values(Etab), M, []),
+ merge_1(T, [FsM|Acc]);
+merge_1([#we{mat=FsMs}|T], Acc) ->
+ merge_1(T, [FsMs|Acc]);
+merge_1([], Acc) -> lists:merge(Acc).
+
+merge_2([#edge{lf=Lf,rf=Rf}|T], M, Acc) ->
+ merge_2(T, M, [{Lf,M},{Rf,M}|Acc]);
+merge_2([], _, Acc) -> ordsets:from_list(Acc).
+
+merge_all_same([#we{mat=M}|Wes], M) -> merge_all_same(Wes, M);
+merge_all_same([_|_], _) -> false;
+merge_all_same([], _) -> true.
+
+%%%
+%%% Local functions.
+%%%
+
+assign_1(Mat, Fs, #we{fs=Ftab}=We) ->
+ case length(Fs) =:= gb_trees:size(Ftab) of
+ true -> We#we{mat=Mat};
+ false -> assign_2(Mat, Fs, We)
+ end.
+
+assign_2(Mat, Fs0, #we{fs=Ftab,mat=Mat0}=We) when is_atom(Mat0) ->
+ Fs = ordsets:from_list(Fs0),
+ OtherFaces = ordsets:subtract(gb_trees:keys(Ftab), Fs),
+ Tab0 = make_tab(OtherFaces, Mat0),
+ Tab1 = make_tab(Fs, Mat),
+ Tab = lists:merge(Tab0, Tab1),
+ We#we{mat=Tab};
+assign_2(Mat, Fs0, #we{mat=Tab0}=We) when is_list(Tab0) ->
+ Fs = ordsets:from_list(Fs0),
+ Tab1 = make_tab(Fs, Mat),
+ Tab = mat_merge(Tab1, Tab0, []),
+ We#we{mat=Tab}.
+
+assign_face_ms(Tab, #we{fs=Ftab}=We) ->
+ case length(Tab) =:= gb_trees:size(Ftab) of
+ true -> We#we{mat=compress(Tab)};
+ false -> assign_face_ms_1(Tab, We)
+ end.
+
+assign_face_ms_1(Tab1, #we{fs=Ftab,mat=Mat0}=We) when is_atom(Mat0) ->
+ Tab0 = make_tab(gb_trees:keys(Ftab), Mat0),
+ Tab = mat_merge(Tab1, Tab0, []),
+ We#we{mat=Tab};
+assign_face_ms_1(Tab1, #we{mat=Tab0}=We) when is_list(Tab0) ->
+ Tab = mat_merge(Tab1, Tab0, []),
+ We#we{mat=Tab}.
+
+mat_merge([{Fn,_}|_]=Fns, [{Fo,_}=Fold|Fos], Acc) when Fo < Fn ->
+ mat_merge(Fns, Fos, [Fold|Acc]);
+mat_merge([{Fn,_}=Fnew|Fns], [{Fo,_}|_]=Fos, Acc) when Fo > Fn ->
+ mat_merge(Fns, Fos, [Fnew|Acc]);
+mat_merge([Fnew|Fns], [_|Fos], Acc) -> % Equality
+ mat_merge(Fns, Fos, [Fnew|Acc]);
+mat_merge([], Fos, Acc) ->
+ rev_compress(Acc, Fos);
+mat_merge(Fns, [], Acc) ->
+ rev_compress(Acc, Fns).
+
+make_tab(Fs, M) ->
+ make_tab_1(Fs, M, []).
+
+make_tab_1([F|Fs], M, Acc) ->
+ make_tab_1(Fs, M, [{F,M}|Acc]);
+make_tab_1([], _, Acc) -> reverse(Acc).
+
+
+visible_faces(#we{fs=Ftab}) ->
+ visible_faces_1(gb_trees:keys(Ftab)).
+
+visible_faces_1([F|Fs]) when F < 0 ->
+ visible_faces_1(Fs);
+visible_faces_1(Fs) -> Fs.
+
+remove_invisible([{F,_}|Fs]) when F < 0 ->
+ remove_invisible(Fs);
+remove_invisible(Fs) -> Fs.
+
+hide_faces_1([{F,_}=P|Fms], Ftab, Acc) when F < 0 ->
+ hide_faces_1(Fms, Ftab, [P|Acc]);
+hide_faces_1([{F,M}=P|Fms], Ftab, Acc) ->
+ case gb_trees:is_defined(F, Ftab) of
+ false -> hide_faces_1(Fms, Ftab, [{-F-1,M}|Acc]);
+ true -> hide_faces_1(Fms, Ftab, [P|Acc])
+ end;
+hide_faces_1([], _, Acc) -> sort(Acc).
+
+show_faces_1([{F,M}|Fms], Acc) when F < 0 ->
+ show_faces_1(Fms, [{-F-1,M}|Acc]);
+show_faces_1(Fs, Acc) -> sort(Acc++Fs).
+
+renumber_1([{F,M}|T], Fmap, Acc) ->
+ renumber_1(T, Fmap, [{gb_trees:get(F, Fmap),M}|Acc]);
+renumber_1([], _, Acc) -> sort(Acc).
+
+%% rev_compress([{Face,Mat}], [{Face,Mat}]) -> [{Face,Mat}] | Mat.
+%% Reverse just like lists:reverse/2, but if all materials
+%% turns out to be just the same, return that material.
+rev_compress(L, Acc) ->
+ case same_mat(Acc) of
+ [] -> reverse(L, Acc);
+ M -> rev_compress_1(L, M, Acc)
+ end.
+
+rev_compress_1([{_,M}=E|T], M, Acc) ->
+ %% Same material.
+ rev_compress_1(T, M, [E|Acc]);
+rev_compress_1([_|_]=L, _, Acc) ->
+ %% Another material. Finish by using reverse/2.
+ reverse(L, Acc);
+rev_compress_1([], M, _) ->
+ %% All materials turned out to be the same.
+ M.
+
+%% compress(MaterialTab) -> [{Face,Mat}] | Mat.
+%% Compress a face mapping if possible.
+compress(M) when is_atom(M) -> M;
+compress(L) when is_list(L) ->
+ case same_mat(L) of
+ [] -> L;
+ M -> M
+ end.
+
+same_mat([]) -> [];
+same_mat([{_,M}|T]) -> same_mat_1(T, M).
+
+same_mat_1([{_,M}|T], M) -> same_mat_1(T, M);
+same_mat_1([], M) -> M;
+same_mat_1(_, _) -> [].
+
+used_materials_1([{_,M}|T], [M|_]=Acc) ->
+ used_materials_1(T, Acc);
+used_materials_1([{_,M}|T], Acc) ->
+ used_materials_1(T, [M|Acc]);
+used_materials_1([], Acc) ->
+ ordsets:from_list(Acc).
+
+mat_faces_1([{F1,_}|_]=Fs, [{F2,_}|Ms], Acc) when F2 < F1 ->
+ mat_faces_1(Fs, Ms, Acc);
+mat_faces_1([{F,Info}|Fs], [{F,Mat}|Ms], Acc) ->
+ mat_faces_1(Fs, Ms, [{Mat,{F,Info}}|Acc]);
+mat_faces_1([], _, Acc) -> wings_util:rel2fam(Acc).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_intl.hrl b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_intl.hrl
new file mode 100644
index 0000000000..ebcb560f27
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_intl.hrl
@@ -0,0 +1,15 @@
+%%
+%% wings_intl.hrl --
+%%
+%% Defines for translations
+%%
+%% Copyright (c) 2001-2005 Bjorn Gustavsson
+%%
+%% See the file "license.terms" for information on usage and redistribution
+%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+%%
+%% $Id: wings_intl.hrl,v 1.1 2009/01/25 18:55:33 kostis Exp $
+%%
+
+-define(STR(A,B,Str), wings_lang:str({?MODULE,A,B},Str)).
+-define(__(Key,Str), wings_lang:str({?MODULE,Key},Str)).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_io.erl b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_io.erl
new file mode 100644
index 0000000000..39002c675d
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_io.erl
@@ -0,0 +1,37 @@
+%%
+%% wings_io.erl --
+%%
+%% This module contains most of the low-level GUI for Wings.
+%%
+
+-module(wings_io).
+
+-export([get_matching_events/1]).
+
+-define(EVENT_QUEUE, wings_io_event_queue).
+
+%%%
+%%% Input.
+%%%
+
+get_matching_events(Filter) ->
+ Eq = get(?EVENT_QUEUE),
+ get_matching_events_1(Filter, Eq, [], []).
+
+get_matching_events_1(Filter, Eq0, Match, NoMatch) ->
+ case queue:out(Eq0) of
+ {{value,Ev},Eq} ->
+ case Filter(Ev) of
+ false ->
+ get_matching_events_1(Filter, Eq, Match, [Ev|NoMatch]);
+ true ->
+ get_matching_events_1(Filter, Eq, [Ev|Match], NoMatch)
+ end;
+ {empty,{In,Out}} ->
+ case Match of
+ [] -> [];
+ _ ->
+ put(?EVENT_QUEUE, {In, lists:reverse(NoMatch, Out)}),
+ Match
+ end
+ end.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_sel.erl b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_sel.erl
new file mode 100644
index 0000000000..eef797027e
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_sel.erl
@@ -0,0 +1,68 @@
+%%
+%% wings_sel.erl --
+%%
+%% This module implements selection utilities.
+%%
+
+-module(wings_sel).
+
+-export([face_regions/2, fold/3, set/3]).
+
+-include("wings.hrl").
+
+set(Mode, Sel, St) ->
+ St#st{selmode=Mode, sel=lists:sort(Sel), sh=false}.
+
+%%%
+%%% Fold over the selection.
+%%%
+
+fold(F, Acc, #st{sel=Sel,shapes=Shapes}) ->
+ fold_1(F, Acc, Shapes, Sel).
+
+fold_1(F, Acc0, Shapes, [{Id,Items}|T]) ->
+ We = gb_trees:get(Id, Shapes),
+ ?ASSERT(We#we.id =:= Id),
+ fold_1(F, F(Items, We, Acc0), Shapes, T);
+fold_1(_F, Acc, _Shapes, []) -> Acc.
+
+%%%
+%%% Divide the face selection into regions where each face shares at least
+%%% one edge with another face in the same region. Two faces can share a
+%%% vertex without necessarily being in the same region.
+%%%
+
+face_regions(Faces, We) when is_list(Faces) ->
+ face_regions_1(gb_sets:from_list(Faces), We);
+face_regions(Faces, We) ->
+ face_regions_1(Faces, We).
+
+face_regions_1(Faces, We) ->
+ find_face_regions(Faces, We, fun collect_face_fun/5, []).
+
+find_face_regions(Faces0, We, Coll, Acc) ->
+ case gb_sets:is_empty(Faces0) of
+ true -> Acc;
+ false ->
+ {Face,Faces1} = gb_sets:take_smallest(Faces0),
+ Ws = [Face],
+ {Reg,Faces} = collect_face_region(Ws, We, Coll, [], Faces1),
+ find_face_regions(Faces, We, Coll, [Reg|Acc])
+ end.
+
+collect_face_region([_|_]=Ws0, We, Coll, Reg0, Faces0) ->
+ Reg = Ws0++Reg0,
+ {Ws,Faces} = wings_face:fold_faces(Coll, {[],Faces0}, Ws0, We),
+ collect_face_region(Ws, We, Coll, Reg, Faces);
+collect_face_region([], _, _, Reg, Faces) ->
+ {gb_sets:from_list(Reg),Faces}.
+
+collect_face_fun(Face, _, _, Rec, {Ws,Faces}=A) ->
+ Of = case Rec of
+ #edge{lf=Face,rf=Of0} -> Of0;
+ #edge{rf=Face,lf=Of0} -> Of0
+ end,
+ case gb_sets:is_member(Of, Faces) of
+ true -> {[Of|Ws],gb_sets:delete(Of, Faces)};
+ false -> A
+ end.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_shape.erl b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_shape.erl
new file mode 100644
index 0000000000..0df8ca68eb
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_shape.erl
@@ -0,0 +1,69 @@
+%%
+%% wings_shape.erl --
+%%
+%% Utilities for shape records.
+%%
+
+-module(wings_shape).
+
+-export([insert/3]).
+
+-include("wings.hrl").
+
+%%%
+%%% Exported functions.
+%%%
+
+%% new(We, Suffix, St0) -> St.
+%% Suffix = cut | clone | copy | extract | sep
+%%
+%% Create a new object based on an old object. The name
+%% will be created from the old name (with digits and known
+%% suffixes stripped) with the given Suffix and a number
+%% appended.
+insert(#we{name=OldName}=We0, Suffix, #st{shapes=Shapes0,onext=Oid}=St) ->
+ Name = new_name(OldName, Suffix, Oid),
+ We = We0#we{id=Oid,name=Name},
+ Shapes = gb_trees:insert(Oid, We, Shapes0),
+ St#st{shapes=Shapes,onext=Oid+1}.
+
+%%%
+%%% Local functions follow.
+%%%
+
+new_name(OldName, Suffix0, Id) ->
+ Suffix = suffix(Suffix0),
+ Base = base(lists:reverse(OldName)),
+ lists:reverse(Base, "_" ++ Suffix ++ integer_to_list(Id)).
+
+%% Note: Filename suffixes are intentionally not translated.
+%% If we are to translate them in the future, base/1 below
+%% must be updated to strip suffixes (both for the current language
+%% and for English).
+
+suffix(cut) -> "cut";
+suffix(clone) -> "clone";
+suffix(copy) -> "copy";
+suffix(extract) -> "extract";
+suffix(mirror) -> "mirror";
+suffix(sep) -> "sep".
+
+%% base_1(ReversedName) -> ReversedBaseName
+%% Given an object name, strip digits and known suffixes to
+%% create a base name. Returns the unchanged name if
+%% no known suffix could be stripped.
+
+base(OldName) ->
+ case base_1(OldName) of
+ error -> OldName;
+ Base -> Base
+ end.
+
+base_1([H|T]) when $0 =< H, H =< $9 -> base_1(T);
+base_1("tuc_"++Base) -> Base; %"_cut"
+base_1("enolc_"++Base) -> Base; %"_clone"
+base_1("ypoc_"++Base) -> Base; %"_copy"
+base_1("tcartxe_"++Base) -> Base; %"_extract"
+base_1("rorrim_"++Base) -> Base; %"_mirror"
+base_1("pes_"++Base) -> Base; %"_sep"
+base_1(_Base) -> error.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_util.erl b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_util.erl
new file mode 100644
index 0000000000..8f0da1f5dc
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_util.erl
@@ -0,0 +1,38 @@
+%%
+%% wings_util.erl --
+%%
+%% Various utility functions that not obviously fit somewhere else.
+%%
+
+-module(wings_util).
+
+-export([gb_trees_smallest_key/1, gb_trees_largest_key/1,
+ gb_trees_map/2, rel2fam/1]).
+
+-include("wings.hrl").
+
+rel2fam(Rel) ->
+ sofs:to_external(sofs:relation_to_family(sofs:relation(Rel))).
+
+%% a definition that does not violate the opaqueness of gb_tree()
+gb_trees_smallest_key(Tree) ->
+ {Key, _V} = gb_trees:smallest(Tree),
+ Key.
+
+%% a definition that violates the opaqueness of gb_tree()
+gb_trees_largest_key({_, Tree}) ->
+ largest_key1(Tree).
+
+largest_key1({Key, _Value, _Smaller, nil}) ->
+ Key;
+largest_key1({_Key, _Value, _Smaller, Larger}) ->
+ largest_key1(Larger).
+
+gb_trees_map(F, {Size,Tree}) ->
+ {Size,gb_trees_map_1(F, Tree)}.
+
+gb_trees_map_1(_, nil) -> nil;
+gb_trees_map_1(F, {K,V,Smaller,Larger}) ->
+ {K,F(K, V),
+ gb_trees_map_1(F, Smaller),
+ gb_trees_map_1(F, Larger)}.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_we.erl b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_we.erl
new file mode 100644
index 0000000000..6a93363445
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_we.erl
@@ -0,0 +1,250 @@
+%%
+%% wings_we.erl --
+%%
+%% This module contains functions to build and manipulate
+%% we records (winged-edged records, the central data structure
+%% in Wings 3D).
+
+-module(wings_we).
+
+-export([rebuild/1, is_consistent/1, is_face_consistent/2, new_id/1,
+ new_items_as_ordset/3, validate_mirror/1, visible/1, visible_edges/1]).
+
+-include("wings.hrl").
+
+%%%
+%%% API.
+%%%
+
+validate_mirror(#we{mirror=none}=We) -> We;
+validate_mirror(#we{fs=Ftab,mirror=Face}=We) ->
+ case gb_trees:is_defined(Face, Ftab) of
+ false -> We#we{mirror=none};
+ true -> We
+ end.
+
+%% rebuild(We) -> We'
+%% Rebuild any missing 'vc' and 'fs' tables. If there are
+%% fewer elements in the 'vc' table than in the 'vp' table,
+%% remove redundant entries in the 'vp' table. Updated id
+%% bounds.
+rebuild(#we{vc=undefined,fs=undefined,es=Etab0}=We0) ->
+ Etab = gb_trees:to_list(Etab0),
+ Ftab = rebuild_ftab(Etab),
+ VctList = rebuild_vct(Etab),
+ We = We0#we{vc=gb_trees:from_orddict(VctList),fs=Ftab},
+ rebuild_1(VctList, We);
+rebuild(#we{vc=undefined,es=Etab}=We) ->
+ VctList = rebuild_vct(gb_trees:to_list(Etab), []),
+ rebuild_1(VctList, We#we{vc=gb_trees:from_orddict(VctList)});
+rebuild(#we{fs=undefined,es=Etab}=We) ->
+ Ftab = rebuild_ftab(gb_trees:to_list(Etab)),
+ rebuild(We#we{fs=Ftab});
+rebuild(We) -> update_id_bounds(We).
+
+%%% Utilities for allocating IDs.
+
+new_id(#we{next_id=Id}=We) ->
+ {Id,We#we{next_id=Id+1}}.
+
+%%% Returns sets of newly created items.
+
+new_items_as_ordset(vertex, #we{next_id=Wid}, #we{next_id=NewWid,vp=Tab}) ->
+ new_items_as_ordset_1(Tab, Wid, NewWid);
+new_items_as_ordset(edge, #we{next_id=Wid}, #we{next_id=NewWid,es=Tab}) ->
+ new_items_as_ordset_1(Tab, Wid, NewWid);
+new_items_as_ordset(face, #we{next_id=Wid}, #we{next_id=NewWid,fs=Tab}) ->
+ new_items_as_ordset_1(Tab, Wid, NewWid).
+
+any_hidden(#we{fs=Ftab}) ->
+ not gb_trees:is_empty(Ftab) andalso
+ wings_util:gb_trees_smallest_key(Ftab) < 0.
+
+%%%
+%%% Local functions.
+%%%
+
+rebuild_1(VctList, #we{vc=Vct,vp=Vtab0}=We) ->
+ case {gb_trees:size(Vct),gb_trees:size(Vtab0)} of
+ {Same,Same} -> rebuild(We);
+ {Sz1,Sz2} when Sz1 < Sz2 ->
+ Vtab = vertex_gc_1(VctList, gb_trees:to_list(Vtab0), []),
+ rebuild(We#we{vp=Vtab})
+ end.
+
+rebuild_vct(Es) ->
+ rebuild_vct(Es, []).
+
+rebuild_vct([{Edge,#edge{vs=Va,ve=Vb}}|Es], Acc0) ->
+ Acc = rebuild_maybe_add(Va, Vb, Edge, Acc0),
+ rebuild_vct(Es, Acc);
+rebuild_vct([], VtoE) ->
+ build_incident_tab(VtoE).
+
+rebuild_ftab(Es) ->
+ rebuild_ftab_1(Es, []).
+
+rebuild_ftab_1([{Edge,#edge{lf=Lf,rf=Rf}}|Es], Acc0) ->
+ Acc = rebuild_maybe_add(Lf, Rf, Edge, Acc0),
+ rebuild_ftab_1(Es, Acc);
+rebuild_ftab_1([], FtoE) ->
+ gb_trees:from_orddict(build_incident_tab(FtoE)).
+
+rebuild_maybe_add(Ka, Kb, E, [_,{Ka,_}|_]=Acc) ->
+ [{Kb,E}|Acc];
+rebuild_maybe_add(Ka, Kb, E, [_,{Kb,_}|_]=Acc) ->
+ [{Ka,E}|Acc];
+rebuild_maybe_add(Ka, Kb, E, [{Ka,_}|_]=Acc) ->
+ [{Kb,E}|Acc];
+rebuild_maybe_add(Ka, Kb, E, [{Kb,_}|_]=Acc) ->
+ [{Ka,E}|Acc];
+rebuild_maybe_add(Ka, Kb, E, Acc) ->
+ [{Ka,E},{Kb,E}|Acc].
+
+vertex_gc_1([{V,_}|Vct], [{V,_}=Vtx|Vpos], Acc) ->
+ vertex_gc_1(Vct, Vpos, [Vtx|Acc]);
+vertex_gc_1([_|_]=Vct, [_|Vpos], Acc) ->
+ vertex_gc_1(Vct, Vpos, Acc);
+vertex_gc_1([], _, Acc) ->
+ gb_trees:from_orddict(lists:reverse(Acc)).
+
+%%%
+%%% Handling of hidden faces.
+%%%
+
+visible(#we{mirror=none,fs=Ftab}) ->
+ visible_2(gb_trees:keys(Ftab));
+visible(#we{mirror=Face,fs=Ftab}) ->
+ visible_2(gb_trees:keys(gb_trees:delete(Face, Ftab))).
+
+visible_2([F|Fs]) when F < 0 -> visible_2(Fs);
+visible_2(Fs) -> Fs.
+
+visible_edges(#we{es=Etab,mirror=Face}=We) ->
+ case any_hidden(We) of
+ false -> gb_trees:keys(Etab);
+ true -> visible_es_1(gb_trees:to_list(Etab), Face, [])
+ end.
+
+visible_es_1([{E,#edge{lf=Lf,rf=Rf}}|Es], Face, Acc) ->
+ if
+ Lf < 0 ->
+ %% Left face hidden.
+ if
+ Rf < 0; Rf =:= Face ->
+ %% Both faces invisible (in some way).
+ visible_es_1(Es, Face, Acc);
+ true ->
+ %% Right face is visible.
+ visible_es_1(Es, Face, [E|Acc])
+ end;
+ Lf =:= Face, Rf < 0 ->
+ %% Left face mirror, right face hidden.
+ visible_es_1(Es, Face, Acc);
+ true ->
+ %% At least one face visible.
+ visible_es_1(Es, Face, [E|Acc])
+ end;
+visible_es_1([], _, Acc) -> ordsets:from_list(Acc).
+
+update_id_bounds(#we{vp=Vtab,es=Etab,fs=Ftab}=We) ->
+ case gb_trees:is_empty(Etab) of
+ true -> We#we{next_id=0};
+ false ->
+ LastId = lists:max([wings_util:gb_trees_largest_key(Vtab),
+ wings_util:gb_trees_largest_key(Etab),
+ wings_util:gb_trees_largest_key(Ftab)]),
+ We#we{next_id=LastId+1}
+ end.
+
+%% build_incident_tab([{Elem,Edge}]) -> [{Elem,Edge}]
+%% Elem = Face or Vertex
+%% Build the table of incident edges for either faces or vertices.
+%% Returns an ordered list where each Elem is unique.
+
+build_incident_tab(ElemToEdgeRel) ->
+ T = ets:new(?MODULE, [ordered_set]),
+ ets:insert(T, ElemToEdgeRel),
+ R = ets:tab2list(T),
+ ets:delete(T),
+ R.
+
+%%%
+%%% Calculate normals.
+%%%
+
+new_items_as_ordset_1(Tab, Wid, NewWid) when NewWid-Wid < 32 ->
+ new_items_as_ordset_2(Wid, NewWid, Tab, []);
+new_items_as_ordset_1(Tab, Wid, _NewWid) ->
+ [Item || Item <- gb_trees:keys(Tab), Item >= Wid].
+
+new_items_as_ordset_2(Wid, NewWid, Tab, Acc) when Wid < NewWid ->
+ case gb_trees:is_defined(Wid, Tab) of
+ true -> new_items_as_ordset_2(Wid+1, NewWid, Tab, [Wid|Acc]);
+ false -> new_items_as_ordset_2(Wid+1, NewWid, Tab, Acc)
+ end;
+new_items_as_ordset_2(_Wid, _NewWid, _Tab, Acc) -> lists:reverse(Acc).
+
+%%%
+%%% Test the consistency of a #we{}.
+%%%
+
+is_consistent(#we{}=We) ->
+ try
+ validate_vertex_tab(We),
+ validate_faces(We)
+ catch error:_ -> false
+ end.
+
+is_face_consistent(Face, #we{fs=Ftab,es=Etab}) ->
+ Edge = gb_trees:get(Face, Ftab),
+ try validate_face(Face, Edge, Etab)
+ catch error:_ -> false
+ end.
+
+validate_faces(#we{fs=Ftab,es=Etab}) ->
+ validate_faces_1(gb_trees:to_list(Ftab), Etab).
+
+validate_faces_1([{Face,Edge}|Fs], Etab) ->
+ validate_face(Face, Edge, Etab),
+ validate_faces_1(Fs, Etab);
+validate_faces_1([], _) -> true.
+
+validate_face(Face, Edge, Etab) ->
+ Ccw = walk_face_ccw(Edge, Etab, Face, Edge, []),
+ Edge = walk_face_cw(Edge, Etab, Face, Ccw),
+ [V|Vs] = lists:sort(Ccw),
+ validate_face_vertices(Vs, V).
+
+validate_face_vertices([V|_], V) ->
+ erlang:error(repeated_vertex);
+validate_face_vertices([_], _) ->
+ true;
+validate_face_vertices([V|Vs], _) ->
+ validate_face_vertices(Vs, V).
+
+walk_face_ccw(LastEdge, _, _, LastEdge, [_|_]=Acc) -> Acc;
+walk_face_ccw(Edge, Etab, Face, LastEdge, Acc) ->
+ case gb_trees:get(Edge, Etab) of
+ #edge{ve=V,lf=Face,ltpr=Next} ->
+ walk_face_ccw(Next, Etab, Face, LastEdge, [V|Acc]);
+ #edge{vs=V,rf=Face,rtpr=Next} ->
+ walk_face_ccw(Next, Etab, Face, LastEdge, [V|Acc])
+ end.
+
+walk_face_cw(Edge, _, _, []) -> Edge;
+walk_face_cw(Edge, Etab, Face, [V|Vs]) ->
+ case gb_trees:get(Edge, Etab) of
+ #edge{vs=V,lf=Face,ltsu=Next} ->
+ walk_face_cw(Next, Etab, Face, Vs);
+ #edge{ve=V,rf=Face,rtsu=Next} ->
+ walk_face_cw(Next, Etab, Face, Vs)
+ end.
+
+validate_vertex_tab(#we{es=Etab,vc=Vct}) ->
+ lists:foreach(fun({V,Edge}) ->
+ case gb_trees:get(Edge, Etab) of
+ #edge{vs=V} -> ok;
+ #edge{ve=V} -> ok
+ end
+ end, gb_trees:to_list(Vct)).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis1.erl b/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis1.erl
new file mode 100644
index 0000000000..82bcf2edcf
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis1.erl
@@ -0,0 +1,14 @@
+-module(zoltan_kis1).
+
+-export([f/0, gen/0]).
+
+-opaque id() :: string().
+
+-spec f() -> integer().
+
+%BIF and Unification(t_unify) issue
+f() -> erlang:length(gen()).
+
+-spec gen() -> id().
+
+gen() -> "Dummy".
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis2.erl b/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis2.erl
new file mode 100644
index 0000000000..a4b0a18157
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis2.erl
@@ -0,0 +1,14 @@
+-module(zoltan_kis2).
+
+-export([get/2]).
+
+-opaque data() :: gb_tree().
+
+-spec get(term(), data()) -> term().
+
+get(Key, Data) ->
+ %%Should unopaque data for remote calls
+ case gb_trees:lookup(Key, Data) of
+ 'none' -> 'undefined';
+ {'value', Val} -> Val
+ end.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis3.erl b/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis3.erl
new file mode 100644
index 0000000000..4977ca4bc9
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis3.erl
@@ -0,0 +1,14 @@
+-module(zoltan_kis3).
+
+-export([f/0, gen/0]).
+
+-opaque id() :: string().
+
+-spec f() -> char().
+
+%%List pattern matching issue
+f() -> [H|_T] = gen(), H.
+
+-spec gen() -> id().
+
+gen() -> "Dummy".
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis4.erl b/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis4.erl
new file mode 100644
index 0000000000..ad3771aa2c
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis4.erl
@@ -0,0 +1,14 @@
+-module(zoltan_kis4).
+
+-export([f/0, gen/0]).
+
+-opaque id() :: string().
+
+-spec f() -> boolean().
+
+%%Equality test issue
+f() -> "Dummy" == gen().
+
+-spec gen() -> id().
+
+gen() -> "Dummy".
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis5.erl b/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis5.erl
new file mode 100644
index 0000000000..ecf14c91c1
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis5.erl
@@ -0,0 +1,14 @@
+-module(zoltan_kis5).
+
+-export([f/0, gen/0]).
+
+-opaque id() :: string().
+
+-spec f() -> boolean().
+
+%% Equality test issue
+f() -> "Dummy" == gen().
+
+-spec gen() -> id().
+
+gen() -> "Dummy".
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis6.erl b/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis6.erl
new file mode 100644
index 0000000000..6f0779d7d1
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/zoltan_kis6.erl
@@ -0,0 +1,14 @@
+-module(zoltan_kis6).
+
+-export([f/0, gen/0]).
+
+-opaque id() :: {integer(),atom()}.
+
+%%-spec f() -> id().
+
+%% Tuple Unification (t_unify) issue
+f() -> {X,Y} = gen().
+
+-spec gen() -> id().
+
+gen() -> {34, leprecon}.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/dialyzer_options b/lib/dialyzer/test/opaque_tests_SUITE_data/dialyzer_options
deleted file mode 100644
index 3ff26b87db..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/dialyzer_options
+++ /dev/null
@@ -1 +0,0 @@
-{dialyzer_options, [{warnings, [no_unused, no_return]}]}.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/array b/lib/dialyzer/test/opaque_tests_SUITE_data/results/array
deleted file mode 100644
index b05d088a03..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/results/array
+++ /dev/null
@@ -1,3 +0,0 @@
-
-array_use.erl:12: The type test is_tuple(array()) breaks the opaqueness of the term array()
-array_use.erl:9: The attempt to match a term of type array() against the pattern {'array', _, _, 'undefined', _} breaks the opaqueness of the term
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/crash b/lib/dialyzer/test/opaque_tests_SUITE_data/results/crash
deleted file mode 100644
index 6bdd934169..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/results/crash
+++ /dev/null
@@ -1,7 +0,0 @@
-
-crash_1.erl:42: The specification for crash_1:empty/0 states that the function might also return crash_1:targetlist() but the inferred return is none()
-crash_1.erl:45: Record construction #targetlist{list::[]} violates the declared type of field list::'undefined' | crash_1:target()
-crash_1.erl:48: The call crash_1:get_using_branch2(Branch::maybe_improper_list(),L::'undefined' | crash_1:target()) contains an opaque term as 2nd argument when terms of different types are expected in these positions
-crash_1.erl:50: The pattern <_Branch, []> can never match the type
-crash_1.erl:52: The pattern can never match the type
-crash_1.erl:54: The pattern can never match the type
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/dict b/lib/dialyzer/test/opaque_tests_SUITE_data/results/dict
deleted file mode 100644
index 5c6bf6a927..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/results/dict
+++ /dev/null
@@ -1,15 +0,0 @@
-
-dict_use.erl:41: The attempt to match a term of type dict() against the pattern 'gazonk' breaks the opaqueness of the term
-dict_use.erl:45: The attempt to match a term of type dict() against the pattern [] breaks the opaqueness of the term
-dict_use.erl:46: The attempt to match a term of type dict() against the pattern 42 breaks the opaqueness of the term
-dict_use.erl:51: The attempt to match a term of type dict() against the pattern [] breaks the opaqueness of the term
-dict_use.erl:52: The attempt to match a term of type dict() against the pattern 42 breaks the opaqueness of the term
-dict_use.erl:58: Attempt to test for equality between a term of type maybe_improper_list() and a term of opaque type dict()
-dict_use.erl:60: Attempt to test for inequality between a term of type atom() and a term of opaque type dict()
-dict_use.erl:64: Guard test length(D::dict()) breaks the opaqueness of its argument
-dict_use.erl:65: Guard test is_atom(D::dict()) breaks the opaqueness of its argument
-dict_use.erl:66: Guard test is_list(D::dict()) breaks the opaqueness of its argument
-dict_use.erl:70: The type test is_list(dict()) breaks the opaqueness of the term dict()
-dict_use.erl:73: The call dict:fetch('foo',[1 | 2 | 3,...]) does not have an opaque term of type dict() as 2nd argument
-dict_use.erl:76: The call dict:merge(Fun::any(),42,[1 | 2,...]) does not have opaque terms as 2nd and 3rd arguments
-dict_use.erl:79: The call dict:store(42,'elli',{'dict',0,16,16,8,80,48,{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},{{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}}}) does not have an opaque term of type dict() as 3rd argument
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/ets b/lib/dialyzer/test/opaque_tests_SUITE_data/results/ets
deleted file mode 100644
index 5498ba1538..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/results/ets
+++ /dev/null
@@ -1,3 +0,0 @@
-
-ets_use.erl:12: Guard test is_integer(T::atom() | tid()) breaks the opaqueness of its argument
-ets_use.erl:7: Guard test is_integer(T::tid()) breaks the opaqueness of its argument
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/gb_sets b/lib/dialyzer/test/opaque_tests_SUITE_data/results/gb_sets
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/inf_loop1 b/lib/dialyzer/test/opaque_tests_SUITE_data/results/inf_loop1
deleted file mode 100644
index eb8f304905..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/results/inf_loop1
+++ /dev/null
@@ -1,5 +0,0 @@
-
-inf_loop1.erl:119: The pattern [{_, LNorms}] can never match the type []
-inf_loop1.erl:121: The pattern [{LinksA, LNormA}, {LinksB, LNormB}] can never match the type []
-inf_loop1.erl:129: The pattern [{_, Norm} | _] can never match the type []
-inf_loop1.erl:71: The call gb_trees:get(Edge::any(),Etab::array()) contains an opaque term as 2nd argument when terms of different types are expected in these positions
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/int b/lib/dialyzer/test/opaque_tests_SUITE_data/results/int
deleted file mode 100644
index 3ee4def34b..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/results/int
+++ /dev/null
@@ -1,3 +0,0 @@
-
-int_adt.erl:28: Invalid type specification for function int_adt:add_f/2. The success typing is (number(),float()) -> number()
-int_adt.erl:32: Invalid type specification for function int_adt:div_f/2. The success typing is (number(),number()) -> float()
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/mixed_opaque b/lib/dialyzer/test/opaque_tests_SUITE_data/results/mixed_opaque
deleted file mode 100644
index ab850b613e..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/results/mixed_opaque
+++ /dev/null
@@ -1,2 +0,0 @@
-
-mixed_opaque_use.erl:31: The call mixed_opaque_rec_adt:get_a(Q::mixed_opaque_queue_adt:my_queue()) contains an opaque term as 1st argument when an opaque term of type mixed_opaque_rec_adt:rec() is expected
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/my_digraph b/lib/dialyzer/test/opaque_tests_SUITE_data/results/my_digraph
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/my_queue b/lib/dialyzer/test/opaque_tests_SUITE_data/results/my_queue
deleted file mode 100644
index 2860b91084..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/results/my_queue
+++ /dev/null
@@ -1,7 +0,0 @@
-
-my_queue_use.erl:15: The call my_queue_adt:is_empty([]) does not have an opaque term of type my_queue_adt:my_queue() as 1st argument
-my_queue_use.erl:19: The call my_queue_adt:add(42,Q0::[]) does not have an opaque term of type my_queue_adt:my_queue() as 2nd argument
-my_queue_use.erl:24: The attempt to match a term of type my_queue_adt:my_queue() against the pattern [42 | Q2] breaks the opaqueness of the term
-my_queue_use.erl:30: Attempt to test for equality between a term of type [] and a term of opaque type my_queue_adt:my_queue()
-my_queue_use.erl:34: Cons will produce an improper list since its 2nd argument is my_queue_adt:my_queue()
-my_queue_use.erl:34: The call my_queue_adt:dequeue(nonempty_improper_list(42,my_queue_adt:my_queue())) does not have an opaque term of type my_queue_adt:my_queue() as 1st argument
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/opaque b/lib/dialyzer/test/opaque_tests_SUITE_data/results/opaque
deleted file mode 100644
index ca76f57b54..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/results/opaque
+++ /dev/null
@@ -1,2 +0,0 @@
-
-opaque_bug4.erl:20: The attempt to match a term of type opaque_adt:abc() against the pattern 'a' breaks the opaqueness of the term
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/queue b/lib/dialyzer/test/opaque_tests_SUITE_data/results/queue
deleted file mode 100644
index 59ce33f098..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/results/queue
+++ /dev/null
@@ -1,11 +0,0 @@
-
-queue_use.erl:18: The call queue:is_empty({[],[]}) does not have an opaque term of type queue() as 1st argument
-queue_use.erl:22: The call queue:in(42,Q0::{[],[]}) does not have an opaque term of type queue() as 2nd argument
-queue_use.erl:27: The attempt to match a term of type queue() against the pattern {"*", Q2} breaks the opaqueness of the term
-queue_use.erl:33: Attempt to test for equality between a term of type {[42,...],[]} and a term of opaque type queue()
-queue_use.erl:36: The attempt to match a term of type queue() against the pattern {F, _R} breaks the opaqueness of the term
-queue_use.erl:40: The call queue:out({[42,...],[]}) does not have an opaque term of type queue() as 1st argument
-queue_use.erl:51: The call queue_use:is_in_queue(E::42,DB::#db{p::[],q::queue()}) contains an opaque term as 2nd argument when terms of different types are expected in these positions
-queue_use.erl:56: The attempt to match a term of type #db{p::[],q::queue()} against the pattern {'db', _, {L1, L2}} breaks the opaqueness of queue()
-queue_use.erl:62: The call queue_use:tuple_queue({42,'gazonk'}) does not have a term of type {_,queue()} (with opaque subterms) as 1st argument
-queue_use.erl:65: The call queue:in(F::42,Q::'gazonk') does not have an opaque term of type queue() as 2nd argument
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/rec b/lib/dialyzer/test/opaque_tests_SUITE_data/results/rec
deleted file mode 100644
index 72736b3b3c..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/results/rec
+++ /dev/null
@@ -1,6 +0,0 @@
-
-rec_use.erl:17: The attempt to match a term of type rec_adt:rec() against the pattern {'rec', _, 42} breaks the opaqueness of the term
-rec_use.erl:18: Guard test tuple_size(R::rec_adt:rec()) breaks the opaqueness of its argument
-rec_use.erl:23: The call rec_adt:get_a(R::tuple()) does not have an opaque term of type rec_adt:rec() as 1st argument
-rec_use.erl:27: Attempt to test for equality between a term of type {'rec','gazonk',42} and a term of opaque type rec_adt:rec()
-rec_use.erl:30: The call erlang:tuple_size(rec_adt:rec()) contains an opaque term as 1st argument when a structured term of type tuple() is expected
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/timer b/lib/dialyzer/test/opaque_tests_SUITE_data/results/timer
deleted file mode 100644
index e917b76b08..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/results/timer
+++ /dev/null
@@ -1,4 +0,0 @@
-
-timer_use.erl:16: The pattern 'gazonk' can never match the type {'error',_} | {'ok',timer:tref()}
-timer_use.erl:17: The attempt to match a term of type {'ok',timer:tref()} against the pattern {'ok', 42} breaks the opaqueness of timer:tref()
-timer_use.erl:18: The attempt to match a term of type {'error',_} | {'ok',timer:tref()} against the pattern {Tag, 'gazonk'} breaks the opaqueness of timer:tref()
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/union b/lib/dialyzer/test/opaque_tests_SUITE_data/results/union
deleted file mode 100644
index 98829b424a..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/results/union
+++ /dev/null
@@ -1,5 +0,0 @@
-
-union_use.erl:12: The attempt to match a term of type union_adt:u() against the pattern 'aaa' breaks the opaqueness of the term
-union_use.erl:16: The type test is_tuple(union_adt:u()) breaks the opaqueness of the term union_adt:u()
-union_use.erl:7: Guard test is_atom(A::union_adt:u()) breaks the opaqueness of its argument
-union_use.erl:8: Guard test is_tuple(T::union_adt:u()) breaks the opaqueness of its argument
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/wings b/lib/dialyzer/test/opaque_tests_SUITE_data/results/wings
deleted file mode 100644
index a9571441f8..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/results/wings
+++ /dev/null
@@ -1,11 +0,0 @@
-
-wings_dissolve.erl:103: Guard test is_list(List::gb_set()) breaks the opaqueness of its argument
-wings_dissolve.erl:19: Guard test is_list(Faces::gb_set()) breaks the opaqueness of its argument
-wings_dissolve.erl:272: Guard test is_list(Faces::gb_set()) breaks the opaqueness of its argument
-wings_dissolve.erl:31: The call gb_sets:is_empty(Faces::[any(),...]) does not have an opaque term of type gb_set() as 1st argument
-wings_edge.erl:205: The pattern can never match the type <_,'soft',_>
-wings_edge_cmd.erl:30: The call gb_trees:size(P::gb_set()) contains an opaque term as 1st argument when an opaque term of type gb_tree() is expected
-wings_edge_cmd.erl:32: The pattern [_ | Parts] can never match the type []
-wings_edge_cmd.erl:32: The pattern [{_, P} | _] can never match the type []
-wings_io.erl:30: The attempt to match a term of type {'empty',queue()} against the pattern {'empty', {In, Out}} breaks the opaqueness of queue()
-wings_we.erl:155: The call wings_util:gb_trees_largest_key(Etab::gb_tree()) contains an opaque term as 1st argument when a structured term of type {_,{_,_,_,'nil' | {_,_,_,'nil' | {_,_,_,_}}}} is expected
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/array/array_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/array/array_use.erl
deleted file mode 100644
index 1702dc8f03..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/array/array_use.erl
+++ /dev/null
@@ -1,15 +0,0 @@
--module(array_use).
-
--export([ok1/0, wrong1/0, wrong2/0]).
-
-ok1() ->
- array:set(17, gazonk, array:new()).
-
-wrong1() ->
- {array, _, _, undefined, _} = array:new(42).
-
-wrong2() ->
- case is_tuple(array:new(42)) of
- true -> structure_is_exposed;
- false -> cannot_possibly_be
- end.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/crash/crash_1.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/crash/crash_1.erl
deleted file mode 100644
index eebeed15af..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/crash/crash_1.erl
+++ /dev/null
@@ -1,55 +0,0 @@
-%%%-------------------------------------------------------------------
-%%% From : Fredrik Thulin
-%%%
-%%% A module with an erroneous record field declaration which mixes up
-%%% structured and opaque terms and causes a crash in dialyzer.
-%%%
-%%% In addition, it revealed that the compiler produced extraneous
-%%% warnings about unused record definitions when in fact they are
-%%% needed for type declarations. This is now fixed.
-%%%-------------------------------------------------------------------
--module(crash_1).
-
--export([add/3, empty/0]).
-
-%%--------------------------------------------------------------------
-
--record(sipurl, {proto = "sip" :: string(), host :: string()}).
--record(keylist, {list = [] :: [_]}).
--type sip_headers() :: #keylist{}.
--record(request, {uri :: #sipurl{}, header :: sip_headers()}).
--type sip_request() :: #request{}.
-
-%%--------------------------------------------------------------------
-
--record(target, {branch :: string(), request :: sip_request()}).
--opaque target() :: #target{}.
-
--record(targetlist, {list :: target()}). % XXX: THIS ONE SHOULD READ [target()]
--opaque targetlist() :: #targetlist{}.
-
-%%====================================================================
-
-add(Branch, #request{} = Request, #targetlist{list = L} = TargetList) ->
- case get_using_branch(Branch, TargetList) of
- none ->
- NewTarget = #target{branch = Branch, request = Request},
- #targetlist{list = L ++ [NewTarget]};
- #target{} ->
- TargetList
- end.
-
--spec empty() -> targetlist().
-
-empty() ->
- #targetlist{list = []}.
-
-get_using_branch(Branch, #targetlist{list = L}) when is_list(Branch) ->
- get_using_branch2(Branch, L).
-
-get_using_branch2(_Branch, []) ->
- none;
-get_using_branch2(Branch, [#target{branch=Branch}=H | _T]) ->
- H;
-get_using_branch2(Branch, [#target{} | T]) ->
- get_using_branch2(Branch, T).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/dict/dict_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/dict/dict_use.erl
deleted file mode 100644
index 2a632a910d..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/dict/dict_use.erl
+++ /dev/null
@@ -1,83 +0,0 @@
--module(dict_use).
-
--export([ok1/0, ok2/0, ok3/0, ok4/0, ok5/0, ok6/0]).
--export([middle/0]).
--export([w1/0, w2/0, w3/0, w4/1, w5/0, w6/0, w7/0, w8/1, w9/0]).
-
--define(DICT, dict).
-
-%%---------------------------------------------------------------------
-%% Cases that are OK
-%%---------------------------------------------------------------------
-
-ok1() ->
- dict:new().
-
-ok2() ->
- case dict:new() of X -> X end.
-
-ok3() ->
- Dict1 = dict:new(),
- Dict2 = dict:new(),
- Dict1 =:= Dict2.
-
-ok4() ->
- dict:fetch(foo, dict:new()).
-
-ok5() -> % this is OK since some_mod:new/0 might be returning a dict()
- dict:fetch(foo, some_mod:new()).
-
-ok6() ->
- dict:store(42, elli, dict:new()).
-
-middle() ->
- {w1(), w2()}.
-
-%%---------------------------------------------------------------------
-%% Cases that are problematic w.r.t. opaqueness of types
-%%---------------------------------------------------------------------
-
-w1() ->
- gazonk = dict:new().
-
-w2() ->
- case dict:new() of
- [] -> nil;
- 42 -> weird
- end.
-
-w3() ->
- try dict:new() of
- [] -> nil;
- 42 -> weird
- catch
- _:_ -> exception
- end.
-
-w4(Dict) when is_list(Dict) ->
- Dict =:= dict:new();
-w4(Dict) when is_atom(Dict) ->
- Dict =/= dict:new().
-
-w5() ->
- case dict:new() of
- D when length(D) =/= 42 -> weird;
- D when is_atom(D) -> weirder;
- D when is_list(D) -> gazonk
- end.
-
-w6() ->
- is_list(dict:new()).
-
-w7() ->
- dict:fetch(foo, [1,2,3]).
-
-w8(Fun) ->
- dict:merge(Fun, 42, [1,2]).
-
-w9() ->
- dict:store(42, elli,
- {dict,0,16,16,8,80,48,
- {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
- {{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}}}).
-
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/ets/ets_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/ets/ets_use.erl
deleted file mode 100644
index 20be9803eb..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/ets/ets_use.erl
+++ /dev/null
@@ -1,17 +0,0 @@
--module(ets_use).
--export([t1/0, t2/0]).
-
-t1() ->
- case n() of
- T when is_atom(T) -> atm;
- T when is_integer(T) -> int
- end.
-
-t2() ->
- case n() of
- T when is_integer(T) -> int;
- T when is_atom(T) -> atm
- end.
-
-n() -> ets:new(n, [named_table]).
-
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/gb_sets/gb_sets_rec.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/gb_sets/gb_sets_rec.erl
deleted file mode 100644
index 008b0a486a..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/gb_sets/gb_sets_rec.erl
+++ /dev/null
@@ -1,23 +0,0 @@
-%%---------------------------------------------------------------------
-%% This module does not test gb_sets. Instead it tests that we can
-%% create records whose fields are declared with an opaque type and
-%% retrieve these fields without problems. Unitialized record fields
-%% used to cause trouble for the analysis due to the implicit
-%% 'undefined' value that record fields contain. The problem was the
-%% strange interaction of ?opaque() and ?union() in the definition of
-%% erl_types:t_inf/3. This was fixed 18/1/2009.
-%% --------------------------------------------------------------------
-
--module(gb_sets_rec).
-
--export([new/0, get_g/1]).
-
--record(rec, {g :: gb_set()}).
-
--spec new() -> #rec{}.
-new() ->
- #rec{g = gb_sets:empty()}.
-
--spec get_g(#rec{}) -> gb_set().
-get_g(R) ->
- R#rec.g.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/inf_loop1.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/inf_loop1.erl
deleted file mode 100644
index 0dff16cf14..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/inf_loop1.erl
+++ /dev/null
@@ -1,172 +0,0 @@
-%% -*- erlang-indent-level: 2 -*-
-%%----------------------------------------------------------------------------
-%% Non-sensical (i.e., stripped-down) program that sends the analysis
-%% into an infinite loop. The #we.es field was originally a gb_tree()
-%% but the programmer declared it as an array in order to change it to
-%% that data type instead. In the file, there are two calls to function
-%% gb_trees:get/2 which seem to be the ones responsible for sending the
-%% analysis into an infinite loop. Currently, these calls are marked and
-%% have been changed to gbee_trees:get/2 in order to be able to see that
-%% the analysis works if these two calls are taken out of the picture.
-%%----------------------------------------------------------------------------
--module(inf_loop1).
-
--export([command/1]).
-
--record(we, {id,
- es = array:new() :: array(),
- vp,
- mirror = none}).
--record(edge, {vs,ve,a = none,b = none,lf,rf,ltpr,ltsu,rtpr,rtsu}).
-
-command(St) ->
- State = drag_mode(offset_region),
- SetupSt = wings_sel_conv:more(St),
- Tvs = wings_sel:fold(fun(Faces, #we{id = Id} = We, Acc) ->
- FaceRegions = wings_sel:face_regions(Faces, We),
- {AllVs0,VsData} =
- collect_offset_regions_data(FaceRegions, We, [], []),
- AllVs = ordsets:from_list(AllVs0),
- [{Id,{AllVs,offset_regions_fun(VsData, State)}}|Acc]
- end,
- [],
- SetupSt),
- wings_drag:setup(Tvs, 42, [], St).
-
-drag_mode(Type) ->
- {Mode,Norm} = wings_pref:get_value(Type, {average,loop}),
- {Type,Mode,Norm}.
-
-collect_offset_regions_data([Faces|Regions], We, AllVs, VsData) ->
- {FaceNormTab,OuterEdges,RegVs} =
- some_fake_module:faces_data_0(Faces, We, [], [], []),
- {LoopNorm,LoopVsData,LoopVs} =
- offset_regions_loop_data(OuterEdges, Faces, We, FaceNormTab),
- Vs = RegVs -- LoopVs,
- RegVsData = vertex_normals(Vs, FaceNormTab, We, LoopVsData),
- collect_offset_regions_data(Regions, We, RegVs ++ AllVs,
- [{LoopNorm,RegVsData}|VsData]);
-collect_offset_regions_data([], _, AllVs, VsData) ->
- {AllVs,VsData}.
-
-offset_regions_loop_data(Edges, Faces, We, FNtab) ->
- EdgeSet = gb_sets:from_list(Edges),
- offset_loop_data_0(EdgeSet, Faces, We, FNtab, [], [], []).
-
-offset_loop_data_0(EdgeSet0, Faces, We, FNtab, LNorms, VData0, Vs0) ->
- case gb_sets:is_empty(EdgeSet0) of
- false ->
- {Edge,EdgeSet1} = gb_sets:take_smallest(EdgeSet0),
- {EdgeSet,VData,Links,LoopNorm,Vs} =
- offset_loop_data_1(Edge, EdgeSet1, Faces, We, FNtab, VData0, Vs0),
- offset_loop_data_0(EdgeSet, Faces, We, FNtab,
- [{Links,LoopNorm}|LNorms], VData, Vs);
- true ->
- AvgLoopNorm = average_loop_norm(LNorms),
- {AvgLoopNorm,VData0,Vs0}
- end.
-
-offset_loop_data_1(Edge, EdgeSet, _Faces,
- #we{es = Etab, vp = Vtab} = We, FNtab, VData, Vs) ->
- #edge{vs = Va, ve = Vb, lf = Lf, ltsu = NextLeft} = gb_trees:get(Edge, Etab),
- VposA = gb_trees:get(Va, Vtab),
- VposB = gb_trees:get(Vb, Vtab),
- VDir = e3d_vec:sub(VposB, VposA),
- FNorm = wings_face:normal(Lf, We),
- EdgeData = gb_trees:get(NextLeft, Etab),
- offset_loop_data_2(NextLeft, EdgeData, Va, VposA, Lf, Edge, We, FNtab,
- EdgeSet, VDir, [], [FNorm], VData, [], Vs, 0).
-
-offset_loop_data_2(CurE, #edge{vs = Va, ve = Vb, lf = PrevFace,
- rtsu = NextEdge, ltsu = IfCurIsMember},
- Vb, VposB, PrevFace, LastE,
- #we{mirror = M} = We,
- FNtab, EdgeSet0, VDir, EDir0, VNorms0, VData0, VPs0, Vs0,
- Links) ->
- Mirror = M == PrevFace,
- offset_loop_is_member(Mirror, Vb, Va, VposB, CurE, IfCurIsMember, VNorms0,
- NextEdge, EdgeSet0, VDir, EDir0, FNtab, PrevFace,
- LastE, We, VData0, VPs0, Vs0, Links).
-
-offset_loop_is_member(Mirror, V1, V2, Vpos1, CurE, NextE, VNorms0, NEdge,
- EdgeSet0, VDir, EDir0, FNtab, PFace, LastE, We,
- VData0, VPs0, Vs0, Links) ->
- #we{es = Etab, vp = Vtab} = We,
- Vpos2 = gb_trees:get(V2, Vtab),
- Dir = e3d_vec:sub(Vpos2, Vpos1),
- NextVDir = e3d_vec:neg(Dir),
- EdgeSet = gb_sets:delete(CurE, EdgeSet0),
- EdgeData = gbee_trees:get(NextE, Etab), %% HERE
- [FNorm|_] = VNorms0,
- VData = offset_loop_data_3(Mirror, V1, Vpos1, VNorms0, NEdge, VDir,
- Dir, EDir0, FNtab, We, VData0),
- VPs = [Vpos1|VPs0],
- Vs = [V1|Vs0],
- offset_loop_data_2(NextE, EdgeData, V2, Vpos2, PFace, LastE, We, FNtab,
- EdgeSet, NextVDir, [], [FNorm], VData, VPs, Vs, Links + 1).
-
-offset_loop_data_3(false, V, Vpos, VNorms0, NextEdge,
- VDir, Dir, EDir0, FNtab, We, VData0) ->
- #we{es = Etab} = We,
- VNorm = e3d_vec:norm(e3d_vec:add(VNorms0)),
- NV = wings_vertex:other(V, gbee_trees:get(NextEdge, Etab)), %% HERE
- ANorm = vertex_normal(NV, FNtab, We),
- EDir = some_fake_module:average_edge_dir(VNorm, VDir, Dir, EDir0),
- AvgDir = some_fake_module:evaluate_vdata(VDir, Dir, VNorm),
- ScaledDir = some_fake_module:along_edge_scale_factor(VDir, Dir, EDir, ANorm),
- [{V,{Vpos,AvgDir,EDir,ScaledDir}}|VData0].
-
-average_loop_norm([{_,LNorms}]) ->
- e3d_vec:norm(LNorms);
-average_loop_norm([{LinksA,LNormA},{LinksB,LNormB}]) ->
- case LinksA < LinksB of
- true ->
- e3d_vec:norm(e3d_vec:add(e3d_vec:neg(LNormA), LNormB));
- false ->
- e3d_vec:norm(e3d_vec:add(e3d_vec:neg(LNormB), LNormA))
- end;
-average_loop_norm(LNorms) ->
- LoopNorms = [Norm || {_,Norm} <- LNorms],
- e3d_vec:norm(e3d_vec:neg(e3d_vec:add(LoopNorms))).
-
-vertex_normals([V|Vs], FaceNormTab, #we{vp = Vtab, mirror = M} = We, Acc) ->
- FaceNorms =
- wings_vertex:fold(fun(_, Face, _, A) when Face == M ->
- [e3d_vec:neg(wings_face:normal(M, We))|A];
- (_, Face, _, A) ->
- [gb_trees:get(Face, FaceNormTab)|A]
- end, [], V, We),
- VNorm = e3d_vec:norm(e3d_vec:add(FaceNorms)),
- Vpos = gb_trees:get(V, Vtab),
- vertex_normals(Vs, FaceNormTab, We, [{V,{Vpos,VNorm}}|Acc]);
-vertex_normals([], _, _, Acc) ->
- Acc.
-
-vertex_normal(V, FaceNormTab, #we{mirror = M} = We) ->
- wings_vertex:fold(fun(_, Face, _, A) when Face == M ->
- [e3d_vec:neg(wings_face:normal(Face, We))|A];
- (_, Face, _, A) ->
- N = gb_trees:get(Face, FaceNormTab),
- case e3d_vec:is_zero(N) of
- true -> A;
- false -> [N|A]
- end
- end, [], V, We).
-
-offset_regions_fun(OffsetData, {_,Solution,_} = State) ->
- fun(new_mode_data, {NewState,_}) ->
- offset_regions_fun(OffsetData, NewState);
- ([Dist,_,_,Bump|_], A) ->
- lists:foldl(fun({LoopNormal,VsData}, VsAcc0) ->
- lists:foldl(fun({V,{Vpos0,VNorm}}, VsAcc) ->
- [{V,Vpos0}|VsAcc];
- ({V,{Vpos0,Dir,EDir,ScaledEDir}}, VsAcc) ->
- Vec = case Solution of
- average -> Dir;
- along_edges -> EDir;
- scaled -> ScaledEDir
- end,
- [{V,Vpos0}|VsAcc]
- end, VsAcc0, VsData)
- end, A, OffsetData)
- end.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/int/int_adt.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/int/int_adt.erl
deleted file mode 100644
index 99f8cbdc4a..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/int/int_adt.erl
+++ /dev/null
@@ -1,33 +0,0 @@
-%%----------------------------------------------------------------------------
-%% Module that tests consistency of spec declarations in the presence of
-%% opaque types. Contains both valid and invalid contracts with opaque types.
-%%----------------------------------------------------------------------------
-
--module(int_adt).
-
--export([new_i/0, add_i/2, div_i/2, add_f/2, div_f/2]).
-
--export_type([int/0]).
-
--opaque int() :: integer().
-
-%% the user has declared the return to be an opaque type, but the success
-%% typing inference is too strong and finds a subtype as a return: this is OK
--spec new_i() -> int().
-new_i() -> 42.
-
-%% the success typing is more general than the contract: this is OK
--spec add_i(int(), int()) -> int().
-add_i(X, Y) -> X + Y.
-
-%% the success typing coincides with the contract: this is OK, of course
--spec div_i(int(), int()) -> int().
-div_i(X, Y) -> X div Y.
-
-%% the success typing has an incompatible domain element: this is invalid
--spec add_f(int(), int()) -> int().
-add_f(X, Y) when is_float(Y) -> X + trunc(Y).
-
-%% the success typing has an incompatible range: this is invalid
--spec div_f(int(), int()) -> int().
-div_f(X, Y) -> X / Y.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/int/int_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/int/int_use.erl
deleted file mode 100644
index b4471e1cee..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/int/int_use.erl
+++ /dev/null
@@ -1,11 +0,0 @@
-%%---------------------------------------------------------------------------
-%% Module that uses the opaque types of int_adt.
-%% TODO: Should be extended with invalid contracts.
-%%---------------------------------------------------------------------------
--module(int_use).
-
--export([test/0]).
-
--spec test() -> int_adt:int().
-test() ->
- int_adt:new_i().
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_queue_adt.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_queue_adt.erl
deleted file mode 100644
index ac59f19cd3..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_queue_adt.erl
+++ /dev/null
@@ -1,26 +0,0 @@
-%%---------------------------------------------------------------------------
-%% A clone of 'queue_adt' so as to test its combination with 'rec_adt'
-%%---------------------------------------------------------------------------
--module(mixed_opaque_queue_adt).
-
--export([new/0, add/2, dequeue/1, is_empty/1]).
-
--opaque my_queue() :: list().
-
--spec new() -> my_queue().
-new() ->
- [].
-
--spec add(term(), my_queue()) -> my_queue().
-add(E, Q) ->
- Q ++ [E].
-
--spec dequeue(my_queue()) -> {term(), my_queue()}.
-dequeue([H|T]) ->
- {H, T}.
-
--spec is_empty(my_queue()) -> boolean().
-is_empty([]) ->
- true;
-is_empty([_|_]) ->
- false.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_rec_adt.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_rec_adt.erl
deleted file mode 100644
index 61bae5110d..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_rec_adt.erl
+++ /dev/null
@@ -1,25 +0,0 @@
-%%---------------------------------------------------------------------------
-%% A clone of 'rec_adt' so as to test its combination with 'queue_adt'
-%%---------------------------------------------------------------------------
--module(mixed_opaque_rec_adt).
-
--export([new/0, get_a/1, get_b/1, set_a/2, set_b/2]).
-
--record(rec, {a :: atom(), b = 0 :: integer()}).
-
--opaque rec() :: #rec{}.
-
--spec new() -> rec().
-new() -> #rec{a = gazonk, b = 42}.
-
--spec get_a(rec()) -> atom().
-get_a(#rec{a = A}) -> A.
-
--spec get_b(rec()) -> integer().
-get_b(#rec{b = B}) -> B.
-
--spec set_a(rec(), atom()) -> rec().
-set_a(R, A) -> R#rec{a = A}.
-
--spec set_b(rec(), integer()) -> rec().
-set_b(R, B) -> R#rec{b = B}.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_use.erl
deleted file mode 100644
index e82dcd5f38..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_use.erl
+++ /dev/null
@@ -1,31 +0,0 @@
-%%---------------------------------------------------------------------------
-%% Test that tries some combinations of using more than one opaque data type
-%% in the same function(s).
-%%----------------------------------------------------------------------------
--module(mixed_opaque_use).
-
--export([ok1/1, ok2/0, wrong1/0]).
-
--define(REC, mixed_opaque_rec_adt).
--define(QUEUE, mixed_opaque_queue_adt).
-
-%% Currently returning unions of opaque types is considered OK
-ok1(Type) ->
- case Type of
- queue -> ?QUEUE:new();
- rec -> ?REC:new()
- end.
-
-%% Constructing a queue of records is OK
-ok2() ->
- Q0 = ?QUEUE:new(),
- R0 = ?REC:new(),
- Q1 = ?QUEUE:add(R0, Q0),
- {R1,_Q2} = ?QUEUE:dequeue(Q1),
- ?REC:get_a(R1).
-
-%% But of course calling a function expecting some opaque type
-%% with some other opaque typs is not OK
-wrong1() ->
- Q = ?QUEUE:new(),
- ?REC:get_a(Q).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/my_digraph/my_digraph_adt.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/my_digraph/my_digraph_adt.erl
deleted file mode 100644
index 20c72aa6eb..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/my_digraph/my_digraph_adt.erl
+++ /dev/null
@@ -1,51 +0,0 @@
--module(my_digraph_adt).
-
--export([new/0, new/1]).
-
--record(my_digraph, {vtab = notable,
- etab = notable,
- ntab = notable,
- cyclic = true :: boolean()}).
-
--opaque my_digraph() :: #my_digraph{}.
-
--type d_protection() :: 'private' | 'protected'.
--type d_cyclicity() :: 'acyclic' | 'cyclic'.
--type d_type() :: d_cyclicity() | d_protection().
-
--spec new() -> my_digraph().
-new() -> new([]).
-
--spec new([atom()]) -> my_digraph().
-new(Type) ->
- try check_type(Type, protected, []) of
- {Access, Ts} ->
- V = ets:new(vertices, [set, Access]),
- E = ets:new(edges, [set, Access]),
- N = ets:new(neighbours, [bag, Access]),
- ets:insert(N, [{'$vid', 0}, {'$eid', 0}]),
- set_type(Ts, #my_digraph{vtab=V, etab=E, ntab=N})
- catch
- throw:Error -> throw(Error)
- end.
-
--spec check_type([atom()], d_protection(), [{'cyclic', boolean()}]) ->
- {d_protection(), [{'cyclic', boolean()}]}.
-
-check_type([acyclic|Ts], A, L) ->
- check_type(Ts, A,[{cyclic,false} | L]);
-check_type([cyclic | Ts], A, L) ->
- check_type(Ts, A, [{cyclic,true} | L]);
-check_type([protected | Ts], _, L) ->
- check_type(Ts, protected, L);
-check_type([private | Ts], _, L) ->
- check_type(Ts, private, L);
-check_type([T | _], _, _) ->
- throw({error, {unknown_type, T}});
-check_type([], A, L) -> {A, L}.
-
--spec set_type([{'cyclic', boolean()}], my_digraph()) -> my_digraph().
-
-set_type([{cyclic,V} | Ks], G) ->
- set_type(Ks, G#my_digraph{cyclic = V});
-set_type([], G) -> G.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/my_queue/my_queue_adt.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/my_queue/my_queue_adt.erl
deleted file mode 100644
index 52688062ce..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/my_queue/my_queue_adt.erl
+++ /dev/null
@@ -1,23 +0,0 @@
--module(my_queue_adt).
-
--export([new/0, add/2, dequeue/1, is_empty/1]).
-
--opaque my_queue() :: list().
-
--spec new() -> my_queue().
-new() ->
- [].
-
--spec add(term(), my_queue()) -> my_queue().
-add(E, Q) ->
- Q ++ [E].
-
--spec dequeue(my_queue()) -> {term(), my_queue()}.
-dequeue([H|T]) ->
- {H, T}.
-
--spec is_empty(my_queue()) -> boolean().
-is_empty([]) ->
- true;
-is_empty([_|_]) ->
- false.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/my_queue/my_queue_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/my_queue/my_queue_use.erl
deleted file mode 100644
index 98f9972c1e..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/my_queue/my_queue_use.erl
+++ /dev/null
@@ -1,35 +0,0 @@
--module(my_queue_use).
-
--export([ok1/0, ok2/0, wrong1/0, wrong2/0, wrong3/0, wrong4/0, wrong5/0]).
-
-ok1() ->
- my_queue_adt:is_empty(my_queue_adt:new()).
-
-ok2() ->
- Q0 = my_queue_adt:new(),
- Q1 = my_queue_adt:add(42, Q0),
- {42, Q2} = my_queue_adt:dequeue(Q1),
- my_queue_adt:is_empty(Q2).
-
-wrong1() ->
- my_queue_adt:is_empty([]).
-
-wrong2() ->
- Q0 = [],
- my_queue_adt:add(42, Q0).
-
-wrong3() ->
- Q0 = my_queue_adt:new(),
- Q1 = my_queue_adt:add(42, Q0),
- [42|Q2] = Q1,
- Q2.
-
-wrong4() ->
- Q0 = my_queue_adt:new(),
- Q1 = my_queue_adt:add(42, Q0),
- Q1 =:= [].
-
-wrong5() ->
- Q0 = my_queue_adt:new(),
- {42, Q2} = my_queue_adt:dequeue([42|Q0]),
- Q2.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_adt.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_adt.erl
deleted file mode 100644
index 3456f0e9c6..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_adt.erl
+++ /dev/null
@@ -1,9 +0,0 @@
--module(opaque_adt).
--export([atom_or_list/1]).
-
--opaque abc() :: 'a' | 'b' | 'c'.
-
-atom_or_list(1) -> a;
-atom_or_list(2) -> b;
-atom_or_list(3) -> c;
-atom_or_list(N) -> lists:duplicate(N, a).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug1.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug1.erl
deleted file mode 100644
index ff0b1d05ab..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug1.erl
+++ /dev/null
@@ -1,17 +0,0 @@
-%%---------------------------------------------------------------------
-%% A test for which the analysis went into an infinite loop due to
-%% specialization using structured type instead of the opaque one.
-%%---------------------------------------------------------------------
-
--module(opaque_bug1).
-
--export([test/1]).
-
--record(c, {a::atom()}).
-
--opaque erl_type() :: 'any' | #c{}.
-
-test(#c{a=foo} = T) -> local(T).
-
-local(#c{a=foo}) -> any.
-
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug2.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug2.erl
deleted file mode 100644
index f193a58f59..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug2.erl
+++ /dev/null
@@ -1,13 +0,0 @@
-%%---------------------------------------------------------------------
-%% A test for which the analysis gave a bogus warning due to
-%% considering the function call name to be of opaque type...
-%%---------------------------------------------------------------------
-
--module(opaque_bug2).
-
--export([test/0]).
-
--opaque o() :: 'map'.
-
-test() ->
- lists:map(fun(X) -> X+1 end, [1,2]).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug3.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug3.erl
deleted file mode 100644
index 71da82a1f6..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug3.erl
+++ /dev/null
@@ -1,19 +0,0 @@
-%%---------------------------------------------------------------------
-%% A test for which the analysis gave wrong results because it did not
-%% handle the is_tuple/1 guard properly.
-%%---------------------------------------------------------------------
-
--module(opaque_bug3).
-
--export([test/1]).
-
--record(c, {}).
-
--opaque o() :: 'a' | #c{}.
-
--spec test(o()) -> 42.
-
-test(#c{} = O) -> t(O).
-
-t(T) when is_tuple(T) -> 42;
-t(a) -> gazonk.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug4.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug4.erl
deleted file mode 100644
index a7ddc80fe8..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug4.erl
+++ /dev/null
@@ -1,21 +0,0 @@
-%%---------------------------------------------------------------------
-%% A test for which the analysis gave wrong results due to erroneous
-%% specialization and incorrect handling of unions.
-%%---------------------------------------------------------------------
-
--module(opaque_bug4).
-
--export([ok/0, wrong/0]).
-
-%-spec ok() -> 'ok'.
-ok() ->
- L = opaque_adt:atom_or_list(42),
- foo(L).
-
-%-spec wrong() -> 'not_ok'.
-wrong() ->
- A = opaque_adt:atom_or_list(1),
- foo(A).
-
-foo(a) -> not_ok;
-foo([_|_]) -> ok.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/queue/queue_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/queue/queue_use.erl
deleted file mode 100644
index 5682f2281e..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/queue/queue_use.erl
+++ /dev/null
@@ -1,66 +0,0 @@
--module(queue_use).
-
--export([ok1/0, ok2/0]).
--export([wrong1/0, wrong2/0, wrong3/0, wrong4/0, wrong5/0, wrong6/0, wrong7/0, wrong8/0]).
-
-ok1() ->
- queue:is_empty(queue:new()).
-
-ok2() ->
- Q0 = queue:new(),
- Q1 = queue:in(42, Q0),
- {{value, 42}, Q2} = queue:out(Q1),
- queue:is_empty(Q2).
-
-%%--------------------------------------------------
-
-wrong1() ->
- queue:is_empty({[],[]}).
-
-wrong2() ->
- Q0 = {[],[]},
- queue:in(42, Q0).
-
-wrong3() ->
- Q0 = queue:new(),
- Q1 = queue:in(42, Q0),
- {[42],Q2} = Q1,
- Q2.
-
-wrong4() ->
- Q0 = queue:new(),
- Q1 = queue:in(42, Q0),
- Q1 =:= {[42],[]}.
-
-wrong5() ->
- {F, _R} = queue:new(),
- F.
-
-wrong6() ->
- {{value, 42}, Q2} = queue:out({[42],[]}),
- Q2.
-
-%%--------------------------------------------------
-
--record(db, {p, q}).
-
-wrong7() ->
- add_unique(42, #db{p = [], q = queue:new()}).
-
-add_unique(E, DB) ->
- case is_in_queue(E, DB) of
- true -> DB;
- false -> DB#db{q = queue:in(E, DB#db.q)}
- end.
-
-is_in_queue(P, #db{q = {L1,L2}}) ->
- lists:member(P, L1) orelse lists:member(P, L2).
-
-%%--------------------------------------------------
-
-wrong8() ->
- tuple_queue({42, gazonk}).
-
-tuple_queue({F, Q}) ->
- queue:in(F, Q).
-
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/rec/rec_adt.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/rec/rec_adt.erl
deleted file mode 100644
index f01cc5e519..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/rec/rec_adt.erl
+++ /dev/null
@@ -1,22 +0,0 @@
--module(rec_adt).
-
--export([new/0, get_a/1, get_b/1, set_a/2, set_b/2]).
-
--record(rec, {a :: atom(), b = 0 :: integer()}).
-
--opaque rec() :: #rec{}.
-
--spec new() -> rec().
-new() -> #rec{a = gazonk, b = 42}.
-
--spec get_a(rec()) -> atom().
-get_a(#rec{a = A}) -> A.
-
--spec get_b(rec()) -> integer().
-get_b(#rec{b = B}) -> B.
-
--spec set_a(rec(), atom()) -> rec().
-set_a(R, A) -> R#rec{a = A}.
-
--spec set_b(rec(), integer()) -> rec().
-set_b(R, B) -> R#rec{b = B}.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/rec/rec_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/rec/rec_use.erl
deleted file mode 100644
index 358e9f918c..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/rec/rec_use.erl
+++ /dev/null
@@ -1,30 +0,0 @@
--module(rec_use).
-
--export([ok1/0, ok2/0, wrong1/0, wrong2/0, wrong3/0, wrong4/0]).
-
-ok1() ->
- rec_adt:set_a(rec_adt:new(), foo).
-
-ok2() ->
- R1 = rec_adt:new(),
- B1 = rec_adt:get_b(R1),
- R2 = rec_adt:set_b(R1, 42),
- B2 = rec_adt:get_b(R2),
- B1 =:= B2.
-
-wrong1() ->
- case rec_adt:new() of
- {rec, _, 42} -> weird1;
- R when tuple_size(R) =:= 3 -> weird2
- end.
-
-wrong2() ->
- R = list_to_tuple([rec, a, 42]),
- rec_adt:get_a(R).
-
-wrong3() ->
- R = rec_adt:new(),
- R =:= {rec, gazonk, 42}.
-
-wrong4() ->
- tuple_size(rec_adt:new()).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/timer/timer_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/timer/timer_use.erl
deleted file mode 100644
index 9c8ea0af1c..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/timer/timer_use.erl
+++ /dev/null
@@ -1,20 +0,0 @@
-%%---------------------------------------------------------------------------
-%% A test case with:
-%% - a genuine matching error -- 1st branch
-%% - a violation of the opaqueness of timer:tref() -- 2nd branch
-%% - a subtle violation of the opaqueness of timer:tref() -- 3rd branch
-%% The test is supposed to check that these cases are treated properly.
-%%---------------------------------------------------------------------------
-
--module(timer_use).
--export([wrong/0]).
-
--spec wrong() -> error.
-
-wrong() ->
- case timer:kill_after(42, self()) of
- gazonk -> weird;
- {ok, 42} -> weirder;
- {Tag, gazonk} when Tag =/= error -> weirdest;
- {error, _} -> error
- end.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/union/union_adt.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/union/union_adt.erl
deleted file mode 100644
index 5ca3202bba..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/union/union_adt.erl
+++ /dev/null
@@ -1,19 +0,0 @@
--module(union_adt).
--export([new/1, new_a/1, new_rec/1]).
-
--record(rec, {x = 42 :: integer()}).
-
--opaque u() :: 'aaa' | 'bbb' | #rec{}.
-
-new(a) -> aaa;
-new(b) -> bbb;
-new(X) when is_integer(X) ->
- #rec{x = X}.
-
-%% the following two functions (and their uses in union_use.erl) test
-%% that the return type is the opaque one and not just a subtype of it
-
-new_a(a) -> aaa.
-
-new_rec(X) when is_integer(X) ->
- #rec{x = X}.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/union/union_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/union/union_use.erl
deleted file mode 100644
index 6a103279cd..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/union/union_use.erl
+++ /dev/null
@@ -1,16 +0,0 @@
--module(union_use).
-
--export([test/1, wrong_a/0, wrong_rec/0]).
-
-test(X) ->
- case union_adt:new(X) of
- A when is_atom(A) -> atom;
- T when is_tuple(T) -> tuple
- end.
-
-wrong_a() ->
- aaa = union_adt:new_a(a),
- ok.
-
-wrong_rec() ->
- is_tuple(union_adt:new_rec(42)).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings.hrl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings.hrl
deleted file mode 100644
index b9339a8eb1..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings.hrl
+++ /dev/null
@@ -1,205 +0,0 @@
-%%
-%% wings.hrl --
-%%
-%% Global record definition and defines.
-%%
-%% Copyright (c) 2001-2005 Bjorn Gustavsson
-%%
-%% See the file "license.terms" for information on usage and redistribution
-%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
-%%
-%% $Id: wings.hrl,v 1.1 2009/01/25 18:55:33 kostis Exp $
-%%
-
--include("wings_intl.hrl").
-
--ifdef(NEED_ESDL).
--include_lib("esdl/include/sdl.hrl").
--include_lib("esdl/include/sdl_events.hrl").
--include_lib("esdl/include/sdl_video.hrl").
--include_lib("esdl/include/sdl_keyboard.hrl").
--include_lib("esdl/include/sdl_mouse.hrl").
--include_lib("esdl/src/sdl_util.hrl").
--define(CTRL_BITS, ?KMOD_CTRL).
--define(ALT_BITS, ?KMOD_ALT).
--define(SHIFT_BITS, ?KMOD_SHIFT).
--define(META_BITS, ?KMOD_META).
--endif.
-
--define(WINGS_VERSION, ?wings_version).
-
--define(CHAR_HEIGHT, wings_text:height()).
--define(CHAR_WIDTH, wings_text:width()).
-
--define(LINE_HEIGHT, (?CHAR_HEIGHT+2)).
--define(GROUND_GRID_SIZE, 1).
--define(CAMERA_DIST, (8.0*?GROUND_GRID_SIZE)).
--define(NORMAL_LINEWIDTH, 1.0).
--define(DEGREE, 176). %Degree character.
-
--define(HIT_BUF_SIZE, (1024*1024)).
-
--define(PANE_COLOR, {0.52,0.52,0.52}).
--define(BEVEL_HIGHLIGHT, {0.9,0.9,0.9}).
--define(BEVEL_LOWLIGHT, {0.3,0.3,0.3}).
--define(BEVEL_HIGHLIGHT_MIX, 0.5).
--define(BEVEL_LOWLIGHT_MIX, 0.5).
-
--define(SLOW(Cmd), begin wings_io:hourglass(), Cmd end).
--define(TC(Cmd), wings_util:tc(fun() -> Cmd end, ?MODULE, ?LINE)).
-
--ifdef(DEBUG).
--define(ASSERT(E), case E of
- true -> ok;
- _ ->
- erlang:error({assertion_failed,?MODULE,?LINE})
- end).
--define(CHECK_ERROR(), wings_gl:check_error(?MODULE, ?LINE)).
--else.
--define(ASSERT(E),ok).
--define(CHECK_ERROR(), ok).
--endif.
-
-%% Display lists per object.
-%% Important: Plain integers and integers in lists will be assumed to
-%% be display lists. Arbitrary integers must be stored inside a tuple
-%% or record to not be interpreted as a display list.
--record(dlo,
- {work=none, %Workmode faces.
- smooth=none, %Smooth-shaded faces.
- edges=none, %Edges and wire-frame.
- vs=none, %Unselected vertices.
- hard=none, %Hard edges.
- sel=none, %Selected items.
- orig_sel=none, %Original selection.
- normals=none, %Normals.
- pick=none, %For picking.
- proxy_faces=none, %Smooth proxy faces.
- proxy_edges=none, %Smooth proxy edges.
-
- %% Miscellanous.
- hilite=none, %Hilite display list.
- mirror=none, %Virtual mirror data.
- ns=none, %Normals/positions per face.
-
- %% Source for display lists.
- src_we=none, %Source object.
- src_sel=none, %Source selection.
- orig_mode=none, %Original selection mode.
- split=none, %Split data.
- drag=none, %For dragging.
- transparent=false, %Object includes transparancy.
- proxy_data=none, %Data for smooth proxy.
- open=false, %Open (has hole).
-
- %% List of display lists known to be needed only based
- %% on display modes, not whether the lists themselves exist.
- %% Example: [work,edges]
- needed=[]
- }).
-
-%% Main state record containing all objects and other important state.
--record(st,
- {shapes, %All visible shapes
- selmode, %Selection mode:
- % vertex, edge, face, body
- sh=false, %Smart highlight active: true|false
- sel=[], %Current sel: [{Id,GbSet}]
- ssels=[], %Saved selections:
- % [{Name,Mode,GbSet}]
- temp_sel=none, %Selection only temporary?
-
- mat, %Defined materials (GbTree).
- pal=[], %Palette
- file, %Current filename.
- saved, %True if model has been saved.
- onext, %Next object id to use.
- bb=none, %Saved bounding box.
- edge_loop=none, %Previous edge loop.
- views={0,{}}, %{Current,TupleOfViews}
- pst=gb_trees:empty(), %Plugin State Info
- % gb_tree where key is plugin module
-
- %% Previous commands.
- repeatable, %Last repeatable command.
- ask_args, %Ask arguments.
- drag_args, %Drag arguments for command.
- def, %Default operations.
-
- %% Undo information.
- top, %Top of stack.
- bottom, %Bottom of stack.
- next_is_undo, %State of undo/redo toggle.
- undone %States that were undone.
- }).
-
-%% The Winged-Edge data structure.
-%% See http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/model/winged-e.html
--record(we,
- {id, %Shape id.
- perm=0, %Permissions:
- % 0 - Everything allowed.
- % 1 - Visible, can't select.
- % [] or {Mode,GbSet} -
- % Invisible, can't select.
- % The GbSet contains the
- % object's selection.
- name, %Name.
- es, %gb_tree containing edges
- fs, %gb_tree containing faces
- he, %gb_sets containing hard edges
- vc, %Connection info (=incident edge)
- % for vertices.
- vp, %Vertex positions.
- pst=gb_trees:empty(), %Plugin State Info,
- % gb_tree where key is plugin module
- mat=default, %Materials.
- next_id, %Next free ID for vertices,
- % edges, and faces.
- % (Needed because we never re-use
- % IDs.)
- mode, %'vertex'/'material'/'uv'
- mirror=none, %Mirror: none|Face
- light=none, %Light data: none|Light
- has_shape=true %true|false
- }).
-
--define(IS_VISIBLE(Perm), (Perm =< 1)).
--define(IS_NOT_VISIBLE(Perm), (Perm > 1)).
--define(IS_SELECTABLE(Perm), (Perm == 0)).
--define(IS_NOT_SELECTABLE(Perm), (Perm =/= 0)).
-
--define(IS_LIGHT(We), ((We#we.light =/= none) and (not We#we.has_shape))).
--define(IS_ANY_LIGHT(We), (We#we.light =/= none)).
--define(HAS_SHAPE(We), (We#we.has_shape)).
-%-define(IS_LIGHT(We), (We#we.light =/= none)).
-%-define(IS_NOT_LIGHT(We), (We#we.light =:= none)).
-
-%% Edge in a winged-edge shape.
--record(edge,
- {vs, %Start vertex for edge
- ve, %End vertex for edge
- a=none, %Color or UV coordinate.
- b=none, %Color or UV coordinate.
- lf, %Left face
- rf, %Right face
- ltpr, %Left traversal predecessor
- ltsu, %Left traversal successor
- rtpr, %Right traversal predecessor
- rtsu %Right traversal successor
- }).
-
-%% The current view/camera.
--record(view,
- {origin,
- distance, % From origo.
- azimuth,
- elevation,
- pan_x, %Panning in X direction.
- pan_y, %Panning in Y direction.
- along_axis=none, %Which axis viewed along.
- fov, %Field of view.
- hither, %Near clipping plane.
- yon %Far clipping plane.
- }).
-
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_dissolve.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_dissolve.erl
deleted file mode 100644
index d7af9bb1d3..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_dissolve.erl
+++ /dev/null
@@ -1,375 +0,0 @@
-%%
-%% wings_dissolve.erl --
-%%
-%% This module implements dissolve of faces.
-%%
-
--module(wings_dissolve).
-
--export([faces/2, complement/2]).
-
--include("wings.hrl").
-
-%% faces([Face], We) -> We'
-%% Dissolve the given faces.
-faces([], We) -> We;
-faces(Faces, #we{fs=Ftab0}=We) ->
- case gb_sets:is_empty(Faces) of
- true -> We;
- false when is_list(Faces) ->
- Complement = ordsets:subtract(gb_trees:keys(Ftab0),
- ordsets:from_list(Faces)),
- dissolve_1(Faces, Complement, We);
- false ->
- Complement = ordsets:subtract(gb_trees:keys(Ftab0),
- gb_sets:to_list(Faces)),
- dissolve_1(Faces, Complement, We)
- end.
-
-faces([], _, We) -> We;
-faces(Faces,Complement,We) ->
- case gb_sets:is_empty(Faces) of
- true -> We;
- false -> dissolve_1(Faces, Complement,We)
- end.
-
-dissolve_1(Faces, Complement, We0) ->
- We1 = optimistic_dissolve(Faces,Complement,We0#we{vc=undefined}),
- NewFaces = wings_we:new_items_as_ordset(face, We0, We1),
- We2 = wings_face:delete_bad_faces(NewFaces, We1),
- We = wings_we:rebuild(We2),
- case wings_we:is_consistent(We) of
- true ->
- We;
- false ->
- io:format("Dissolving would cause an inconsistent object structure.")
- end.
-
-%% complement([Face], We) -> We'
-%% Dissolve all faces BUT the given faces. Also invalidate the
-%% mirror face if it existed and was dissolved.
-complement(Fs0, #we{fs=Ftab0}=We0) when is_list(Fs0) ->
- Fs = ordsets:subtract(gb_trees:keys(Ftab0), ordsets:from_list(Fs0)),
- case faces(Fs, Fs0, We0) of
- #we{mirror=none}=We -> We;
- #we{mirror=Face,fs=Ftab}=We ->
- case gb_trees:is_defined(Face, Ftab) of
- false -> We;
- true -> We#we{mirror=none}
- end
- end;
-complement(Fs, We) -> complement(gb_sets:to_list(Fs), We).
-
-optimistic_dissolve(Faces0, Compl, We0) ->
- %% Optimistically assume that we have a simple region without
- %% any holes.
- case outer_edge_loop(Faces0, We0) of
- error ->
- %% Assumption was wrong. We need to partition the selection
- %% and dissolve each partition in turn.
- Parts = wings_sel:face_regions(Faces0, We0),
- complex_dissolve(Parts, We0);
- [_|_]=Loop ->
- %% Assumption was correct.
- simple_dissolve(Faces0, Compl, Loop, We0)
- end.
-
-%% simple_dissolve(Faces, Loop, We0) -> We
-%% Dissolve a region of faces with no holes and no
-%% repeated vertices in the outer edge loop.
-
-simple_dissolve(Faces0, Compl, Loop, We0) ->
- Faces = to_gb_set(Faces0),
- OldFace = gb_sets:smallest(Faces),
- Mat = wings_facemat:face(OldFace, We0),
- We1 = fix_materials(Faces, Compl, We0),
- #we{es=Etab0,fs=Ftab0,he=Htab0} = We1,
- {Ftab1,Etab1,Htab} = simple_del(Faces, Ftab0, Etab0, Htab0, We1),
- {NewFace,We2} = wings_we:new_id(We1),
- Ftab = gb_trees:insert(NewFace, hd(Loop), Ftab1),
- Last = lists:last(Loop),
- Etab = update_outer([Last|Loop], Loop, NewFace, Ftab, Etab1),
- We = We2#we{es=Etab,fs=Ftab,he=Htab},
- wings_facemat:assign(Mat, [NewFace], We).
-
-fix_materials(Del,Keep,We) ->
- case gb_sets:size(Del) < length(Keep) of
- true ->
- wings_facemat:delete_faces(Del,We);
- false ->
- wings_facemat:keep_faces(Keep,We)
- end.
-
-to_gb_set(List) when is_list(List) ->
- gb_sets:from_list(List);
-to_gb_set(S) -> S.
-
-%% Delete faces and inner edges for a simple region.
-simple_del(Faces, Ftab0, Etab0, Htab0, We) ->
- case {gb_trees:size(Ftab0),gb_sets:size(Faces)} of
- {AllSz,FaceSz} when AllSz < 2*FaceSz ->
- %% At least half of the faces are selected.
- %% It is faster to find the edges for the
- %% unselected faces.
- UnselFaces = ordsets:subtract(gb_trees:keys(Ftab0),
- gb_sets:to_list(Faces)),
-
- UnselSet = sofs:from_external(UnselFaces, [face]),
- Ftab1 = sofs:from_external(gb_trees:to_list(Ftab0),
- [{face,edge}]),
- Ftab2 = sofs:restriction(Ftab1, UnselSet),
- Ftab = gb_trees:from_orddict(sofs:to_external(Ftab2)),
-
- Keep0 = wings_face:to_edges(UnselFaces, We),
- Keep = sofs:set(Keep0, [edge]),
- Etab1 = sofs:from_external(gb_trees:to_list(Etab0),
- [{edge,info}]),
- Etab2 = sofs:restriction(Etab1, Keep),
- Etab = gb_trees:from_orddict(sofs:to_external(Etab2)),
-
- Htab = simple_del_hard(Htab0, sofs:to_external(Keep), undefined),
- {Ftab,Etab,Htab};
- {_,_} ->
- Ftab = lists:foldl(fun(Face, Ft) ->
- gb_trees:delete(Face, Ft)
- end, Ftab0, gb_sets:to_list(Faces)),
- Inner = wings_face:inner_edges(Faces, We),
- Etab = lists:foldl(fun(Edge, Et) ->
- gb_trees:delete(Edge, Et)
- end, Etab0, Inner),
- Htab = simple_del_hard(Htab0, undefined, Inner),
- {Ftab,Etab,Htab}
- end.
-
-simple_del_hard(Htab, Keep, Remove) ->
- case gb_sets:is_empty(Htab) of
- true -> Htab;
- false -> simple_del_hard_1(Htab, Keep, Remove)
- end.
-
-simple_del_hard_1(Htab, Keep, undefined) ->
- gb_sets:intersection(Htab, gb_sets:from_ordset(Keep));
-simple_del_hard_1(Htab, undefined, Remove) ->
- gb_sets:difference(Htab, gb_sets:from_ordset(Remove)).
-
-%% complex([Partition], We0) -> We0
-%% The general dissolve.
-
-complex_dissolve([Faces|T], We0) ->
- Face = gb_sets:smallest(Faces),
- Mat = wings_facemat:face(Face, We0),
- We1 = wings_facemat:delete_faces(Faces, We0),
- Parts = outer_edge_partition(Faces, We1),
- We = do_dissolve(Faces, Parts, Mat, We0, We1),
- complex_dissolve(T, We);
-complex_dissolve([], We) -> We.
-
-do_dissolve(Faces, Ess, Mat, WeOrig, We0) ->
- We1 = do_dissolve_faces(Faces, We0),
- Inner = wings_face:inner_edges(Faces, WeOrig),
- We2 = delete_inner(Inner, We1),
- #we{he=Htab0} = We = do_dissolve_1(Ess, Mat, We2),
- Htab = gb_sets:difference(Htab0, gb_sets:from_list(Inner)),
- We#we{he=Htab}.
-
-do_dissolve_1([EdgeList|Ess], Mat, #we{es=Etab0,fs=Ftab0}=We0) ->
- {Face,We1} = wings_we:new_id(We0),
- Ftab = gb_trees:insert(Face, hd(EdgeList), Ftab0),
- Last = lists:last(EdgeList),
- Etab = update_outer([Last|EdgeList], EdgeList, Face, Ftab, Etab0),
- We2 = We1#we{es=Etab,fs=Ftab},
- We = wings_facemat:assign(Mat, [Face], We2),
- do_dissolve_1(Ess, Mat, We);
-do_dissolve_1([], _Mat, We) -> We.
-
-do_dissolve_faces(Faces, #we{fs=Ftab0}=We) ->
- Ftab = lists:foldl(fun(Face, Ft) ->
- gb_trees:delete(Face, Ft)
- end, Ftab0, gb_sets:to_list(Faces)),
- We#we{fs=Ftab}.
-
-delete_inner(Inner, #we{es=Etab0}=We) ->
- Etab = lists:foldl(fun(Edge, Et) ->
- gb_trees:delete(Edge, Et)
- end, Etab0, Inner),
- We#we{es=Etab}.
-
-update_outer([Pred|[Edge|Succ]=T], More, Face, Ftab, Etab0) ->
- #edge{rf=Rf} = R0 = gb_trees:get(Edge, Etab0),
- Rec = case gb_trees:is_defined(Rf, Ftab) of
- true ->
- ?ASSERT(false == gb_trees:is_defined(R0#edge.lf, Ftab)),
- LS = succ(Succ, More),
- R0#edge{lf=Face,ltpr=Pred,ltsu=LS};
- false ->
- ?ASSERT(true == gb_trees:is_defined(R0#edge.lf, Ftab)),
- RS = succ(Succ, More),
- R0#edge{rf=Face,rtpr=Pred,rtsu=RS}
- end,
- Etab = gb_trees:update(Edge, Rec, Etab0),
- update_outer(T, More, Face, Ftab, Etab);
-update_outer([_], _More, _Face, _Ftab, Etab) -> Etab.
-
-succ([Succ|_], _More) -> Succ;
-succ([], [Succ|_]) -> Succ.
-
-%% outer_edge_loop(FaceSet,WingedEdge) -> [Edge] | error.
-%% Partition the outer edges of the FaceSet into a single closed loop.
-%% Return 'error' if the faces in FaceSet does not form a
-%% simple region without holes.
-%%
-%% Equvivalent to
-%% case outer_edge_partition(FaceSet,WingedEdge) of
-%% [Loop] -> Loop;
-%% [_|_] -> error
-%% end.
-%% but faster.
-
-outer_edge_loop(Faces, We) ->
- case lists:sort(collect_outer_edges(Faces, We)) of
- [] -> error;
- [{Key,Val}|Es0] ->
- case any_duplicates(Es0, Key) of
- false ->
- Es = gb_trees:from_orddict(Es0),
- N = gb_trees:size(Es),
- outer_edge_loop_1(Val, Es, Key, N, []);
- true -> error
- end
- end.
-
-outer_edge_loop_1({Edge,V}, _, V, 0, Acc) ->
- %% This edge completes the loop, and we have used all possible edges.
- [Edge|Acc];
-outer_edge_loop_1({_,V}, _, V, _N, _) ->
- %% Loop is complete, but we haven't used all edges.
- error;
-outer_edge_loop_1({_,_}, _, _, 0, _) ->
- %% We have used all possible edges, but somehow the loop
- %% is not complete. I can't see how this is possible.
- erlang:error(internal_error);
-outer_edge_loop_1({Edge,Vb}, Es, EndV, N, Acc0) ->
- Acc = [Edge|Acc0],
- outer_edge_loop_1(gb_trees:get(Vb, Es), Es, EndV, N-1, Acc).
-
-any_duplicates([{V,_}|_], V) -> true;
-any_duplicates([_], _) -> false;
-any_duplicates([{V,_}|Es], _) -> any_duplicates(Es, V).
-
-%% outer_edge_partition(FaceSet, WingedEdge) -> [[Edge]].
-%% Partition the outer edges of the FaceSet. Each partion
-%% of edges form a closed loop with no repeated vertices.
-%% Outer edges are edges that have one face in FaceSet
-%% and one outside.
-%% It is assumed that FaceSet consists of one region returned by
-%% wings_sel:face_regions/2.
-
-outer_edge_partition(Faces, We) ->
- F0 = collect_outer_edges(Faces, We),
- F = gb_trees:from_orddict(wings_util:rel2fam(F0)),
- partition_edges(F, []).
-
-collect_outer_edges(Faces, We) when is_list(Faces) ->
- collect_outer_edges_1(Faces, gb_sets:from_list(Faces), We);
-collect_outer_edges(Faces, We) ->
- collect_outer_edges_1(gb_sets:to_list(Faces), Faces, We).
-
-collect_outer_edges_1(Fs0, Faces0, #we{fs=Ftab}=We) ->
- case {gb_trees:size(Ftab),gb_sets:size(Faces0)} of
- {AllSz,FaceSz} when AllSz < 2*FaceSz ->
- Fs = ordsets:subtract(gb_trees:keys(Ftab), Fs0),
- Faces = gb_sets:from_ordset(Fs),
- Coll = collect_outer_edges_a(Faces),
- wings_face:fold_faces(Coll, [], Fs, We);
- {_,_} ->
- Coll = collect_outer_edges_b(Faces0),
- wings_face:fold_faces(Coll, [], Fs0, We)
- end.
-
-collect_outer_edges_a(Faces) ->
- fun(Face, _, Edge, #edge{ve=V,vs=OtherV,lf=Face,rf=Other}, Acc) ->
- case gb_sets:is_member(Other, Faces) of
- false -> [{V,{Edge,OtherV}}|Acc];
- true -> Acc
- end;
- (Face, _, Edge, #edge{ve=OtherV,vs=V,rf=Face,lf=Other}, Acc) ->
- case gb_sets:is_member(Other, Faces) of
- false -> [{V,{Edge,OtherV}}|Acc];
- true -> Acc
- end
- end.
-
-collect_outer_edges_b(Faces) ->
- fun(Face, _, Edge, #edge{vs=V,ve=OtherV,lf=Face,rf=Other}, Acc) ->
- case gb_sets:is_member(Other, Faces) of
- false -> [{V,{Edge,OtherV}}|Acc];
- true -> Acc
- end;
- (Face, _, Edge, #edge{vs=OtherV,ve=V,rf=Face,lf=Other}, Acc) ->
- case gb_sets:is_member(Other, Faces) of
- false -> [{V,{Edge,OtherV}}|Acc];
- true -> Acc
- end
- end.
-
-partition_edges(Es0, Acc) ->
- case gb_trees:is_empty(Es0) of
- true -> Acc;
- false ->
- {Key,Val,Es1} = gb_trees:take_smallest(Es0),
- {Cycle,Es} = part_collect_cycle(Key, Val, Es1, []),
- partition_edges(Es, [Cycle|Acc])
- end.
-
-%% part_collect_cycle(Vertex, VertexInfo, EdgeInfo, Acc0) ->
-%% none | {[Edge],EdgeInfo}
-%% Collect the cycle starting with Vertex.
-%%
-%% Note: This function can only return 'none' when called
-%% recursively.
-
-part_collect_cycle(_, repeated, _, _) ->
- %% Repeated vertex - we are not allowed to go this way.
- %% Can only happen if we were called recursively because
- %% a fork was encountered.
- none;
-part_collect_cycle(_Va, [{Edge,Vb}], Es0, Acc0) ->
- %% Basic case. Only one way to go.
- Acc = [Edge|Acc0],
- case gb_trees:lookup(Vb, Es0) of
- none ->
- {Acc,Es0};
- {value,Val} ->
- Es = gb_trees:delete(Vb, Es0),
- part_collect_cycle(Vb, Val, Es, Acc)
- end;
-part_collect_cycle(Va, [Val|More], Es0, []) ->
- %% No cycle started yet and we have multiple choice of
- %% edges out from this vertex. It doesn't matter which
- %% edge we follow, so we'll follow the first one.
- {Cycle,Es} = part_collect_cycle(Va, [Val], Es0, []),
- {Cycle,gb_trees:insert(Va, More, Es)};
-part_collect_cycle(Va, Edges, Es0, Acc) ->
- %% We have a partially collected cycle and we have a
- %% fork (multiple choice of edges). Here we must choose
- %% an edge that closes the cycle without passing Va
- %% again (because repeated vertices are not allowed).
- Es = gb_trees:insert(Va, repeated, Es0),
- part_fork(Va, Edges, Es, Acc, []).
-
-part_fork(Va, [Val|More], Es0, Acc, Tried) ->
- %% Try to complete the cycle by following this edge.
- case part_collect_cycle(Va, [Val], Es0, Acc) of
- none ->
- %% Failure - try the next edge.
- part_fork(Va, More, Es0, Acc, [Val|Tried]);
- {Cycle,Es} ->
- %% Found a cycle. Update the vertex information
- %% with all edges remaining.
- {Cycle,gb_trees:update(Va, lists:reverse(Tried, More), Es)}
- end;
-part_fork(_, [], _, _, _) ->
- %% None of edges were possible. Can only happen if this function
- %% was called recursively (i.e. if we hit another fork while
- %% processing a fork).
- none.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_edge.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_edge.erl
deleted file mode 100644
index 3483acb711..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_edge.erl
+++ /dev/null
@@ -1,243 +0,0 @@
-%%
-%% wings_edge.erl --
-%%
-%% This module contains most edge command and edge utility functions.
-%%
-%% Copyright (c) 2001-2008 Bjorn Gustavsson.
-%%
-%% See the file "license.terms" for information on usage and redistribution
-%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
-%%
-%% $Id: wings_edge.erl,v 1.1 2009/01/25 18:55:33 kostis Exp $
-%%
-
--module(wings_edge).
-
--export([dissolve_edges/2]).
-
--include("wings.hrl").
-
-%%%
-%%% Dissolve.
-%%%
-
-dissolve_edges(Edges0, We0) when is_list(Edges0) ->
- #we{es=Etab} = We1 = lists:foldl(fun internal_dissolve_edge/2, We0, Edges0),
- case [E || E <- Edges0, gb_trees:is_defined(E, Etab)] of
- Edges0 ->
- %% No edge was deleted in the last pass. We are done.
- We = wings_we:rebuild(We0#we{vc=undefined}),
- wings_we:validate_mirror(We);
- Edges ->
- dissolve_edges(Edges, We1)
- end;
-dissolve_edges(Edges, We) ->
- dissolve_edges(gb_sets:to_list(Edges), We).
-
-internal_dissolve_edge(Edge, #we{es=Etab}=We0) ->
- case gb_trees:lookup(Edge, Etab) of
- none -> We0;
- {value,#edge{ltpr=Same,ltsu=Same,rtpr=Same,rtsu=Same}} ->
- Empty = gb_trees:empty(),
- We0#we{vc=Empty,vp=Empty,es=Empty,fs=Empty,he=gb_sets:empty()};
- {value,#edge{rtpr=Back,ltsu=Back}=Rec} ->
- merge_edges(backward, Edge, Rec, We0);
- {value,#edge{rtsu=Forward,ltpr=Forward}=Rec} ->
- merge_edges(forward, Edge, Rec, We0);
- {value,Rec} ->
- try dissolve_edge_1(Edge, Rec, We0) of
- We -> We
- catch
- throw:hole -> We0
- end
- end.
-
-%% dissolve_edge_1(Edge, EdgeRecord, We) -> We
-%% Remove an edge and a face. If one of the faces is degenerated
-%% (only consists of two edges), remove that one. Otherwise, it
-%% doesn't matter which face we remove.
-dissolve_edge_1(Edge, #edge{lf=Remove,rf=Keep,ltpr=Same,ltsu=Same}=Rec, We) ->
- dissolve_edge_2(Edge, Remove, Keep, Rec, We);
-dissolve_edge_1(Edge, #edge{lf=Keep,rf=Remove}=Rec, We) ->
- dissolve_edge_2(Edge, Remove, Keep, Rec, We).
-
-dissolve_edge_2(Edge, FaceRemove, FaceKeep,
- #edge{ltpr=LP,ltsu=LS,rtpr=RP,rtsu=RS},
- #we{fs=Ftab0,es=Etab0,he=Htab0}=We0) ->
- %% First change face for all edges surrounding the face we will remove.
- Etab1 = wings_face:fold(
- fun (_, E, _, IntEtab) when E =:= Edge -> IntEtab;
- (_, E, R, IntEtab) ->
- case R of
- #edge{lf=FaceRemove,rf=FaceKeep} ->
- throw(hole);
- #edge{rf=FaceRemove,lf=FaceKeep} ->
- throw(hole);
- #edge{lf=FaceRemove} ->
- gb_trees:update(E, R#edge{lf=FaceKeep}, IntEtab);
- #edge{rf=FaceRemove} ->
- gb_trees:update(E, R#edge{rf=FaceKeep}, IntEtab)
- end
- end, Etab0, FaceRemove, We0),
-
- %% Patch all predecessors and successor of the edge we will remove.
- Etab2 = patch_edge(LP, RS, Edge, Etab1),
- Etab3 = patch_edge(LS, RP, Edge, Etab2),
- Etab4 = patch_edge(RP, LS, Edge, Etab3),
- Etab5 = patch_edge(RS, LP, Edge, Etab4),
-
- %% Remove the edge.
- Etab = gb_trees:delete(Edge, Etab5),
- Htab = hardness(Edge, soft, Htab0),
-
- %% Remove the face. Patch the face entry for the remaining face.
- Ftab1 = gb_trees:delete(FaceRemove, Ftab0),
- We1 = wings_facemat:delete_face(FaceRemove, We0),
- Ftab = gb_trees:update(FaceKeep, LP, Ftab1),
-
- %% Return result.
- We = We1#we{es=Etab,fs=Ftab,vc=undefined,he=Htab},
- AnEdge = gb_trees:get(FaceKeep, Ftab),
- case gb_trees:get(AnEdge, Etab) of
- #edge{lf=FaceKeep,ltpr=Same,ltsu=Same} ->
- internal_dissolve_edge(AnEdge, We);
- #edge{rf=FaceKeep,rtpr=Same,rtsu=Same} ->
- internal_dissolve_edge(AnEdge, We);
- _Other ->
- case wings_we:is_face_consistent(FaceKeep, We) of
- true ->
- We;
- false ->
- io:format("Dissolving would cause a badly formed face.")
- end
- end.
-
-%%
-%% We like winged edges, but not winged vertices (a vertex with
-%% only two edges connected to it). We will remove the winged vertex
-%% by joining the two edges connected to it.
-%%
-
-merge_edges(Dir, Edge, Rec, #we{es=Etab}=We) ->
- {Va,Vb,_,_,_,_,To,To} = half_edge(Dir, Rec),
- case gb_trees:get(To, Etab) of
- #edge{vs=Va,ve=Vb} ->
- del_2edge_face(Dir, Edge, Rec, To, We);
- #edge{vs=Vb,ve=Va} ->
- del_2edge_face(Dir, Edge, Rec, To, We);
- _Other ->
- merge_1(Dir, Edge, Rec, To, We)
- end.
-
-merge_1(Dir, Edge, Rec, To, #we{es=Etab0,fs=Ftab0,he=Htab0}=We) ->
- OtherDir = reverse_dir(Dir),
- {Vkeep,Vdelete,Lf,Rf,A,B,L,R} = half_edge(OtherDir, Rec),
- Etab1 = patch_edge(L, To, Edge, Etab0),
- Etab2 = patch_edge(R, To, Edge, Etab1),
- Etab3 = patch_half_edge(To, Vkeep, Lf, A, L, Rf, B, R, Vdelete, Etab2),
- Htab = hardness(Edge, soft, Htab0),
- Etab = gb_trees:delete(Edge, Etab3),
- #edge{lf=Lf,rf=Rf} = Rec,
- Ftab1 = update_face(Lf, To, Edge, Ftab0),
- Ftab = update_face(Rf, To, Edge, Ftab1),
- merge_2(To, We#we{es=Etab,fs=Ftab,he=Htab,vc=undefined}).
-
-merge_2(Edge, #we{es=Etab}=We) ->
- %% If the merged edge is part of a two-edge face, we must
- %% remove that edge too.
- case gb_trees:get(Edge, Etab) of
- #edge{ltpr=Same,ltsu=Same} ->
- internal_dissolve_edge(Edge, We);
- #edge{rtpr=Same,rtsu=Same} ->
- internal_dissolve_edge(Edge, We);
- _Other -> We
- end.
-
-update_face(Face, Edge, OldEdge, Ftab) ->
- case gb_trees:get(Face, Ftab) of
- OldEdge -> gb_trees:update(Face, Edge, Ftab);
- _Other -> Ftab
- end.
-
-del_2edge_face(Dir, EdgeA, RecA, EdgeB,
- #we{es=Etab0,fs=Ftab0,he=Htab0}=We) ->
- {_,_,Lf,Rf,_,_,_,_} = half_edge(reverse_dir(Dir), RecA),
- RecB = gb_trees:get(EdgeB, Etab0),
- Del = gb_sets:from_list([EdgeA,EdgeB]),
- EdgeANear = stabile_neighbor(RecA, Del),
- EdgeBNear = stabile_neighbor(RecB, Del),
- Etab1 = patch_edge(EdgeANear, EdgeBNear, EdgeA, Etab0),
- Etab2 = patch_edge(EdgeBNear, EdgeANear, EdgeB, Etab1),
- Etab3 = gb_trees:delete(EdgeA, Etab2),
- Etab = gb_trees:delete(EdgeB, Etab3),
-
- %% Patch hardness table.
- Htab1 = hardness(EdgeA, soft, Htab0),
- Htab = hardness(EdgeB, soft, Htab1),
-
- %% Patch the face table.
- #edge{lf=Klf,rf=Krf} = gb_trees:get(EdgeANear, Etab),
- KeepFaces = ordsets:from_list([Klf,Krf]),
- EdgeAFaces = ordsets:from_list([Lf,Rf]),
- [DelFace] = ordsets:subtract(EdgeAFaces, KeepFaces),
- Ftab1 = gb_trees:delete(DelFace, Ftab0),
- [KeepFace] = ordsets:intersection(KeepFaces, EdgeAFaces),
- Ftab2 = update_face(KeepFace, EdgeANear, EdgeA, Ftab1),
- Ftab = update_face(KeepFace, EdgeBNear, EdgeB, Ftab2),
-
- %% Return result.
- We#we{vc=undefined,es=Etab,fs=Ftab,he=Htab}.
-
-stabile_neighbor(#edge{ltpr=Ea,ltsu=Eb,rtpr=Ec,rtsu=Ed}, Del) ->
- [Edge] = lists:foldl(fun(E, A) ->
- case gb_sets:is_member(E, Del) of
- true -> A;
- false -> [E|A]
- end
- end, [], [Ea,Eb,Ec,Ed]),
- Edge.
-
-%%%
-%%% Setting hard/soft edges.
-%%%
-
-hardness(Edge, soft, Htab) -> gb_sets:delete_any(Edge, Htab);
-hardness(Edge, hard, Htab) -> gb_sets:add(Edge, Htab).
-
-%%%
-%%% Utilities.
-%%%
-
-reverse_dir(forward) -> backward;
-reverse_dir(backward) -> forward.
-
-half_edge(backward, #edge{vs=Va,ve=Vb,lf=Lf,rf=Rf,a=A,b=B,ltsu=L,rtpr=R}) ->
- {Va,Vb,Lf,Rf,A,B,L,R};
-half_edge(forward, #edge{ve=Va,vs=Vb,lf=Lf,rf=Rf,a=A,b=B,ltpr=L,rtsu=R}) ->
- {Va,Vb,Lf,Rf,A,B,L,R}.
-
-patch_half_edge(Edge, V, FaceA, A, Ea, FaceB, B, Eb, OrigV, Etab) ->
- New = case gb_trees:get(Edge, Etab) of
- #edge{vs=OrigV,lf=FaceA,rf=FaceB}=Rec ->
- Rec#edge{a=A,vs=V,ltsu=Ea,rtpr=Eb};
- #edge{vs=OrigV,lf=FaceB,rf=FaceA}=Rec ->
- Rec#edge{a=B,vs=V,ltsu=Eb,rtpr=Ea};
- #edge{ve=OrigV,lf=FaceA,rf=FaceB}=Rec ->
- Rec#edge{b=B,ve=V,ltpr=Ea,rtsu=Eb};
- #edge{ve=OrigV,lf=FaceB,rf=FaceA}=Rec ->
- Rec#edge{b=A,ve=V,ltpr=Eb,rtsu=Ea}
- end,
- gb_trees:update(Edge, New, Etab).
-
-patch_edge(Edge, ToEdge, OrigEdge, Etab) ->
- New = case gb_trees:get(Edge, Etab) of
- #edge{ltsu=OrigEdge}=R ->
- R#edge{ltsu=ToEdge};
- #edge{ltpr=OrigEdge}=R ->
- R#edge{ltpr=ToEdge};
- #edge{rtsu=OrigEdge}=R ->
- R#edge{rtsu=ToEdge};
- #edge{rtpr=OrigEdge}=R ->
- R#edge{rtpr=ToEdge}
- end,
- gb_trees:update(Edge, New, Etab).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_edge_cmd.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_edge_cmd.erl
deleted file mode 100644
index e478ec245b..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_edge_cmd.erl
+++ /dev/null
@@ -1,91 +0,0 @@
-%%
-%% wings_edge.erl --
-%%
-%% This module contains most edge command and edge utility functions.
-%%
-
--module(wings_edge_cmd).
-
--export([loop_cut/1]).
-
--include("wings.hrl").
-
-%%%
-%%% The Loop Cut command.
-%%%
-
-loop_cut(St0) ->
- {Sel,St} = wings_sel:fold(fun loop_cut/3, {[],St0}, St0),
- wings_sel:set(body, Sel, St).
-
-loop_cut(Edges, #we{name=Name,id=Id,fs=Ftab}=We0, {Sel,St0}) ->
- AdjFaces = wings_face:from_edges(Edges, We0),
- case loop_cut_partition(AdjFaces, Edges, We0, []) of
- [_] ->
- io:format("Edge loop doesn't divide ~p into two parts.", [Name]);
- Parts0 ->
- %% We arbitrarily decide that the largest part of the object
- %% will be left unselected and will keep the name of the object.
-
- Parts1 = [{gb_trees:size(P),P} || P <- Parts0],
- Parts2 = lists:reverse(lists:sort(Parts1)),
- [_|Parts] = [gb_sets:to_list(P) || {_,P} <- Parts2],
-
- %% Also, this first part will also contain any sub-object
- %% that was not reachable from any of the edges. Therefore,
- %% we calculate the first part as the complement of the union
- %% of all other parts.
-
- FirstComplement = ordsets:union(Parts),
- First = ordsets:subtract(gb_trees:keys(Ftab), FirstComplement),
-
- We = wings_dissolve:complement(First, We0),
- Shs = St0#st.shapes,
- St = St0#st{shapes=gb_trees:update(Id, We, Shs)},
- loop_cut_make_copies(Parts, We0, Sel, St)
- end.
-
-loop_cut_make_copies([P|Parts], We0, Sel0, #st{onext=Id}=St0) ->
- Sel = [{Id,gb_sets:singleton(0)}|Sel0],
- We = wings_dissolve:complement(P, We0),
- St = wings_shape:insert(We, cut, St0),
- loop_cut_make_copies(Parts, We0, Sel, St);
-loop_cut_make_copies([], _, Sel, St) -> {Sel,St}.
-
-loop_cut_partition(Faces0, Edges, We, Acc) ->
- case gb_sets:is_empty(Faces0) of
- true -> Acc;
- false ->
- {AFace,Faces1} = gb_sets:take_smallest(Faces0),
- Reachable = collect_faces(AFace, Edges, We),
- Faces = gb_sets:difference(Faces1, Reachable),
- loop_cut_partition(Faces, Edges, We, [Reachable|Acc])
- end.
-
-collect_faces(Face, Edges, We) ->
- collect_faces(gb_sets:singleton(Face), We, Edges, gb_sets:empty()).
-
-collect_faces(Work0, We, Edges, Acc0) ->
- case gb_sets:is_empty(Work0) of
- true -> Acc0;
- false ->
- {Face,Work1} = gb_sets:take_smallest(Work0),
- Acc = gb_sets:insert(Face, Acc0),
- Work = collect_maybe_add(Work1, Face, Edges, We, Acc),
- collect_faces(Work, We, Edges, Acc)
- end.
-
-collect_maybe_add(Work, Face, Edges, We, Res) ->
- wings_face:fold(
- fun(_, Edge, Rec, A) ->
- case gb_sets:is_member(Edge, Edges) of
- true -> A;
- false ->
- Of = wings_face:other(Face, Rec),
- case gb_sets:is_member(Of, Res) of
- true -> A;
- false -> gb_sets:add(Of, A)
- end
- end
- end, Work, Face, We).
-
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_face.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_face.erl
deleted file mode 100644
index 487c05aa58..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_face.erl
+++ /dev/null
@@ -1,127 +0,0 @@
-%%
-%% wings_face.erl --
-%%
-%% This module contains help routines for faces, such as fold functions
-%% face iterators.
-%%
-
--module(wings_face).
-
--export([delete_bad_faces/2, fold/4, fold_faces/4, from_edges/2,
- inner_edges/2, to_edges/2, other/2]).
-
--include("wings.hrl").
-
-from_edges(Es, #we{es=Etab}) when is_list(Es) ->
- from_edges_1(Es, Etab, []);
-from_edges(Es, We) ->
- from_edges(gb_sets:to_list(Es), We).
-
-from_edges_1([E|Es], Etab, Acc) ->
- #edge{lf=Lf,rf=Rf} = gb_trees:get(E, Etab),
- from_edges_1(Es, Etab, [Lf,Rf|Acc]);
-from_edges_1([], _, Acc) -> gb_sets:from_list(Acc).
-
-%% other(Face, EdgeRecord) -> OtherFace
-%% Pick up the "other face" from an edge record.
-other(Face, #edge{lf=Face,rf=Other}) -> Other;
-other(Face, #edge{rf=Face,lf=Other}) -> Other.
-
-%% to_edges(Faces, We) -> [Edge]
-%% Convert a set or list of faces to a list of edges.
-to_edges(Fs, We) ->
- ordsets:from_list(to_edges_raw(Fs, We)).
-
-%% inner_edges(Faces, We) -> [Edge]
-%% Given a set of faces, return all inner edges.
-inner_edges(Faces, We) ->
- S = to_edges_raw(Faces, We),
- inner_edges_1(lists:sort(S), []).
-
-inner_edges_1([E,E|T], In) ->
- inner_edges_1(T, [E|In]);
-inner_edges_1([_|T], In) ->
- inner_edges_1(T, In);
-inner_edges_1([], In) -> lists:reverse(In).
-
-%% Fold over all edges surrounding a face.
-
-fold(F, Acc, Face, #we{es=Etab,fs=Ftab}) ->
- Edge = gb_trees:get(Face, Ftab),
- fold(Edge, Etab, F, Acc, Face, Edge, not_done).
-
-fold(LastEdge, _, _, Acc, _, LastEdge, done) -> Acc;
-fold(Edge, Etab, F, Acc0, Face, LastEdge, _) ->
- case gb_trees:get(Edge, Etab) of
- #edge{ve=V,lf=Face,ltsu=NextEdge}=E ->
- Acc = F(V, Edge, E, Acc0),
- fold(NextEdge, Etab, F, Acc, Face, LastEdge, done);
- #edge{vs=V,rf=Face,rtsu=NextEdge}=E ->
- Acc = F(V, Edge, E, Acc0),
- fold(NextEdge, Etab, F, Acc, Face, LastEdge, done)
- end.
-
-%% Fold over a set of faces.
-
-fold_faces(F, Acc0, [Face|Faces], #we{es=Etab,fs=Ftab}=We) ->
- Edge = gb_trees:get(Face, Ftab),
- Acc = fold_faces_1(Edge, Etab, F, Acc0, Face, Edge, not_done),
- fold_faces(F, Acc, Faces, We);
-fold_faces(_F, Acc, [], _We) -> Acc;
-fold_faces(F, Acc, Faces, We) ->
- fold_faces(F, Acc, gb_sets:to_list(Faces), We).
-
-fold_faces_1(LastEdge, _, _, Acc, _, LastEdge, done) -> Acc;
-fold_faces_1(Edge, Etab, F, Acc0, Face, LastEdge, _) ->
- case gb_trees:get(Edge, Etab) of
- #edge{ve=V,lf=Face,ltsu=NextEdge}=E ->
- Acc = F(Face, V, Edge, E, Acc0),
- fold_faces_1(NextEdge, Etab, F, Acc, Face, LastEdge, done);
- #edge{vs=V,rf=Face,rtsu=NextEdge}=E ->
- Acc = F(Face, V, Edge, E, Acc0),
- fold_faces_1(NextEdge, Etab, F, Acc, Face, LastEdge, done)
- end.
-
-%% Return an unsorted list of edges for the faces (with duplicates).
-
-to_edges_raw(Faces, #we{es=Etab,fs=Ftab}) when is_list(Faces) ->
- to_edges_raw(Faces, Ftab, Etab, []);
-to_edges_raw(Faces, We) ->
- to_edges_raw(gb_sets:to_list(Faces), We).
-
-to_edges_raw([Face|Faces], Ftab, Etab, Acc0) ->
- Edge = gb_trees:get(Face, Ftab),
- Acc = to_edges_raw_1(Edge, Etab, Acc0, Face, Edge, not_done),
- to_edges_raw(Faces, Ftab, Etab, Acc);
-to_edges_raw([], _, _, Acc) -> Acc.
-
-to_edges_raw_1(LastEdge, _, Acc, _, LastEdge, done) -> Acc;
-to_edges_raw_1(Edge, Etab, Acc, Face, LastEdge, _) ->
- case gb_trees:get(Edge, Etab) of
- #edge{lf=Face,ltsu=NextEdge} ->
- to_edges_raw_1(NextEdge, Etab, [Edge|Acc], Face, LastEdge, done);
- #edge{rf=Face,rtsu=NextEdge} ->
- to_edges_raw_1(NextEdge, Etab, [Edge|Acc], Face, LastEdge, done)
- end.
-
-delete_bad_faces(Fs, #we{fs=Ftab,es=Etab}=We) when is_list(Fs) ->
- Es = bad_edges(Fs, Ftab, Etab, []),
- wings_edge:dissolve_edges(Es, We);
-delete_bad_faces(Fs, We) ->
- delete_bad_faces(gb_sets:to_list(Fs), We).
-
-bad_edges([F|Fs], Ftab, Etab, Acc) ->
- case gb_trees:lookup(F, Ftab) of
- {value,Edge} ->
- case gb_trees:get(Edge, Etab) of
- #edge{ltpr=Same,ltsu=Same,rtpr=Same,rtsu=Same} ->
- erlang:error({internal_error,one_edged_face,F});
- #edge{ltpr=Same,ltsu=Same} ->
- bad_edges(Fs, Ftab, Etab, [Edge|Acc]);
- #edge{rtpr=Same,rtsu=Same} ->
- bad_edges(Fs, Ftab, Etab, [Edge|Acc]);
- _ -> bad_edges(Fs, Ftab, Etab, Acc)
- end;
- none -> bad_edges(Fs, Ftab, Etab, Acc)
- end;
-bad_edges([], _, _, Acc) -> Acc.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_facemat.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_facemat.erl
deleted file mode 100644
index 6e018e49b5..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_facemat.erl
+++ /dev/null
@@ -1,299 +0,0 @@
-%%
-%% wings_facemat.erl --
-%%
-%% This module keeps tracks of the mapping from a face number
-%% to its material name.
-%%
-%% Copyright (c) 2001-2005 Bjorn Gustavsson
-%%
-%% See the file "license.terms" for information on usage and redistribution
-%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
-%%
-%% $Id: wings_facemat.erl,v 1.1 2009/01/25 18:55:33 kostis Exp $
-%%
-%%
-%%
-
--module(wings_facemat).
--export([all/1,face/2,used_materials/1,mat_faces/2,
- assign/2,assign/3,
- delete_face/2,delete_faces/2,keep_faces/2,
- hide_faces/1,show_faces/1,
- renumber/2,gc/1,merge/1]).
-
--include("wings.hrl").
--import(lists, [keysearch/3,reverse/1,reverse/2,sort/1]).
-
-%%%
-%%% API functions for retrieving information.
-%%%
-
-%% all(We) -> [{Face,MaterialName}]
-%% Return materials for all faces as an ordered list.
-all(#we{mat=M}=We) when is_atom(M) ->
- Vis = visible_faces(We),
- make_tab(Vis, M);
-all(#we{mat=L}) when is_list(L) ->
- remove_invisible(L).
-
-%% face(Face, We) -> MaterialName
-%% Return the material for the face Face.
-face(_, #we{mat=M}) when is_atom(M) -> M;
-face(Face, #we{mat=Tab}) ->
- {value,{_,Mat}} = keysearch(Face, 1, Tab),
- Mat.
-
-%% used_materials(We) -> [MaterialName]
-%% Return an ordered list of all materials used in the We.
-used_materials(#we{mat=M}) when is_atom(M) -> [M];
-used_materials(#we{mat=L}) when is_list(L) ->
- used_materials_1(L, []).
-
-%% mat_faces([{Face,Info}], We) -> [{Mat,[{Face,Info}]}]
-%% Group face tab into groups based on material.
-%% Used for displaying objects.
-mat_faces(Ftab, #we{mat=AtomMat}) when is_atom(AtomMat) ->
- [{AtomMat,Ftab}];
-mat_faces(Ftab, #we{mat=MatTab}) ->
- mat_faces_1(Ftab, remove_invisible(MatTab), []).
-
-%%%
-%%% API functions for updating material name mapping.
-%%%
-
-%% assign([{Face,MaterialName}], We) -> We'
-%% Assign materials.
-assign([], We) -> We;
-assign([{F,M}|_]=FaceMs, We) when is_atom(M), is_integer(F) ->
- Tab = ordsets:from_list(FaceMs),
- assign_face_ms(Tab, We).
-
-%% assign(MaterialName, Faces, We) -> We'
-%% Assign MaterialName to all faces Faces.
-assign(Mat, _, #we{mat=Mat}=We) when is_atom(Mat) -> We;
-assign(Mat, Fs, We) when is_atom(Mat), is_list(Fs) ->
- assign_1(Mat, Fs, We);
-assign(Mat, Fs, We) when is_atom(Mat) ->
- assign_1(Mat, gb_sets:to_list(Fs), We).
-
-%% delete_face(Face, We) -> We'
-%% Delete the material name mapping for the face Face.
-delete_face(_, #we{mat=AtomMat}=We) when is_atom(AtomMat) -> We;
-delete_face(Face, #we{mat=MatTab0}=We) ->
- MatTab = orddict:erase(Face, MatTab0),
- We#we{mat=MatTab}.
-
-%% delete_face(Faces, We) -> We'
-%% Delete the material name mapping for all faces Faces.
-delete_faces(_, #we{mat=AtomMat}=We) when is_atom(AtomMat) -> We;
-delete_faces(Faces0, #we{mat=MatTab0}=We) when is_list(Faces0) ->
- Faces = sofs:from_external(Faces0, [face]),
- MatTab1 = sofs:from_external(MatTab0, [{face,mat}]),
- MatTab2 = sofs:drestriction(MatTab1, Faces),
- MatTab = sofs:to_external(MatTab2),
- We#we{mat=MatTab};
-delete_faces(Faces, We) ->
- delete_faces(gb_sets:to_list(Faces), We).
-
-%% keep_faces(Faces, We) -> We'
-%% Delete all the other material names mapping for all faces other Faces.
-keep_faces(_, #we{mat=AtomMat}=We) when is_atom(AtomMat) -> We;
-keep_faces([Face], We) ->
- Mat = face(Face,We),
- We#we{mat=[{Face,Mat}]};
-keep_faces(Faces0, #we{mat=MatTab0}=We) when is_list(Faces0) ->
- Faces = sofs:from_external(Faces0, [face]),
- MatTab1 = sofs:from_external(MatTab0, [{face,mat}]),
- MatTab2 = sofs:restriction(MatTab1, Faces),
- MatTab = sofs:to_external(MatTab2),
- We#we{mat=MatTab};
-keep_faces(Faces, We) ->
- keep_faces(gb_sets:to_list(Faces), We).
-
-%% hide_faces(We) -> We'
-%% Update the material name mapping in the We to reflect
-%% the newly hidden faces in the face tab.
-hide_faces(#we{mat=M}=We) when is_atom(M) -> We;
-hide_faces(#we{mat=L0,fs=Ftab}=We) ->
- L = hide_faces_1(L0, Ftab, []),
- We#we{mat=L}.
-
-%% show_faces(We) -> We'
-%% Update the material name mapping in the We to reflect
-%% that all faces are again visible.
-show_faces(#we{mat=M}=We) when is_atom(M) -> We;
-show_faces(#we{mat=L0}=We) ->
- L = show_faces_1(L0, []),
- We#we{mat=L}.
-
-%% renumber(MaterialMapping, FaceOldToNew) -> MaterialMapping.
-%% Renumber face number in material name mapping.
-renumber(Mat, _) when is_atom(Mat) -> Mat;
-renumber(L, Fmap) when is_list(L) -> renumber_1(L, Fmap, []).
-
-%% gc(We) -> We'
-%% Garbage collect the material mapping information, removing
-%% the mapping for any face no longer present in the face table.
-gc(#we{mat=Mat}=We) when is_atom(Mat) -> We;
-gc(#we{mat=Tab0,fs=Ftab}=We) ->
- Fs = sofs:from_external(gb_trees:keys(Ftab), [face]),
- Tab1 = sofs:from_external(Tab0, [{face,material}]),
- Tab2 = sofs:restriction(Tab1, Fs),
- Tab = sofs:to_external(Tab2),
- We#we{mat=compress(Tab)}.
-
-%% merge([We]) -> [{Face,MaterialName}] | MaterialName.
-%% Merge materials for several objects.
-merge([#we{mat=M}|Wes]=L) when is_atom(M) ->
- case merge_all_same(Wes, M) of
- true -> M;
- false -> merge_1(L, [])
- end;
-merge(L) -> merge_1(L, []).
-
-merge_1([#we{mat=M,es=Etab}|T], Acc) when is_atom(M) ->
- FsM = merge_2(gb_trees:values(Etab), M, []),
- merge_1(T, [FsM|Acc]);
-merge_1([#we{mat=FsMs}|T], Acc) ->
- merge_1(T, [FsMs|Acc]);
-merge_1([], Acc) -> lists:merge(Acc).
-
-merge_2([#edge{lf=Lf,rf=Rf}|T], M, Acc) ->
- merge_2(T, M, [{Lf,M},{Rf,M}|Acc]);
-merge_2([], _, Acc) -> ordsets:from_list(Acc).
-
-merge_all_same([#we{mat=M}|Wes], M) -> merge_all_same(Wes, M);
-merge_all_same([_|_], _) -> false;
-merge_all_same([], _) -> true.
-
-%%%
-%%% Local functions.
-%%%
-
-assign_1(Mat, Fs, #we{fs=Ftab}=We) ->
- case length(Fs) =:= gb_trees:size(Ftab) of
- true -> We#we{mat=Mat};
- false -> assign_2(Mat, Fs, We)
- end.
-
-assign_2(Mat, Fs0, #we{fs=Ftab,mat=Mat0}=We) when is_atom(Mat0) ->
- Fs = ordsets:from_list(Fs0),
- OtherFaces = ordsets:subtract(gb_trees:keys(Ftab), Fs),
- Tab0 = make_tab(OtherFaces, Mat0),
- Tab1 = make_tab(Fs, Mat),
- Tab = lists:merge(Tab0, Tab1),
- We#we{mat=Tab};
-assign_2(Mat, Fs0, #we{mat=Tab0}=We) when is_list(Tab0) ->
- Fs = ordsets:from_list(Fs0),
- Tab1 = make_tab(Fs, Mat),
- Tab = mat_merge(Tab1, Tab0, []),
- We#we{mat=Tab}.
-
-assign_face_ms(Tab, #we{fs=Ftab}=We) ->
- case length(Tab) =:= gb_trees:size(Ftab) of
- true -> We#we{mat=compress(Tab)};
- false -> assign_face_ms_1(Tab, We)
- end.
-
-assign_face_ms_1(Tab1, #we{fs=Ftab,mat=Mat0}=We) when is_atom(Mat0) ->
- Tab0 = make_tab(gb_trees:keys(Ftab), Mat0),
- Tab = mat_merge(Tab1, Tab0, []),
- We#we{mat=Tab};
-assign_face_ms_1(Tab1, #we{mat=Tab0}=We) when is_list(Tab0) ->
- Tab = mat_merge(Tab1, Tab0, []),
- We#we{mat=Tab}.
-
-mat_merge([{Fn,_}|_]=Fns, [{Fo,_}=Fold|Fos], Acc) when Fo < Fn ->
- mat_merge(Fns, Fos, [Fold|Acc]);
-mat_merge([{Fn,_}=Fnew|Fns], [{Fo,_}|_]=Fos, Acc) when Fo > Fn ->
- mat_merge(Fns, Fos, [Fnew|Acc]);
-mat_merge([Fnew|Fns], [_|Fos], Acc) -> % Equality
- mat_merge(Fns, Fos, [Fnew|Acc]);
-mat_merge([], Fos, Acc) ->
- rev_compress(Acc, Fos);
-mat_merge(Fns, [], Acc) ->
- rev_compress(Acc, Fns).
-
-make_tab(Fs, M) ->
- make_tab_1(Fs, M, []).
-
-make_tab_1([F|Fs], M, Acc) ->
- make_tab_1(Fs, M, [{F,M}|Acc]);
-make_tab_1([], _, Acc) -> reverse(Acc).
-
-
-visible_faces(#we{fs=Ftab}) ->
- visible_faces_1(gb_trees:keys(Ftab)).
-
-visible_faces_1([F|Fs]) when F < 0 ->
- visible_faces_1(Fs);
-visible_faces_1(Fs) -> Fs.
-
-remove_invisible([{F,_}|Fs]) when F < 0 ->
- remove_invisible(Fs);
-remove_invisible(Fs) -> Fs.
-
-hide_faces_1([{F,_}=P|Fms], Ftab, Acc) when F < 0 ->
- hide_faces_1(Fms, Ftab, [P|Acc]);
-hide_faces_1([{F,M}=P|Fms], Ftab, Acc) ->
- case gb_trees:is_defined(F, Ftab) of
- false -> hide_faces_1(Fms, Ftab, [{-F-1,M}|Acc]);
- true -> hide_faces_1(Fms, Ftab, [P|Acc])
- end;
-hide_faces_1([], _, Acc) -> sort(Acc).
-
-show_faces_1([{F,M}|Fms], Acc) when F < 0 ->
- show_faces_1(Fms, [{-F-1,M}|Acc]);
-show_faces_1(Fs, Acc) -> sort(Acc++Fs).
-
-renumber_1([{F,M}|T], Fmap, Acc) ->
- renumber_1(T, Fmap, [{gb_trees:get(F, Fmap),M}|Acc]);
-renumber_1([], _, Acc) -> sort(Acc).
-
-%% rev_compress([{Face,Mat}], [{Face,Mat}]) -> [{Face,Mat}] | Mat.
-%% Reverse just like lists:reverse/2, but if all materials
-%% turns out to be just the same, return that material.
-rev_compress(L, Acc) ->
- case same_mat(Acc) of
- [] -> reverse(L, Acc);
- M -> rev_compress_1(L, M, Acc)
- end.
-
-rev_compress_1([{_,M}=E|T], M, Acc) ->
- %% Same material.
- rev_compress_1(T, M, [E|Acc]);
-rev_compress_1([_|_]=L, _, Acc) ->
- %% Another material. Finish by using reverse/2.
- reverse(L, Acc);
-rev_compress_1([], M, _) ->
- %% All materials turned out to be the same.
- M.
-
-%% compress(MaterialTab) -> [{Face,Mat}] | Mat.
-%% Compress a face mapping if possible.
-compress(M) when is_atom(M) -> M;
-compress(L) when is_list(L) ->
- case same_mat(L) of
- [] -> L;
- M -> M
- end.
-
-same_mat([]) -> [];
-same_mat([{_,M}|T]) -> same_mat_1(T, M).
-
-same_mat_1([{_,M}|T], M) -> same_mat_1(T, M);
-same_mat_1([], M) -> M;
-same_mat_1(_, _) -> [].
-
-used_materials_1([{_,M}|T], [M|_]=Acc) ->
- used_materials_1(T, Acc);
-used_materials_1([{_,M}|T], Acc) ->
- used_materials_1(T, [M|Acc]);
-used_materials_1([], Acc) ->
- ordsets:from_list(Acc).
-
-mat_faces_1([{F1,_}|_]=Fs, [{F2,_}|Ms], Acc) when F2 < F1 ->
- mat_faces_1(Fs, Ms, Acc);
-mat_faces_1([{F,Info}|Fs], [{F,Mat}|Ms], Acc) ->
- mat_faces_1(Fs, Ms, [{Mat,{F,Info}}|Acc]);
-mat_faces_1([], _, Acc) -> wings_util:rel2fam(Acc).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_intl.hrl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_intl.hrl
deleted file mode 100644
index ebcb560f27..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_intl.hrl
+++ /dev/null
@@ -1,15 +0,0 @@
-%%
-%% wings_intl.hrl --
-%%
-%% Defines for translations
-%%
-%% Copyright (c) 2001-2005 Bjorn Gustavsson
-%%
-%% See the file "license.terms" for information on usage and redistribution
-%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
-%%
-%% $Id: wings_intl.hrl,v 1.1 2009/01/25 18:55:33 kostis Exp $
-%%
-
--define(STR(A,B,Str), wings_lang:str({?MODULE,A,B},Str)).
--define(__(Key,Str), wings_lang:str({?MODULE,Key},Str)).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_io.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_io.erl
deleted file mode 100644
index 39002c675d..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_io.erl
+++ /dev/null
@@ -1,37 +0,0 @@
-%%
-%% wings_io.erl --
-%%
-%% This module contains most of the low-level GUI for Wings.
-%%
-
--module(wings_io).
-
--export([get_matching_events/1]).
-
--define(EVENT_QUEUE, wings_io_event_queue).
-
-%%%
-%%% Input.
-%%%
-
-get_matching_events(Filter) ->
- Eq = get(?EVENT_QUEUE),
- get_matching_events_1(Filter, Eq, [], []).
-
-get_matching_events_1(Filter, Eq0, Match, NoMatch) ->
- case queue:out(Eq0) of
- {{value,Ev},Eq} ->
- case Filter(Ev) of
- false ->
- get_matching_events_1(Filter, Eq, Match, [Ev|NoMatch]);
- true ->
- get_matching_events_1(Filter, Eq, [Ev|Match], NoMatch)
- end;
- {empty,{In,Out}} ->
- case Match of
- [] -> [];
- _ ->
- put(?EVENT_QUEUE, {In, lists:reverse(NoMatch, Out)}),
- Match
- end
- end.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_sel.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_sel.erl
deleted file mode 100644
index eef797027e..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_sel.erl
+++ /dev/null
@@ -1,68 +0,0 @@
-%%
-%% wings_sel.erl --
-%%
-%% This module implements selection utilities.
-%%
-
--module(wings_sel).
-
--export([face_regions/2, fold/3, set/3]).
-
--include("wings.hrl").
-
-set(Mode, Sel, St) ->
- St#st{selmode=Mode, sel=lists:sort(Sel), sh=false}.
-
-%%%
-%%% Fold over the selection.
-%%%
-
-fold(F, Acc, #st{sel=Sel,shapes=Shapes}) ->
- fold_1(F, Acc, Shapes, Sel).
-
-fold_1(F, Acc0, Shapes, [{Id,Items}|T]) ->
- We = gb_trees:get(Id, Shapes),
- ?ASSERT(We#we.id =:= Id),
- fold_1(F, F(Items, We, Acc0), Shapes, T);
-fold_1(_F, Acc, _Shapes, []) -> Acc.
-
-%%%
-%%% Divide the face selection into regions where each face shares at least
-%%% one edge with another face in the same region. Two faces can share a
-%%% vertex without necessarily being in the same region.
-%%%
-
-face_regions(Faces, We) when is_list(Faces) ->
- face_regions_1(gb_sets:from_list(Faces), We);
-face_regions(Faces, We) ->
- face_regions_1(Faces, We).
-
-face_regions_1(Faces, We) ->
- find_face_regions(Faces, We, fun collect_face_fun/5, []).
-
-find_face_regions(Faces0, We, Coll, Acc) ->
- case gb_sets:is_empty(Faces0) of
- true -> Acc;
- false ->
- {Face,Faces1} = gb_sets:take_smallest(Faces0),
- Ws = [Face],
- {Reg,Faces} = collect_face_region(Ws, We, Coll, [], Faces1),
- find_face_regions(Faces, We, Coll, [Reg|Acc])
- end.
-
-collect_face_region([_|_]=Ws0, We, Coll, Reg0, Faces0) ->
- Reg = Ws0++Reg0,
- {Ws,Faces} = wings_face:fold_faces(Coll, {[],Faces0}, Ws0, We),
- collect_face_region(Ws, We, Coll, Reg, Faces);
-collect_face_region([], _, _, Reg, Faces) ->
- {gb_sets:from_list(Reg),Faces}.
-
-collect_face_fun(Face, _, _, Rec, {Ws,Faces}=A) ->
- Of = case Rec of
- #edge{lf=Face,rf=Of0} -> Of0;
- #edge{rf=Face,lf=Of0} -> Of0
- end,
- case gb_sets:is_member(Of, Faces) of
- true -> {[Of|Ws],gb_sets:delete(Of, Faces)};
- false -> A
- end.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_shape.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_shape.erl
deleted file mode 100644
index 0df8ca68eb..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_shape.erl
+++ /dev/null
@@ -1,69 +0,0 @@
-%%
-%% wings_shape.erl --
-%%
-%% Utilities for shape records.
-%%
-
--module(wings_shape).
-
--export([insert/3]).
-
--include("wings.hrl").
-
-%%%
-%%% Exported functions.
-%%%
-
-%% new(We, Suffix, St0) -> St.
-%% Suffix = cut | clone | copy | extract | sep
-%%
-%% Create a new object based on an old object. The name
-%% will be created from the old name (with digits and known
-%% suffixes stripped) with the given Suffix and a number
-%% appended.
-insert(#we{name=OldName}=We0, Suffix, #st{shapes=Shapes0,onext=Oid}=St) ->
- Name = new_name(OldName, Suffix, Oid),
- We = We0#we{id=Oid,name=Name},
- Shapes = gb_trees:insert(Oid, We, Shapes0),
- St#st{shapes=Shapes,onext=Oid+1}.
-
-%%%
-%%% Local functions follow.
-%%%
-
-new_name(OldName, Suffix0, Id) ->
- Suffix = suffix(Suffix0),
- Base = base(lists:reverse(OldName)),
- lists:reverse(Base, "_" ++ Suffix ++ integer_to_list(Id)).
-
-%% Note: Filename suffixes are intentionally not translated.
-%% If we are to translate them in the future, base/1 below
-%% must be updated to strip suffixes (both for the current language
-%% and for English).
-
-suffix(cut) -> "cut";
-suffix(clone) -> "clone";
-suffix(copy) -> "copy";
-suffix(extract) -> "extract";
-suffix(mirror) -> "mirror";
-suffix(sep) -> "sep".
-
-%% base_1(ReversedName) -> ReversedBaseName
-%% Given an object name, strip digits and known suffixes to
-%% create a base name. Returns the unchanged name if
-%% no known suffix could be stripped.
-
-base(OldName) ->
- case base_1(OldName) of
- error -> OldName;
- Base -> Base
- end.
-
-base_1([H|T]) when $0 =< H, H =< $9 -> base_1(T);
-base_1("tuc_"++Base) -> Base; %"_cut"
-base_1("enolc_"++Base) -> Base; %"_clone"
-base_1("ypoc_"++Base) -> Base; %"_copy"
-base_1("tcartxe_"++Base) -> Base; %"_extract"
-base_1("rorrim_"++Base) -> Base; %"_mirror"
-base_1("pes_"++Base) -> Base; %"_sep"
-base_1(_Base) -> error.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_util.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_util.erl
deleted file mode 100644
index 9572e19955..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_util.erl
+++ /dev/null
@@ -1,39 +0,0 @@
-%%
-%% wings_util.erl --
-%%
-%% Various utility functions that not obviously fit somewhere else.
-%%
-
--module(wings_util).
-
--export([gb_trees_smallest_key/1, gb_trees_largest_key/1,
- gb_trees_map/2, rel2fam/1]).
-
--include("wings.hrl").
-
-rel2fam(Rel) ->
- sofs:to_external(sofs:relation_to_family(sofs:relation(Rel))).
-
-%% a definition that does not violate the opaqueness of gb_tree()
-gb_trees_smallest_key(Tree) ->
- {Key, _V} = gb_trees:smallest(Tree),
- Key.
-
-%% a definition that violates the opaqueness of gb_tree()
-gb_trees_largest_key({_, Tree}) ->
- largest_key1(Tree).
-
-largest_key1({Key, _Value, _Smaller, nil}) ->
- Key;
-largest_key1({_Key, _Value, _Smaller, Larger}) ->
- largest_key1(Larger).
-
-gb_trees_map(F, {Size,Tree}) ->
- {Size,gb_trees_map_1(F, Tree)}.
-
-gb_trees_map_1(_, nil) -> nil;
-gb_trees_map_1(F, {K,V,Smaller,Larger}) ->
- {K,F(K, V),
- gb_trees_map_1(F, Smaller),
- gb_trees_map_1(F, Larger)}.
-
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_we.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_we.erl
deleted file mode 100644
index d782144def..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_we.erl
+++ /dev/null
@@ -1,250 +0,0 @@
-%%
-%% wings_we.erl --
-%%
-%% This module contains functions to build and manipulate
-%% we records (winged-edged records, the central data structure
-%% in Wings 3D).
-
--module(wings_we).
-
--export([rebuild/1, is_consistent/1, is_face_consistent/2, new_id/1,
- new_items_as_ordset/3, validate_mirror/1, visible/1, visible_edges/1]).
-
--include("wings.hrl").
-
-%%%
-%%% API.
-%%%
-
-validate_mirror(#we{mirror=none}=We) -> We;
-validate_mirror(#we{fs=Ftab,mirror=Face}=We) ->
- case gb_trees:is_defined(Face, Ftab) of
- false -> We#we{mirror=none};
- true -> We
- end.
-
-%% rebuild(We) -> We'
-%% Rebuild any missing 'vc' and 'fs' tables. If there are
-%% fewer elements in the 'vc' table than in the 'vp' table,
-%% remove redundant entries in the 'vp' table. Updated id
-%% bounds.
-rebuild(#we{vc=undefined,fs=undefined,es=Etab0}=We0) ->
- Etab = gb_trees:to_list(Etab0),
- Ftab = rebuild_ftab(Etab),
- VctList = rebuild_vct(Etab),
- We = We0#we{vc=gb_trees:from_orddict(VctList),fs=Ftab},
- rebuild_1(VctList, We);
-rebuild(#we{vc=undefined,es=Etab}=We) ->
- VctList = rebuild_vct(gb_trees:to_list(Etab), []),
- rebuild_1(VctList, We#we{vc=gb_trees:from_orddict(VctList)});
-rebuild(#we{fs=undefined,es=Etab}=We) ->
- Ftab = rebuild_ftab(gb_trees:to_list(Etab)),
- rebuild(We#we{fs=Ftab});
-rebuild(We) -> update_id_bounds(We).
-
-%%% Utilities for allocating IDs.
-
-new_id(#we{next_id=Id}=We) ->
- {Id,We#we{next_id=Id+1}}.
-
-%%% Returns sets of newly created items.
-
-new_items_as_ordset(vertex, #we{next_id=Wid}, #we{next_id=NewWid,vp=Tab}) ->
- new_items_as_ordset_1(Tab, Wid, NewWid);
-new_items_as_ordset(edge, #we{next_id=Wid}, #we{next_id=NewWid,es=Tab}) ->
- new_items_as_ordset_1(Tab, Wid, NewWid);
-new_items_as_ordset(face, #we{next_id=Wid}, #we{next_id=NewWid,fs=Tab}) ->
- new_items_as_ordset_1(Tab, Wid, NewWid).
-
-any_hidden(#we{fs=Ftab}) ->
- not gb_trees:is_empty(Ftab) andalso
- wings_util:gb_trees_smallest_key(Ftab) < 0.
-
-%%%
-%%% Local functions.
-%%%
-
-rebuild_1(VctList, #we{vc=Vct,vp=Vtab0}=We) ->
- case {gb_trees:size(Vct),gb_trees:size(Vtab0)} of
- {Same,Same} -> rebuild(We);
- {Sz1,Sz2} when Sz1 < Sz2 ->
- Vtab = vertex_gc_1(VctList, gb_trees:to_list(Vtab0), []),
- rebuild(We#we{vp=Vtab})
- end.
-
-rebuild_vct(Es) ->
- rebuild_vct(Es, []).
-
-rebuild_vct([{Edge,#edge{vs=Va,ve=Vb}}|Es], Acc0) ->
- Acc = rebuild_maybe_add(Va, Vb, Edge, Acc0),
- rebuild_vct(Es, Acc);
-rebuild_vct([], VtoE) ->
- build_incident_tab(VtoE).
-
-rebuild_ftab(Es) ->
- rebuild_ftab_1(Es, []).
-
-rebuild_ftab_1([{Edge,#edge{lf=Lf,rf=Rf}}|Es], Acc0) ->
- Acc = rebuild_maybe_add(Lf, Rf, Edge, Acc0),
- rebuild_ftab_1(Es, Acc);
-rebuild_ftab_1([], FtoE) ->
- gb_trees:from_orddict(build_incident_tab(FtoE)).
-
-rebuild_maybe_add(Ka, Kb, E, [_,{Ka,_}|_]=Acc) ->
- [{Kb,E}|Acc];
-rebuild_maybe_add(Ka, Kb, E, [_,{Kb,_}|_]=Acc) ->
- [{Ka,E}|Acc];
-rebuild_maybe_add(Ka, Kb, E, [{Ka,_}|_]=Acc) ->
- [{Kb,E}|Acc];
-rebuild_maybe_add(Ka, Kb, E, [{Kb,_}|_]=Acc) ->
- [{Ka,E}|Acc];
-rebuild_maybe_add(Ka, Kb, E, Acc) ->
- [{Ka,E},{Kb,E}|Acc].
-
-vertex_gc_1([{V,_}|Vct], [{V,_}=Vtx|Vpos], Acc) ->
- vertex_gc_1(Vct, Vpos, [Vtx|Acc]);
-vertex_gc_1([_|_]=Vct, [_|Vpos], Acc) ->
- vertex_gc_1(Vct, Vpos, Acc);
-vertex_gc_1([], _, Acc) ->
- gb_trees:from_orddict(lists:reverse(Acc)).
-
-%%%
-%%% Handling of hidden faces.
-%%%
-
-visible(#we{mirror=none,fs=Ftab}) ->
- visible_2(gb_trees:keys(Ftab));
-visible(#we{mirror=Face,fs=Ftab}) ->
- visible_2(gb_trees:keys(gb_trees:delete(Face, Ftab))).
-
-visible_2([F|Fs]) when F < 0 -> visible_2(Fs);
-visible_2(Fs) -> Fs.
-
-visible_edges(#we{es=Etab,mirror=Face}=We) ->
- case any_hidden(We) of
- false -> gb_trees:keys(Etab);
- true -> visible_es_1(gb_trees:to_list(Etab), Face, [])
- end.
-
-visible_es_1([{E,#edge{lf=Lf,rf=Rf}}|Es], Face, Acc) ->
- if
- Lf < 0 ->
- %% Left face hidden.
- if
- Rf < 0; Rf =:= Face ->
- %% Both faces invisible (in some way).
- visible_es_1(Es, Face, Acc);
- true ->
- %% Right face is visible.
- visible_es_1(Es, Face, [E|Acc])
- end;
- Lf =:= Face, Rf < 0 ->
- %% Left face mirror, right face hidden.
- visible_es_1(Es, Face, Acc);
- true ->
- %% At least one face visible.
- visible_es_1(Es, Face, [E|Acc])
- end;
-visible_es_1([], _, Acc) -> ordsets:from_list(Acc).
-
-update_id_bounds(#we{vp=Vtab,es=Etab,fs=Ftab}=We) ->
- case gb_trees:is_empty(Etab) of
- true -> We#we{next_id=0};
- false ->
- LastId = lists:max([wings_util:gb_trees_largest_key(Vtab),
- wings_util:gb_trees_largest_key(Etab),
- wings_util:gb_trees_largest_key(Ftab)]),
- We#we{next_id=LastId+1}
- end.
-
-%% build_incident_tab([{Elem,Edge}]) -> [{Elem,Edge}]
-%% Elem = Face or Vertex
-%% Build the table of incident edges for either faces or vertices.
-%% Returns an ordered list where each Elem is unique.
-
-build_incident_tab(ElemToEdgeRel) ->
- T = ets:new(?MODULE, [ordered_set]),
- ets:insert(T, ElemToEdgeRel),
- R = ets:tab2list(T),
- ets:delete(T),
- R.
-
-%%%
-%%% Calculate normals.
-%%%
-
-new_items_as_ordset_1(Tab, Wid, NewWid) when NewWid-Wid < 32 ->
- new_items_as_ordset_2(Wid, NewWid, Tab, []);
-new_items_as_ordset_1(Tab, Wid, _NewWid) ->
- [Item || Item <- gb_trees:keys(Tab), Item >= Wid].
-
-new_items_as_ordset_2(Wid, NewWid, Tab, Acc) when Wid < NewWid ->
- case gb_trees:is_defined(Wid, Tab) of
- true -> new_items_as_ordset_2(Wid+1, NewWid, Tab, [Wid|Acc]);
- false -> new_items_as_ordset_2(Wid+1, NewWid, Tab, Acc)
- end;
-new_items_as_ordset_2(_Wid, _NewWid, _Tab, Acc) -> lists:reverse(Acc).
-
-%%%
-%%% Test the consistency of a #we{}.
-%%%
-
-is_consistent(#we{}=We) ->
- try
- validate_vertex_tab(We),
- validate_faces(We)
- catch error:_ -> false
- end.
-
-is_face_consistent(Face, #we{fs=Ftab,es=Etab}) ->
- Edge = gb_trees:get(Face, Ftab),
- try validate_face(Face, Edge, Etab)
- catch error:_ -> false
- end.
-
-validate_faces(#we{fs=Ftab,es=Etab}) ->
- validate_faces_1(gb_trees:to_list(Ftab), Etab).
-
-validate_faces_1([{Face,Edge}|Fs], Etab) ->
- validate_face(Face, Edge, Etab),
- validate_faces_1(Fs, Etab);
-validate_faces_1([], _) -> true.
-
-validate_face(Face, Edge, Etab) ->
- Ccw = walk_face_ccw(Edge, Etab, Face, Edge, []),
- Edge = walk_face_cw(Edge, Etab, Face, Ccw),
- [V|Vs] = lists:sort(Ccw),
- validate_face_vertices(Vs, V).
-
-validate_face_vertices([V|_], V) ->
- erlang:error(repeated_vertex);
-validate_face_vertices([_], _) ->
- true;
-validate_face_vertices([V|Vs], _) ->
- validate_face_vertices(Vs, V).
-
-walk_face_ccw(LastEdge, _, _, LastEdge, [_|_]=Acc) -> Acc;
-walk_face_ccw(Edge, Etab, Face, LastEdge, Acc) ->
- case gb_trees:get(Edge, Etab) of
- #edge{ve=V,lf=Face,ltpr=Next} ->
- walk_face_ccw(Next, Etab, Face, LastEdge, [V|Acc]);
- #edge{vs=V,rf=Face,rtpr=Next} ->
- walk_face_ccw(Next, Etab, Face, LastEdge, [V|Acc])
- end.
-
-walk_face_cw(Edge, _, _, []) -> Edge;
-walk_face_cw(Edge, Etab, Face, [V|Vs]) ->
- case gb_trees:get(Edge, Etab) of
- #edge{vs=V,lf=Face,ltsu=Next} ->
- walk_face_cw(Next, Etab, Face, Vs);
- #edge{ve=V,rf=Face,rtsu=Next} ->
- walk_face_cw(Next, Etab, Face, Vs)
- end.
-
-validate_vertex_tab(#we{es=Etab,vc=Vct}) ->
- lists:foreach(fun({V,Edge}) ->
- case gb_trees:get(Edge, Etab) of
- #edge{vs=V} -> ok;
- #edge{ve=V} -> ok
- end
- end, gb_trees:to_list(Vct)).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis1.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis1.erl
deleted file mode 100644
index 82bcf2edcf..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis1.erl
+++ /dev/null
@@ -1,14 +0,0 @@
--module(zoltan_kis1).
-
--export([f/0, gen/0]).
-
--opaque id() :: string().
-
--spec f() -> integer().
-
-%BIF and Unification(t_unify) issue
-f() -> erlang:length(gen()).
-
--spec gen() -> id().
-
-gen() -> "Dummy".
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis2.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis2.erl
deleted file mode 100644
index 3a269622fd..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis2.erl
+++ /dev/null
@@ -1,14 +0,0 @@
--module(zoltan_kis2).
-
--export([get/2]).
-
--opaque data() :: gb_tree().
-
--spec get(term(), data()) -> term().
-
-get(Key, Data) ->
- %%Should unopaque data for remote calls
- case gb_trees:lookup(Key, Data) of
- 'none' -> 'undefined';
- {'value', Val} -> Val
- end.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis3.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis3.erl
deleted file mode 100644
index d92c6766ff..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis3.erl
+++ /dev/null
@@ -1,14 +0,0 @@
--module(zoltan_kis3).
-
--export([f/0, gen/0]).
-
--opaque id() :: string().
-
--spec f() -> char().
-
-%%List pattern matching issue
-f() -> [H|_T] = gen(), H.
-
--spec gen() -> id().
-
-gen() -> "Dummy".
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis4.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis4.erl
deleted file mode 100644
index aa1a4abcb7..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis4.erl
+++ /dev/null
@@ -1,14 +0,0 @@
--module(zoltan_kis4).
-
--export([f/0, gen/0]).
-
--opaque id() :: string().
-
--spec f() -> boolean().
-
-%%Equality test issue
-f() -> "Dummy" == gen().
-
--spec gen() -> id().
-
-gen() -> "Dummy".
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis5.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis5.erl
deleted file mode 100644
index 30cebf806a..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis5.erl
+++ /dev/null
@@ -1,14 +0,0 @@
--module(zoltan_kis5).
-
--export([f/0, gen/0]).
-
--opaque id() :: string().
-
--spec f() -> boolean().
-
-%% Equality test issue
-f() -> "Dummy" == gen().
-
--spec gen() -> id().
-
-gen() -> "Dummy".
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis6.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis6.erl
deleted file mode 100644
index 6f0779d7d1..0000000000
--- a/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis6.erl
+++ /dev/null
@@ -1,14 +0,0 @@
--module(zoltan_kis6).
-
--export([f/0, gen/0]).
-
--opaque id() :: {integer(),atom()}.
-
-%%-spec f() -> id().
-
-%% Tuple Unification (t_unify) issue
-f() -> {X,Y} = gen().
-
--spec gen() -> id().
-
-gen() -> {34, leprecon}.
diff --git a/lib/dialyzer/test/options1_SUITE_data/dialyzer_options b/lib/dialyzer/test/options1_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..c612e77d3e
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/dialyzer_options
@@ -0,0 +1,2 @@
+{dialyzer_options, [{include_dirs, ["my_include"]}, {defines, [{'COMPILER_VSN', 42}]}, {warnings, [no_improper_lists]}]}.
+{time_limit, 30}.
diff --git a/lib/dialyzer/test/options1_SUITE_data/my_include/CVS/Entries b/lib/dialyzer/test/options1_SUITE_data/my_include/CVS/Entries
new file mode 100644
index 0000000000..513d4a315a
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/my_include/CVS/Entries
@@ -0,0 +1,3 @@
+/erl_bits.hrl/1.1/Wed Dec 17 09:53:40 2008//
+/erl_compile.hrl/1.1/Wed Dec 17 09:53:40 2008//
+D
diff --git a/lib/dialyzer/test/options1_SUITE_data/my_include/CVS/Repository b/lib/dialyzer/test/options1_SUITE_data/my_include/CVS/Repository
new file mode 100644
index 0000000000..1c6511fec3
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/my_include/CVS/Repository
@@ -0,0 +1 @@
+dialyzer_tests/option_tests/compiler/my_include
diff --git a/lib/dialyzer/test/options1_SUITE_data/my_include/CVS/Root b/lib/dialyzer/test/options1_SUITE_data/my_include/CVS/Root
new file mode 100644
index 0000000000..f6cdd6158b
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/my_include/CVS/Root
@@ -0,0 +1 @@
+:pserver:stavros@cvs.srv.it.uu.se:/hipe
diff --git a/lib/dialyzer/test/options1_SUITE_data/my_include/erl_bits.hrl b/lib/dialyzer/test/options1_SUITE_data/my_include/erl_bits.hrl
new file mode 100644
index 0000000000..45045ebb33
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/my_include/erl_bits.hrl
@@ -0,0 +1,43 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.0, (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License at
+%% http://www.erlang.org/EPL1_0.txt
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Original Code is Erlang-4.7.3, December, 1998.
+%%
+%% The Initial Developer of the Original Code is Ericsson Telecom
+%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson
+%% Telecom AB. All Rights Reserved.
+%%
+%% Contributor(s): ______________________________________.''
+%%
+%% This is an -*- erlang -*- file.
+%% Generic compiler options, passed from the erl_compile module.
+
+-record(bittype, {
+ type, %% integer/float/binary
+ unit, %% element unit
+ sign, %% signed/unsigned
+ endian %% big/little
+ }).
+
+-record(bitdefault, {
+ integer, %% default type for integer
+ float, %% default type for float
+ binary %% default type for binary
+ }).
+
+%%% (From config.hrl in the bitsyntax branch.)
+-define(SYS_ENDIAN, big).
+-define(SIZEOF_CHAR, 1).
+-define(SIZEOF_DOUBLE, 8).
+-define(SIZEOF_FLOAT, 4).
+-define(SIZEOF_INT, 4).
+-define(SIZEOF_LONG, 4).
+-define(SIZEOF_LONG_LONG, 8).
+-define(SIZEOF_SHORT, 2).
diff --git a/lib/dialyzer/test/options1_SUITE_data/my_include/erl_compile.hrl b/lib/dialyzer/test/options1_SUITE_data/my_include/erl_compile.hrl
new file mode 100644
index 0000000000..c10ffa235c
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/my_include/erl_compile.hrl
@@ -0,0 +1,41 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: erl_compile.hrl,v 1.1 2008/12/17 09:53:40 mikpe Exp $
+%%
+
+%% Generic compiler options, passed from the erl_compile module.
+
+-record(options,
+ {includes=[], % Include paths (list of absolute
+ % directory names).
+ outdir=".", % Directory for result (absolute
+ % path).
+ output_type=undefined, % Type of output file (atom).
+ defines=[], % Preprocessor defines. Each
+ % element is an atom (the name to
+ % define), or a {Name, Value}
+ % tuple.
+ warning=1, % Warning level (0 - no
+ % warnings, 1 - standard level,
+ % 2, 3, ... - more warnings).
+ verbose=false, % Verbose (true/false).
+ optimize=999, % Optimize options.
+ specific=[], % Compiler specific options.
+ outfile="", % Name of output file (internal
+ % use in erl_compile.erl).
+ cwd % Current working directory
+ % for erlc.
+ }).
diff --git a/lib/dialyzer/test/options1_SUITE_data/results/compiler b/lib/dialyzer/test/options1_SUITE_data/results/compiler
new file mode 100644
index 0000000000..924ef389df
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/results/compiler
@@ -0,0 +1,35 @@
+
+beam_asm.erl:32: The pattern {'error', Error} can never match the type <<_:64,_:_*8>>
+beam_bool.erl:193: The pattern {[], _} can never match the type {[{_,_,_,_},...],[any()]}
+beam_bool.erl:510: The pattern [{'set', [Dst], _, _}, {'%live', _}] can never match the type [{_,_,_,_}]
+beam_disasm.erl:537: The variable X can never match since previous clauses completely covered the type 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
+beam_type.erl:284: The pattern <'pi', 0> can never match the type <_,1 | 2>
+beam_validator.erl:396: The pattern <{'jump', {'f', _}}, Vst = {'vst', 'none', _}> can never match the type <_,#vst{current::#st{ct::[]}}>
+beam_validator.erl:690: The pattern <'term', OldT> can never match the type <{'tuple',[any(),...]},_>
+beam_validator.erl:692: Clause guard cannot succeed. The pattern was matched against the type <{'tuple',[any(),...]},_>
+beam_validator.erl:699: Clause guard cannot succeed. The pattern was matched against the type <{'tuple',[any(),...]},_>
+beam_validator.erl:702: The pattern <'number', OldT = {Type, _}> can never match the type <{'tuple',[any(),...]},_>
+beam_validator.erl:705: The pattern <'bool', {'atom', A}> can never match the type <{'tuple',[any(),...]},_>
+beam_validator.erl:707: The pattern <{'atom', A}, 'bool'> can never match the type <{'tuple',[any(),...]},_>
+beam_validator.erl:713: Guard test is_integer(Sz::[any(),...]) can never succeed
+beam_validator.erl:727: Function upgrade_bool/1 will never be called
+cerl_inline.erl:190: The pattern 'true' can never match the type 'false'
+cerl_inline.erl:219: The pattern 'true' can never match the type 'false'
+cerl_inline.erl:230: The pattern 'true' can never match the type 'false'
+cerl_inline.erl:2333: The pattern 'true' can never match the type 'false'
+cerl_inline.erl:2355: The pattern 'true' can never match the type 'false'
+cerl_inline.erl:238: The pattern 'true' can never match the type 'false'
+cerl_inline.erl:2436: Function filename/1 will never be called
+cerl_inline.erl:2700: The pattern 'true' can never match the type 'false'
+cerl_inline.erl:2730: The pattern <{F, L, D}, Vs> can never match the type <[1..255,...],[any()]>
+cerl_inline.erl:2738: The pattern <{F, L, D}, Vs> can never match the type <[1..255,...],[any()]>
+cerl_inline.erl:2750: The pattern <{[], L, D}, Vs> can never match the type <[1..255,...],[any()]>
+cerl_inline.erl:2752: The pattern <{[], _L, D}, Vs> can never match the type <[1..255,...],[any()]>
+cerl_inline.erl:2754: The pattern <{F, L, D}, Vs> can never match the type <[1..255,...],[any()]>
+cerl_inline.erl:2756: The pattern <{F, _L, D}, Vs> can never match the type <[1..255,...],[any()]>
+compile.erl:788: The pattern {'error', Es} can never match the type {'ok',<<_:64,_:_*8>>}
+core_lint.erl:473: The pattern <{'c_atom', _, 'all'}, 'binary', _Def, St> can never match the type <_,#c_nil{} | {'c_atom' | 'c_char' | 'c_float' | 'c_int' | 'c_string' | 'c_tuple',_,_} | #c_cons{hd::#c_nil{} | {'c_atom' | 'c_char' | 'c_float' | 'c_int' | 'c_string' | 'c_tuple',_,_} | #c_cons{hd::{_,_} | {_,_,_} | {_,_,_,_},tl::{_,_} | {_,_,_} | {_,_,_,_}},tl::#c_nil{} | {'c_atom' | 'c_char' | 'c_float' | 'c_int' | 'c_string' | 'c_tuple',_,_} | #c_cons{hd::{_,_} | {_,_,_} | {_,_,_,_},tl::{_,_} | {_,_,_} | {_,_,_,_}}},[any()],_>
+core_lint.erl:505: The pattern <_Req, 'unknown', St> can never match the type
+v3_codegen.erl:1569: The call v3_codegen:load_reg_1(V::any(),I::0,Rs::any(),pos_integer()) will never return since it differs in the 4th argument from the success typing arguments: (any(),0,maybe_improper_list(),0)
+v3_codegen.erl:1571: The call v3_codegen:load_reg_1(V::any(),I::0,[],pos_integer()) will never return since it differs in the 4th argument from the success typing arguments: (any(),0,maybe_improper_list(),0)
+v3_core.erl:646: The pattern can never match the type <#c_nil{anno::[any(),...]} | {'c_atom' | 'c_char' | 'c_float' | 'c_int' | 'c_string' | 'c_tuple' | 'c_var' | 'ibinary' | 'icatch' | 'ireceive1',[any(),...] | {_,_,_,_},_} | #c_cons{anno::[any(),...]} | #c_fname{anno::[any(),...]} | #iletrec{anno::{_,_,_,_},defs::[any(),...],body::[any(),...]} | #icase{anno::{_,_,_,_},args::[any()],clauses::[any()],fc::{_,_,_,_,_,_}} | #ireceive2{anno::{_,_,_,_},clauses::[any()],action::[any()]} | #ifun{anno::{_,_,_,_},id::[any(),...],vars::[any()],clauses::[any(),...],fc::{_,_,_,_,_,_}} | #imatch{anno::{_,_,_,_},guard::[],fc::{_,_,_,_,_,_}} | #itry{anno::{_,_,_,_},args::[any()],vars::[any(),...],body::[any(),...],evars::[any(),...],handler::[any(),...]},_>
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_asm.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_asm.erl
new file mode 100644
index 0000000000..e3746f3fb6
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_asm.erl
@@ -0,0 +1,358 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_asm.erl,v 1.1 2008/12/17 09:53:40 mikpe Exp $
+%%
+%% Purpose : Assembler for threaded Beam.
+
+-module(beam_asm).
+
+-export([module/4,format_error/1]).
+-export([encode/2]).
+
+-import(lists, [map/2,member/2,keymember/3,duplicate/2]).
+-include("beam_opcodes.hrl").
+
+-define(bs_aligned, 1).
+
+module(Code, Abst, SourceFile, Opts) ->
+ case assemble(Code, Abst, SourceFile, Opts) of
+ {error, Error} ->
+ {error, [{none, ?MODULE, Error}]};
+ Bin when binary(Bin) ->
+ {ok, Bin}
+ end.
+
+format_error({crashed, Why}) ->
+ io_lib:format("beam_asm_int: EXIT: ~p", [Why]).
+
+assemble({Mod,Exp,Attr,Asm,NumLabels}, Abst, SourceFile, Opts) ->
+ {1,Dict0} = beam_dict:atom(Mod, beam_dict:new()),
+ NumFuncs = length(Asm),
+ {Code,Dict1} = assemble_1(Asm, Exp, Dict0, []),
+ build_file(Code, Attr, Dict1, NumLabels, NumFuncs, Abst, SourceFile, Opts).
+
+assemble_1([{function,Name,Arity,Entry,Asm}|T], Exp, Dict0, Acc) ->
+ Dict1 = case member({Name,Arity}, Exp) of
+ true ->
+ beam_dict:export(Name, Arity, Entry, Dict0);
+ false ->
+ beam_dict:local(Name, Arity, Entry, Dict0)
+ end,
+ {Code, Dict2} = assemble_function(Asm, Acc, Dict1),
+ assemble_1(T, Exp, Dict2, Code);
+assemble_1([], _Exp, Dict0, Acc) ->
+ {IntCodeEnd,Dict1} = make_op(int_code_end, Dict0),
+ {list_to_binary(lists:reverse(Acc, [IntCodeEnd])),Dict1}.
+
+assemble_function([H|T], Acc, Dict0) ->
+ {Code, Dict} = make_op(H, Dict0),
+ assemble_function(T, [Code| Acc], Dict);
+assemble_function([], Code, Dict) ->
+ {Code, Dict}.
+
+build_file(Code, Attr, Dict, NumLabels, NumFuncs, Abst, SourceFile, Opts) ->
+ %% Create the code chunk.
+
+ CodeChunk = chunk(<<"Code">>,
+ <<16:32,
+ (beam_opcodes:format_number()):32,
+ (beam_dict:highest_opcode(Dict)):32,
+ NumLabels:32,
+ NumFuncs:32>>,
+ Code),
+
+ %% Create the atom table chunk.
+
+ {NumAtoms, AtomTab} = beam_dict:atom_table(Dict),
+ AtomChunk = chunk(<<"Atom">>, <>, AtomTab),
+
+ %% Create the import table chunk.
+
+ {NumImps, ImpTab0} = beam_dict:import_table(Dict),
+ Imp = flatten_imports(ImpTab0),
+ ImportChunk = chunk(<<"ImpT">>, <>, Imp),
+
+ %% Create the export table chunk.
+
+ {NumExps, ExpTab0} = beam_dict:export_table(Dict),
+ Exp = flatten_exports(ExpTab0),
+ ExpChunk = chunk(<<"ExpT">>, <>, Exp),
+
+ %% Create the local function table chunk.
+
+ {NumLocals, Locals} = beam_dict:local_table(Dict),
+ Loc = flatten_exports(Locals),
+ LocChunk = chunk(<<"LocT">>, <>, Loc),
+
+ %% Create the string table chunk.
+
+ {_,StringTab} = beam_dict:string_table(Dict),
+ StringChunk = chunk(<<"StrT">>, StringTab),
+
+ %% Create the fun table chunk. It is important not to build an empty chunk,
+ %% as that would change the MD5.
+
+ LambdaChunk = case beam_dict:lambda_table(Dict) of
+ {0,[]} -> [];
+ {NumLambdas,LambdaTab} ->
+ chunk(<<"FunT">>, <>, LambdaTab)
+ end,
+
+ %% Create the attributes and compile info chunks.
+
+ Essentials = [AtomChunk,CodeChunk,StringChunk,ImportChunk,ExpChunk,LambdaChunk],
+ {Attributes,Compile} = build_attributes(Opts, SourceFile, Attr, Essentials),
+ AttrChunk = chunk(<<"Attr">>, Attributes),
+ CompileChunk = chunk(<<"CInf">>, Compile),
+
+ %% Create the abstract code chunk.
+
+ AbstChunk = chunk(<<"Abst">>, Abst),
+
+ %% Create IFF chunk.
+
+ Chunks = case member(slim, Opts) of
+ true -> [Essentials,AttrChunk,CompileChunk,AbstChunk];
+ false -> [Essentials,LocChunk,AttrChunk,CompileChunk,AbstChunk]
+ end,
+ build_form(<<"BEAM">>, Chunks).
+
+%% Build an IFF form.
+
+build_form(Id, Chunks0) when size(Id) == 4, list(Chunks0) ->
+ Chunks = list_to_binary(Chunks0),
+ Size = size(Chunks),
+ 0 = Size rem 4, % Assertion: correct padding?
+ <<"FOR1",(Size+4):32,Id/binary,Chunks/binary>>.
+
+%% Build a correctly padded chunk (with no sub-header).
+
+chunk(Id, Contents) when size(Id) == 4, binary(Contents) ->
+ Size = size(Contents),
+ [<>,Contents|pad(Size)];
+chunk(Id, Contents) when list(Contents) ->
+ chunk(Id, list_to_binary(Contents)).
+
+%% Build a correctly padded chunk (with a sub-header).
+
+chunk(Id, Head, Contents) when size(Id) == 4, is_binary(Head), is_binary(Contents) ->
+ Size = size(Head)+size(Contents),
+ [<>,Contents|pad(Size)];
+chunk(Id, Head, Contents) when list(Contents) ->
+ chunk(Id, Head, list_to_binary(Contents)).
+
+pad(Size) ->
+ case Size rem 4 of
+ 0 -> [];
+ Rem -> duplicate(4 - Rem, 0)
+ end.
+
+flatten_exports(Exps) ->
+ list_to_binary(map(fun({F,A,L}) -> <> end, Exps)).
+
+flatten_imports(Imps) ->
+ list_to_binary(map(fun({M,F,A}) -> <> end, Imps)).
+
+build_attributes(Opts, SourceFile, Attr, Essentials) ->
+ Misc = case member(slim, Opts) of
+ false ->
+ {{Y,Mo,D},{H,Mi,S}} = erlang:universaltime(),
+ [{time,{Y,Mo,D,H,Mi,S}},{source,SourceFile}];
+ true -> []
+ end,
+ Compile = [{options,Opts},{version,?COMPILER_VSN}|Misc],
+ {term_to_binary(calc_vsn(Attr, Essentials)),term_to_binary(Compile)}.
+
+%%
+%% If the attributes contains no 'vsn' attribute, we'll insert one
+%% with an MD5 "checksum" calculated on the code as its value.
+%% We'll not change an existing 'vsn' attribute.
+%%
+
+calc_vsn(Attr, Essentials) ->
+ case keymember(vsn, 1, Attr) of
+ true -> Attr;
+ false ->
+ <> = erlang:md5(Essentials),
+ [{vsn,[Number]}|Attr]
+ end.
+
+bif_type('-', 1) -> negate;
+bif_type('+', 2) -> {op, m_plus};
+bif_type('-', 2) -> {op, m_minus};
+bif_type('*', 2) -> {op, m_times};
+bif_type('/', 2) -> {op, m_div};
+bif_type('div', 2) -> {op, int_div};
+bif_type('rem', 2) -> {op, int_rem};
+bif_type('band', 2) -> {op, int_band};
+bif_type('bor', 2) -> {op, int_bor};
+bif_type('bxor', 2) -> {op, int_bxor};
+bif_type('bsl', 2) -> {op, int_bsl};
+bif_type('bsr', 2) -> {op, int_bsr};
+bif_type('bnot', 1) -> {op, int_bnot};
+bif_type(fnegate, 1) -> {op, fnegate};
+bif_type(fadd, 2) -> {op, fadd};
+bif_type(fsub, 2) -> {op, fsub};
+bif_type(fmul, 2) -> {op, fmul};
+bif_type(fdiv, 2) -> {op, fdiv};
+bif_type(_, _) -> bif.
+
+make_op(Comment, Dict) when element(1, Comment) == '%' ->
+ {[],Dict};
+make_op({'%live',_R}, Dict) ->
+ {[],Dict};
+make_op({bif, Bif, nofail, [], Dest}, Dict) ->
+ encode_op(bif0, [{extfunc, erlang, Bif, 0}, Dest], Dict);
+make_op({bif, raise, _Fail, [A1,A2], _Dest}, Dict) ->
+ encode_op(raise, [A1,A2], Dict);
+make_op({bif, Bif, Fail, Args, Dest}, Dict) ->
+ Arity = length(Args),
+ case bif_type(Bif, Arity) of
+ {op, Op} ->
+ make_op(list_to_tuple([Op, Fail|Args++[Dest]]), Dict);
+ negate ->
+ %% Fake negation operator.
+ make_op({m_minus, Fail, {integer,0}, hd(Args), Dest}, Dict);
+ bif ->
+ BifOp = list_to_atom(lists:concat([bif, Arity])),
+ encode_op(BifOp, [Fail, {extfunc, erlang, Bif, Arity}|Args++[Dest]],
+ Dict)
+ end;
+make_op({bs_add=Op,Fail,[Src1,Src2,Unit],Dest}, Dict) ->
+ encode_op(Op, [Fail,Src1,Src2,Unit,Dest], Dict);
+make_op({test,Cond,Fail,Ops}, Dict) when list(Ops) ->
+ encode_op(Cond, [Fail|Ops], Dict);
+make_op({make_fun2,{f,Lbl},Index,OldUniq,NumFree}, Dict0) ->
+ {Fun,Dict} = beam_dict:lambda(Lbl, Index, OldUniq, NumFree, Dict0),
+ make_op({make_fun2,Fun}, Dict);
+make_op(Op, Dict) when atom(Op) ->
+ encode_op(Op, [], Dict);
+make_op({kill,Y}, Dict) ->
+ make_op({init,Y}, Dict);
+make_op({Name,Arg1}, Dict) ->
+ encode_op(Name, [Arg1], Dict);
+make_op({Name,Arg1,Arg2}, Dict) ->
+ encode_op(Name, [Arg1,Arg2], Dict);
+make_op({Name,Arg1,Arg2,Arg3}, Dict) ->
+ encode_op(Name, [Arg1,Arg2,Arg3], Dict);
+make_op({Name,Arg1,Arg2,Arg3,Arg4}, Dict) ->
+ encode_op(Name, [Arg1,Arg2,Arg3,Arg4], Dict);
+make_op({Name,Arg1,Arg2,Arg3,Arg4,Arg5}, Dict) ->
+ encode_op(Name, [Arg1,Arg2,Arg3,Arg4,Arg5], Dict);
+make_op({Name,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6}, Dict) ->
+ encode_op(Name, [Arg1,Arg2,Arg3,Arg4,Arg5,Arg6], Dict).
+
+encode_op(Name, Args, Dict0) when atom(Name) ->
+ {EncArgs,Dict1} = encode_args(Args, Dict0),
+ Op = beam_opcodes:opcode(Name, length(Args)),
+ Dict2 = beam_dict:opcode(Op, Dict1),
+ {list_to_binary([Op|EncArgs]),Dict2}.
+
+encode_args([Arg| T], Dict0) ->
+ {EncArg, Dict1} = encode_arg(Arg, Dict0),
+ {EncTail, Dict2} = encode_args(T, Dict1),
+ {[EncArg| EncTail], Dict2};
+encode_args([], Dict) ->
+ {[], Dict}.
+
+encode_arg({x, X}, Dict) when X >= 0 ->
+ {encode(?tag_x, X), Dict};
+encode_arg({y, Y}, Dict) when Y >= 0 ->
+ {encode(?tag_y, Y), Dict};
+encode_arg({atom, Atom}, Dict0) when atom(Atom) ->
+ {Index, Dict} = beam_dict:atom(Atom, Dict0),
+ {encode(?tag_a, Index), Dict};
+encode_arg({integer, N}, Dict) ->
+ {encode(?tag_i, N), Dict};
+encode_arg(nil, Dict) ->
+ {encode(?tag_a, 0), Dict};
+encode_arg({f, W}, Dict) ->
+ {encode(?tag_f, W), Dict};
+encode_arg({'char', C}, Dict) ->
+ {encode(?tag_h, C), Dict};
+encode_arg({string, String}, Dict0) ->
+ {Offset, Dict} = beam_dict:string(String, Dict0),
+ {encode(?tag_u, Offset), Dict};
+encode_arg({extfunc, M, F, A}, Dict0) ->
+ {Index, Dict} = beam_dict:import(M, F, A, Dict0),
+ {encode(?tag_u, Index), Dict};
+encode_arg({list, List}, Dict0) ->
+ {L, Dict} = encode_list(List, Dict0, []),
+ {[encode(?tag_z, 1), encode(?tag_u, length(List))|L], Dict};
+encode_arg({float, Float}, Dict) when float(Float) ->
+ {[encode(?tag_z, 0)|<>], Dict};
+encode_arg({fr,Fr}, Dict) ->
+ {[encode(?tag_z, 2),encode(?tag_u,Fr)], Dict};
+encode_arg({field_flags,Flags0}, Dict) ->
+ Flags = lists:foldl(fun (F, S) -> S bor flag_to_bit(F) end, 0, Flags0),
+ {encode(?tag_u, Flags), Dict};
+encode_arg({alloc,List}, Dict) ->
+ {encode_alloc_list(List),Dict};
+encode_arg(Int, Dict) when is_integer(Int) ->
+ {encode(?tag_u, Int),Dict}.
+
+flag_to_bit(aligned) -> 16#01;
+flag_to_bit(little) -> 16#02;
+flag_to_bit(big) -> 16#00;
+flag_to_bit(signed) -> 16#04;
+flag_to_bit(unsigned)-> 16#00;
+flag_to_bit(exact) -> 16#08;
+flag_to_bit(native) -> 16#10.
+
+encode_list([H|T], _Dict, _Acc) when is_list(H) ->
+ exit({illegal_nested_list,encode_arg,[H|T]});
+encode_list([H|T], Dict0, Acc) ->
+ {Enc,Dict} = encode_arg(H, Dict0),
+ encode_list(T, Dict, [Enc|Acc]);
+encode_list([], Dict, Acc) ->
+ {lists:reverse(Acc), Dict}.
+
+encode_alloc_list(L0) ->
+ L = encode_alloc_list_1(L0),
+ [encode(?tag_z, 3),encode(?tag_u, length(L0))|L].
+
+encode_alloc_list_1([{words,Words}|T]) ->
+ [encode(?tag_u, 0),encode(?tag_u, Words)|encode_alloc_list_1(T)];
+encode_alloc_list_1([{floats,Floats}|T]) ->
+ [encode(?tag_u, 1),encode(?tag_u, Floats)|encode_alloc_list_1(T)];
+encode_alloc_list_1([]) -> [].
+
+encode(Tag, N) when N < 0 ->
+ encode1(Tag, negative_to_bytes(N, []));
+encode(Tag, N) when N < 16 ->
+ (N bsl 4) bor Tag;
+encode(Tag, N) when N < 16#800 ->
+ [((N bsr 3) band 2#11100000) bor Tag bor 2#00001000, N band 16#ff];
+encode(Tag, N) ->
+ encode1(Tag, to_bytes(N, [])).
+
+encode1(Tag, Bytes) ->
+ case length(Bytes) of
+ Num when 2 =< Num, Num =< 8 ->
+ [((Num-2) bsl 5) bor 2#00011000 bor Tag| Bytes];
+ Num when 8 < Num ->
+ [2#11111000 bor Tag, encode(?tag_u, Num-9)| Bytes]
+ end.
+
+to_bytes(0, [B|Acc]) when B < 128 ->
+ [B|Acc];
+to_bytes(N, Acc) ->
+ to_bytes(N bsr 8, [N band 16#ff| Acc]).
+
+negative_to_bytes(-1, [B1, B2|T]) when B1 > 127 ->
+ [B1, B2|T];
+negative_to_bytes(N, Acc) ->
+ negative_to_bytes(N bsr 8, [N band 16#ff|Acc]).
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_block.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_block.erl
new file mode 100644
index 0000000000..0e3589cdf5
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_block.erl
@@ -0,0 +1,601 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_block.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%
+%% Purpose : Partitions assembly instructions into basic blocks and
+%% optimizes them.
+
+-module(beam_block).
+
+-export([module/2]).
+-export([live_at_entry/1]). %Used by beam_type, beam_bool.
+-export([is_killed/2]). %Used by beam_dead, beam_type, beam_bool.
+-export([is_not_used/2]). %Used by beam_bool.
+-export([merge_blocks/2]). %Used by beam_jump.
+-import(lists, [map/2,mapfoldr/3,reverse/1,reverse/2,foldl/3,
+ member/2,sort/1,all/2]).
+-define(MAXREG, 1024).
+
+module({Mod,Exp,Attr,Fs,Lc}, _Opt) ->
+ {ok,{Mod,Exp,Attr,map(fun function/1, Fs),Lc}}.
+
+function({function,Name,Arity,CLabel,Is0}) ->
+ %% Collect basic blocks and optimize them.
+ Is = blockify(Is0),
+
+ %% Done.
+ {function,Name,Arity,CLabel,Is}.
+
+%% blockify(Instructions0) -> Instructions
+%% Collect sequences of instructions to basic blocks and
+%% optimize the contents of the blocks. Also do some simple
+%% optimations on instructions outside the blocks.
+
+blockify(Is) ->
+ blockify(Is, []).
+
+blockify([{loop_rec,{f,Fail},{x,0}},{loop_rec_end,_Lbl},{label,Fail}|Is], Acc) ->
+ %% Useless instruction sequence.
+ blockify(Is, Acc);
+blockify([{test,bs_test_tail,F,[Bits]}|Is],
+ [{test,bs_skip_bits,F,[{integer,I},Unit,_Flags]}|Acc]) ->
+ blockify(Is, [{test,bs_test_tail,F,[Bits+I*Unit]}|Acc]);
+blockify([{test,bs_skip_bits,F,[{integer,I1},Unit1,_]}|Is],
+ [{test,bs_skip_bits,F,[{integer,I2},Unit2,Flags]}|Acc]) ->
+ blockify(Is, [{test,bs_skip_bits,F,
+ [{integer,I1*Unit1+I2*Unit2},1,Flags]}|Acc]);
+blockify([{test,is_atom,{f,Fail},[Reg]}=I|
+ [{select_val,Reg,{f,Fail},
+ {list,[{atom,false},{f,_}=BrFalse,
+ {atom,true}=AtomTrue,{f,_}=BrTrue]}}|Is]=Is0],
+ [{block,Bl}|_]=Acc) ->
+ case is_last_bool(Bl, Reg) of
+ false ->
+ blockify(Is0, [I|Acc]);
+ true ->
+ blockify(Is, [{jump,BrTrue},
+ {test,is_eq_exact,BrFalse,[Reg,AtomTrue]}|Acc])
+ end;
+blockify([{test,is_atom,{f,Fail},[Reg]}=I|
+ [{select_val,Reg,{f,Fail},
+ {list,[{atom,true}=AtomTrue,{f,_}=BrTrue,
+ {atom,false},{f,_}=BrFalse]}}|Is]=Is0],
+ [{block,Bl}|_]=Acc) ->
+ case is_last_bool(Bl, Reg) of
+ false ->
+ blockify(Is0, [I|Acc]);
+ true ->
+ blockify(Is, [{jump,BrTrue},
+ {test,is_eq_exact,BrFalse,[Reg,AtomTrue]}|Acc])
+ end;
+blockify([I|Is0]=IsAll, Acc) ->
+ case is_bs_put(I) of
+ true ->
+ {BsPuts0,Is} = collect_bs_puts(IsAll),
+ BsPuts = opt_bs_puts(BsPuts0),
+ blockify(Is, reverse(BsPuts, Acc));
+ false ->
+ case collect(I) of
+ error -> blockify(Is0, [I|Acc]);
+ Instr when is_tuple(Instr) ->
+ {Block0,Is} = collect_block(IsAll),
+ Block = opt_block(Block0),
+ blockify(Is, [{block,Block}|Acc])
+ end
+ end;
+blockify([], Acc) -> reverse(Acc).
+
+is_last_bool([I,{'%live',_}], Reg) ->
+ is_last_bool([I], Reg);
+is_last_bool([{set,[Reg],As,{bif,N,_}}], Reg) ->
+ Ar = length(As),
+ erl_internal:new_type_test(N, Ar) orelse erl_internal:comp_op(N, Ar)
+ orelse erl_internal:bool_op(N, Ar);
+is_last_bool([_|Is], Reg) -> is_last_bool(Is, Reg);
+is_last_bool([], _) -> false.
+
+collect_block(Is) ->
+ collect_block(Is, []).
+
+collect_block([{allocate_zero,Ns,R},{test_heap,Nh,R}|Is], Acc) ->
+ collect_block(Is, [{allocate,R,{no_opt,Ns,Nh,[]}}|Acc]);
+collect_block([I|Is]=Is0, Acc) ->
+ case collect(I) of
+ error -> {reverse(Acc),Is0};
+ Instr -> collect_block(Is, [Instr|Acc])
+ end;
+collect_block([], Acc) -> {reverse(Acc),[]}.
+
+collect({allocate_zero,N,R}) -> {allocate,R,{zero,N,0,[]}};
+collect({test_heap,N,R}) -> {allocate,R,{nozero,nostack,N,[]}};
+collect({bif,N,nofail,As,D}) -> {set,[D],As,{bif,N}};
+collect({bif,N,F,As,D}) -> {set,[D],As,{bif,N,F}};
+collect({move,S,D}) -> {set,[D],[S],move};
+collect({put_list,S1,S2,D}) -> {set,[D],[S1,S2],put_list};
+collect({put_tuple,A,D}) -> {set,[D],[],{put_tuple,A}};
+collect({put,S}) -> {set,[],[S],put};
+collect({put_string,L,S,D}) -> {set,[D],[],{put_string,L,S}};
+collect({get_tuple_element,S,I,D}) -> {set,[D],[S],{get_tuple_element,I}};
+collect({set_tuple_element,S,D,I}) -> {set,[],[S,D],{set_tuple_element,I}};
+collect({get_list,S,D1,D2}) -> {set,[D1,D2],[S],get_list};
+collect(remove_message) -> {set,[],[],remove_message};
+collect({'catch',R,L}) -> {set,[R],[],{'catch',L}};
+collect({'%live',_}=Live) -> Live;
+collect(_) -> error.
+
+opt_block(Is0) ->
+ %% We explicitly move any allocate instruction upwards before optimising
+ %% moves, to avoid any potential problems with the calculation of live
+ %% registers.
+ Is1 = find_fixpoint(fun move_allocates/1, Is0),
+ Is2 = find_fixpoint(fun opt/1, Is1),
+ Is = opt_alloc(Is2),
+ share_floats(Is).
+
+find_fixpoint(OptFun, Is0) ->
+ case OptFun(Is0) of
+ Is0 -> Is0;
+ Is1 -> find_fixpoint(OptFun, Is1)
+ end.
+
+move_allocates([{set,_Ds,_Ss,{set_tuple_element,_}}|_]=Is) -> Is;
+move_allocates([{set,Ds,Ss,_Op}=Set,{allocate,R,Alloc}|Is]) when is_integer(R) ->
+ [{allocate,live_regs(Ds, Ss, R),Alloc},Set|Is];
+move_allocates([{allocate,R1,Alloc1},{allocate,R2,Alloc2}|Is]) ->
+ R1 = R2, % Assertion.
+ move_allocates([{allocate,R1,combine_alloc(Alloc1, Alloc2)}|Is]);
+move_allocates([I|Is]) ->
+ [I|move_allocates(Is)];
+move_allocates([]) -> [].
+
+combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) ->
+ {zero,Ns,Nh1+Nh2,Init}.
+
+merge_blocks([{allocate,R,{Attr,Ns,Nh1,Init}}|B1],
+ [{allocate,_,{_,nostack,Nh2,[]}}|B2]) ->
+ Alloc = {allocate,R,{Attr,Ns,Nh1+Nh2,Init}},
+ [Alloc|merge_blocks(B1, B2)];
+merge_blocks(B1, B2) -> merge_blocks_1(B1++[{set,[],[],stop_here}|B2]).
+
+merge_blocks_1([{set,[],_,stop_here}|Is]) -> Is;
+merge_blocks_1([{set,[D],_,move}=I|Is]) ->
+ case is_killed(D, Is) of
+ true -> merge_blocks_1(Is);
+ false -> [I|merge_blocks_1(Is)]
+ end;
+merge_blocks_1([I|Is]) -> [I|merge_blocks_1(Is)].
+
+opt([{set,[Dst],As,{bif,Bif,Fail}}=I1,
+ {set,[Dst],[Dst],{bif,'not',Fail}}=I2|Is]) ->
+ %% Get rid of the 'not' if the operation can be inverted.
+ case inverse_comp_op(Bif) of
+ none -> [I1,I2|opt(Is)];
+ RevBif -> [{set,[Dst],As,{bif,RevBif,Fail}}|opt(Is)]
+ end;
+opt([{set,[X],[X],move}|Is]) -> opt(Is);
+opt([{set,[D1],[{integer,Idx1},Reg],{bif,element,{f,0}}}=I1,
+ {set,[D2],[{integer,Idx2},Reg],{bif,element,{f,0}}}=I2|Is])
+ when Idx1 < Idx2, D1 =/= D2, D1 =/= Reg, D2 =/= Reg ->
+ opt([I2,I1|Is]);
+opt([{set,Ds0,Ss,Op}|Is0]) ->
+ {Ds,Is} = opt_moves(Ds0, Is0),
+ [{set,Ds,Ss,Op}|opt(Is)];
+opt([I|Is]) -> [I|opt(Is)];
+opt([]) -> [].
+
+opt_moves([], Is0) -> {[],Is0};
+opt_moves([D0], Is0) ->
+ {D1,Is1} = opt_move(D0, Is0),
+ {[D1],Is1};
+opt_moves([X0,Y0]=Ds, Is0) ->
+ {X1,Is1} = opt_move(X0, Is0),
+ case opt_move(Y0, Is1) of
+ {Y1,Is2} when X1 =/= Y1 -> {[X1,Y1],Is2};
+ _Other when X1 =/= Y0 -> {[X1,Y0],Is1};
+ _Other -> {Ds,Is0}
+ end.
+
+opt_move(R, [{set,[D],[R],move}|Is]=Is0) ->
+ case is_killed(R, Is) of
+ true -> {D,Is};
+ false -> {R,Is0}
+ end;
+opt_move(R, [I|Is0]) ->
+ case is_transparent(R, I) of
+ true ->
+ {D,Is1} = opt_move(R, Is0),
+ case is_transparent(D, I) of
+ true -> {D,[I|Is1]};
+ false -> {R,[I|Is0]}
+ end;
+ false -> {R,[I|Is0]}
+ end;
+opt_move(R, []) -> {R,[]}.
+
+is_transparent(R, {set,Ds,Ss,_Op}) ->
+ case member(R, Ds) of
+ true -> false;
+ false -> not member(R, Ss)
+ end;
+is_transparent(_, _) -> false.
+
+%% is_killed(Register, [Instruction]) -> true|false
+%% Determine whether a register is killed by the instruction sequence.
+%% If true is returned, it means that the register will not be
+%% referenced in ANY way (not even indirectly by an allocate instruction);
+%% i.e. it is OK to enter the instruction sequence with Register
+%% containing garbage.
+
+is_killed({x,N}=R, [{block,Blk}|Is]) ->
+ case is_killed(R, Blk) of
+ true -> true;
+ false ->
+ %% Before looking beyond the block, we must be
+ %% sure that the register is not referenced by
+ %% any allocate instruction in the block.
+ case all(fun({allocate,Live,_}) when N < Live -> false;
+ (_) -> true
+ end, Blk) of
+ true -> is_killed(R, Is);
+ false -> false
+ end
+ end;
+is_killed(R, [{block,Blk}|Is]) ->
+ case is_killed(R, Blk) of
+ true -> true;
+ false -> is_killed(R, Is)
+ end;
+is_killed(R, [{set,Ds,Ss,_Op}|Is]) ->
+ case member(R, Ss) of
+ true -> false;
+ false ->
+ case member(R, Ds) of
+ true -> true;
+ false -> is_killed(R, Is)
+ end
+ end;
+is_killed(R, [{case_end,Used}|_]) -> R =/= Used;
+is_killed(R, [{badmatch,Used}|_]) -> R =/= Used;
+is_killed(_, [if_end|_]) -> true;
+is_killed(R, [{func_info,_,_,Ar}|_]) ->
+ case R of
+ {x,X} when X < Ar -> false;
+ _ -> true
+ end;
+is_killed(R, [{kill,R}|_]) -> true;
+is_killed(R, [{kill,_}|Is]) -> is_killed(R, Is);
+is_killed(R, [{bs_init2,_,_,_,_,_,Dst}|Is]) ->
+ if
+ R =:= Dst -> true;
+ true -> is_killed(R, Is)
+ end;
+is_killed(R, [{bs_put_string,_,_}|Is]) -> is_killed(R, Is);
+is_killed({x,R}, [{'%live',Live}|_]) when R >= Live -> true;
+is_killed({x,R}, [{'%live',_}|Is]) -> is_killed(R, Is);
+is_killed({x,R}, [{allocate,Live,_}|_]) ->
+ %% Note: To be safe here, we must return either true or false,
+ %% not looking further at the instructions beyond the allocate
+ %% instruction.
+ R >= Live;
+is_killed({x,R}, [{call,Live,_}|_]) when R >= Live -> true;
+is_killed({x,R}, [{call_last,Live,_,_}|_]) when R >= Live -> true;
+is_killed({x,R}, [{call_only,Live,_}|_]) when R >= Live -> true;
+is_killed({x,R}, [{call_ext,Live,_}|_]) when R >= Live -> true;
+is_killed({x,R}, [{call_ext_last,Live,_,_}|_]) when R >= Live -> true;
+is_killed({x,R}, [{call_ext_only,Live,_}|_]) when R >= Live -> true;
+is_killed({x,R}, [return|_]) when R > 0 -> true;
+is_killed(_, _) -> false.
+
+%% is_not_used(Register, [Instruction]) -> true|false
+%% Determine whether a register is used by the instruction sequence.
+%% If true is returned, it means that the register will not be
+%% referenced directly, but it may be referenced by an allocate
+%% instruction (meaning that it is NOT allowed to contain garbage).
+
+is_not_used(R, [{block,Blk}|Is]) ->
+ case is_not_used(R, Blk) of
+ true -> true;
+ false -> is_not_used(R, Is)
+ end;
+is_not_used({x,R}=Reg, [{allocate,Live,_}|Is]) ->
+ if
+ R >= Live -> true;
+ true -> is_not_used(Reg, Is)
+ end;
+is_not_used(R, [{set,Ds,Ss,_Op}|Is]) ->
+ case member(R, Ss) of
+ true -> false;
+ false ->
+ case member(R, Ds) of
+ true -> true;
+ false -> is_not_used(R, Is)
+ end
+ end;
+is_not_used(R, Is) -> is_killed(R, Is).
+
+%% opt_alloc(Instructions) -> Instructions'
+%% Optimises all allocate instructions.
+
+opt_alloc([{allocate,R,{_,Ns,Nh,[]}}|Is]) ->
+ [opt_alloc(Is, Ns, Nh, R)|opt(Is)];
+opt_alloc([I|Is]) -> [I|opt_alloc(Is)];
+opt_alloc([]) -> [].
+
+%% opt_alloc(Instructions, FrameSize, HeapNeed, LivingRegs) -> [Instr]
+%% Generates the optimal sequence of instructions for
+%% allocating and initalizing the stack frame and needed heap.
+
+opt_alloc(_Is, nostack, Nh, LivingRegs) ->
+ {allocate,LivingRegs,{nozero,nostack,Nh,[]}};
+opt_alloc(Is, Ns, Nh, LivingRegs) ->
+ InitRegs = init_yreg(Is, 0),
+ case count_ones(InitRegs) of
+ N when N*2 > Ns ->
+ {allocate,LivingRegs,{nozero,Ns,Nh,gen_init(Ns, InitRegs)}};
+ _ ->
+ {allocate,LivingRegs,{zero,Ns,Nh,[]}}
+ end.
+
+gen_init(Fs, Regs) -> gen_init(Fs, Regs, 0, []).
+
+gen_init(SameFs, _Regs, SameFs, Acc) -> reverse(Acc);
+gen_init(Fs, Regs, Y, Acc) when Regs band 1 == 0 ->
+ gen_init(Fs, Regs bsr 1, Y+1, [{init, {y,Y}}|Acc]);
+gen_init(Fs, Regs, Y, Acc) ->
+ gen_init(Fs, Regs bsr 1, Y+1, Acc).
+
+%% init_yreg(Instructions, RegSet) -> RegSetInitialized
+%% Calculate the set of initialized y registers.
+
+init_yreg([{set,_,_,{bif,_,_}}|_], Reg) -> Reg;
+init_yreg([{set,Ds,_,_}|Is], Reg) -> init_yreg(Is, add_yregs(Ds, Reg));
+init_yreg(_Is, Reg) -> Reg.
+
+add_yregs(Ys, Reg) -> foldl(fun(Y, R0) -> add_yreg(Y, R0) end, Reg, Ys).
+
+add_yreg({y,Y}, Reg) -> Reg bor (1 bsl Y);
+add_yreg(_, Reg) -> Reg.
+
+count_ones(Bits) -> count_ones(Bits, 0).
+count_ones(0, Acc) -> Acc;
+count_ones(Bits, Acc) ->
+ count_ones(Bits bsr 1, Acc + (Bits band 1)).
+
+%% live_at_entry(Is) -> NumberOfRegisters
+%% Calculate the number of register live at the entry to the code
+%% sequence.
+
+live_at_entry([{block,[{allocate,R,_}|_]}|_]) ->
+ R;
+live_at_entry([{label,_}|Is]) ->
+ live_at_entry(Is);
+live_at_entry([{block,Bl}|_]) ->
+ live_at_entry(Bl);
+live_at_entry([{func_info,_,_,Ar}|_]) ->
+ Ar;
+live_at_entry(Is0) ->
+ case reverse(Is0) of
+ [{'%live',Regs}|Is] -> live_at_entry_1(Is, (1 bsl Regs)-1);
+ _ -> unknown
+ end.
+
+live_at_entry_1([{set,Ds,Ss,_}|Is], Rset0) ->
+ Rset = x_live(Ss, x_dead(Ds, Rset0)),
+ live_at_entry_1(Is, Rset);
+live_at_entry_1([{allocate,_,_}|Is], Rset) ->
+ live_at_entry_1(Is, Rset);
+live_at_entry_1([], Rset) -> live_regs_1(0, Rset).
+
+%% Calculate the new number of live registers when we move an allocate
+%% instruction upwards, passing a 'set' instruction.
+
+live_regs(Ds, Ss, Regs0) ->
+ Rset = x_live(Ss, x_dead(Ds, (1 bsl Regs0)-1)),
+ live_regs_1(0, Rset).
+
+live_regs_1(N, 0) -> N;
+live_regs_1(N, Regs) -> live_regs_1(N+1, Regs bsr 1).
+
+x_dead([{x,N}|Rs], Regs) -> x_dead(Rs, Regs band (bnot (1 bsl N)));
+x_dead([_|Rs], Regs) -> x_dead(Rs, Regs);
+x_dead([], Regs) -> Regs.
+
+x_live([{x,N}|Rs], Regs) -> x_live(Rs, Regs bor (1 bsl N));
+x_live([_|Rs], Regs) -> x_live(Rs, Regs);
+x_live([], Regs) -> Regs.
+
+%%
+%% If a floating point literal occurs more than once, move it into
+%% a free register and re-use it.
+%%
+
+share_floats([{allocate,_,_}=Alloc|Is]) ->
+ [Alloc|share_floats(Is)];
+share_floats(Is0) ->
+ All = get_floats(Is0, []),
+ MoreThanOnce0 = more_than_once(sort(All), gb_sets:empty()),
+ case gb_sets:is_empty(MoreThanOnce0) of
+ true -> Is0;
+ false ->
+ MoreThanOnce = gb_sets:to_list(MoreThanOnce0),
+ FreeX = highest_used(Is0, -1) + 1,
+ Regs0 = make_reg_map(MoreThanOnce, FreeX, []),
+ Regs = gb_trees:from_orddict(Regs0),
+ Is = map(fun({set,Ds,[{float,F}],Op}=I) ->
+ case gb_trees:lookup(F, Regs) of
+ none -> I;
+ {value,R} -> {set,Ds,[R],Op}
+ end;
+ (I) -> I
+ end, Is0),
+ [{set,[R],[{float,F}],move} || {F,R} <- Regs0] ++ Is
+ end.
+
+get_floats([{set,_,[{float,F}],_}|Is], Acc) ->
+ get_floats(Is, [F|Acc]);
+get_floats([_|Is], Acc) ->
+ get_floats(Is, Acc);
+get_floats([], Acc) -> Acc.
+
+more_than_once([F,F|Fs], Set) ->
+ more_than_once(Fs, gb_sets:add(F, Set));
+more_than_once([_|Fs], Set) ->
+ more_than_once(Fs, Set);
+more_than_once([], Set) -> Set.
+
+highest_used([{set,Ds,Ss,_}|Is], High) ->
+ highest_used(Is, highest(Ds, highest(Ss, High)));
+highest_used([{'%live',Live}|Is], High) when Live > High ->
+ highest_used(Is, Live);
+highest_used([_|Is], High) ->
+ highest_used(Is, High);
+highest_used([], High) -> High.
+
+highest([{x,R}|Rs], High) when R > High ->
+ highest(Rs, R);
+highest([_|Rs], High) ->
+ highest(Rs, High);
+highest([], High) -> High.
+
+make_reg_map([F|Fs], R, Acc) when R < ?MAXREG ->
+ make_reg_map(Fs, R+1, [{F,{x,R}}|Acc]);
+make_reg_map(_, _, Acc) -> sort(Acc).
+
+%% inverse_comp_op(Op) -> none|RevOp
+
+inverse_comp_op('=:=') -> '=/=';
+inverse_comp_op('=/=') -> '=:=';
+inverse_comp_op('==') -> '/=';
+inverse_comp_op('/=') -> '==';
+inverse_comp_op('>') -> '=<';
+inverse_comp_op('<') -> '>=';
+inverse_comp_op('>=') -> '<';
+inverse_comp_op('=<') -> '>';
+inverse_comp_op(_) -> none.
+
+%%%
+%%% Evaluation of constant bit fields.
+%%%
+
+is_bs_put({bs_put_integer,_,_,_,_,_}) -> true;
+is_bs_put({bs_put_float,_,_,_,_,_}) -> true;
+is_bs_put(_) -> false.
+
+collect_bs_puts(Is) ->
+ collect_bs_puts_1(Is, []).
+
+collect_bs_puts_1([I|Is]=Is0, Acc) ->
+ case is_bs_put(I) of
+ false -> {reverse(Acc),Is0};
+ true -> collect_bs_puts_1(Is, [I|Acc])
+ end;
+collect_bs_puts_1([], Acc) -> {reverse(Acc),[]}.
+
+opt_bs_puts(Is) ->
+ opt_bs_1(Is, []).
+
+opt_bs_1([{bs_put_float,Fail,{integer,Sz},1,Flags0,Src}=I0|Is], Acc) ->
+ case catch eval_put_float(Src, Sz, Flags0) of
+ {'EXIT',_} ->
+ opt_bs_1(Is, [I0|Acc]);
+ <> ->
+ Flags = force_big(Flags0),
+ I = {bs_put_integer,Fail,{integer,Sz},1,Flags,{integer,Int}},
+ opt_bs_1([I|Is], Acc)
+ end;
+opt_bs_1([{bs_put_integer,_,{integer,8},1,_,{integer,_}}|_]=IsAll, Acc0) ->
+ {Is,Acc} = bs_collect_string(IsAll, Acc0),
+ opt_bs_1(Is, Acc);
+opt_bs_1([{bs_put_integer,Fail,{integer,Sz},1,F,{integer,N}}=I|Is0], Acc) when Sz > 8 ->
+ case field_endian(F) of
+ big ->
+ case bs_split_int(N, Sz, Fail, Is0) of
+ no_split -> opt_bs_1(Is0, [I|Acc]);
+ Is -> opt_bs_1(Is, Acc)
+ end;
+ little ->
+ case catch <> of
+ {'EXIT',_} ->
+ opt_bs_1(Is0, [I|Acc]);
+ <> ->
+ Flags = force_big(F),
+ Is = [{bs_put_integer,Fail,{integer,Sz},1,
+ Flags,{integer,Int}}|Is0],
+ opt_bs_1(Is, Acc)
+ end;
+ native -> opt_bs_1(Is0, [I|Acc])
+ end;
+opt_bs_1([{Op,Fail,{integer,Sz},U,F,Src}|Is], Acc) when U > 1 ->
+ opt_bs_1([{Op,Fail,{integer,U*Sz},1,F,Src}|Is], Acc);
+opt_bs_1([I|Is], Acc) ->
+ opt_bs_1(Is, [I|Acc]);
+opt_bs_1([], Acc) -> reverse(Acc).
+
+eval_put_float(Src, Sz, Flags) ->
+ Val = value(Src),
+ case field_endian(Flags) of
+ little -> <>;
+ big -> <>
+ %% native intentionally not handled here - we can't optimize it.
+ end.
+
+value({integer,I}) -> I;
+value({float,F}) -> F;
+value({atom,A}) -> A.
+
+bs_collect_string(Is, [{bs_put_string,Len,{string,Str}}|Acc]) ->
+ bs_coll_str_1(Is, Len, reverse(Str), Acc);
+bs_collect_string(Is, Acc) ->
+ bs_coll_str_1(Is, 0, [], Acc).
+
+bs_coll_str_1([{bs_put_integer,_,{integer,Sz},U,_,{integer,V}}|Is],
+ Len, StrAcc, IsAcc) when U*Sz =:= 8 ->
+ Byte = V band 16#FF,
+ bs_coll_str_1(Is, Len+1, [Byte|StrAcc], IsAcc);
+bs_coll_str_1(Is, Len, StrAcc, IsAcc) ->
+ {Is,[{bs_put_string,Len,{string,reverse(StrAcc)}}|IsAcc]}.
+
+field_endian({field_flags,F}) -> field_endian_1(F).
+
+field_endian_1([big=E|_]) -> E;
+field_endian_1([little=E|_]) -> E;
+field_endian_1([native=E|_]) -> E;
+field_endian_1([_|Fs]) -> field_endian_1(Fs).
+
+force_big({field_flags,F}) ->
+ {field_flags,force_big_1(F)}.
+
+force_big_1([big|_]=Fs) -> Fs;
+force_big_1([little|Fs]) -> [big|Fs];
+force_big_1([F|Fs]) -> [F|force_big_1(Fs)].
+
+bs_split_int(0, Sz, _, _) when Sz > 64 ->
+ %% We don't want to split in this case because the
+ %% string will consist of only zeroes.
+ no_split;
+bs_split_int(N, Sz, Fail, Acc) ->
+ FirstByteSz = case Sz rem 8 of
+ 0 -> 8;
+ Rem -> Rem
+ end,
+ bs_split_int_1(N, FirstByteSz, Sz, Fail, Acc).
+
+bs_split_int_1(N, ByteSz, Sz, Fail, Acc) when Sz > 0 ->
+ Mask = (1 bsl ByteSz) - 1,
+ I = {bs_put_integer,Fail,{integer,ByteSz},1,
+ {field_flags,[big]},{integer,N band Mask}},
+ bs_split_int_1(N bsr ByteSz, 8, Sz-ByteSz, Fail, [I|Acc]);
+bs_split_int_1(_, _, _, _, Acc) -> Acc.
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_bool.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_bool.erl
new file mode 100644
index 0000000000..b7b28a41a5
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_bool.erl
@@ -0,0 +1,617 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_bool.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%
+%% Purpose: Optimizes booleans in guards.
+
+-module(beam_bool).
+
+-export([module/2]).
+
+-import(lists, [reverse/1,foldl/3,mapfoldl/3,sort/1,member/2]).
+-define(MAXREG, 1024).
+
+-record(st,
+ {next, %Next label number.
+ ll %Live regs at labels.
+ }).
+
+module({Mod,Exp,Attr,Fs0,Lc}, _Opts) ->
+ %%io:format("~p:\n", [Mod]),
+ {Fs,_} = mapfoldl(fun(Fn, Lbl) -> function(Fn, Lbl) end, 100000000, Fs0),
+ {ok,{Mod,Exp,Attr,Fs,Lc}}.
+
+function({function,Name,Arity,CLabel,Is0}, Lbl0) ->
+ %%io:format("~p/~p:\n", [Name,Arity]),
+ {Is,#st{next=Lbl}} = bool_opt(Is0, Lbl0),
+ {{function,Name,Arity,CLabel,Is},Lbl}.
+
+%%
+%% Optimize boolean expressions that use guard bifs. Rewrite to
+%% use test instructions if possible.
+%%
+
+bool_opt(Asm, Lbl) ->
+ LiveInfo = index_instructions(Asm),
+ bopt(Asm, [], #st{next=Lbl,ll=LiveInfo}).
+
+bopt([{block,Bl0}=Block|
+ [{jump,{f,Succ}},
+ {label,Fail},
+ {block,[{set,[Dst],[{atom,false}],move},{'%live',Live}]},
+ {label,Succ}|Is]=Is0], Acc0, St) ->
+ case split_block(Bl0, Dst, Fail) of
+ failed ->
+ bopt(Is0, [Block|Acc0], St);
+ {Bl,PreBlock} ->
+ Acc1 = case PreBlock of
+ [] -> Acc0;
+ _ -> [{block,PreBlock}|Acc0]
+ end,
+ Acc = [{protected,[Dst],Bl,{Fail,Succ,Live}}|Acc1],
+ bopt(Is, Acc, St)
+ end;
+bopt([{test,is_eq_exact,{f,Fail},[Reg,{atom,true}]}=I|Is], [{block,_}|_]=Acc0, St0) ->
+ case bopt_block(Reg, Fail, Is, Acc0, St0) of
+ failed -> bopt(Is, [I|Acc0], St0);
+ {Acc,St} -> bopt(Is, Acc, St)
+ end;
+bopt([I|Is], Acc, St) ->
+ bopt(Is, [I|Acc], St);
+bopt([], Acc, St) ->
+ {bopt_reverse(Acc, []),St}.
+
+bopt_reverse([{protected,[Dst],Block,{Fail,Succ,Live}}|Is], Acc0) ->
+ Acc = [{block,Block},{jump,{f,Succ}},
+ {label,Fail},
+ {block,[{set,[Dst],[{atom,false}],move},{'%live',Live}]},
+ {label,Succ}|Acc0],
+ bopt_reverse(Is, Acc);
+bopt_reverse([I|Is], Acc) ->
+ bopt_reverse(Is, [I|Acc]);
+bopt_reverse([], Acc) -> Acc.
+
+%% bopt_block(Reg, Fail, OldIs, Accumulator, St) -> failed | {NewAcc,St}
+%% Attempt to optimized a block of guard BIFs followed by a test
+%% instruction.
+bopt_block(Reg, Fail, OldIs, [{block,Bl0}|Acc0], St0) ->
+ case split_block(Bl0, Reg, Fail) of
+ failed ->
+ %% Reason for failure: The block either contained no
+ %% guard BIFs with the failure label Fail, or the final
+ %% instruction in the block did not assign the Reg register.
+
+ %%io:format("split ~p: ~P\n", [Reg,Bl0,20]),
+ failed;
+ {Bl1,BlPre} ->
+ %% The block has been splitted. Bl1 is a non-empty list
+ %% of guard BIF instructions having the failure label Fail.
+ %% BlPre is a (possibly empty list) of instructions preceeding
+ %% Bl1.
+ Acc1 = make_block(BlPre, Acc0),
+ {Bl,Acc} = extend_block(Bl1, Fail, Acc1),
+ case catch bopt_block_1(Bl, Fail, St0) of
+ {'EXIT',_Reason} ->
+ %% Optimization failed for one of the following reasons:
+ %%
+ %% 1. Not possible to rewrite because a boolean value is
+ %% passed to another guard bif, e.g. 'abs(A > B)'
+ %% (in this case, obviously nonsense code). Rare in
+ %% practice.
+ %%
+ %% 2. Not possible to rewrite because we have not seen
+ %% the complete boolan expression (it is spread out
+ %% over several blocks with jumps and labels).
+ %% The 'or' and 'and' instructions need to that fully
+ %% known operands in order to be eliminated.
+ %%
+ %% 3. Other bug or limitation.
+
+ %%io:format("~P\n", [_Reason,20]),
+ failed;
+ {NewCode,St} ->
+ case is_opt_safe(Bl, NewCode, OldIs, St) of
+ false ->
+ %% The optimization is not safe. (A register
+ %% used by the instructions following the
+ %% optimized code is either not assigned a
+ %% value at all or assigned a different value.)
+
+ %%io:format("\nNot safe:\n"),
+ %%io:format("~p\n", [Bl]),
+ %%io:format("~p\n", [reverse(NewCode)]),
+ failed;
+ true -> {NewCode++Acc,St}
+ end
+ end
+ end.
+
+bopt_block_1(Block, Fail, St) ->
+ {Pre0,[{_,Tree}]} = bopt_tree(Block),
+ Pre = update_fail_label(Pre0, Fail, []),
+ bopt_cg(Tree, Fail, make_block(Pre, []), St).
+
+%% is_opt_safe(OriginalCode, OptCode, FollowingCode, State) -> true|false
+%% Comparing the original code to the optimized code, determine
+%% whether the optimized code is guaranteed to work in the same
+%% way as the original code.
+
+is_opt_safe(Bl, NewCode, OldIs, St) ->
+ %% Here are the conditions that must be true for the
+ %% optimization to be safe.
+ %%
+ %% 1. Any register that was assigned a value in the original
+ %% code, but is not in the optimized code, must be guaranteed
+ %% to be KILLED in the following code. (NotSet below.)
+ %%
+ %% 2. Any register that is assigned a value in the optimized
+ %% code must be UNUSED in the following code. (NewDst, Set.)
+ %% (Possible future improvement: Registers that are known
+ %% to be assigned the SAME value in the original and optimized
+ %% code don't need to be unused in the following code.)
+
+ PrevDst = dst_regs(Bl),
+ NewDst = dst_regs(NewCode),
+ NotSet = ordsets:subtract(PrevDst, NewDst),
+
+ %% Note: The following line is an optimization. We don't need
+ %% to test whether variables in NotSet for being unused, because
+ %% they will all be tested for being killed (a stronger condition
+ %% than being unused).
+
+ Set = ordsets:subtract(NewDst, NotSet),
+
+ all_killed(NotSet, OldIs, St) andalso
+ none_used(Set, OldIs, St).
+
+% update_fail_label([{set,_,_,{bif,_,{f,0}}}=I|Is], Fail, Acc) ->
+% update_fail_label(Is, Fail, [I|Acc]);
+update_fail_label([{set,Ds,As,{bif,N,{f,_}}}|Is], Fail, Acc) ->
+ update_fail_label(Is, Fail, [{set,Ds,As,{bif,N,{f,Fail}}}|Acc]);
+update_fail_label([], _, Acc) -> Acc.
+
+make_block([], Acc) -> Acc;
+make_block(Bl, Acc) -> [{block,Bl}|Acc].
+
+extend_block(BlAcc, Fail, [{protected,_,_,_}=Prot|OldAcc]) ->
+ extend_block([Prot|BlAcc], Fail, OldAcc);
+extend_block(BlAcc0, Fail, [{block,Is0}|OldAcc]=OldAcc0) ->
+ case extend_block_1(reverse(Is0), Fail, BlAcc0) of
+ {[],_} -> {BlAcc0,OldAcc0};
+ {BlAcc,[]} -> extend_block(BlAcc, Fail, OldAcc);
+ {BlAcc,Is} -> {BlAcc,[{block,Is}|OldAcc]}
+ end;
+extend_block(BlAcc, _, OldAcc) -> {BlAcc,OldAcc}.
+
+extend_block_1([{set,[_],_,{bif,_,{f,Fail}}}=I|Is], Fail, Acc) ->
+ extend_block_1(Is, Fail, [I|Acc]);
+extend_block_1([{set,[_],As,{bif,Bif,_}}=I|Is]=Is0, Fail, Acc) ->
+ case safe_bool_op(Bif, length(As)) of
+ false -> {Acc,reverse(Is0)};
+ true -> extend_block_1(Is, Fail, [I|Acc])
+ end;
+extend_block_1([_|_]=Is, _, Acc) -> {Acc,reverse(Is)};
+extend_block_1([], _, Acc) -> {Acc,[]}.
+
+split_block(Is0, Dst, Fail) ->
+ case reverse(Is0) of
+ [{'%live',_}|[{set,[Dst],_,_}|_]=Is] ->
+ split_block_1(Is, Fail);
+ [{set,[Dst],_,_}|_]=Is ->
+ split_block_1(Is, Fail);
+ _ -> failed
+ end.
+
+split_block_1(Is, Fail) ->
+ case split_block_2(Is, Fail, []) of
+ {[],_} -> failed;
+ {_,_}=Res -> Res
+ end.
+
+% split_block_2([{set,[_],_,{bif,_,{f,0}}}=I|Is], Fail, Acc) ->
+% split_block_2(Is, Fail, [I|Acc]);
+split_block_2([{set,[_],_,{bif,_,{f,Fail}}}=I|Is], Fail, Acc) ->
+ split_block_2(Is, Fail, [I|Acc]);
+split_block_2([{'%live',_}|Is], Fail, Acc) ->
+ split_block_2(Is, Fail, Acc);
+split_block_2(Is, _, Acc) -> {Acc,reverse(Is)}.
+
+dst_regs(Is) ->
+ dst_regs(Is, []).
+
+dst_regs([{block,Bl}|Is], Acc) ->
+ dst_regs(Bl, dst_regs(Is, Acc));
+dst_regs([{set,[D],_,{bif,_,{f,_}}}|Is], Acc) ->
+ dst_regs(Is, [D|Acc]);
+dst_regs([_|Is], Acc) ->
+ dst_regs(Is, Acc);
+dst_regs([], Acc) -> ordsets:from_list(Acc).
+
+all_killed([R|Rs], OldIs, St) ->
+ case is_killed(R, OldIs, St) of
+ false -> false;
+ true -> all_killed(Rs, OldIs, St)
+ end;
+all_killed([], _, _) -> true.
+
+none_used([R|Rs], OldIs, St) ->
+ case is_not_used(R, OldIs, St) of
+ false -> false;
+ true -> none_used(Rs, OldIs, St)
+ end;
+none_used([], _, _) -> true.
+
+bopt_tree(Block0) ->
+ Block = ssa_block(Block0),
+ Reg = free_variables(Block),
+ %%io:format("~p\n", [Block]),
+ %%io:format("~p\n", [Reg]),
+ Res = bopt_tree_1(Block, Reg, []),
+ %%io:format("~p\n", [Res]),
+ Res.
+
+bopt_tree_1([{set,[Dst],As0,{bif,'not',_}}|Is], Forest0, Pre) ->
+ {[Arg],Forest1} = bopt_bool_args(As0, Forest0),
+ Forest = gb_trees:enter(Dst, {'not',Arg}, Forest1),
+ bopt_tree_1(Is, Forest, Pre);
+bopt_tree_1([{set,[Dst],As0,{bif,'and',_}}|Is], Forest0, Pre) ->
+ {As,Forest1} = bopt_bool_args(As0, Forest0),
+ AndList = make_and_list(As),
+ Forest = gb_trees:enter(Dst, {'and',AndList}, Forest1),
+ bopt_tree_1(Is, Forest, Pre);
+bopt_tree_1([{set,[Dst],[L0,R0],{bif,'or',_}}|Is], Forest0, Pre) ->
+ L = gb_trees:get(L0, Forest0),
+ R = gb_trees:get(R0, Forest0),
+ Forest1 = gb_trees:delete(L0, gb_trees:delete(R0, Forest0)),
+ OrList = make_or_list([L,R]),
+ Forest = gb_trees:enter(Dst, {'or',OrList}, Forest1),
+ bopt_tree_1(Is, Forest, Pre);
+bopt_tree_1([{protected,[Dst],_,_}=Prot|Is], Forest0, Pre) ->
+ Forest = gb_trees:enter(Dst, Prot, Forest0),
+ bopt_tree_1(Is, Forest, Pre);
+bopt_tree_1([{set,[Dst],As,{bif,N,_}}=Bif|Is], Forest0, Pre) ->
+ Ar = length(As),
+ case safe_bool_op(N, Ar) of
+ false ->
+ bopt_good_args(As, Forest0),
+ Forest = gb_trees:enter(Dst, any, Forest0),
+ bopt_tree_1(Is, Forest, [Bif|Pre]);
+ true ->
+ bopt_good_args(As, Forest0),
+ Test = bif_to_test(Dst, N, As),
+ Forest = gb_trees:enter(Dst, Test, Forest0),
+ bopt_tree_1(Is, Forest, Pre)
+ end;
+bopt_tree_1([], Forest, Pre) ->
+ {Pre,[R || {_,V}=R <- gb_trees:to_list(Forest), V =/= any]}.
+
+safe_bool_op(internal_is_record, 3) -> true;
+safe_bool_op(N, Ar) ->
+ erl_internal:new_type_test(N, Ar) orelse erl_internal:comp_op(N, Ar).
+
+bopt_bool_args(As, Forest) ->
+ mapfoldl(fun bopt_bool_arg/2, Forest, As).
+
+bopt_bool_arg({T,_}=R, Forest) when T == x; T == y ->
+ {gb_trees:get(R, Forest),gb_trees:delete(R, Forest)};
+bopt_bool_arg(Term, Forest) ->
+ {Term,Forest}.
+
+bopt_good_args([A|As], Regs) ->
+ bopt_good_arg(A, Regs),
+ bopt_good_args(As, Regs);
+bopt_good_args([], _) -> ok.
+
+bopt_good_arg({x,_}=X, Regs) ->
+ case gb_trees:get(X, Regs) of
+ any -> ok;
+ _Other ->
+ %%io:format("not any: ~p: ~p\n", [X,_Other]),
+ exit(bad_contents)
+ end;
+bopt_good_arg(_, _) -> ok.
+
+bif_to_test(_, N, As) ->
+ bif_to_test(N, As).
+
+bif_to_test(internal_is_record, [_,_,_]=As) ->
+ {test,internal_is_record,fail,As};
+bif_to_test('=:=', As) -> {test,is_eq_exact,fail,As};
+bif_to_test('=/=', As) -> {test,is_ne_exact,fail,As};
+bif_to_test('==', As) -> {test,is_eq,fail,As};
+bif_to_test('/=', As) -> {test,is_ne,fail,As};
+bif_to_test('=<', [L,R]) -> {test,is_ge,fail,[R,L]};
+bif_to_test('>=', As) -> {test,is_ge,fail,As};
+bif_to_test('>', [L,R]) -> {test,is_lt,fail,[R,L]};
+bif_to_test('<', As) -> {test,is_lt,fail,As};
+bif_to_test(Name, [_]=As) ->
+ case erl_internal:new_type_test(Name, 1) of
+ false -> exit({bif_to_test,Name,As,failed});
+ true -> {test,Name,fail,As}
+ end.
+
+make_and_list([{'and',As}|Is]) ->
+ make_and_list(As++Is);
+make_and_list([I|Is]) ->
+ [I|make_and_list(Is)];
+make_and_list([]) -> [].
+
+make_or_list([{'or',As}|Is]) ->
+ make_or_list(As++Is);
+make_or_list([I|Is]) ->
+ [I|make_or_list(Is)];
+make_or_list([]) -> [].
+
+%% Code generation for a boolean tree.
+
+bopt_cg({'not',Arg}, Fail, Acc, St) ->
+ I = bopt_cg_not(Arg),
+ bopt_cg(I, Fail, Acc, St);
+bopt_cg({'and',As}, Fail, Acc, St) ->
+ bopt_cg_and(As, Fail, Acc, St);
+bopt_cg({'or',As}, Fail, Acc, St0) ->
+ {Succ,St} = new_label(St0),
+ bopt_cg_or(As, Succ, Fail, Acc, St);
+bopt_cg({test,is_tuple_element,fail,[Tmp,Tuple,RecordTag]}, Fail, Acc, St) ->
+ {[{test,is_eq_exact,{f,Fail},[Tmp,RecordTag]},
+ {get_tuple_element,Tuple,0,Tmp}|Acc],St};
+bopt_cg({inverted_test,is_tuple_element,fail,[Tmp,Tuple,RecordTag]}, Fail, Acc, St) ->
+ {[{test,is_ne_exact,{f,Fail},[Tmp,RecordTag]},
+ {get_tuple_element,Tuple,0,Tmp}|Acc],St};
+bopt_cg({test,N,fail,As}, Fail, Acc, St) ->
+ Test = {test,N,{f,Fail},As},
+ {[Test|Acc],St};
+bopt_cg({inverted_test,N,fail,As}, Fail, Acc, St0) ->
+ {Lbl,St} = new_label(St0),
+ {[{label,Lbl},{jump,{f,Fail}},{test,N,{f,Lbl},As}|Acc],St};
+bopt_cg({protected,_,Bl0,{_,_,_}}, Fail, Acc, St0) ->
+ {Bl,St} = bopt_block_1(Bl0, Fail, St0),
+ {Bl++Acc,St};
+bopt_cg([_|_]=And, Fail, Acc, St) ->
+ bopt_cg_and(And, Fail, Acc, St).
+
+bopt_cg_not({'and',As0}) ->
+ As = [bopt_cg_not(A) || A <- As0],
+ {'or',As};
+bopt_cg_not({'or',As0}) ->
+ As = [bopt_cg_not(A) || A <- As0],
+ {'and',As};
+bopt_cg_not({test,Test,Fail,As}) ->
+ {inverted_test,Test,Fail,As}.
+
+bopt_cg_and([{atom,false}|_], Fail, _, St) ->
+ {[{jump,{f,Fail}}],St};
+bopt_cg_and([{atom,true}|Is], Fail, Acc, St) ->
+ bopt_cg_and(Is, Fail, Acc, St);
+bopt_cg_and([I|Is], Fail, Acc0, St0) ->
+ {Acc,St} = bopt_cg(I, Fail, Acc0, St0),
+ bopt_cg_and(Is, Fail, Acc, St);
+bopt_cg_and([], _, Acc, St) -> {Acc,St}.
+
+bopt_cg_or([I], Succ, Fail, Acc0, St0) ->
+ {Acc,St} = bopt_cg(I, Fail, Acc0, St0),
+ {[{label,Succ}|Acc],St};
+bopt_cg_or([I|Is], Succ, Fail, Acc0, St0) ->
+ {Lbl,St1} = new_label(St0),
+ {Acc,St} = bopt_cg(I, Lbl, Acc0, St1),
+ bopt_cg_or(Is, Succ, Fail, [{label,Lbl},{jump,{f,Succ}}|Acc], St).
+
+new_label(#st{next=LabelNum}=St) when is_integer(LabelNum) ->
+ {LabelNum,St#st{next=LabelNum+1}}.
+
+free_variables(Is) ->
+ E = gb_sets:empty(),
+ free_vars_1(Is, E, E).
+
+free_vars_1([{set,[Dst],As,{bif,_,_}}|Is], F0, N0) ->
+ F = gb_sets:union(F0, gb_sets:difference(var_list(As), N0)),
+ N = gb_sets:union(N0, var_list([Dst])),
+ free_vars_1(Is, F, N);
+free_vars_1([{protected,_,Pa,_}|Is], F, N) ->
+ free_vars_1(Pa++Is, F, N);
+free_vars_1([], F, _) ->
+ gb_trees:from_orddict([{K,any} || K <- gb_sets:to_list(F)]).
+
+var_list(Is) ->
+ var_list_1(Is, gb_sets:empty()).
+
+var_list_1([{x,_}=X|Is], D) ->
+ var_list_1(Is, gb_sets:add(X, D));
+var_list_1([_|Is], D) ->
+ var_list_1(Is, D);
+var_list_1([], D) -> D.
+
+%%%
+%%% Convert a block to Static Single Assignment (SSA) form.
+%%%
+
+-record(ssa,
+ {live,
+ sub}).
+
+ssa_block(Is0) ->
+ Next = ssa_first_free(Is0, 0),
+ {Is,_} = ssa_block_1(Is0, #ssa{live=Next,sub=gb_trees:empty()}, []),
+ Is.
+
+ssa_block_1([{protected,[_],Pa0,Pb}|Is], Sub0, Acc) ->
+ {Pa,Sub} = ssa_block_1(Pa0, Sub0, []),
+ Dst = ssa_last_target(Pa),
+ ssa_block_1(Is, Sub, [{protected,[Dst],Pa,Pb}|Acc]);
+ssa_block_1([{set,[Dst],As,Bif}|Is], Sub0, Acc0) ->
+ Sub1 = ssa_in_use_list(As, Sub0),
+ Sub = ssa_assign(Dst, Sub1),
+ Acc = [{set,[ssa_sub(Dst, Sub)],ssa_sub_list(As, Sub0),Bif}|Acc0],
+ ssa_block_1(Is, Sub, Acc);
+ssa_block_1([], Sub, Acc) -> {reverse(Acc),Sub}.
+
+ssa_in_use_list(As, Sub) ->
+ foldl(fun ssa_in_use/2, Sub, As).
+
+ssa_in_use({x,_}=R, #ssa{sub=Sub0}=Ssa) ->
+ case gb_trees:is_defined(R, Sub0) of
+ true -> Ssa;
+ false ->
+ Sub = gb_trees:insert(R, R, Sub0),
+ Ssa#ssa{sub=Sub}
+ end;
+ssa_in_use(_, Ssa) -> Ssa.
+
+ssa_assign({x,_}=R, #ssa{sub=Sub0}=Ssa0) ->
+ case gb_trees:is_defined(R, Sub0) of
+ false ->
+ Sub = gb_trees:insert(R, R, Sub0),
+ Ssa0#ssa{sub=Sub};
+ true ->
+ {NewReg,Ssa} = ssa_new_reg(Ssa0),
+ Sub1 = gb_trees:update(R, NewReg, Sub0),
+ Sub = gb_trees:insert(NewReg, NewReg, Sub1),
+ Ssa#ssa{sub=Sub}
+ end;
+ssa_assign(_, Ssa) -> Ssa.
+
+ssa_sub_list(List, Sub) ->
+ [ssa_sub(E, Sub) || E <- List].
+
+ssa_sub(R0, #ssa{sub=Sub}) ->
+ case gb_trees:lookup(R0, Sub) of
+ none -> R0;
+ {value,R} -> R
+ end.
+
+ssa_new_reg(#ssa{live=Reg}=Ssa) ->
+ {{x,Reg},Ssa#ssa{live=Reg+1}}.
+
+ssa_first_free([{protected,Ds,_,_}|Is], Next0) ->
+ Next = ssa_first_free_list(Ds, Next0),
+ ssa_first_free(Is, Next);
+ssa_first_free([{set,[Dst],As,_}|Is], Next0) ->
+ Next = ssa_first_free_list([Dst|As], Next0),
+ ssa_first_free(Is, Next);
+ssa_first_free([], Next) -> Next.
+
+ssa_first_free_list(Regs, Next) ->
+ foldl(fun({x,R}, N) when R >= N -> R+1;
+ (_, N) -> N end, Next, Regs).
+
+ssa_last_target([{set,[Dst],_,_},{'%live',_}]) -> Dst;
+ssa_last_target([{set,[Dst],_,_}]) -> Dst;
+ssa_last_target([_|Is]) -> ssa_last_target(Is).
+
+%% index_instructions(FunctionIs) -> GbTree([{Label,Is}])
+%% Index the instruction sequence so that we can quickly
+%% look up the instruction following a specific label.
+
+index_instructions(Is) ->
+ ii_1(Is, []).
+
+ii_1([{label,Lbl}|Is0], Acc) ->
+ Is = lists:dropwhile(fun({label,_}) -> true;
+ (_) -> false end, Is0),
+ ii_1(Is0, [{Lbl,Is}|Acc]);
+ii_1([_|Is], Acc) ->
+ ii_1(Is, Acc);
+ii_1([], Acc) -> gb_trees:from_orddict(sort(Acc)).
+
+%% is_killed(Register, [Instruction], State) -> true|false
+%% Determine whether a register is killed in the instruction sequence.
+%% The state is used to allow us to determine the kill state
+%% across branches.
+
+is_killed(R, Is, St) ->
+ case is_killed_1(R, Is, St) of
+ false ->
+ %%io:format("nk ~p: ~P\n", [R,Is,15]),
+ false;
+ true -> true
+ end.
+
+is_killed_1(R, [{block,Blk}|Is], St) ->
+ case is_killed_1(R, Blk, St) of
+ true -> true;
+ false -> is_killed_1(R, Is, St)
+ end;
+is_killed_1(R, [{test,_,{f,Fail},As}|Is], St) ->
+ case not member(R, As) andalso is_reg_killed_at(R, Fail, St) of
+ false -> false;
+ true -> is_killed_1(R, Is, St)
+ end;
+is_killed_1(R, [{select_val,R,_,_}|_], _) -> false;
+is_killed_1(R, [{select_val,_,Fail,{list,Branches}}|_], St) ->
+ is_killed_at_all(R, [Fail|Branches], St);
+is_killed_1(R, [{jump,{f,F}}|_], St) ->
+ is_reg_killed_at(R, F, St);
+is_killed_1(Reg, Is, _) ->
+ beam_block:is_killed(Reg, Is).
+
+is_reg_killed_at(R, Lbl, #st{ll=Ll}=St) ->
+ Is = gb_trees:get(Lbl, Ll),
+ is_killed_1(R, Is, St).
+
+is_killed_at_all(R, [{f,Lbl}|T], St) ->
+ case is_reg_killed_at(R, Lbl, St) of
+ false -> false;
+ true -> is_killed_at_all(R, T, St)
+ end;
+is_killed_at_all(R, [_|T], St) ->
+ is_killed_at_all(R, T, St);
+is_killed_at_all(_, [], _) -> true.
+
+%% is_not_used(Register, [Instruction], State) -> true|false
+%% Determine whether a register is never used in the instruction sequence
+%% (it could still referenced by an allocate instruction, meaning that
+%% it MUST be initialized).
+%% The state is used to allow us to determine the usage state
+%% across branches.
+
+is_not_used(R, Is, St) ->
+ case is_not_used_1(R, Is, St) of
+ false ->
+ %%io:format("used ~p: ~P\n", [R,Is,15]),
+ false;
+ true -> true
+ end.
+
+is_not_used_1(R, [{block,Blk}|Is], St) ->
+ case is_not_used_1(R, Blk, St) of
+ true -> true;
+ false -> is_not_used_1(R, Is, St)
+ end;
+is_not_used_1(R, [{test,_,{f,Fail},As}|Is], St) ->
+ case not member(R, As) andalso is_reg_not_used_at(R, Fail, St) of
+ false -> false;
+ true -> is_not_used_1(R, Is, St)
+ end;
+is_not_used_1(R, [{select_val,R,_,_}|_], _) -> false;
+is_not_used_1(R, [{select_val,_,Fail,{list,Branches}}|_], St) ->
+ is_used_at_none(R, [Fail|Branches], St);
+is_not_used_1(R, [{jump,{f,F}}|_], St) ->
+ is_reg_not_used_at(R, F, St);
+is_not_used_1(Reg, Is, _) ->
+ beam_block:is_not_used(Reg, Is).
+
+is_reg_not_used_at(R, Lbl, #st{ll=Ll}=St) ->
+ Is = gb_trees:get(Lbl, Ll),
+ is_not_used_1(R, Is, St).
+
+is_used_at_none(R, [{f,Lbl}|T], St) ->
+ case is_reg_not_used_at(R, Lbl, St) of
+ false -> false;
+ true -> is_used_at_none(R, T, St)
+ end;
+is_used_at_none(R, [_|T], St) ->
+ is_used_at_none(R, T, St);
+is_used_at_none(_, [], _) -> true.
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_clean.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_clean.erl
new file mode 100644
index 0000000000..04225e9bd0
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_clean.erl
@@ -0,0 +1,232 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_clean.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%
+%% Purpose : Clean up, such as removing unused labels and unused functions.
+
+-module(beam_clean).
+
+-export([module/2]).
+-import(lists, [member/2,map/2,foldl/3,mapfoldl/3,reverse/1]).
+
+module({Mod,Exp,Attr,Fs0,_}, _Opt) ->
+ Order = [Lbl || {function,_,_,Lbl,_} <- Fs0],
+ All = foldl(fun({function,_,_,Lbl,_}=Func,D) -> dict:store(Lbl, Func, D) end,
+ dict:new(), Fs0),
+ {WorkList,Used0} = exp_to_labels(Fs0, Exp),
+ Used = find_all_used(WorkList, All, Used0),
+ Fs1 = remove_unused(Order, Used, All),
+ {Fs,Lc} = clean_labels(Fs1),
+ {ok,{Mod,Exp,Attr,Fs,Lc}}.
+
+%% Convert the export list ({Name,Arity} pairs) to a list of entry labels.
+
+exp_to_labels(Fs, Exp) -> exp_to_labels(Fs, Exp, [], sets:new()).
+
+exp_to_labels([{function,Name,Arity,Lbl,_}|Fs], Exp, Acc, Used) ->
+ case member({Name,Arity}, Exp) of
+ true -> exp_to_labels(Fs, Exp, [Lbl|Acc], sets:add_element(Lbl, Used));
+ false -> exp_to_labels(Fs, Exp, Acc, Used)
+ end;
+exp_to_labels([], _, Acc, Used) -> {Acc,Used}.
+
+%% Remove the unused functions.
+
+remove_unused([F|Fs], Used, All) ->
+ case sets:is_element(F, Used) of
+ false -> remove_unused(Fs, Used, All);
+ true -> [dict:fetch(F, All)|remove_unused(Fs, Used, All)]
+ end;
+remove_unused([], _, _) -> [].
+
+%% Find all used functions.
+
+find_all_used([F|Fs0], All, Used0) ->
+ {function,_,_,_,Code} = dict:fetch(F, All),
+ {Fs,Used} = update_work_list(Code, {Fs0,Used0}),
+ find_all_used(Fs, All, Used);
+find_all_used([], _All, Used) -> Used.
+
+update_work_list([{call,_,{f,L}}|Is], Sets) ->
+ update_work_list(Is, add_to_work_list(L, Sets));
+update_work_list([{call_last,_,{f,L},_}|Is], Sets) ->
+ update_work_list(Is, add_to_work_list(L, Sets));
+update_work_list([{call_only,_,{f,L}}|Is], Sets) ->
+ update_work_list(Is, add_to_work_list(L, Sets));
+update_work_list([{make_fun,{f,L},_,_}|Is], Sets) ->
+ update_work_list(Is, add_to_work_list(L, Sets));
+update_work_list([{make_fun2,{f,L},_,_,_}|Is], Sets) ->
+ update_work_list(Is, add_to_work_list(L, Sets));
+update_work_list([_|Is], Sets) ->
+ update_work_list(Is, Sets);
+update_work_list([], Sets) -> Sets.
+
+add_to_work_list(F, {Fs,Used}=Sets) ->
+ case sets:is_element(F, Used) of
+ true -> Sets;
+ false -> {[F|Fs],sets:add_element(F, Used)}
+ end.
+
+
+%%%
+%%% Coalesce adjacent labels. Renumber all labels to eliminate gaps.
+%%% This cleanup will slightly reduce file size and slightly speed up loading.
+%%%
+%%% We also expand internal_is_record/3 to a sequence of instructions. It is done
+%%% here merely because this module will always be called even if optimization
+%%% is turned off. We don't want to do the expansion in beam_asm because we
+%%% want to see the expanded code in a .S file.
+%%%
+
+-record(st, {lmap, %Translation tables for labels.
+ entry, %Number of entry label.
+ lc %Label counter
+ }).
+
+clean_labels(Fs0) ->
+ St0 = #st{lmap=dict:new(),lc=1},
+ {Fs1,#st{lmap=Lmap,lc=Lc}} = mapfoldl(fun function_renumber/2, St0, Fs0),
+ {map(fun(F) -> function_replace(F, Lmap) end, Fs1),Lc}.
+
+function_renumber({function,Name,Arity,_Entry,Asm0}, St0) ->
+ {Asm,St} = renumber_labels(Asm0, [], St0),
+ {{function,Name,Arity,St#st.entry,Asm},St}.
+
+renumber_labels([{bif,internal_is_record,{f,_},
+ [Term,Tag,{integer,Arity}],Dst}|Is], Acc, St) ->
+ ContLabel = 900000000+2*St#st.lc,
+ FailLabel = ContLabel+1,
+ Fail = {f,FailLabel},
+ Tmp = Dst,
+ renumber_labels([{test,is_tuple,Fail,[Term]},
+ {test,test_arity,Fail,[Term,Arity]},
+ {get_tuple_element,Term,0,Tmp},
+ {test,is_eq_exact,Fail,[Tmp,Tag]},
+ {move,{atom,true},Dst},
+ {jump,{f,ContLabel}},
+ {label,FailLabel},
+ {move,{atom,false},Dst},
+ {label,ContLabel}|Is], Acc, St);
+renumber_labels([{test,internal_is_record,{f,_}=Fail,
+ [Term,Tag,{integer,Arity}]}|Is], Acc, St) ->
+ Tmp = {x,1023},
+ case Term of
+ {Reg,_} when Reg == x; Reg == y ->
+ renumber_labels([{test,is_tuple,Fail,[Term]},
+ {test,test_arity,Fail,[Term,Arity]},
+ {get_tuple_element,Term,0,Tmp},
+ {test,is_eq_exact,Fail,[Tmp,Tag]}|Is], Acc, St);
+ _ ->
+ renumber_labels([{jump,Fail}|Is], Acc, St)
+ end;
+renumber_labels([{label,Old}|Is], [{label,New}|_]=Acc, #st{lmap=D0}=St) ->
+ D = dict:store(Old, New, D0),
+ renumber_labels(Is, Acc, St#st{lmap=D});
+renumber_labels([{label,Old}|Is], Acc, St0) ->
+ New = St0#st.lc,
+ D = dict:store(Old, New, St0#st.lmap),
+ renumber_labels(Is, [{label,New}|Acc], St0#st{lmap=D,lc=New+1});
+renumber_labels([{func_info,_,_,_}=Fi|Is], Acc, St0) ->
+ renumber_labels(Is, [Fi|Acc], St0#st{entry=St0#st.lc});
+renumber_labels([I|Is], Acc, St0) ->
+ renumber_labels(Is, [I|Acc], St0);
+renumber_labels([], Acc, St0) -> {Acc,St0}.
+
+function_replace({function,Name,Arity,Entry,Asm0}, Dict) ->
+ Asm = case catch replace(Asm0, [], Dict) of
+ {'EXIT',_}=Reason ->
+ exit(Reason);
+ {error,{undefined_label,Lbl}=Reason} ->
+ io:format("Function ~s/~w refers to undefined label ~w\n",
+ [Name,Arity,Lbl]),
+ exit(Reason);
+ Asm1 when list(Asm1) -> Asm1
+ end,
+ {function,Name,Arity,Entry,Asm}.
+
+replace([{test,Test,{f,Lbl},Ops}|Is], Acc, D) ->
+ replace(Is, [{test,Test,{f,label(Lbl, D)},Ops}|Acc], D);
+replace([{select_val,R,{f,Fail0},{list,Vls0}}|Is], Acc, D) ->
+ Vls1 = map(fun ({f,L}) -> {f,label(L, D)};
+ (Other) -> Other end, Vls0),
+ Fail = label(Fail0, D),
+ case redundant_values(Vls1, Fail, []) of
+ [] ->
+ %% Oops, no choices left. The loader will not accept that.
+ %% Convert to a plain jump.
+ replace(Is, [{jump,{f,Fail}}|Acc], D);
+ Vls ->
+ replace(Is, [{select_val,R,{f,Fail},{list,Vls}}|Acc], D)
+ end;
+replace([{select_tuple_arity,R,{f,Fail},{list,Vls0}}|Is], Acc, D) ->
+ Vls = map(fun ({f,L}) -> {f,label(L, D)};
+ (Other) -> Other end, Vls0),
+ replace(Is, [{select_tuple_arity,R,{f,label(Fail, D)},{list,Vls}}|Acc], D);
+replace([{'try',R,{f,Lbl}}|Is], Acc, D) ->
+ replace(Is, [{'try',R,{f,label(Lbl, D)}}|Acc], D);
+replace([{'catch',R,{f,Lbl}}|Is], Acc, D) ->
+ replace(Is, [{'catch',R,{f,label(Lbl, D)}}|Acc], D);
+replace([{jump,{f,Lbl}}|Is], Acc, D) ->
+ replace(Is, [{jump,{f,label(Lbl, D)}}|Acc], D);
+replace([{loop_rec,{f,Lbl},R}|Is], Acc, D) ->
+ replace(Is, [{loop_rec,{f,label(Lbl, D)},R}|Acc], D);
+replace([{loop_rec_end,{f,Lbl}}|Is], Acc, D) ->
+ replace(Is, [{loop_rec_end,{f,label(Lbl, D)}}|Acc], D);
+replace([{wait,{f,Lbl}}|Is], Acc, D) ->
+ replace(Is, [{wait,{f,label(Lbl, D)}}|Acc], D);
+replace([{wait_timeout,{f,Lbl},To}|Is], Acc, D) ->
+ replace(Is, [{wait_timeout,{f,label(Lbl, D)},To}|Acc], D);
+replace([{bif,Name,{f,Lbl},As,R}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bif,Name,{f,label(Lbl, D)},As,R}|Acc], D);
+replace([{call,Ar,{f,Lbl}}|Is], Acc, D) ->
+ replace(Is, [{call,Ar,{f,label(Lbl,D)}}|Acc], D);
+replace([{call_last,Ar,{f,Lbl},N}|Is], Acc, D) ->
+ replace(Is, [{call_last,Ar,{f,label(Lbl,D)},N}|Acc], D);
+replace([{call_only,Ar,{f,Lbl}}|Is], Acc, D) ->
+ replace(Is, [{call_only,Ar,{f,label(Lbl, D)}}|Acc], D);
+replace([{make_fun,{f,Lbl},U1,U2}|Is], Acc, D) ->
+ replace(Is, [{make_fun,{f,label(Lbl, D)},U1,U2}|Acc], D);
+replace([{make_fun2,{f,Lbl},U1,U2,U3}|Is], Acc, D) ->
+ replace(Is, [{make_fun2,{f,label(Lbl, D)},U1,U2,U3}|Acc], D);
+replace([{bs_init2,{f,Lbl},Sz,Words,R,F,Dst}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_init2,{f,label(Lbl, D)},Sz,Words,R,F,Dst}|Acc], D);
+replace([{bs_put_integer,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_put_integer,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D);
+replace([{bs_put_binary,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_put_binary,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D);
+replace([{bs_put_float,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_put_float,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D);
+replace([{bs_final,{f,Lbl},R}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_final,{f,label(Lbl, D)},R}|Acc], D);
+replace([{bs_add,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_add,{f,label(Lbl, D)},Src,Dst}|Acc], D);
+replace([{bs_bits_to_bytes,{f,Lbl},Bits,Dst}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_bits_to_bytes,{f,label(Lbl, D)},Bits,Dst}|Acc], D);
+replace([I|Is], Acc, D) ->
+ replace(Is, [I|Acc], D);
+replace([], Acc, _) -> Acc.
+
+label(Old, D) ->
+ case dict:find(Old, D) of
+ {ok,Val} -> Val;
+ error -> throw({error,{undefined_label,Old}})
+ end.
+
+redundant_values([_,{f,Fail}|Vls], Fail, Acc) ->
+ redundant_values(Vls, Fail, Acc);
+redundant_values([Val,Lbl|Vls], Fail, Acc) ->
+ redundant_values(Vls, Fail, [Lbl,Val|Acc]);
+redundant_values([], _, Acc) -> reverse(Acc).
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_dict.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_dict.erl
new file mode 100644
index 0000000000..08eca2fc00
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_dict.erl
@@ -0,0 +1,196 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_dict.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%
+%% Purpose : Maintain atom, import, and export tables for assembler.
+
+-module(beam_dict).
+
+-export([new/0, opcode/2, highest_opcode/1,
+ atom/2, local/4, export/4, import/4, string/2, lambda/5,
+ atom_table/1, local_table/1, export_table/1, import_table/1,
+ string_table/1,lambda_table/1]).
+
+-record(asm_dict,
+ {atoms = [], % [{Index, Atom}]
+ exports = [], % [{F, A, Label}]
+ locals = [], % [{F, A, Label}]
+ imports = [], % [{Index, {M, F, A}]
+ strings = [], % Deep list of characters
+ lambdas = [], % [{...}]
+ next_atom = 1,
+ next_import = 0,
+ string_offset = 0,
+ highest_opcode = 0
+ }).
+
+new() ->
+ #asm_dict{}.
+
+%% Remembers highest opcode.
+
+opcode(Op, Dict) when Dict#asm_dict.highest_opcode > Op -> Dict;
+opcode(Op, Dict) -> Dict#asm_dict{highest_opcode=Op}.
+
+%% Returns the highest opcode encountered.
+
+highest_opcode(#asm_dict{highest_opcode=Op}) -> Op.
+
+%% Returns the index for an atom (adding it to the atom table if necessary).
+%% atom(Atom, Dict) -> {Index, Dict'}
+
+atom(Atom, Dict) when atom(Atom) ->
+ NextIndex = Dict#asm_dict.next_atom,
+ case lookup_store(Atom, Dict#asm_dict.atoms, NextIndex) of
+ {Index, _, NextIndex} ->
+ {Index, Dict};
+ {Index, Atoms, NewIndex} ->
+ {Index, Dict#asm_dict{atoms=Atoms, next_atom=NewIndex}}
+ end.
+
+%% Remembers an exported function.
+%% export(Func, Arity, Label, Dict) -> Dict'
+
+export(Func, Arity, Label, Dict0) when atom(Func), integer(Arity), integer(Label) ->
+ {Index, Dict1} = atom(Func, Dict0),
+ Dict1#asm_dict{exports = [{Index, Arity, Label}| Dict1#asm_dict.exports]}.
+
+%% Remembers a local function.
+%% local(Func, Arity, Label, Dict) -> Dict'
+
+local(Func, Arity, Label, Dict0) when atom(Func), integer(Arity), integer(Label) ->
+ {Index,Dict1} = atom(Func, Dict0),
+ Dict1#asm_dict{locals = [{Index,Arity,Label}| Dict1#asm_dict.locals]}.
+
+%% Returns the index for an import entry (adding it to the import table if necessary).
+%% import(Mod, Func, Arity, Dict) -> {Index, Dict'}
+
+import(Mod, Func, Arity, Dict) when atom(Mod), atom(Func), integer(Arity) ->
+ NextIndex = Dict#asm_dict.next_import,
+ case lookup_store({Mod, Func, Arity}, Dict#asm_dict.imports, NextIndex) of
+ {Index, _, NextIndex} ->
+ {Index, Dict};
+ {Index, Imports, NewIndex} ->
+ {_, D1} = atom(Mod, Dict#asm_dict{imports=Imports, next_import=NewIndex}),
+ {_, D2} = atom(Func, D1),
+ {Index, D2}
+ end.
+
+%% Returns the index for a string in the string table (adding the string to the
+%% table if necessary).
+%% string(String, Dict) -> {Offset, Dict'}
+
+string(Str, Dict) when list(Str) ->
+ #asm_dict{strings = Strings, string_offset = NextOffset} = Dict,
+ case old_string(Str, Strings) of
+ {true, Offset} ->
+ {Offset, Dict};
+ false ->
+ NewDict = Dict#asm_dict{strings = Strings++Str,
+ string_offset = NextOffset+length(Str)},
+ {NextOffset, NewDict}
+ end.
+
+%% Returns the index for a funentry (adding it to the table if necessary).
+%% lambda(Dict, Lbl, Index, Uniq, NumFree) -> {Index,Dict'}
+
+lambda(Lbl, Index, OldUniq, NumFree, #asm_dict{lambdas=Lambdas0}=Dict) ->
+ OldIndex = length(Lambdas0),
+ Lambdas = [{Lbl,{OldIndex,Lbl,Index,NumFree,OldUniq}}|Lambdas0],
+ {OldIndex,Dict#asm_dict{lambdas=Lambdas}}.
+
+%% Returns the atom table.
+%% atom_table(Dict) -> [Length,AtomString...]
+
+atom_table(#asm_dict{atoms=Atoms, next_atom=NumAtoms}) ->
+ Sorted = lists:sort(Atoms),
+ Fun = fun({_, A}) ->
+ L = atom_to_list(A),
+ [length(L)|L]
+ end,
+ {NumAtoms-1, lists:map(Fun, Sorted)}.
+
+%% Returns the table of local functions.
+%% local_table(Dict) -> {NumLocals, [{Function, Arity, Label}...]}
+
+local_table(#asm_dict{locals = Locals}) ->
+ {length(Locals),Locals}.
+
+%% Returns the export table.
+%% export_table(Dict) -> {NumExports, [{Function, Arity, Label}...]}
+
+export_table(#asm_dict{exports = Exports}) ->
+ {length(Exports), Exports}.
+
+%% Returns the import table.
+%% import_table(Dict) -> {NumImports, [{Module, Function, Arity}...]}
+
+import_table(Dict) ->
+ #asm_dict{imports = Imports, next_import = NumImports} = Dict,
+ Sorted = lists:sort(Imports),
+ Fun = fun({_, {Mod, Func, Arity}}) ->
+ {Atom0, _} = atom(Mod, Dict),
+ {Atom1, _} = atom(Func, Dict),
+ {Atom0, Atom1, Arity}
+ end,
+ {NumImports, lists:map(Fun, Sorted)}.
+
+string_table(#asm_dict{strings = Strings, string_offset = Size}) ->
+ {Size, Strings}.
+
+lambda_table(#asm_dict{locals=Loc0,lambdas=Lambdas0}) ->
+ Lambdas1 = sofs:relation(Lambdas0),
+ Loc = sofs:relation([{Lbl,{F,A}} || {F,A,Lbl} <- Loc0]),
+ Lambdas2 = sofs:relative_product1(Lambdas1, Loc),
+ Lambdas = [<> ||
+ {{_,Lbl,Index,NumFree,OldUniq},{F,A}} <- sofs:to_external(Lambdas2)],
+ {length(Lambdas),Lambdas}.
+
+%%% Local helper functions.
+
+lookup_store(Key, Dict, NextIndex) ->
+ case catch lookup_store1(Key, Dict, NextIndex) of
+ Index when integer(Index) ->
+ {Index, Dict, NextIndex};
+ {Index, NewDict} ->
+ {Index, NewDict, NextIndex+1}
+ end.
+
+lookup_store1(Key, [Pair|Dict], NextIndex) when Key > element(2, Pair) ->
+ {Index, NewDict} = lookup_store1(Key, Dict, NextIndex),
+ {Index, [Pair|NewDict]};
+lookup_store1(Key, [{Index, Key}|_Dict], _NextIndex) ->
+ throw(Index);
+lookup_store1(Key, Dict, NextIndex) ->
+ {NextIndex, [{NextIndex, Key}|Dict]}.
+
+%% Search for string Str in the string pool Pool.
+%% old_string(Str, Pool) -> false | {true, Offset}
+
+old_string(Str, Pool) ->
+ old_string(Str, Pool, 0).
+
+old_string([C|Str], [C|Pool], Index) ->
+ case lists:prefix(Str, Pool) of
+ true ->
+ {true, Index};
+ false ->
+ old_string([C|Str], Pool, Index+1)
+ end;
+old_string(Str, [_|Pool], Index) ->
+ old_string(Str, Pool, Index+1);
+old_string(_Str, [], _Index) ->
+ false.
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_disasm.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_disasm.erl
new file mode 100644
index 0000000000..0108f91b7f
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_disasm.erl
@@ -0,0 +1,964 @@
+%% -*- erlang-indent-level: 4 -*-
+%%=======================================================================
+%% File : beam_disasm.erl
+%% Author : Kostis Sagonas
+%% Description : Disassembles an R5-R10 .beam file into symbolic BEAM code
+%%=======================================================================
+%% $Id: beam_disasm.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%=======================================================================
+%% Notes:
+%% 1. It does NOT work for .beam files of previous BEAM versions.
+%% 2. If handling of new BEAM instructions is needed, this should be
+%% inserted at the end of function resolve_inst().
+%%=======================================================================
+
+-module(beam_disasm).
+
+-export([file/1, format_error/1]).
+
+-author("Kostis Sagonas").
+
+-include("beam_opcodes.hrl").
+
+%%-----------------------------------------------------------------------
+
+-define(NO_DEBUG(Str,Xs),ok).
+-define(DEBUG(Str,Xs),io:format(Str,Xs)).
+-define(exit(Reason),exit({?MODULE,?LINE,Reason})).
+
+%%-----------------------------------------------------------------------
+%% Error information
+
+format_error({error, Module, Error}) ->
+ Module:format_error(Error);
+format_error({internal, Error}) ->
+ io_lib:format("~p: disassembly failed with reason ~P.",
+ [?MODULE, Error, 25]).
+
+%%-----------------------------------------------------------------------
+%% The main exported function
+%% File is either a file name or a binary containing the code.
+%% Returns `{beam_file, [...]}' or `{error, Module, Reason}'.
+%% Call `format_error({error, Module, Reason})' for an error string.
+%%-----------------------------------------------------------------------
+
+file(File) ->
+ case beam_lib:info(File) of
+ Info when list(Info) ->
+ {value,{chunks,Chunks}} = lists:keysearch(chunks,1,Info),
+ case catch process_chunks(File, Chunks) of
+ {'EXIT', Error} ->
+ {error, ?MODULE, {internal, Error}};
+ Result ->
+ Result
+ end;
+ Error ->
+ Error
+ end.
+
+%%-----------------------------------------------------------------------
+%% Interface might need to be revised -- do not depend on it.
+%%-----------------------------------------------------------------------
+
+process_chunks(F,ChunkInfoList) ->
+ {ok,{_,Chunks}} = beam_lib:chunks(F, ["Atom","Code","StrT","ImpT","ExpT"]),
+ [{"Atom",AtomBin},{"Code",CodeBin},{"StrT",StrBin},
+ {"ImpT",ImpBin},{"ExpT",ExpBin}] = Chunks,
+ LambdaBin = optional_chunk(F, "FunT", ChunkInfoList),
+ LocBin = optional_chunk(F, "LocT", ChunkInfoList),
+ AttrBin = optional_chunk(F, "Attr", ChunkInfoList),
+ CompBin = optional_chunk(F, "CInf", ChunkInfoList),
+ Atoms = beam_disasm_atoms(AtomBin),
+ Exports = beam_disasm_exports(ExpBin, Atoms),
+ Imports = beam_disasm_imports(ImpBin, Atoms),
+ LocFuns = beam_disasm_exports(LocBin, Atoms),
+ Lambdas = beam_disasm_lambdas(LambdaBin, Atoms),
+ Str = beam_disasm_strings(StrBin),
+ Str1 = binary_to_list(Str), %% for debugging -- use Str as far as poss.
+ Sym_Code = beam_disasm_code(CodeBin,Atoms,Imports,Str,Lambdas),
+ Attributes = beam_disasm_attributes(AttrBin),
+ CompInfo = beam_disasm_compilation_info(CompBin),
+ All = [{exports,Exports},
+ {imports,Imports},
+ {code,Sym_Code},
+ {atoms,Atoms},
+ {local_funs,LocFuns},
+ {strings,Str1},
+ {attributes,Attributes},
+ {comp_info,CompInfo}],
+ {beam_file,[Item || {_Key,Data}=Item <- All, Data =/= none]}.
+
+%%-----------------------------------------------------------------------
+%% Retrieve an optional chunk or none if the chunk doesn't exist.
+%%-----------------------------------------------------------------------
+
+optional_chunk(F, ChunkTag, ChunkInfo) ->
+ case lists:keymember(ChunkTag, 1, ChunkInfo) of
+ true ->
+ {ok,{_,[{ChunkTag,Chunk}]}} = beam_lib:chunks(F, [ChunkTag]),
+ Chunk;
+ false -> none
+ end.
+
+%%-----------------------------------------------------------------------
+%% UTILITIES -- these actually exist in file "beam_lib"
+%% -- they should be moved into a common utils file.
+%%-----------------------------------------------------------------------
+
+i32([X1,X2,X3,X4]) ->
+ (X1 bsl 24) bor (X2 bsl 16) bor (X3 bsl 8) bor X4.
+
+get_int(B) ->
+ {I, B1} = split_binary(B, 4),
+ {i32(binary_to_list(I)), B1}.
+
+%%-----------------------------------------------------------------------
+%% Disassembles the atom table of a BEAM file.
+%% - atoms are stored in order 1 ... N (N = Num_atoms, in fact),
+%% - each atom name consists of a length byte, followed by that many
+%% bytes of name
+%% (nb: atom names max 255 chars?!)
+%%-----------------------------------------------------------------------
+
+beam_disasm_atoms(AtomTabBin) ->
+ {_NumAtoms,B} = get_int(AtomTabBin),
+ disasm_atoms(B).
+
+disasm_atoms(AtomBin) ->
+ disasm_atoms(binary_to_list(AtomBin),1).
+
+disasm_atoms([Len|Xs],N) ->
+ {AtomName,Rest} = get_atom_name(Len,Xs),
+ [{N,list_to_atom(AtomName)}|disasm_atoms(Rest,N+1)];
+disasm_atoms([],_) ->
+ [].
+
+get_atom_name(Len,Xs) ->
+ get_atom_name(Len,Xs,[]).
+
+get_atom_name(N,[X|Xs],RevName) when N > 0 ->
+ get_atom_name(N-1,Xs,[X|RevName]);
+get_atom_name(0,Xs,RevName) ->
+ { lists:reverse(RevName), Xs }.
+
+%%-----------------------------------------------------------------------
+%% Disassembles the export table of a BEAM file.
+%%-----------------------------------------------------------------------
+
+beam_disasm_exports(none, _) -> none;
+beam_disasm_exports(ExpTabBin, Atoms) ->
+ {_NumAtoms,B} = get_int(ExpTabBin),
+ disasm_exports(B,Atoms).
+
+disasm_exports(Bin,Atoms) ->
+ resolve_exports(collect_exports(binary_to_list(Bin)),Atoms).
+
+collect_exports([F3,F2,F1,F0,A3,A2,A1,A0,L3,L2,L1,L0|Exps]) ->
+ [{i32([F3,F2,F1,F0]), % F = function (atom ID)
+ i32([A3,A2,A1,A0]), % A = arity (int)
+ i32([L3,L2,L1,L0])} % L = label (int)
+ |collect_exports(Exps)];
+collect_exports([]) ->
+ [].
+
+resolve_exports(Exps,Atoms) ->
+ [ {lookup_key(F,Atoms), A, L} || {F,A,L} <- Exps ].
+
+%%-----------------------------------------------------------------------
+%% Disassembles the import table of a BEAM file.
+%%-----------------------------------------------------------------------
+
+beam_disasm_imports(ExpTabBin,Atoms) ->
+ {_NumAtoms,B} = get_int(ExpTabBin),
+ disasm_imports(B,Atoms).
+
+disasm_imports(Bin,Atoms) ->
+ resolve_imports(collect_imports(binary_to_list(Bin)),Atoms).
+
+collect_imports([M3,M2,M1,M0,F3,F2,F1,F0,A3,A2,A1,A0|Exps]) ->
+ [{i32([M3,M2,M1,M0]), % M = module (atom ID)
+ i32([F3,F2,F1,F0]), % F = function (atom ID)
+ i32([A3,A2,A1,A0])} % A = arity (int)
+ |collect_imports(Exps)];
+collect_imports([]) ->
+ [].
+
+resolve_imports(Exps,Atoms) ->
+ [{extfunc,lookup_key(M,Atoms),lookup_key(F,Atoms),A} || {M,F,A} <- Exps ].
+
+%%-----------------------------------------------------------------------
+%% Disassembles the lambda (fun) table of a BEAM file.
+%%-----------------------------------------------------------------------
+
+beam_disasm_lambdas(none, _) -> none;
+beam_disasm_lambdas(<<_:32,Tab/binary>>, Atoms) ->
+ disasm_lambdas(Tab, Atoms, 0).
+
+disasm_lambdas(<>,
+ Atoms, OldIndex) ->
+ Info = {lookup_key(F, Atoms),A,Lbl,Index,NumFree,OldUniq},
+ [{OldIndex,Info}|disasm_lambdas(More, Atoms, OldIndex+1)];
+disasm_lambdas(<<>>, _, _) -> [].
+
+%%-----------------------------------------------------------------------
+%% Disassembles the code chunk of a BEAM file:
+%% - The code is first disassembled into a long list of instructions.
+%% - This list is then split into functions and all names are resolved.
+%%-----------------------------------------------------------------------
+
+beam_disasm_code(CodeBin,Atoms,Imports,Str,Lambdas) ->
+ [_SS3,_SS2,_SS1,_SS0, % Sub-Size (length of information before code)
+ _IS3,_IS2,_IS1,_IS0, % Instruction Set Identifier (always 0)
+ _OM3,_OM2,_OM1,_OM0, % Opcode Max
+ _L3,_L2,_L1,_L0,_F3,_F2,_F1,_F0|Code] = binary_to_list(CodeBin),
+ case catch disasm_code(Code, Atoms) of
+ {'EXIT',Rsn} ->
+ ?NO_DEBUG('code disasm failed: ~p~n',[Rsn]),
+ ?exit(Rsn);
+ DisasmCode ->
+ Functions = get_function_chunks(DisasmCode),
+ LocLabels = local_labels(Functions),
+ [resolve_names(F,Imports,Str,LocLabels,Lambdas) || F <- Functions]
+ end.
+
+%%-----------------------------------------------------------------------
+
+disasm_code([B|Bs], Atoms) ->
+ {Instr,RestBs} = disasm_instr(B, Bs, Atoms),
+ [Instr|disasm_code(RestBs, Atoms)];
+disasm_code([], _) -> [].
+
+%%-----------------------------------------------------------------------
+%% Splits the code stream into chunks representing the code of functions.
+%%
+%% NOTE: code actually looks like
+%% label L1: ... label Ln:
+%% func_info ...
+%% label entry:
+%% ...
+%%
+%% ...
+%% So the labels before each func_info should be included as well.
+%% Ideally, only one such label is needed, but the BEAM compiler
+%% before R8 didn't care to remove the redundant ones.
+%%-----------------------------------------------------------------------
+
+get_function_chunks([I|Code]) ->
+ {LastI,RestCode,Labs} = split_head_labels(I,Code,[]),
+ get_funs(LastI,RestCode,Labs,[]);
+get_function_chunks([]) ->
+ ?exit(empty_code_segment).
+
+get_funs(PrevI,[I|Is],RevF,RevFs) ->
+ case I of
+ {func_info,_Info} ->
+ [H|T] = RevF,
+ {Last,Fun,TrailingLabels} = split_head_labels(H,T,[]),
+ get_funs(I, Is, [PrevI|TrailingLabels], add_funs([Last|Fun],RevFs));
+ _ ->
+ get_funs(I, Is, [PrevI|RevF], RevFs)
+ end;
+get_funs(PrevI,[],RevF,RevFs) ->
+ case PrevI of
+ {int_code_end,[]} ->
+ emit_funs(add_fun(RevF,RevFs));
+ _ ->
+ ?DEBUG('warning: code segment did not end with int_code_end~n',[]),
+ emit_funs(add_funs([PrevI|RevF],RevFs))
+ end.
+
+split_head_labels({label,L},[I|Code],Labs) ->
+ split_head_labels(I,Code,[{label,L}|Labs]);
+split_head_labels(I,Code,Labs) ->
+ {I,Code,Labs}.
+
+add_fun([],Fs) ->
+ Fs;
+add_fun(F,Fs) ->
+ add_funs(F,Fs).
+
+add_funs(F,Fs) ->
+ [ lists:reverse(F) | Fs ].
+
+emit_funs(Fs) ->
+ lists:reverse(Fs).
+
+%%-----------------------------------------------------------------------
+%% Collects local labels -- I am not sure this is 100% what is needed.
+%%-----------------------------------------------------------------------
+
+local_labels(Funs) ->
+ [local_label(Fun) || Fun <- Funs].
+
+%% The first clause below attempts to provide some (limited form of)
+%% backwards compatibility; it is not needed for .beam files generated
+%% by the R8 compiler. The clause should one fine day be taken out.
+local_label([{label,_},{label,L}|Code]) ->
+ local_label([{label,L}|Code]);
+local_label([{label,_},
+ {func_info,[M0,F0,{u,A}]},
+ {label,[{u,L1}]}|_]) ->
+ {atom,M} = resolve_arg(M0),
+ {atom,F} = resolve_arg(F0),
+ {L1, {M, F, A}};
+local_label(Code) ->
+ io:format('beam_disasm: no label in ~p~n', [Code]),
+ {-666,{none,none,0}}.
+
+%%-----------------------------------------------------------------------
+%% Disassembles a single BEAM instruction; most instructions are handled
+%% in a generic way; indexing instructions are handled separately.
+%%-----------------------------------------------------------------------
+
+disasm_instr(B, Bs, Atoms) ->
+ {SymOp,Arity} = beam_opcodes:opname(B),
+ case SymOp of
+ select_val ->
+ disasm_select_inst(select_val, Bs, Atoms);
+ select_tuple_arity ->
+ disasm_select_inst(select_tuple_arity, Bs, Atoms);
+ _ ->
+ case catch decode_n_args(Arity, Bs, Atoms) of
+ {'EXIT',Rsn} ->
+ ?NO_DEBUG("decode_n_args(~p,~p) failed~n",[Arity,Bs]),
+ {{'EXIT',{SymOp,Arity,Rsn}},[]};
+ {Args,RestBs} ->
+ ?NO_DEBUG("instr ~p~n",[{SymOp,Args}]),
+ {{SymOp,Args}, RestBs}
+ end
+ end.
+
+%%-----------------------------------------------------------------------
+%% Disassembles a BEAM select_* instruction used for indexing.
+%% Currently handles {select_val,3} and {select_tuple_arity,3} insts.
+%%
+%% The arruments of a "select"-type instruction look as follows:
+%% , {f,FailLabel}, {list, , [ ... ]}
+%% where each case is of the form [symbol,{f,Label}].
+%%-----------------------------------------------------------------------
+
+disasm_select_inst(Inst, Bs, Atoms) ->
+ {X, Bs1} = decode_arg(Bs, Atoms),
+ {F, Bs2} = decode_arg(Bs1, Atoms),
+ {Z, Bs3} = decode_arg(Bs2, Atoms),
+ {U, Bs4} = decode_arg(Bs3, Atoms),
+ {u,Len} = U,
+ {List, RestBs} = decode_n_args(Len, Bs4, Atoms),
+ {{Inst,[X,F,{Z,U,List}]},RestBs}.
+
+%%-----------------------------------------------------------------------
+%% decode_arg([Byte]) -> { Arg, [Byte] }
+%%
+%% - an arg can have variable length, so we must return arg + remaining bytes
+%% - decodes an argument into its 'raw' form: { Tag, Value }
+%% several types map to a single tag, so the byte code instr must then
+%% assign a type to it
+%%-----------------------------------------------------------------------
+
+decode_arg([B|Bs]) ->
+ Tag = decode_tag(B band 2#111),
+ ?NO_DEBUG('Tag = ~p, B = ~p, Bs = ~p~n',[Tag,B,Bs]),
+ case Tag of
+ z ->
+ decode_z_tagged(Tag, B, Bs);
+ _ ->
+ %% all other cases are handled as if they were integers
+ decode_int(Tag, B, Bs)
+ end.
+
+decode_arg([B|Bs0], Atoms) ->
+ Tag = decode_tag(B band 2#111),
+ ?NO_DEBUG('Tag = ~p, B = ~p, Bs = ~p~n',[Tag,B,Bs]),
+ case Tag of
+ z ->
+ decode_z_tagged(Tag, B, Bs0);
+ a ->
+ %% atom or nil
+ case decode_int(Tag, B, Bs0) of
+ {{a,0},Bs} -> {nil,Bs};
+ {{a,I},Bs} -> {{atom,lookup_key(I, Atoms)},Bs}
+ end;
+ _ ->
+ %% all other cases are handled as if they were integers
+ decode_int(Tag, B, Bs0)
+ end.
+
+%%-----------------------------------------------------------------------
+%% Decodes an integer value. Handles positives, negatives, and bignums.
+%%
+%% Tries to do the opposite of:
+%% beam_asm:encode(1, 5) = [81]
+%% beam_asm:encode(1, 1000) = [105,232]
+%% beam_asm:encode(1, 2047) = [233,255]
+%% beam_asm:encode(1, 2048) = [25,8,0]
+%% beam_asm:encode(1,-1) = [25,255,255]
+%% beam_asm:encode(1,-4294967295) = [121,255,0,0,0,1]
+%% beam_asm:encode(1, 4294967295) = [121,0,255,255,255,255]
+%% beam_asm:encode(1, 429496729501) = [121,99,255,255,255,157]
+%%-----------------------------------------------------------------------
+
+decode_int(Tag,B,Bs) when (B band 16#08) == 0 ->
+ %% N < 16 = 4 bits, NNNN:0:TTT
+ N = B bsr 4,
+ {{Tag,N},Bs};
+decode_int(Tag,B,Bs) when (B band 16#10) == 0 ->
+ %% N < 2048 = 11 bits = 3:8 bits, NNN:01:TTT, NNNNNNNN
+ [B1|Bs1] = Bs,
+ Val0 = B band 2#11100000,
+ N = (Val0 bsl 3) bor B1,
+ ?NO_DEBUG('NNN:01:TTT, NNNNNNNN = ~n~p:01:~p, ~p = ~p~n', [Val0,Tag,B,N]),
+ {{Tag,N},Bs1};
+decode_int(Tag,B,Bs) ->
+ {Len,Bs1} = decode_int_length(B,Bs),
+ {IntBs,RemBs} = take_bytes(Len,Bs1),
+ N = build_arg(IntBs),
+ [F|_] = IntBs,
+ Num = if F > 127, Tag == i -> decode_negative(N,Len);
+ true -> N
+ end,
+ ?NO_DEBUG('Len = ~p, IntBs = ~p, Num = ~p~n', [Len,IntBs,Num]),
+ {{Tag,Num},RemBs}.
+
+decode_int_length(B,Bs) ->
+ %% The following imitates get_erlang_integer() in beam_load.c
+ %% Len is the size of the integer value in bytes
+ case B bsr 5 of
+ 7 ->
+ {Arg,ArgBs} = decode_arg(Bs),
+ case Arg of
+ {u,L} ->
+ {L+9,ArgBs}; % 9 stands for 7+2
+ _ ->
+ ?exit({decode_int,weird_bignum_sublength,Arg})
+ end;
+ L ->
+ {L+2,Bs}
+ end.
+
+decode_negative(N,Len) ->
+ N - (1 bsl (Len*8)). % 8 is number of bits in a byte
+
+%%-----------------------------------------------------------------------
+%% Decodes lists and floating point numbers.
+%%-----------------------------------------------------------------------
+
+decode_z_tagged(Tag,B,Bs) when (B band 16#08) == 0 ->
+ N = B bsr 4,
+ case N of
+ 0 -> % float
+ decode_float(Bs);
+ 1 -> % list
+ {{Tag,N},Bs};
+ 2 -> % fr
+ decode_fr(Bs);
+ 3 -> % allocation list
+ decode_alloc_list(Bs);
+ _ ->
+ ?exit({decode_z_tagged,{invalid_extended_tag,N}})
+ end;
+decode_z_tagged(_,B,_) ->
+ ?exit({decode_z_tagged,{weird_value,B}}).
+
+decode_float(Bs) ->
+ {FL,RestBs} = take_bytes(8,Bs),
+ <> = list_to_binary(FL),
+ {{float,Float},RestBs}.
+
+decode_fr(Bs) ->
+ {{u,Fr},RestBs} = decode_arg(Bs),
+ {{fr,Fr},RestBs}.
+
+decode_alloc_list(Bs) ->
+ {{u,N},RestBs} = decode_arg(Bs),
+ decode_alloc_list_1(N, RestBs, []).
+
+decode_alloc_list_1(0, RestBs, Acc) ->
+ {{u,{alloc,lists:reverse(Acc)}},RestBs};
+decode_alloc_list_1(N, Bs0, Acc) ->
+ {{u,Type},Bs1} = decode_arg(Bs0),
+ {{u,Val},Bs} = decode_arg(Bs1),
+ case Type of
+ 0 ->
+ decode_alloc_list_1(N-1, Bs, [{words,Val}|Acc]);
+ 1 ->
+ decode_alloc_list_1(N-1, Bs, [{floats,Val}|Acc])
+ end.
+
+%%-----------------------------------------------------------------------
+%% take N bytes from a stream, return { Taken_bytes, Remaining_bytes }
+%%-----------------------------------------------------------------------
+
+take_bytes(N,Bs) ->
+ take_bytes(N,Bs,[]).
+
+take_bytes(N,[B|Bs],Acc) when N > 0 ->
+ take_bytes(N-1,Bs,[B|Acc]);
+take_bytes(0,Bs,Acc) ->
+ { lists:reverse(Acc), Bs }.
+
+%%-----------------------------------------------------------------------
+%% from a list of bytes Bn,Bn-1,...,B1,B0
+%% build (Bn << 8*n) bor ... bor B1 << 8 bor B0 << 0
+%%-----------------------------------------------------------------------
+
+build_arg(Bs) ->
+ build_arg(Bs,0).
+
+build_arg([B|Bs],N) ->
+ build_arg(Bs, (N bsl 8) bor B);
+build_arg([],N) ->
+ N.
+
+%%-----------------------------------------------------------------------
+%% Decodes a bunch of arguments and returns them in a list
+%%-----------------------------------------------------------------------
+
+decode_n_args(N, Bs, Atoms) when N >= 0 ->
+ decode_n_args(N, [], Bs, Atoms).
+
+decode_n_args(N, Acc, Bs0, Atoms) when N > 0 ->
+ {A1,Bs} = decode_arg(Bs0, Atoms),
+ decode_n_args(N-1, [A1|Acc], Bs, Atoms);
+decode_n_args(0, Acc, Bs, _) ->
+ {lists:reverse(Acc),Bs}.
+
+%%-----------------------------------------------------------------------
+%% Convert a numeric tag value into a symbolic one
+%%-----------------------------------------------------------------------
+
+decode_tag(?tag_u) -> u;
+decode_tag(?tag_i) -> i;
+decode_tag(?tag_a) -> a;
+decode_tag(?tag_x) -> x;
+decode_tag(?tag_y) -> y;
+decode_tag(?tag_f) -> f;
+decode_tag(?tag_h) -> h;
+decode_tag(?tag_z) -> z;
+decode_tag(X) -> ?exit({unknown_tag,X}).
+
+%%-----------------------------------------------------------------------
+%% - replace all references {a,I} with the atom with index I (or {atom,A})
+%% - replace all references to {i,K} in an external call position with
+%% the proper MFA (position in list, first elt = 0, yields MFA to use)
+%% - resolve strings, represented as , into their
+%% actual values by using string table
+%% (note: string table should be passed as a BINARY so that we can
+%% use binary_to_list/3!)
+%% - convert instruction to its readable form ...
+%%
+%% Currently, only the first three are done (systematically, at least).
+%%
+%% Note: It MAY be premature to remove the lists of args, since that
+%% representation means it is simpler to iterate over all args, etc.
+%%-----------------------------------------------------------------------
+
+resolve_names(Fun, Imports, Str, Lbls, Lambdas) ->
+ [resolve_inst(Instr, Imports, Str, Lbls, Lambdas) || Instr <- Fun].
+
+%%
+%% New make_fun2/4 instruction added in August 2001 (R8).
+%% We handle it specially here to avoid adding an argument to
+%% the clause for every instruction.
+%%
+
+resolve_inst({make_fun2,Args},_,_,Lbls,Lambdas) ->
+ [OldIndex] = resolve_args(Args),
+ {value,{OldIndex,{F,A,_Lbl,_Index,NumFree,OldUniq}}} =
+ lists:keysearch(OldIndex, 1, Lambdas),
+ [{_,{M,_,_}}|_] = Lbls, % Slighly kludgy.
+ {make_fun2,{M,F,A},OldIndex,OldUniq,NumFree};
+resolve_inst(Instr, Imports, Str, Lbls, _Lambdas) ->
+ resolve_inst(Instr, Imports, Str, Lbls).
+
+resolve_inst({label,[{u,L}]},_,_,_) ->
+ {label,L};
+resolve_inst({func_info,RawMFA},_,_,_) ->
+ {func_info,resolve_args(RawMFA)};
+% resolve_inst(int_code_end,_,_,_,_) -> % instruction already handled
+% int_code_end; % should not really be handled here
+resolve_inst({call,[{u,N},{f,L}]},_,_,Lbls) ->
+ {call,N,catch lookup_key(L,Lbls)};
+resolve_inst({call_last,[{u,N},{f,L},{u,U}]},_,_,Lbls) ->
+ {call_last,N,catch lookup_key(L,Lbls),U};
+resolve_inst({call_only,[{u,N},{f,L}]},_,_,Lbls) ->
+ {call_only,N,catch lookup_key(L,Lbls)};
+resolve_inst({call_ext,[{u,N},{u,MFAix}]},Imports,_,_) ->
+ {call_ext,N,catch lists:nth(MFAix+1,Imports)};
+resolve_inst({call_ext_last,[{u,N},{u,MFAix},{u,X}]},Imports,_,_) ->
+ {call_ext_last,N,catch lists:nth(MFAix+1,Imports),X};
+resolve_inst({bif0,Args},Imports,_,_) ->
+ [Bif,Reg] = resolve_args(Args),
+ {extfunc,_Mod,BifName,_Arity} = lists:nth(Bif+1,Imports),
+ %?NO_DEBUG('bif0(~p, ~p)~n',[BifName,Reg]),
+ {bif,BifName,nofail,[],Reg};
+resolve_inst({bif1,Args},Imports,_,_) ->
+ [F,Bif,A1,Reg] = resolve_args(Args),
+ {extfunc,_Mod,BifName,_Arity} = lists:nth(Bif+1,Imports),
+ %?NO_DEBUG('bif1(~p, ~p, ~p, ~p, ~p)~n',[Bif,BifName,F,[A1],Reg]),
+ {bif,BifName,F,[A1],Reg};
+resolve_inst({bif2,Args},Imports,_,_) ->
+ [F,Bif,A1,A2,Reg] = resolve_args(Args),
+ {extfunc,_Mod,BifName,_Arity} = lists:nth(Bif+1,Imports),
+ %?NO_DEBUG('bif2(~p, ~p, ~p, ~p, ~p)~n',[Bif,BifName,F,[A1,A2],Reg]),
+ {bif,BifName,F,[A1,A2],Reg};
+resolve_inst({allocate,[{u,X0},{u,X1}]},_,_,_) ->
+ {allocate,X0,X1};
+resolve_inst({allocate_heap,[{u,X0},{u,X1},{u,X2}]},_,_,_) ->
+ {allocate_heap,X0,X1,X2};
+resolve_inst({allocate_zero,[{u,X0},{u,X1}]},_,_,_) ->
+ {allocate_zero,X0,X1};
+resolve_inst({allocate_heap_zero,[{u,X0},{u,X1},{u,X2}]},_,_,_) ->
+ {allocate_heap_zero,X0,X1,X2};
+resolve_inst({test_heap,[{u,X0},{u,X1}]},_,_,_) ->
+ {test_heap,X0,X1};
+resolve_inst({init,[Dst]},_,_,_) ->
+ {init,Dst};
+resolve_inst({deallocate,[{u,L}]},_,_,_) ->
+ {deallocate,L};
+resolve_inst({return,[]},_,_,_) ->
+ return;
+resolve_inst({send,[]},_,_,_) ->
+ send;
+resolve_inst({remove_message,[]},_,_,_) ->
+ remove_message;
+resolve_inst({timeout,[]},_,_,_) ->
+ timeout;
+resolve_inst({loop_rec,[Lbl,Dst]},_,_,_) ->
+ {loop_rec,Lbl,Dst};
+resolve_inst({loop_rec_end,[Lbl]},_,_,_) ->
+ {loop_rec_end,Lbl};
+resolve_inst({wait,[Lbl]},_,_,_) ->
+ {wait,Lbl};
+resolve_inst({wait_timeout,[Lbl,Int]},_,_,_) ->
+ {wait_timeout,Lbl,resolve_arg(Int)};
+resolve_inst({m_plus,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'+',W,[SrcR1,SrcR2],DstR};
+resolve_inst({m_minus,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'-',W,[SrcR1,SrcR2],DstR};
+resolve_inst({m_times,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'*',W,[SrcR1,SrcR2],DstR};
+resolve_inst({m_div,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'/',W,[SrcR1,SrcR2],DstR};
+resolve_inst({int_div,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'div',W,[SrcR1,SrcR2],DstR};
+resolve_inst({int_rem,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'rem',W,[SrcR1,SrcR2],DstR};
+resolve_inst({int_band,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'band',W,[SrcR1,SrcR2],DstR};
+resolve_inst({int_bor,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'bor',W,[SrcR1,SrcR2],DstR};
+resolve_inst({int_bxor,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'bxor',W,[SrcR1,SrcR2],DstR};
+resolve_inst({int_bsl,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'bsl',W,[SrcR1,SrcR2],DstR};
+resolve_inst({int_bsr,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'bsr',W,[SrcR1,SrcR2],DstR};
+resolve_inst({int_bnot,Args},_,_,_) ->
+ [W,SrcR,DstR] = resolve_args(Args),
+ {arithbif,'bnot',W,[SrcR],DstR};
+resolve_inst({is_lt=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_ge=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_eq=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_ne=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_eq_exact=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_ne_exact=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_integer=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_float=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_number=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_atom=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_pid=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_reference=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_port=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_nil=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_binary=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_constant=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_list=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_nonempty_list=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_tuple=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({test_arity=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({select_val,Args},_,_,_) ->
+ [Reg,FLbl,{{z,1},{u,_Len},List0}] = Args,
+ List = resolve_args(List0),
+ {select_val,Reg,FLbl,{list,List}};
+resolve_inst({select_tuple_arity,Args},_,_,_) ->
+ [Reg,FLbl,{{z,1},{u,_Len},List0}] = Args,
+ List = resolve_args(List0),
+ {select_tuple_arity,Reg,FLbl,{list,List}};
+resolve_inst({jump,[Lbl]},_,_,_) ->
+ {jump,Lbl};
+resolve_inst({'catch',[Dst,Lbl]},_,_,_) ->
+ {'catch',Dst,Lbl};
+resolve_inst({catch_end,[Dst]},_,_,_) ->
+ {catch_end,Dst};
+resolve_inst({move,[Src,Dst]},_,_,_) ->
+ {move,resolve_arg(Src),Dst};
+resolve_inst({get_list,[Src,Dst1,Dst2]},_,_,_) ->
+ {get_list,Src,Dst1,Dst2};
+resolve_inst({get_tuple_element,[Src,{u,Off},Dst]},_,_,_) ->
+ {get_tuple_element,resolve_arg(Src),Off,resolve_arg(Dst)};
+resolve_inst({set_tuple_element,[Src,Dst,{u,Off}]},_,_,_) ->
+ {set_tuple_element,resolve_arg(Src),resolve_arg(Dst),Off};
+resolve_inst({put_string,[{u,Len},{u,Off},Dst]},_,Strings,_) ->
+ String = if Len > 0 -> binary_to_list(Strings, Off+1, Off+Len);
+ true -> ""
+ end,
+?NO_DEBUG('put_string(~p, {string,~p}, ~p)~n',[Len,String,Dst]),
+ {put_string,Len,{string,String},Dst};
+resolve_inst({put_list,[Src1,Src2,Dst]},_,_,_) ->
+ {put_list,resolve_arg(Src1),resolve_arg(Src2),Dst};
+resolve_inst({put_tuple,[{u,Arity},Dst]},_,_,_) ->
+ {put_tuple,Arity,Dst};
+resolve_inst({put,[Src]},_,_,_) ->
+ {put,resolve_arg(Src)};
+resolve_inst({badmatch,[X]},_,_,_) ->
+ {badmatch,resolve_arg(X)};
+resolve_inst({if_end,[]},_,_,_) ->
+ if_end;
+resolve_inst({case_end,[X]},_,_,_) ->
+ {case_end,resolve_arg(X)};
+resolve_inst({call_fun,[{u,N}]},_,_,_) ->
+ {call_fun,N};
+resolve_inst({make_fun,Args},_,_,Lbls) ->
+ [{f,L},Magic,FreeVars] = resolve_args(Args),
+ {make_fun,catch lookup_key(L,Lbls),Magic,FreeVars};
+resolve_inst({is_function=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({call_ext_only,[{u,N},{u,MFAix}]},Imports,_,_) ->
+ {call_ext_only,N,catch lists:nth(MFAix+1,Imports)};
+%%
+%% Instructions for handling binaries added in R7A & R7B
+%%
+resolve_inst({bs_start_match,[F,Reg]},_,_,_) ->
+ {bs_start_match,F,Reg};
+resolve_inst({bs_get_integer=I,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
+ [A2,A5] = resolve_args([Arg2,Arg5]),
+ {test,I,Lbl,[A2,N,decode_field_flags(U),A5]};
+resolve_inst({bs_get_float=I,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
+ [A2,A5] = resolve_args([Arg2,Arg5]),
+ {test,I,Lbl,[A2,N,decode_field_flags(U),A5]};
+resolve_inst({bs_get_binary=I,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
+ [A2,A5] = resolve_args([Arg2,Arg5]),
+ {test,I,Lbl,[A2,N,decode_field_flags(U),A5]};
+resolve_inst({bs_skip_bits,[Lbl,Arg2,{u,N},{u,U}]},_,_,_) ->
+ [A2] = resolve_args([Arg2]),
+ {test,bs_skip_bits,Lbl,[A2,N,decode_field_flags(U)]};
+resolve_inst({bs_test_tail,[F,{u,N}]},_,_,_) ->
+ {test,bs_test_tail,F,[N]};
+resolve_inst({bs_save,[{u,N}]},_,_,_) ->
+ {bs_save,N};
+resolve_inst({bs_restore,[{u,N}]},_,_,_) ->
+ {bs_restore,N};
+resolve_inst({bs_init,[{u,N},{u,U}]},_,_,_) ->
+ {bs_init,N,decode_field_flags(U)};
+resolve_inst({bs_final,[F,X]},_,_,_) ->
+ {bs_final,F,X};
+resolve_inst({bs_put_integer,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
+ [A2,A5] = resolve_args([Arg2,Arg5]),
+ {bs_put_integer,Lbl,A2,N,decode_field_flags(U),A5};
+resolve_inst({bs_put_binary,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
+ [A2,A5] = resolve_args([Arg2,Arg5]),
+ ?NO_DEBUG('bs_put_binary(~p,~p,~p,~p,~p})~n',[Lbl,A2,N,U,A5]),
+ {bs_put_binary,Lbl,A2,N,decode_field_flags(U),A5};
+resolve_inst({bs_put_float,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
+ [A2,A5] = resolve_args([Arg2,Arg5]),
+ ?NO_DEBUG('bs_put_float(~p,~p,~p,~p,~p})~n',[Lbl,A2,N,U,A5]),
+ {bs_put_float,Lbl,A2,N,decode_field_flags(U),A5};
+resolve_inst({bs_put_string,[{u,Len},{u,Off}]},_,Strings,_) ->
+ String = if Len > 0 -> binary_to_list(Strings, Off+1, Off+Len);
+ true -> ""
+ end,
+ ?NO_DEBUG('bs_put_string(~p, {string,~p})~n',[Len,String]),
+ {bs_put_string,Len,{string,String}};
+resolve_inst({bs_need_buf,[{u,N}]},_,_,_) ->
+ {bs_need_buf,N};
+
+%%
+%% Instructions for handling floating point numbers added in June 2001 (R8).
+%%
+resolve_inst({fclearerror,[]},_,_,_) ->
+ fclearerror;
+resolve_inst({fcheckerror,Args},_,_,_) ->
+ [Fail] = resolve_args(Args),
+ {fcheckerror,Fail};
+resolve_inst({fmove,Args},_,_,_) ->
+ [FR,Reg] = resolve_args(Args),
+ {fmove,FR,Reg};
+resolve_inst({fconv,Args},_,_,_) ->
+ [Reg,FR] = resolve_args(Args),
+ {fconv,Reg,FR};
+resolve_inst({fadd=I,Args},_,_,_) ->
+ [F,A1,A2,Reg] = resolve_args(Args),
+ {arithfbif,I,F,[A1,A2],Reg};
+resolve_inst({fsub=I,Args},_,_,_) ->
+ [F,A1,A2,Reg] = resolve_args(Args),
+ {arithfbif,I,F,[A1,A2],Reg};
+resolve_inst({fmul=I,Args},_,_,_) ->
+ [F,A1,A2,Reg] = resolve_args(Args),
+ {arithfbif,I,F,[A1,A2],Reg};
+resolve_inst({fdiv=I,Args},_,_,_) ->
+ [F,A1,A2,Reg] = resolve_args(Args),
+ {arithfbif,I,F,[A1,A2],Reg};
+resolve_inst({fnegate,Args},_,_,_) ->
+ [F,Arg,Reg] = resolve_args(Args),
+ {arithfbif,fnegate,F,[Arg],Reg};
+
+%%
+%% Instructions for try expressions added in January 2003 (R10).
+%%
+
+resolve_inst({'try',[Reg,Lbl]},_,_,_) -> % analogous to 'catch'
+ {'try',Reg,Lbl};
+resolve_inst({try_end,[Reg]},_,_,_) -> % analogous to 'catch_end'
+ {try_end,Reg};
+resolve_inst({try_case,[Reg]},_,_,_) -> % analogous to 'catch_end'
+ {try_case,Reg};
+resolve_inst({try_case_end,[Reg]},_,_,_) ->
+ {try_case_end,Reg};
+resolve_inst({raise,[Reg1,Reg2]},_,_,_) ->
+ {bif,raise,{f,0},[Reg1,Reg2],{x,0}};
+
+%%
+%% New bit syntax instructions added in February 2004 (R10B).
+%%
+
+resolve_inst({bs_init2,[Lbl,Arg2,{u,W},{u,R},{u,F},Arg6]},_,_,_) ->
+ [A2,A6] = resolve_args([Arg2,Arg6]),
+ {bs_init2,Lbl,A2,W,R,decode_field_flags(F),A6};
+resolve_inst({bs_bits_to_bytes,[Lbl,Arg2,Arg3]},_,_,_) ->
+ [A2,A3] = resolve_args([Arg2,Arg3]),
+ {bs_bits_to_bytes,Lbl,A2,A3};
+resolve_inst({bs_add=I,[Lbl,Arg2,Arg3,Arg4,Arg5]},_,_,_) ->
+ [A2,A3,A4,A5] = resolve_args([Arg2,Arg3,Arg4,Arg5]),
+ {I,Lbl,[A2,A3,A4],A5};
+
+%%
+%% New apply instructions added in April 2004 (R10B).
+%%
+resolve_inst({apply,[{u,Arity}]},_,_,_) ->
+ {apply,Arity};
+resolve_inst({apply_last,[{u,Arity},{u,D}]},_,_,_) ->
+ {apply_last,Arity,D};
+
+%%
+%% New test instruction added in April 2004 (R10B).
+%%
+resolve_inst({is_boolean=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+
+%%
+%% Catches instructions that are not yet handled.
+%%
+
+resolve_inst(X,_,_,_) -> ?exit({resolve_inst,X}).
+
+%%-----------------------------------------------------------------------
+%% Resolves arguments in a generic way.
+%%-----------------------------------------------------------------------
+
+resolve_args(Args) -> [resolve_arg(A) || A <- Args].
+
+resolve_arg({u,N}) -> N;
+resolve_arg({i,N}) -> {integer,N};
+resolve_arg({atom,Atom}=A) when is_atom(Atom) -> A;
+resolve_arg(nil) -> nil;
+resolve_arg(Arg) -> Arg.
+
+%%-----------------------------------------------------------------------
+%% The purpose of the following is just to add a hook for future changes.
+%% Currently, field flags are numbers 1-2-4-8 and only two of these
+%% numbers (BSF_LITTLE 2 -- BSF_SIGNED 4) have a semantic significance;
+%% others are just hints for speeding up the execution; see "erl_bits.h".
+%%-----------------------------------------------------------------------
+
+decode_field_flags(FF) ->
+ {field_flags,FF}.
+
+%%-----------------------------------------------------------------------
+%% Each string is denoted in the assembled code by its offset into this
+%% binary. This binary contains all strings concatenated together.
+%%-----------------------------------------------------------------------
+
+beam_disasm_strings(Bin) ->
+ Bin.
+
+%%-----------------------------------------------------------------------
+%% Disassembles the attributes of a BEAM file.
+%%-----------------------------------------------------------------------
+
+beam_disasm_attributes(none) -> none;
+beam_disasm_attributes(AttrBin) -> binary_to_term(AttrBin).
+
+%%-----------------------------------------------------------------------
+%% Disassembles the compilation information of a BEAM file.
+%%-----------------------------------------------------------------------
+
+beam_disasm_compilation_info(none) -> none;
+beam_disasm_compilation_info(Bin) -> binary_to_term(Bin).
+
+%%-----------------------------------------------------------------------
+%% Private Utilities
+%%-----------------------------------------------------------------------
+
+%%-----------------------------------------------------------------------
+
+lookup_key(Key,[{Key,Val}|_]) ->
+ Val;
+lookup_key(Key,[_|KVs]) ->
+ lookup_key(Key,KVs);
+lookup_key(Key,[]) ->
+ ?exit({lookup_key,{key_not_found,Key}}).
+
+%%-----------------------------------------------------------------------
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_flatten.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_flatten.erl
new file mode 100644
index 0000000000..5c08c6a797
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_flatten.erl
@@ -0,0 +1,137 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_flatten.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%
+%% Purpose : Converts intermediate assembly code to final format.
+
+-module(beam_flatten).
+
+-export([module/2]).
+-import(lists, [reverse/1,reverse/2,map/2]).
+
+module({Mod,Exp,Attr,Fs,Lc}, _Opt) ->
+ {ok,{Mod,Exp,Attr,map(fun function/1, Fs),Lc}}.
+
+function({function,Name,Arity,CLabel,Is0}) ->
+ Is1 = block(Is0),
+ Is = opt(Is1),
+ {function,Name,Arity,CLabel,Is}.
+
+block(Is) ->
+ block(Is, []).
+
+block([{block,Is0}|Is1], Acc) -> block(Is1, norm_block(Is0, Acc));
+block([I|Is], Acc) -> block(Is, [I|Acc]);
+block([], Acc) -> reverse(Acc).
+
+norm_block([{allocate,R,Alloc}|Is], Acc0) ->
+ case insert_alloc_in_bs_init(Acc0, Alloc) of
+ not_possible ->
+ norm_block(Is, reverse(norm_allocate(Alloc, R), Acc0));
+ Acc ->
+ norm_block(Is, Acc)
+ end;
+norm_block([I|Is], Acc) -> norm_block(Is, [norm(I)|Acc]);
+norm_block([], Acc) -> Acc.
+
+norm({set,[D],As,{bif,N}}) -> {bif,N,nofail,As,D};
+norm({set,[D],As,{bif,N,F}}) -> {bif,N,F,As,D};
+norm({set,[D],[S],move}) -> {move,S,D};
+norm({set,[D],[S],fmove}) -> {fmove,S,D};
+norm({set,[D],[S],fconv}) -> {fconv,S,D};
+norm({set,[D],[S1,S2],put_list}) -> {put_list,S1,S2,D};
+norm({set,[D],[],{put_tuple,A}}) -> {put_tuple,A,D};
+norm({set,[],[S],put}) -> {put,S};
+norm({set,[D],[],{put_string,L,S}}) -> {put_string,L,S,D};
+norm({set,[D],[S],{get_tuple_element,I}}) -> {get_tuple_element,S,I,D};
+norm({set,[],[S,D],{set_tuple_element,I}}) -> {set_tuple_element,S,D,I};
+norm({set,[D1,D2],[S],get_list}) -> {get_list,S,D1,D2};
+norm({set,[],[],remove_message}) -> remove_message;
+norm({set,[],[],fclearerror}) -> fclearerror;
+norm({set,[],[],fcheckerror}) -> {fcheckerror,{f,0}};
+norm({'%',_}=Comment) -> Comment;
+norm({'%live',R}) -> {'%live',R}.
+
+norm_allocate({_Zero,nostack,Nh,[]}, Regs) ->
+ [{test_heap,Nh,Regs}];
+norm_allocate({_Zero,nostack,Nh,Nf,[]}, Regs) ->
+ [{test_heap,alloc_list(Nh, Nf),Regs}];
+norm_allocate({zero,0,Nh,[]}, Regs) ->
+ norm_allocate({nozero,0,Nh,[]}, Regs);
+norm_allocate({zero,0,Nh,Nf,[]}, Regs) ->
+ norm_allocate({nozero,0,Nh,Nf,[]}, Regs);
+norm_allocate({zero,Ns,0,[]}, Regs) ->
+ [{allocate_zero,Ns,Regs}];
+norm_allocate({zero,Ns,Nh,[]}, Regs) ->
+ [{allocate_heap_zero,Ns,Nh,Regs}];
+norm_allocate({nozero,Ns,0,Inits}, Regs) ->
+ [{allocate,Ns,Regs}|Inits];
+norm_allocate({nozero,Ns,Nh,Inits}, Regs) ->
+ [{allocate_heap,Ns,Nh,Regs}|Inits];
+norm_allocate({nozero,Ns,Nh,Floats,Inits}, Regs) ->
+ [{allocate_heap,Ns,alloc_list(Nh, Floats),Regs}|Inits];
+norm_allocate({zero,Ns,Nh,Floats,Inits}, Regs) ->
+ [{allocate_heap_zero,Ns,alloc_list(Nh, Floats),Regs}|Inits].
+
+insert_alloc_in_bs_init([I|_]=Is, Alloc) ->
+ case is_bs_put(I) of
+ false ->
+ not_possible;
+ true ->
+ insert_alloc_1(Is, Alloc, [])
+ end.
+
+insert_alloc_1([{bs_init2,Fail,Bs,Ws,Regs,F,Dst}|Is], {_,nostack,Nh,Nf,[]}, Acc) ->
+ Al = alloc_list(Ws+Nh, Nf),
+ I = {bs_init2,Fail,Bs,Al,Regs,F,Dst},
+ reverse(Acc, [I|Is]);
+insert_alloc_1([I|Is], Alloc, Acc) ->
+ insert_alloc_1(Is, Alloc, [I|Acc]).
+
+is_bs_put({bs_put_integer,_,_,_,_,_}) -> true;
+is_bs_put({bs_put_float,_,_,_,_,_}) -> true;
+is_bs_put({bs_put_binary,_,_,_,_,_}) -> true;
+is_bs_put({bs_put_string,_,_}) -> true;
+is_bs_put(_) -> false.
+
+alloc_list(Words, Floats) ->
+ {alloc,[{words,Words},{floats,Floats}]}.
+
+
+%% opt(Is0) -> Is
+%% Simple peep-hole optimization to move a {move,Any,{x,0}} past
+%% any kill up to the next call instruction.
+
+opt(Is) ->
+ opt_1(Is, []).
+
+opt_1([{move,_,{x,0}}=I|Is0], Acc0) ->
+ case move_past_kill(Is0, I, Acc0) of
+ impossible -> opt_1(Is0, [I|Acc0]);
+ {Is,Acc} -> opt_1(Is, Acc)
+ end;
+opt_1([I|Is], Acc) ->
+ opt_1(Is, [I|Acc]);
+opt_1([], Acc) -> reverse(Acc).
+
+move_past_kill([{'%live',_}|Is], Move, Acc) ->
+ move_past_kill(Is, Move, Acc);
+move_past_kill([{kill,Src}|_], {move,Src,_}, _) ->
+ impossible;
+move_past_kill([{kill,_}=I|Is], Move, Acc) ->
+ move_past_kill(Is, Move, [I|Acc]);
+move_past_kill(Is, Move, Acc) ->
+ {Is,[Move|Acc]}.
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_jump.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_jump.erl
new file mode 100644
index 0000000000..b3c234c7bb
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_jump.erl
@@ -0,0 +1,477 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_jump.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%
+%%% Purpose : Optimise jumps and remove unreachable code.
+
+-module(beam_jump).
+
+-export([module/2,module_labels/1,
+ is_unreachable_after/1,remove_unused_labels/1]).
+
+%%% The following optimisations are done:
+%%%
+%%% (1) This code with two identical instruction sequences
+%%%
+%%% L1:
+%%% L2:
+%%% . . .
+%%% L3:
+%%% L4:
+%%%
+%%% can be replaced with
+%%%
+%%% L1: jump L3
+%%% L2:
+%%% . . .
+%%% L3:
+%%% L4
+%%%
+%%% Note: The instruction sequence must end with an instruction
+%%% such as a jump that never transfers control to the instruction
+%%% following it.
+%%%
+%%% (2) case_end, if_end, and badmatch, and function calls that cause an
+%%% exit (such as calls to exit/1) are moved to the end of the function.
+%%% The purpose is to allow further optimizations at the place from
+%%% which the code was moved.
+%%%
+%%% (3) Any unreachable code is removed. Unreachable code is code after
+%%% jump, call_last and other instructions which never transfer control
+%%% to the following instruction. Code is unreachable up to the next
+%%% *referenced* label. Note that the optimisations below might
+%%% generate more possibilities for removing unreachable code.
+%%%
+%%% (4) This code:
+%%% L1: jump L2
+%%% . . .
+%%% L2: ...
+%%%
+%%% will be changed to
+%%%
+%%% jump L2
+%%% . . .
+%%% L1:
+%%% L2: ...
+%%%
+%%% If the jump is unreachable, it will be removed according to (1).
+%%%
+%%% (5) In
+%%%
+%%% jump L1
+%%% L1:
+%%%
+%%% the jump will be removed.
+%%%
+%%% (6) If test instructions are used to skip a single jump instruction,
+%%% the test is inverted and the jump is eliminated (provided that
+%%% the test can be inverted). Example:
+%%%
+%%% is_eq L1 {x,1} {x,2}
+%%% jump L2
+%%% L1:
+%%%
+%%% will be changed to
+%%%
+%%% is_ne L2 {x,1} {x,2}
+%%%
+%%% (The label L1 will be retained if there were previous references to it.)
+%%%
+%%% (7) Some redundant uses of is_boolean/1 is optimized away.
+%%%
+%%% Terminology note: The optimisation done here is called unreachable-code
+%%% elimination, NOT dead-code elimination. Dead code elimination
+%%% means the removal of instructions that are executed, but have no visible
+%%% effect on the program state.
+%%%
+
+-import(lists, [reverse/1,reverse/2,map/2,mapfoldl/3,foldl/3,
+ last/1,foreach/2,member/2]).
+
+module({Mod,Exp,Attr,Fs0,Lc}, _Opt) ->
+ Fs = map(fun function/1, Fs0),
+ {ok,{Mod,Exp,Attr,Fs,Lc}}.
+
+module_labels({Mod,Exp,Attr,Fs,Lc}) ->
+ {Mod,Exp,Attr,map(fun function_labels/1, Fs),Lc}.
+
+function_labels({function,Name,Arity,CLabel,Asm0}) ->
+ Asm = remove_unused_labels(Asm0),
+ {function,Name,Arity,CLabel,Asm}.
+
+function({function,Name,Arity,CLabel,Asm0}) ->
+ Asm1 = share(Asm0),
+ Asm2 = bopt(Asm1),
+ Asm3 = move(Asm2),
+ Asm4 = opt(Asm3, CLabel),
+ Asm = remove_unused_labels(Asm4),
+ {function,Name,Arity,CLabel,Asm}.
+
+%%%
+%%% (1) We try to share the code for identical code segments by replacing all
+%%% occurrences except the last with jumps to the last occurrence.
+%%%
+
+share(Is) ->
+ share_1(reverse(Is), gb_trees:empty(), [], []).
+
+share_1([{label,_}=Lbl|Is], Dict, [], Acc) ->
+ share_1(Is, Dict, [], [Lbl|Acc]);
+share_1([{label,L}=Lbl|Is], Dict0, Seq, Acc) ->
+ case is_unreachable_after(last(Seq)) of
+ false ->
+ share_1(Is, Dict0, [], [Lbl|Seq ++ Acc]);
+ true ->
+ case gb_trees:lookup(Seq, Dict0) of
+ none ->
+ Dict = gb_trees:insert(Seq, L, Dict0),
+ share_1(Is, Dict, [], [Lbl|Seq ++ Acc]);
+ {value,Label} ->
+ share_1(Is, Dict0, [], [Lbl,{jump,{f,Label}}|Acc])
+ end
+ end;
+share_1([{func_info,_,_,_}=I|Is], _, [], Acc) ->
+ Is++[I|Acc];
+share_1([I|Is], Dict, Seq, Acc) ->
+ case is_unreachable_after(I) of
+ false ->
+ share_1(Is, Dict, [I|Seq], Acc);
+ true ->
+ share_1(Is, Dict, [I], Acc)
+ end.
+
+%%%
+%%% (2) Move short code sequences ending in an instruction that causes an exit
+%%% to the end of the function.
+%%%
+
+move(Is) ->
+ move_1(Is, [], []).
+
+move_1([I|Is], End, Acc) ->
+ case is_exit_instruction(I) of
+ false -> move_1(Is, End, [I|Acc]);
+ true -> move_2(I, Is, End, Acc)
+ end;
+move_1([], End, Acc) ->
+ reverse(Acc, reverse(End)).
+
+move_2(Exit, Is, End, [{block,_},{label,_},{func_info,_,_,_}|_]=Acc) ->
+ move_1(Is, End, [Exit|Acc]);
+move_2(Exit, Is, End, [{kill,_Y}|Acc]) ->
+ move_2(Exit, Is, End, Acc);
+move_2(Exit, Is, End, [{block,_}=Blk,{label,_}=Lbl,Dead|More]=Acc) ->
+ case is_unreachable_after(Dead) of
+ false ->
+ move_1(Is, End, [Exit|Acc]);
+ true ->
+ move_1([Dead|Is], [Exit,Blk,Lbl|End], More)
+ end;
+move_2(Exit, Is, End, [{label,_}=Lbl,Dead|More]=Acc) ->
+ case is_unreachable_after(Dead) of
+ false ->
+ move_1(Is, End, [Exit|Acc]);
+ true ->
+ move_1([Dead|Is], [Exit,Lbl|End], More)
+ end;
+move_2(Exit, Is, End, Acc) ->
+ move_1(Is, End, [Exit|Acc]).
+
+%%%
+%%% (7) Remove redundant is_boolean tests.
+%%%
+
+bopt(Is) ->
+ bopt_1(Is, []).
+
+bopt_1([{test,is_boolean,_,_}=I|Is], Acc0) ->
+ case opt_is_bool(I, Acc0) of
+ no -> bopt_1(Is, [I|Acc0]);
+ yes -> bopt_1(Is, Acc0);
+ {yes,Acc} -> bopt_1(Is, Acc)
+ end;
+bopt_1([I|Is], Acc) -> bopt_1(Is, [I|Acc]);
+bopt_1([], Acc) -> reverse(Acc).
+
+opt_is_bool({test,is_boolean,{f,Lbl},[Reg]}, Acc) ->
+ opt_is_bool_1(Acc, Reg, Lbl).
+
+opt_is_bool_1([{test,is_eq_exact,{f,Lbl},[Reg,{atom,true}]}|_], Reg, Lbl) ->
+ %% Instruction not needed in this context.
+ yes;
+opt_is_bool_1([{test,is_ne_exact,{f,Lbl},[Reg,{atom,true}]}|Acc], Reg, Lbl) ->
+ %% Rewrite to shorter test.
+ {yes,[{test,is_eq_exact,{f,Lbl},[Reg,{atom,false}]}|Acc]};
+opt_is_bool_1([{test,_,{f,Lbl},_}=Test|Acc0], Reg, Lbl) ->
+ case opt_is_bool_1(Acc0, Reg, Lbl) of
+ {yes,Acc} -> {yes,[Test|Acc]};
+ Other -> Other
+ end;
+opt_is_bool_1(_, _, _) -> no.
+
+%%%
+%%% (3) (4) (5) (6) Jump and unreachable code optimizations.
+%%%
+
+-record(st, {fc, %Label for function class errors.
+ entry, %Entry label (must not be moved).
+ mlbl, %Moved labels.
+ labels %Set of referenced labels.
+ }).
+
+opt([{label,Fc}|_]=Is, CLabel) ->
+ Lbls = initial_labels(Is),
+ St = #st{fc=Fc,entry=CLabel,mlbl=dict:new(),labels=Lbls},
+ opt(Is, [], St).
+
+opt([{test,Test0,{f,Lnum}=Lbl,Ops}=I|Is0], Acc, St) ->
+ case Is0 of
+ [{jump,To}|[{label,Lnum}|Is2]=Is1] ->
+ case invert_test(Test0) of
+ not_possible ->
+ opt(Is0, [I|Acc], label_used(Lbl, St));
+ Test ->
+ Is = case is_label_used(Lnum, St) of
+ true -> Is1;
+ false -> Is2
+ end,
+ opt([{test,Test,To,Ops}|Is], Acc, label_used(To, St))
+ end;
+ _Other ->
+ opt(Is0, [I|Acc], label_used(Lbl, St))
+ end;
+opt([{select_val,_R,Fail,{list,Vls}}=I|Is], Acc, St) ->
+ skip_unreachable(Is, [I|Acc], label_used([Fail|Vls], St));
+opt([{select_tuple_arity,_R,Fail,{list,Vls}}=I|Is], Acc, St) ->
+ skip_unreachable(Is, [I|Acc], label_used([Fail|Vls], St));
+opt([{'try',_R,Lbl}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{'catch',_R,Lbl}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{label,L}=I|Is], Acc, #st{entry=L}=St) ->
+ %% NEVER move the entry label.
+ opt(Is, [I|Acc], St);
+opt([{label,L1},{jump,{f,L2}}=I|Is], [Prev|Acc], St0) ->
+ St = St0#st{mlbl=dict:append(L2, L1, St0#st.mlbl)},
+ opt([Prev,I|Is], Acc, label_used({f,L2}, St));
+opt([{label,Lbl}=I|Is], Acc, #st{mlbl=Mlbl}=St0) ->
+ case dict:find(Lbl, Mlbl) of
+ {ok,Lbls} ->
+ %% Essential to remove the list of labels from the dictionary,
+ %% since we will rescan the inserted labels. We MUST rescan.
+ St = St0#st{mlbl=dict:erase(Lbl, Mlbl)},
+ insert_labels([Lbl|Lbls], Is, Acc, St);
+ error -> opt(Is, [I|Acc], St0)
+ end;
+opt([{jump,{f,Lbl}},{label,Lbl}=I|Is], Acc, St) ->
+ opt([I|Is], Acc, St);
+opt([{jump,Lbl}=I|Is], Acc, St) ->
+ skip_unreachable(Is, [I|Acc], label_used(Lbl, St));
+opt([{loop_rec,Lbl,_R}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{bif,_Name,Lbl,_As,_R}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{bs_put_integer,Lbl,_Bits,_Unit,_Fl,_Val}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{bs_put_binary,Lbl,_Bits,_Unit,_Fl,_Val}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{bs_put_float,Lbl,_Bits,_Unit,_Fl,_Val}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{bs_final,Lbl,_R}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{bs_init2,Lbl,_,_,_,_,_}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{bs_add,Lbl,_,_}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{bs_bits_to_bytes,Lbl,_,_}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([I|Is], Acc, St) ->
+ case is_unreachable_after(I) of
+ true -> skip_unreachable(Is, [I|Acc], St);
+ false -> opt(Is, [I|Acc], St)
+ end;
+opt([], Acc, #st{fc=Fc,mlbl=Mlbl}) ->
+ Code = reverse(Acc),
+ case dict:find(Fc, Mlbl) of
+ {ok,Lbls} -> insert_fc_labels(Lbls, Mlbl, Code);
+ error -> Code
+ end.
+
+insert_fc_labels([L|Ls], Mlbl, Acc0) ->
+ Acc = [{label,L}|Acc0],
+ case dict:find(L, Mlbl) of
+ error ->
+ insert_fc_labels(Ls, Mlbl, Acc);
+ {ok,Lbls} ->
+ insert_fc_labels(Lbls++Ls, Mlbl, Acc)
+ end;
+insert_fc_labels([], _, Acc) -> Acc.
+
+%% invert_test(Test0) -> not_possible | Test
+
+invert_test(is_ge) -> is_lt;
+invert_test(is_lt) -> is_ge;
+invert_test(is_eq) -> is_ne;
+invert_test(is_ne) -> is_eq;
+invert_test(is_eq_exact) -> is_ne_exact;
+invert_test(is_ne_exact) -> is_eq_exact;
+invert_test(_) -> not_possible.
+
+insert_labels([L|Ls], Is, [{jump,{f,L}}|Acc], St) ->
+ insert_labels(Ls, [{label,L}|Is], Acc, St);
+insert_labels([L|Ls], Is, Acc, St) ->
+ insert_labels(Ls, [{label,L}|Is], Acc, St);
+insert_labels([], Is, Acc, St) ->
+ opt(Is, Acc, St).
+
+%% Skip unreachable code up to the next referenced label.
+
+skip_unreachable([{label,L}|Is], [{jump,{f,L}}|Acc], St) ->
+ opt([{label,L}|Is], Acc, St);
+skip_unreachable([{label,L}|Is], Acc, St) ->
+ case is_label_used(L, St) of
+ true -> opt([{label,L}|Is], Acc, St);
+ false -> skip_unreachable(Is, Acc, St)
+ end;
+skip_unreachable([_|Is], Acc, St) ->
+ skip_unreachable(Is, Acc, St);
+skip_unreachable([], Acc, St) ->
+ opt([], Acc, St).
+
+%% Add one or more label to the set of used labels.
+
+label_used({f,0}, St) -> St;
+label_used({f,L}, St) -> St#st{labels=gb_sets:add(L, St#st.labels)};
+label_used([H|T], St0) -> label_used(T, label_used(H, St0));
+label_used([], St) -> St;
+label_used(_Other, St) -> St.
+
+%% Test if label is used.
+
+is_label_used(L, St) ->
+ gb_sets:is_member(L, St#st.labels).
+
+%% is_unreachable_after(Instruction) -> true|false
+%% Test whether the code after Instruction is unreachable.
+
+is_unreachable_after({func_info,_M,_F,_A}) -> true;
+is_unreachable_after(return) -> true;
+is_unreachable_after({call_ext_last,_Ar,_ExtFunc,_D}) -> true;
+is_unreachable_after({call_ext_only,_Ar,_ExtFunc}) -> true;
+is_unreachable_after({call_last,_Ar,_Lbl,_D}) -> true;
+is_unreachable_after({call_only,_Ar,_Lbl}) -> true;
+is_unreachable_after({apply_last,_Ar,_N}) -> true;
+is_unreachable_after({jump,_Lbl}) -> true;
+is_unreachable_after({select_val,_R,_Lbl,_Cases}) -> true;
+is_unreachable_after({select_tuple_arity,_R,_Lbl,_Cases}) -> true;
+is_unreachable_after({loop_rec_end,_}) -> true;
+is_unreachable_after({wait,_}) -> true;
+is_unreachable_after(I) -> is_exit_instruction(I).
+
+%% is_exit_instruction(Instruction) -> true|false
+%% Test whether the instruction Instruction always
+%% causes an exit/failure.
+
+is_exit_instruction({call_ext,_,{extfunc,M,F,A}}) ->
+ is_exit_instruction_1(M, F, A);
+is_exit_instruction({call_ext_last,_,{extfunc,M,F,A},_}) ->
+ is_exit_instruction_1(M, F, A);
+is_exit_instruction({call_ext_only,_,{extfunc,M,F,A}}) ->
+ is_exit_instruction_1(M, F, A);
+is_exit_instruction(if_end) -> true;
+is_exit_instruction({case_end,_}) -> true;
+is_exit_instruction({try_case_end,_}) -> true;
+is_exit_instruction({badmatch,_}) -> true;
+is_exit_instruction(_) -> false.
+
+is_exit_instruction_1(erlang, exit, 1) -> true;
+is_exit_instruction_1(erlang, throw, 1) -> true;
+is_exit_instruction_1(erlang, error, 1) -> true;
+is_exit_instruction_1(erlang, error, 2) -> true;
+is_exit_instruction_1(erlang, fault, 1) -> true;
+is_exit_instruction_1(erlang, fault, 2) -> true;
+is_exit_instruction_1(_, _, _) -> false.
+
+%% remove_unused_labels(Instructions0) -> Instructions
+%% Remove all unused labels.
+
+remove_unused_labels(Is) ->
+ Used0 = initial_labels(Is),
+ Used = foldl(fun ulbl/2, Used0, Is),
+ rem_unused(Is, Used, []).
+
+rem_unused([{label,Lbl}=I|Is], Used, Acc) ->
+ case gb_sets:is_member(Lbl, Used) of
+ false -> rem_unused(Is, Used, Acc);
+ true -> rem_unused(Is, Used, [I|Acc])
+ end;
+rem_unused([I|Is], Used, Acc) ->
+ rem_unused(Is, Used, [I|Acc]);
+rem_unused([], _, Acc) -> reverse(Acc).
+
+initial_labels(Is) ->
+ initial_labels(Is, []).
+
+initial_labels([{label,Lbl}|Is], Acc) ->
+ initial_labels(Is, [Lbl|Acc]);
+initial_labels([{func_info,_,_,_},{label,Lbl}|_], Acc) ->
+ gb_sets:from_list([Lbl|Acc]).
+
+ulbl({test,_,Fail,_}, Used) ->
+ mark_used(Fail, Used);
+ulbl({select_val,_,Fail,{list,Vls}}, Used) ->
+ mark_used_list(Vls, mark_used(Fail, Used));
+ulbl({select_tuple_arity,_,Fail,{list,Vls}}, Used) ->
+ mark_used_list(Vls, mark_used(Fail, Used));
+ulbl({'try',_,Lbl}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({'catch',_,Lbl}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({jump,Lbl}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({loop_rec,Lbl,_}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({loop_rec_end,Lbl}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({wait,Lbl}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({wait_timeout,Lbl,_To}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({bif,_Name,Lbl,_As,_R}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({bs_init2,Lbl,_,_,_,_,_}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({bs_put_integer,Lbl,_Bits,_Unit,_Fl,_Val}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({bs_put_float,Lbl,_Bits,_Unit,_Fl,_Val}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({bs_put_binary,Lbl,_Bits,_Unit,_Fl,_Val}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({bs_final,Lbl,_}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({bs_add,Lbl,_,_}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({bs_bits_to_bytes,Lbl,_,_}, Used) ->
+ mark_used(Lbl, Used);
+ulbl(_, Used) -> Used.
+
+mark_used({f,0}, Used) -> Used;
+mark_used({f,L}, Used) -> gb_sets:add(L, Used);
+mark_used(_, Used) -> Used.
+
+mark_used_list([H|T], Used) ->
+ mark_used_list(T, mark_used(H, Used));
+mark_used_list([], Used) -> Used.
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_listing.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_listing.erl
new file mode 100644
index 0000000000..5def6816b2
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_listing.erl
@@ -0,0 +1,117 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_listing.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%
+-module(beam_listing).
+
+-export([module/2]).
+
+-include("v3_life.hrl").
+
+-import(lists, [foreach/2]).
+
+module(File, Core) when element(1, Core) == c_module ->
+ %% This is a core module.
+ io:put_chars(File, core_pp:format(Core));
+module(File, Kern) when element(1, Kern) == k_mdef ->
+ %% This is a kernel module.
+ io:put_chars(File, v3_kernel_pp:format(Kern));
+ %%io:put_chars(File, io_lib:format("~p~n", [Kern]));
+module(File, {Mod,Exp,Attr,Kern}) ->
+ %% This is output from beam_life (v3).
+ io:fwrite(File, "~w.~n~p.~n~p.~n", [Mod,Exp,Attr]),
+ foreach(fun (F) -> function(File, F) end, Kern);
+module(Stream, {Mod,Exp,Attr,Code,NumLabels}) ->
+ %% This is output from beam_codegen.
+ io:format(Stream, "{module, ~s}. %% version = ~w\n",
+ [Mod, beam_opcodes:format_number()]),
+ io:format(Stream, "\n{exports, ~p}.\n", [Exp]),
+ io:format(Stream, "\n{attributes, ~p}.\n", [Attr]),
+ io:format(Stream, "\n{labels, ~p}.\n", [NumLabels]),
+ foreach(
+ fun ({function,Name,Arity,Entry,Asm}) ->
+ io:format(Stream, "\n\n{function, ~w, ~w, ~w}.\n",
+ [Name, Arity, Entry]),
+ foreach(fun(Op) -> print_op(Stream, Op) end, Asm) end,
+ Code);
+module(Stream, {Mod,Exp,Inter}) ->
+ %% Other kinds of intermediate formats.
+ io:fwrite(Stream, "~w.~n~p.~n", [Mod,Exp]),
+ foreach(fun (F) -> io:format(Stream, "~p.\n", [F]) end, Inter);
+module(Stream, [_|_]=Fs) ->
+ %% Form-based abstract format.
+ foreach(fun (F) -> io:format(Stream, "~p.\n", [F]) end, Fs).
+
+print_op(Stream, Label) when element(1, Label) == label ->
+ io:format(Stream, " ~p.\n", [Label]);
+print_op(Stream, Op) ->
+ io:format(Stream, " ~p.\n", [Op]).
+
+function(File, {function,Name,Arity,Args,Body,Vdb}) ->
+ io:nl(File),
+ io:format(File, "function ~p/~p.\n", [Name,Arity]),
+ io:format(File, " ~p.\n", [Args]),
+ print_vdb(File, Vdb),
+ put(beam_listing_nl, true),
+ foreach(fun(F) -> format(File, F, []) end, Body),
+ nl(File),
+ erase(beam_listing_nl).
+
+format(File, #l{ke=Ke,i=I,vdb=Vdb}, Ind) ->
+ nl(File),
+ ind_format(File, Ind, "~p ", [I]),
+ print_vdb(File, Vdb),
+ nl(File),
+ format(File, Ke, Ind);
+format(File, Tuple, Ind) when is_tuple(Tuple) ->
+ ind_format(File, Ind, "{", []),
+ format_list(File, tuple_to_list(Tuple), [$\s|Ind]),
+ ind_format(File, Ind, "}", []);
+format(File, List, Ind) when is_list(List) ->
+ ind_format(File, Ind, "[", []),
+ format_list(File, List, [$\s|Ind]),
+ ind_format(File, Ind, "]", []);
+format(File, F, Ind) ->
+ ind_format(File, Ind, "~p", [F]).
+
+format_list(File, [F], Ind) ->
+ format(File, F, Ind);
+format_list(File, [F|Fs], Ind) ->
+ format(File, F, Ind),
+ ind_format(File, Ind, ",", []),
+ format_list(File, Fs, Ind);
+format_list(_, [], _) -> ok.
+
+
+print_vdb(File, [{Var,F,E}|Vs]) ->
+ io:format(File, "~p:~p..~p ", [Var,F,E]),
+ print_vdb(File, Vs);
+print_vdb(_, []) -> ok.
+
+ind_format(File, Ind, Format, Args) ->
+ case get(beam_listing_nl) of
+ true ->
+ put(beam_listing_nl, false),
+ io:put_chars(File, Ind);
+ false -> ok
+ end,
+ io:format(File, Format, Args).
+
+nl(File) ->
+ case put(beam_listing_nl, true) of
+ true -> ok;
+ false -> io:nl(File)
+ end.
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_opcodes.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_opcodes.erl
new file mode 100644
index 0000000000..a4f5fd34d2
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_opcodes.erl
@@ -0,0 +1,240 @@
+-module(beam_opcodes).
+%% Warning: Do not edit this file. It was automatically
+%% generated by 'beam_makeops' on Wed Nov 24 17:52:43 2004.
+
+-export([format_number/0]).
+-export([opcode/2,opname/1]).
+
+format_number() -> 0.
+
+opcode(label, 1) -> 1;
+opcode(func_info, 3) -> 2;
+opcode(int_code_end, 0) -> 3;
+opcode(call, 2) -> 4;
+opcode(call_last, 3) -> 5;
+opcode(call_only, 2) -> 6;
+opcode(call_ext, 2) -> 7;
+opcode(call_ext_last, 3) -> 8;
+opcode(bif0, 2) -> 9;
+opcode(bif1, 4) -> 10;
+opcode(bif2, 5) -> 11;
+opcode(allocate, 2) -> 12;
+opcode(allocate_heap, 3) -> 13;
+opcode(allocate_zero, 2) -> 14;
+opcode(allocate_heap_zero, 3) -> 15;
+opcode(test_heap, 2) -> 16;
+opcode(init, 1) -> 17;
+opcode(deallocate, 1) -> 18;
+opcode(return, 0) -> 19;
+opcode(send, 0) -> 20;
+opcode(remove_message, 0) -> 21;
+opcode(timeout, 0) -> 22;
+opcode(loop_rec, 2) -> 23;
+opcode(loop_rec_end, 1) -> 24;
+opcode(wait, 1) -> 25;
+opcode(wait_timeout, 2) -> 26;
+opcode(m_plus, 4) -> 27;
+opcode(m_minus, 4) -> 28;
+opcode(m_times, 4) -> 29;
+opcode(m_div, 4) -> 30;
+opcode(int_div, 4) -> 31;
+opcode(int_rem, 4) -> 32;
+opcode(int_band, 4) -> 33;
+opcode(int_bor, 4) -> 34;
+opcode(int_bxor, 4) -> 35;
+opcode(int_bsl, 4) -> 36;
+opcode(int_bsr, 4) -> 37;
+opcode(int_bnot, 3) -> 38;
+opcode(is_lt, 3) -> 39;
+opcode(is_ge, 3) -> 40;
+opcode(is_eq, 3) -> 41;
+opcode(is_ne, 3) -> 42;
+opcode(is_eq_exact, 3) -> 43;
+opcode(is_ne_exact, 3) -> 44;
+opcode(is_integer, 2) -> 45;
+opcode(is_float, 2) -> 46;
+opcode(is_number, 2) -> 47;
+opcode(is_atom, 2) -> 48;
+opcode(is_pid, 2) -> 49;
+opcode(is_reference, 2) -> 50;
+opcode(is_port, 2) -> 51;
+opcode(is_nil, 2) -> 52;
+opcode(is_binary, 2) -> 53;
+opcode(is_constant, 2) -> 54;
+opcode(is_list, 2) -> 55;
+opcode(is_nonempty_list, 2) -> 56;
+opcode(is_tuple, 2) -> 57;
+opcode(test_arity, 3) -> 58;
+opcode(select_val, 3) -> 59;
+opcode(select_tuple_arity, 3) -> 60;
+opcode(jump, 1) -> 61;
+opcode('catch', 2) -> 62;
+opcode(catch_end, 1) -> 63;
+opcode(move, 2) -> 64;
+opcode(get_list, 3) -> 65;
+opcode(get_tuple_element, 3) -> 66;
+opcode(set_tuple_element, 3) -> 67;
+opcode(put_string, 3) -> 68;
+opcode(put_list, 3) -> 69;
+opcode(put_tuple, 2) -> 70;
+opcode(put, 1) -> 71;
+opcode(badmatch, 1) -> 72;
+opcode(if_end, 0) -> 73;
+opcode(case_end, 1) -> 74;
+opcode(call_fun, 1) -> 75;
+opcode(make_fun, 3) -> 76;
+opcode(is_function, 2) -> 77;
+opcode(call_ext_only, 2) -> 78;
+opcode(bs_start_match, 2) -> 79;
+opcode(bs_get_integer, 5) -> 80;
+opcode(bs_get_float, 5) -> 81;
+opcode(bs_get_binary, 5) -> 82;
+opcode(bs_skip_bits, 4) -> 83;
+opcode(bs_test_tail, 2) -> 84;
+opcode(bs_save, 1) -> 85;
+opcode(bs_restore, 1) -> 86;
+opcode(bs_init, 2) -> 87;
+opcode(bs_final, 2) -> 88;
+opcode(bs_put_integer, 5) -> 89;
+opcode(bs_put_binary, 5) -> 90;
+opcode(bs_put_float, 5) -> 91;
+opcode(bs_put_string, 2) -> 92;
+opcode(bs_need_buf, 1) -> 93;
+opcode(fclearerror, 0) -> 94;
+opcode(fcheckerror, 1) -> 95;
+opcode(fmove, 2) -> 96;
+opcode(fconv, 2) -> 97;
+opcode(fadd, 4) -> 98;
+opcode(fsub, 4) -> 99;
+opcode(fmul, 4) -> 100;
+opcode(fdiv, 4) -> 101;
+opcode(fnegate, 3) -> 102;
+opcode(make_fun2, 1) -> 103;
+opcode('try', 2) -> 104;
+opcode(try_end, 1) -> 105;
+opcode(try_case, 1) -> 106;
+opcode(try_case_end, 1) -> 107;
+opcode(raise, 2) -> 108;
+opcode(bs_init2, 6) -> 109;
+opcode(bs_bits_to_bytes, 3) -> 110;
+opcode(bs_add, 5) -> 111;
+opcode(apply, 1) -> 112;
+opcode(apply_last, 2) -> 113;
+opcode(is_boolean, 2) -> 114;
+opcode(Name, Arity) -> erlang:error(badarg, [Name,Arity]).
+
+opname(1) -> {label,1};
+opname(2) -> {func_info,3};
+opname(3) -> {int_code_end,0};
+opname(4) -> {call,2};
+opname(5) -> {call_last,3};
+opname(6) -> {call_only,2};
+opname(7) -> {call_ext,2};
+opname(8) -> {call_ext_last,3};
+opname(9) -> {bif0,2};
+opname(10) -> {bif1,4};
+opname(11) -> {bif2,5};
+opname(12) -> {allocate,2};
+opname(13) -> {allocate_heap,3};
+opname(14) -> {allocate_zero,2};
+opname(15) -> {allocate_heap_zero,3};
+opname(16) -> {test_heap,2};
+opname(17) -> {init,1};
+opname(18) -> {deallocate,1};
+opname(19) -> {return,0};
+opname(20) -> {send,0};
+opname(21) -> {remove_message,0};
+opname(22) -> {timeout,0};
+opname(23) -> {loop_rec,2};
+opname(24) -> {loop_rec_end,1};
+opname(25) -> {wait,1};
+opname(26) -> {wait_timeout,2};
+opname(27) -> {m_plus,4};
+opname(28) -> {m_minus,4};
+opname(29) -> {m_times,4};
+opname(30) -> {m_div,4};
+opname(31) -> {int_div,4};
+opname(32) -> {int_rem,4};
+opname(33) -> {int_band,4};
+opname(34) -> {int_bor,4};
+opname(35) -> {int_bxor,4};
+opname(36) -> {int_bsl,4};
+opname(37) -> {int_bsr,4};
+opname(38) -> {int_bnot,3};
+opname(39) -> {is_lt,3};
+opname(40) -> {is_ge,3};
+opname(41) -> {is_eq,3};
+opname(42) -> {is_ne,3};
+opname(43) -> {is_eq_exact,3};
+opname(44) -> {is_ne_exact,3};
+opname(45) -> {is_integer,2};
+opname(46) -> {is_float,2};
+opname(47) -> {is_number,2};
+opname(48) -> {is_atom,2};
+opname(49) -> {is_pid,2};
+opname(50) -> {is_reference,2};
+opname(51) -> {is_port,2};
+opname(52) -> {is_nil,2};
+opname(53) -> {is_binary,2};
+opname(54) -> {is_constant,2};
+opname(55) -> {is_list,2};
+opname(56) -> {is_nonempty_list,2};
+opname(57) -> {is_tuple,2};
+opname(58) -> {test_arity,3};
+opname(59) -> {select_val,3};
+opname(60) -> {select_tuple_arity,3};
+opname(61) -> {jump,1};
+opname(62) -> {'catch',2};
+opname(63) -> {catch_end,1};
+opname(64) -> {move,2};
+opname(65) -> {get_list,3};
+opname(66) -> {get_tuple_element,3};
+opname(67) -> {set_tuple_element,3};
+opname(68) -> {put_string,3};
+opname(69) -> {put_list,3};
+opname(70) -> {put_tuple,2};
+opname(71) -> {put,1};
+opname(72) -> {badmatch,1};
+opname(73) -> {if_end,0};
+opname(74) -> {case_end,1};
+opname(75) -> {call_fun,1};
+opname(76) -> {make_fun,3};
+opname(77) -> {is_function,2};
+opname(78) -> {call_ext_only,2};
+opname(79) -> {bs_start_match,2};
+opname(80) -> {bs_get_integer,5};
+opname(81) -> {bs_get_float,5};
+opname(82) -> {bs_get_binary,5};
+opname(83) -> {bs_skip_bits,4};
+opname(84) -> {bs_test_tail,2};
+opname(85) -> {bs_save,1};
+opname(86) -> {bs_restore,1};
+opname(87) -> {bs_init,2};
+opname(88) -> {bs_final,2};
+opname(89) -> {bs_put_integer,5};
+opname(90) -> {bs_put_binary,5};
+opname(91) -> {bs_put_float,5};
+opname(92) -> {bs_put_string,2};
+opname(93) -> {bs_need_buf,1};
+opname(94) -> {fclearerror,0};
+opname(95) -> {fcheckerror,1};
+opname(96) -> {fmove,2};
+opname(97) -> {fconv,2};
+opname(98) -> {fadd,4};
+opname(99) -> {fsub,4};
+opname(100) -> {fmul,4};
+opname(101) -> {fdiv,4};
+opname(102) -> {fnegate,3};
+opname(103) -> {make_fun2,1};
+opname(104) -> {'try',2};
+opname(105) -> {try_end,1};
+opname(106) -> {try_case,1};
+opname(107) -> {try_case_end,1};
+opname(108) -> {raise,2};
+opname(109) -> {bs_init2,6};
+opname(110) -> {bs_bits_to_bytes,3};
+opname(111) -> {bs_add,5};
+opname(112) -> {apply,1};
+opname(113) -> {apply_last,2};
+opname(114) -> {is_boolean,2};
+opname(Number) -> erlang:error(badarg, [Number]).
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_opcodes.hrl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_opcodes.hrl
new file mode 100644
index 0000000000..a330a68f37
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_opcodes.hrl
@@ -0,0 +1,11 @@
+%% Warning: Do not edit this file. It was automatically
+%% generated by 'beam_makeops' on Wed Nov 24 17:52:43 2004.
+
+-define(tag_u, 0).
+-define(tag_i, 1).
+-define(tag_a, 2).
+-define(tag_x, 3).
+-define(tag_y, 4).
+-define(tag_f, 5).
+-define(tag_h, 6).
+-define(tag_z, 7).
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_type.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_type.erl
new file mode 100644
index 0000000000..d2ac3fcd99
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_type.erl
@@ -0,0 +1,551 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_type.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%
+%% Purpose : Type-based optimisations.
+
+-module(beam_type).
+
+-export([module/2]).
+
+-import(lists, [map/2,foldl/3,reverse/1,reverse/2,filter/2,member/2]).
+
+module({Mod,Exp,Attr,Fs0,Lc}, Opt) ->
+ AllowFloatOpts = not member(no_float_opt, Opt),
+ Fs = map(fun(F) -> function(F, AllowFloatOpts) end, Fs0),
+ {ok,{Mod,Exp,Attr,Fs,Lc}}.
+
+function({function,Name,Arity,CLabel,Asm0}, AllowFloatOpts) ->
+ Asm = opt(Asm0, AllowFloatOpts, [], tdb_new()),
+ {function,Name,Arity,CLabel,Asm}.
+
+%% opt([Instruction], AllowFloatOpts, Accumulator, TypeDb) -> {[Instruction'],TypeDb'}
+%% Keep track of type information; try to simplify.
+
+opt([{block,Body1}|Is], AllowFloatOpts, [{block,Body0}|Acc], Ts0) ->
+ {Body2,Ts} = simplify(Body1, Ts0, AllowFloatOpts),
+ Body = beam_block:merge_blocks(Body0, Body2),
+ opt(Is, AllowFloatOpts, [{block,Body}|Acc], Ts);
+opt([{block,Body0}|Is], AllowFloatOpts, Acc, Ts0) ->
+ {Body,Ts} = simplify(Body0, Ts0, AllowFloatOpts),
+ opt(Is, AllowFloatOpts, [{block,Body}|Acc], Ts);
+opt([I0|Is], AllowFloatOpts, Acc, Ts0) ->
+ case simplify([I0], Ts0, AllowFloatOpts) of
+ {[],Ts} -> opt(Is, AllowFloatOpts, Acc, Ts);
+ {[I],Ts} -> opt(Is, AllowFloatOpts, [I|Acc], Ts)
+ end;
+opt([], _, Acc, _) -> reverse(Acc).
+
+%% simplify(Instruction, TypeDb, AllowFloatOpts) -> NewInstruction
+%% Simplify an instruction using type information (this is
+%% technically a "strength reduction").
+
+simplify(Is, TypeDb, false) ->
+ simplify(Is, TypeDb, no_float_opt, []);
+simplify(Is, TypeDb, true) ->
+ case are_live_regs_determinable(Is) of
+ false -> simplify(Is, TypeDb, no_float_opt, []);
+ true -> simplify(Is, TypeDb, [], [])
+ end.
+
+simplify([{set,[D],[{integer,Index},Reg],{bif,element,_}}=I0|Is]=Is0, Ts0, Rs0, Acc0) ->
+ I = case max_tuple_size(Reg, Ts0) of
+ Sz when 0 < Index, Index =< Sz ->
+ {set,[D],[Reg],{get_tuple_element,Index-1}};
+ _Other -> I0
+ end,
+ Ts = update(I, Ts0),
+ {Rs,Acc} = flush(Rs0, Is0, Acc0),
+ simplify(Is, Ts, Rs, [I|checkerror(Acc)]);
+simplify([{set,[D0],[A],{bif,'-',{f,0}}}=I|Is]=Is0, Ts0, Rs0, Acc0)
+ when Rs0 =/= no_float_opt ->
+ case tdb_find(A, Ts0) of
+ float ->
+ {Rs1,Acc1} = load_reg(A, Ts0, Rs0, Acc0),
+ {D,Rs} = find_dest(D0, Rs1),
+ Areg = fetch_reg(A, Rs),
+ Acc = [{set,[D],[Areg],{bif,fnegate,{f,0}}}|clearerror(Acc1)],
+ Ts = tdb_update([{D0,float}], Ts0),
+ simplify(Is, Ts, Rs, Acc);
+ _Other ->
+ Ts = update(I, Ts0),
+ {Rs,Acc} = flush(Rs0, Is0, Acc0),
+ simplify(Is, Ts, Rs, [I|checkerror(Acc)])
+ end;
+simplify([{set,[_],[_],{bif,_,{f,0}}}=I|Is]=Is0, Ts0, Rs0, Acc0) ->
+ Ts = update(I, Ts0),
+ {Rs,Acc} = flush(Rs0, Is0, Acc0),
+ simplify(Is, Ts, Rs, [I|checkerror(Acc)]);
+simplify([{set,[D0],[A,B],{bif,Op0,{f,0}}}=I|Is]=Is0, Ts0, Rs0, Acc0)
+ when Rs0 =/= no_float_opt ->
+ case float_op(Op0, A, B, Ts0) of
+ no ->
+ Ts = update(I, Ts0),
+ {Rs,Acc} = flush(Rs0, Is0, Acc0),
+ simplify(Is, Ts, Rs, [I|checkerror(Acc)]);
+ {yes,Op} ->
+ {Rs1,Acc1} = load_reg(A, Ts0, Rs0, Acc0),
+ {Rs2,Acc2} = load_reg(B, Ts0, Rs1, Acc1),
+ {D,Rs} = find_dest(D0, Rs2),
+ Areg = fetch_reg(A, Rs),
+ Breg = fetch_reg(B, Rs),
+ Acc = [{set,[D],[Areg,Breg],{bif,Op,{f,0}}}|clearerror(Acc2)],
+ Ts = tdb_update([{D0,float}], Ts0),
+ simplify(Is, Ts, Rs, Acc)
+ end;
+simplify([{set,[D],[TupleReg],{get_tuple_element,0}}=I|Is0], Ts0, Rs0, Acc0) ->
+ case tdb_find(TupleReg, Ts0) of
+ {tuple,_,[Contents]} ->
+ Ts = tdb_update([{D,Contents}], Ts0),
+ {Rs,Acc} = flush(Rs0, Is0, Acc0),
+ simplify(Is0, Ts, Rs, [{set,[D],[Contents],move}|Acc]);
+ _ ->
+ Ts = update(I, Ts0),
+ {Rs,Acc} = flush(Rs0, Is0, Acc0),
+ simplify(Is0, Ts, Rs, [I|checkerror(Acc)])
+ end;
+simplify([{set,_,_,{'catch',_}}=I|Is]=Is0, _Ts, Rs0, Acc0) ->
+ Acc = flush_all(Rs0, Is0, Acc0),
+ simplify(Is, tdb_new(), Rs0, [I|Acc]);
+simplify([{test,is_tuple,_,[R]}=I|Is], Ts, Rs, Acc) ->
+ case tdb_find(R, Ts) of
+ {tuple,_,_} -> simplify(Is, Ts, Rs, Acc);
+ _ ->
+ simplify(Is, Ts, Rs, [I|Acc])
+ end;
+simplify([{test,test_arity,_,[R,Arity]}=I|Is], Ts0, Rs, Acc) ->
+ case tdb_find(R, Ts0) of
+ {tuple,Arity,_} ->
+ simplify(Is, Ts0, Rs, Acc);
+ _Other ->
+ Ts = update(I, Ts0),
+ simplify(Is, Ts, Rs, [I|Acc])
+ end;
+simplify([{test,is_eq_exact,Fail,[R,{atom,_}=Atom]}=I|Is0], Ts0, Rs0, Acc0) ->
+ Acc1 = case tdb_find(R, Ts0) of
+ {atom,_}=Atom -> Acc0;
+ {atom,_} -> [{jump,Fail}|Acc0];
+ _ -> [I|Acc0]
+ end,
+ Ts = update(I, Ts0),
+ {Rs,Acc} = flush(Rs0, Is0, Acc1),
+ simplify(Is0, Ts, Rs, Acc);
+simplify([I|Is]=Is0, Ts0, Rs0, Acc0) ->
+ Ts = update(I, Ts0),
+ {Rs,Acc} = flush(Rs0, Is0, Acc0),
+ simplify(Is, Ts, Rs, [I|Acc]);
+simplify([], Ts, Rs, Acc) ->
+ Is0 = reverse(flush_all(Rs, [], Acc)),
+ Is1 = opt_fmoves(Is0, []),
+ Is = add_ftest_heap(Is1),
+ {Is,Ts}.
+
+opt_fmoves([{set,[{x,_}=R],[{fr,_}]=Src,fmove}=I1,
+ {set,[{y,_}]=Dst,[{x,_}=R],move}=I2|Is], Acc) ->
+ case beam_block:is_killed(R, Is) of
+ false -> opt_fmoves(Is, [I2,I1|Acc]);
+ true -> opt_fmoves(Is, [{set,Dst,Src,fmove}|Acc])
+ end;
+opt_fmoves([I|Is], Acc) ->
+ opt_fmoves(Is, [I|Acc]);
+opt_fmoves([], Acc) -> reverse(Acc).
+
+clearerror(Is) ->
+ clearerror(Is, Is).
+
+clearerror([{set,[],[],fclearerror}|_], OrigIs) -> OrigIs;
+clearerror([{set,[],[],fcheckerror}|_], OrigIs) -> [{set,[],[],fclearerror}|OrigIs];
+clearerror([_|Is], OrigIs) -> clearerror(Is, OrigIs);
+clearerror([], OrigIs) -> [{set,[],[],fclearerror}|OrigIs].
+
+%% update(Instruction, TypeDb) -> NewTypeDb
+%% Update the type database to account for executing an instruction.
+%%
+%% First the cases for instructions inside basic blocks.
+update({set,[D],[S],move}, Ts0) ->
+ Ops = case tdb_find(S, Ts0) of
+ error -> [{D,kill}];
+ Info -> [{D,Info}]
+ end,
+ tdb_update(Ops, Ts0);
+update({set,[D],[{integer,I},Reg],{bif,element,_}}, Ts0) ->
+ tdb_update([{Reg,{tuple,I,[]}},{D,kill}], Ts0);
+update({set,[D],[_Index,Reg],{bif,element,_}}, Ts0) ->
+ tdb_update([{Reg,{tuple,0,[]}},{D,kill}], Ts0);
+update({set,[D],[S],{get_tuple_element,0}}, Ts) ->
+ tdb_update([{D,{tuple_element,S,0}}], Ts);
+update({set,[D],[S],{bif,float,{f,0}}}, Ts0) ->
+ %% Make sure we reject non-numeric literal argument.
+ case possibly_numeric(S) of
+ true -> tdb_update([{D,float}], Ts0);
+ false -> Ts0
+ end;
+update({set,[D],[S1,S2],{bif,'/',{f,0}}}, Ts0) ->
+ %% Make sure we reject non-numeric literals.
+ case possibly_numeric(S1) andalso possibly_numeric(S2) of
+ true -> tdb_update([{D,float}], Ts0);
+ false -> Ts0
+ end;
+update({set,[D],[S1,S2],{bif,Op,{f,0}}}, Ts0) ->
+ case arith_op(Op) of
+ no ->
+ tdb_update([{D,kill}], Ts0);
+ {yes,_} ->
+ case {tdb_find(S1, Ts0),tdb_find(S2, Ts0)} of
+ {float,_} -> tdb_update([{D,float}], Ts0);
+ {_,float} -> tdb_update([{D,float}], Ts0);
+ {_,_} -> tdb_update([{D,kill}], Ts0)
+ end
+ end;
+update({set,[],_Src,_Op}, Ts0) -> Ts0;
+update({set,[D],_Src,_Op}, Ts0) ->
+ tdb_update([{D,kill}], Ts0);
+update({set,[D1,D2],_Src,_Op}, Ts0) ->
+ tdb_update([{D1,kill},{D2,kill}], Ts0);
+update({allocate,_,_}, Ts) -> Ts;
+update({init,D}, Ts) ->
+ tdb_update([{D,kill}], Ts);
+update({kill,D}, Ts) ->
+ tdb_update([{D,kill}], Ts);
+update({'%live',_}, Ts) -> Ts;
+
+%% Instructions outside of blocks.
+update({test,is_float,_Fail,[Src]}, Ts0) ->
+ tdb_update([{Src,float}], Ts0);
+update({test,test_arity,_Fail,[Src,Arity]}, Ts0) ->
+ tdb_update([{Src,{tuple,Arity,[]}}], Ts0);
+update({test,is_eq_exact,_,[Reg,{atom,_}=Atom]}, Ts) ->
+ case tdb_find(Reg, Ts) of
+ error ->
+ Ts;
+ {tuple_element,TupleReg,0} ->
+ tdb_update([{TupleReg,{tuple,1,[Atom]}}], Ts);
+ _ ->
+ Ts
+ end;
+update({test,_Test,_Fail,_Other}, Ts) -> Ts;
+update({call_ext,1,{extfunc,math,Math,1}}, Ts) ->
+ case is_math_bif(Math, 1) of
+ true -> tdb_update([{{x,0},float}], Ts);
+ false -> tdb_kill_xregs(Ts)
+ end;
+update({call_ext,2,{extfunc,math,Math,2}}, Ts) ->
+ case is_math_bif(Math, 2) of
+ true -> tdb_update([{{x,0},float}], Ts);
+ false -> tdb_kill_xregs(Ts)
+ end;
+update({call_ext,3,{extfunc,erlang,setelement,3}}, Ts0) ->
+ Op = case tdb_find({x,1}, Ts0) of
+ error -> kill;
+ Info -> Info
+ end,
+ Ts1 = tdb_kill_xregs(Ts0),
+ tdb_update([{{x,0},Op}], Ts1);
+update({call,_Arity,_Func}, Ts) -> tdb_kill_xregs(Ts);
+update({call_ext,_Arity,_Func}, Ts) -> tdb_kill_xregs(Ts);
+update({make_fun2,_,_,_,_}, Ts) -> tdb_kill_xregs(Ts);
+
+%% The instruction is unknown. Kill all information.
+update(_I, _Ts) -> tdb_new().
+
+is_math_bif(cos, 1) -> true;
+is_math_bif(cosh, 1) -> true;
+is_math_bif(sin, 1) -> true;
+is_math_bif(sinh, 1) -> true;
+is_math_bif(tan, 1) -> true;
+is_math_bif(tanh, 1) -> true;
+is_math_bif(acos, 1) -> true;
+is_math_bif(acosh, 1) -> true;
+is_math_bif(asin, 1) -> true;
+is_math_bif(asinh, 1) -> true;
+is_math_bif(atan, 1) -> true;
+is_math_bif(atanh, 1) -> true;
+is_math_bif(erf, 1) -> true;
+is_math_bif(erfc, 1) -> true;
+is_math_bif(exp, 1) -> true;
+is_math_bif(log, 1) -> true;
+is_math_bif(log10, 1) -> true;
+is_math_bif(sqrt, 1) -> true;
+is_math_bif(atan2, 2) -> true;
+is_math_bif(pow, 2) -> true;
+is_math_bif(pi, 0) -> true;
+is_math_bif(_, _) -> false.
+
+%% Reject non-numeric literals.
+possibly_numeric({x,_}) -> true;
+possibly_numeric({y,_}) -> true;
+possibly_numeric({integer,_}) -> true;
+possibly_numeric({float,_}) -> true;
+possibly_numeric(_) -> false.
+
+max_tuple_size(Reg, Ts) ->
+ case tdb_find(Reg, Ts) of
+ {tuple,Sz,_} -> Sz;
+ _Other -> 0
+ end.
+
+float_op('/', A, B, _) ->
+ case possibly_numeric(A) andalso possibly_numeric(B) of
+ true -> {yes,fdiv};
+ false -> no
+ end;
+float_op(Op, {float,_}, B, _) ->
+ case possibly_numeric(B) of
+ true -> arith_op(Op);
+ false -> no
+ end;
+float_op(Op, A, {float,_}, _) ->
+ case possibly_numeric(A) of
+ true -> arith_op(Op);
+ false -> no
+ end;
+float_op(Op, A, B, Ts) ->
+ case {tdb_find(A, Ts),tdb_find(B, Ts)} of
+ {float,_} -> arith_op(Op);
+ {_,float} -> arith_op(Op);
+ {_,_} -> no
+ end.
+
+find_dest(V, Rs0) ->
+ case find_reg(V, Rs0) of
+ {ok,FR} ->
+ {FR,mark(V, Rs0, dirty)};
+ error ->
+ Rs = put_reg(V, Rs0, dirty),
+ {ok,FR} = find_reg(V, Rs),
+ {FR,Rs}
+ end.
+
+load_reg({float,_}=F, _, Rs0, Is0) ->
+ Rs = put_reg(F, Rs0, clean),
+ {ok,FR} = find_reg(F, Rs),
+ Is = [{set,[FR],[F],fmove}|Is0],
+ {Rs,Is};
+load_reg(V, Ts, Rs0, Is0) ->
+ case find_reg(V, Rs0) of
+ {ok,_FR} -> {Rs0,Is0};
+ error ->
+ Rs = put_reg(V, Rs0, clean),
+ {ok,FR} = find_reg(V, Rs),
+ Op = case tdb_find(V, Ts) of
+ float -> fmove;
+ _ -> fconv
+ end,
+ Is = [{set,[FR],[V],Op}|Is0],
+ {Rs,Is}
+ end.
+
+arith_op('+') -> {yes,fadd};
+arith_op('-') -> {yes,fsub};
+arith_op('*') -> {yes,fmul};
+arith_op('/') -> {yes,fdiv};
+arith_op(_) -> no.
+
+flush(no_float_opt, _, Acc) -> {no_float_opt,Acc};
+flush(Rs, [{set,[_],[],{put_tuple,_}}|_]=Is0, Acc0) ->
+ Acc = flush_all(Rs, Is0, Acc0),
+ {[],Acc};
+flush(Rs0, [{set,Ds,Ss,_Op}|_], Acc0) ->
+ Save = gb_sets:from_list(Ss),
+ Acc = save_regs(Rs0, Save, Acc0),
+ Rs1 = foldl(fun(S, A) -> mark(S, A, clean) end, Rs0, Ss),
+ Kill = gb_sets:from_list(Ds),
+ Rs = kill_regs(Rs1, Kill),
+ {Rs,Acc};
+flush(Rs0, Is, Acc0) ->
+ Acc = flush_all(Rs0, Is, Acc0),
+ {[],Acc}.
+
+flush_all(no_float_opt, _, Acc) -> Acc;
+flush_all([{_,{float,_},_}|Rs], Is, Acc) ->
+ flush_all(Rs, Is, Acc);
+flush_all([{I,V,dirty}|Rs], Is, Acc0) ->
+ Acc = checkerror(Acc0),
+ case beam_block:is_killed(V, Is) of
+ true -> flush_all(Rs, Is, Acc);
+ false -> flush_all(Rs, Is, [{set,[V],[{fr,I}],fmove}|Acc])
+ end;
+flush_all([{_,_,clean}|Rs], Is, Acc) -> flush_all(Rs, Is, Acc);
+flush_all([free|Rs], Is, Acc) -> flush_all(Rs, Is, Acc);
+flush_all([], _, Acc) -> Acc.
+
+save_regs(Rs, Save, Acc) ->
+ foldl(fun(R, A) -> save_reg(R, Save, A) end, Acc, Rs).
+
+save_reg({I,V,dirty}, Save, Acc) ->
+ case gb_sets:is_member(V, Save) of
+ true -> [{set,[V],[{fr,I}],fmove}|checkerror(Acc)];
+ false -> Acc
+ end;
+save_reg(_, _, Acc) -> Acc.
+
+kill_regs(Rs, Kill) ->
+ map(fun(R) -> kill_reg(R, Kill) end, Rs).
+
+kill_reg({_,V,_}=R, Kill) ->
+ case gb_sets:is_member(V, Kill) of
+ true -> free;
+ false -> R
+ end;
+kill_reg(R, _) -> R.
+
+mark(V, [{I,V,_}|Rs], Mark) -> [{I,V,Mark}|Rs];
+mark(V, [R|Rs], Mark) -> [R|mark(V, Rs, Mark)];
+mark(_, [], _) -> [].
+
+fetch_reg(V, [{I,V,_}|_]) -> {fr,I};
+fetch_reg(V, [_|SRs]) -> fetch_reg(V, SRs).
+
+find_reg(V, [{I,V,_}|_]) -> {ok,{fr,I}};
+find_reg(V, [_|SRs]) -> find_reg(V, SRs);
+find_reg(_, []) -> error.
+
+put_reg(V, Rs, Dirty) -> put_reg_1(V, Rs, Dirty, 0).
+
+put_reg_1(V, [free|Rs], Dirty, I) -> [{I,V,Dirty}|Rs];
+put_reg_1(V, [R|Rs], Dirty, I) -> [R|put_reg_1(V, Rs, Dirty, I+1)];
+put_reg_1(V, [], Dirty, I) -> [{I,V,Dirty}].
+
+checkerror(Is) ->
+ checkerror_1(Is, Is).
+
+checkerror_1([{set,[],[],fcheckerror}|_], OrigIs) -> OrigIs;
+checkerror_1([{set,[],[],fclearerror}|_], OrigIs) -> OrigIs;
+checkerror_1([{set,_,_,{bif,fadd,_}}|_], OrigIs) -> checkerror_2(OrigIs);
+checkerror_1([{set,_,_,{bif,fsub,_}}|_], OrigIs) -> checkerror_2(OrigIs);
+checkerror_1([{set,_,_,{bif,fmul,_}}|_], OrigIs) -> checkerror_2(OrigIs);
+checkerror_1([{set,_,_,{bif,fdiv,_}}|_], OrigIs) -> checkerror_2(OrigIs);
+checkerror_1([{set,_,_,{bif,fnegate,_}}|_], OrigIs) -> checkerror_2(OrigIs);
+checkerror_1([_|Is], OrigIs) -> checkerror_1(Is, OrigIs);
+checkerror_1([], OrigIs) -> OrigIs.
+
+checkerror_2(OrigIs) -> [{set,[],[],fcheckerror}|OrigIs].
+
+add_ftest_heap(Is) ->
+ add_ftest_heap_1(reverse(Is), 0, []).
+
+add_ftest_heap_1([{set,_,[{fr,_}],fmove}=I|Is], Floats, Acc) ->
+ add_ftest_heap_1(Is, Floats+1, [I|Acc]);
+add_ftest_heap_1([{allocate,_,_}=I|Is], 0, Acc) ->
+ reverse(Is, [I|Acc]);
+add_ftest_heap_1([{allocate,Regs,{Z,Stk,Heap,Inits}}|Is], Floats, Acc) ->
+ reverse(Is, [{allocate,Regs,{Z,Stk,Heap,Floats,Inits}}|Acc]);
+add_ftest_heap_1([I|Is], Floats, Acc) ->
+ add_ftest_heap_1(Is, Floats, [I|Acc]);
+add_ftest_heap_1([], 0, Acc) ->
+ Acc;
+add_ftest_heap_1([], Floats, Is) ->
+ Regs = beam_block:live_at_entry(Is),
+ [{allocate,Regs,{nozero,nostack,0,Floats,[]}}|Is].
+
+are_live_regs_determinable([{allocate,_,_}|_]) -> true;
+are_live_regs_determinable([{'%live',_}|_]) -> true;
+are_live_regs_determinable([_|Is]) -> are_live_regs_determinable(Is);
+are_live_regs_determinable([]) -> false.
+
+
+%%% Routines for maintaining a type database. The type database
+%%% associates type information with registers.
+%%%
+%%% {tuple,Size,First} means that the corresponding register contains a
+%%% tuple with *at least* Size elements. An tuple with unknown
+%%% size is represented as {tuple,0}. First is either [] (meaning that
+%%% the tuple's first element is unknown) or [FirstElement] (the contents
+%%% of the first element).
+%%%
+%%% 'float' means that the register contains a float.
+
+%% tdb_new() -> EmptyDataBase
+%% Creates a new, empty type database.
+
+tdb_new() -> [].
+
+%% tdb_find(Register, Db) -> Information|error
+%% Returns type information or the atom error if there are no type
+%% information available for Register.
+
+tdb_find(Key, [{K,_}|_]) when Key < K -> error;
+tdb_find(Key, [{Key,Info}|_]) -> Info;
+tdb_find(Key, [_|Db]) -> tdb_find(Key, Db);
+tdb_find(_, []) -> error.
+
+%% tdb_update([UpdateOp], Db) -> NewDb
+%% UpdateOp = {Register,kill}|{Register,NewInfo}
+%% Updates a type database. If a 'kill' operation is given, the type
+%% information for that register will be removed from the database.
+%% A kill operation takes precende over other operations for the same
+%% register (i.e. [{{x,0},kill},{{x,0},{tuple,5}}] means that the
+%% the existing type information, if any, will be discarded, and the
+%% the '{tuple,5}' information ignored.
+%%
+%% If NewInfo information is given and there exists information about
+%% the register, the old and new type information will be merged.
+%% For instance, {tuple,5} and {tuple,10} will be merged to produce
+%% {tuple,10}.
+
+tdb_update(Uis0, Ts0) ->
+ Uis1 = filter(fun ({{x,_},_Op}) -> true;
+ ({{y,_},_Op}) -> true;
+ (_) -> false
+ end, Uis0),
+ tdb_update1(lists:sort(Uis1), Ts0).
+
+tdb_update1([{Key,kill}|Ops], [{K,_Old}|_]=Db) when Key < K ->
+ tdb_update1(remove_key(Key, Ops), Db);
+tdb_update1([{Key,_New}=New|Ops], [{K,_Old}|_]=Db) when Key < K ->
+ [New|tdb_update1(Ops, Db)];
+tdb_update1([{Key,kill}|Ops], [{Key,_}|Db]) ->
+ tdb_update1(remove_key(Key, Ops), Db);
+tdb_update1([{Key,NewInfo}|Ops], [{Key,OldInfo}|Db]) ->
+ [{Key,merge_type_info(NewInfo, OldInfo)}|tdb_update1(Ops, Db)];
+tdb_update1([{_,_}|_]=Ops, [Old|Db]) ->
+ [Old|tdb_update1(Ops, Db)];
+tdb_update1([{Key,kill}|Ops], []) ->
+ tdb_update1(remove_key(Key, Ops), []);
+tdb_update1([{_,_}=New|Ops], []) ->
+ [New|tdb_update1(Ops, [])];
+tdb_update1([], Db) -> Db.
+
+%% tdb_kill_xregs(Db) -> NewDb
+%% Kill all information about x registers. Also kill all tuple_element
+%% dependencies from y registers to x registers.
+
+tdb_kill_xregs([{{x,_},_Type}|Db]) -> tdb_kill_xregs(Db);
+tdb_kill_xregs([{{y,_},{tuple_element,{x,_},_}}|Db]) -> tdb_kill_xregs(Db);
+tdb_kill_xregs([Any|Db]) -> [Any|tdb_kill_xregs(Db)];
+tdb_kill_xregs([]) -> [].
+
+remove_key(Key, [{Key,_Op}|Ops]) -> remove_key(Key, Ops);
+remove_key(_, Ops) -> Ops.
+
+merge_type_info(I, I) -> I;
+merge_type_info({tuple,Sz1,Same}, {tuple,Sz2,Same}=Max) when Sz1 < Sz2 ->
+ Max;
+merge_type_info({tuple,Sz1,Same}=Max, {tuple,Sz2,Same}) when Sz1 > Sz2 ->
+ Max;
+merge_type_info({tuple,Sz1,[]}, {tuple,Sz2,First}) ->
+ merge_type_info({tuple,Sz1,First}, {tuple,Sz2,First});
+merge_type_info({tuple,Sz1,First}, {tuple,Sz2,_}) ->
+ merge_type_info({tuple,Sz1,First}, {tuple,Sz2,First});
+merge_type_info(NewType, _) ->
+ verify_type(NewType),
+ NewType.
+
+verify_type({tuple,Sz,[]}) when is_integer(Sz) -> ok;
+verify_type({tuple,Sz,[_]}) when is_integer(Sz) -> ok;
+verify_type({tuple_element,_,_}) -> ok;
+verify_type(float) -> ok;
+verify_type({atom,_}) -> ok.
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_validator.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_validator.erl
new file mode 100644
index 0000000000..87c1c54d0f
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/beam_validator.erl
@@ -0,0 +1,1022 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_validator.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+
+-module(beam_validator).
+
+-export([file/1,files/1]).
+
+%% Interface for compiler.
+-export([module/2,format_error/1]).
+
+-import(lists, [reverse/1,foldl/3]).
+
+-define(MAXREG, 1024).
+
+-define(DEBUG, 1).
+-undef(DEBUG).
+-ifdef(DEBUG).
+-define(DBG_FORMAT(F, D), (io:format((F), (D)))).
+-else.
+-define(DBG_FORMAT(F, D), ok).
+-endif.
+
+%%%
+%%% API functions.
+%%%
+
+files([F|Fs]) ->
+ ?DBG_FORMAT("# Verifying: ~p~n", [F]),
+ case file(F) of
+ ok -> ok;
+ {error,Es} ->
+ io:format("~p:~n~s~n", [F,format_error(Es)])
+ end,
+ files(Fs);
+files([]) -> ok.
+
+file(Name) when is_list(Name) ->
+ case case filename:extension(Name) of
+ ".S" -> s_file(Name);
+ ".beam" -> beam_file(Name)
+ end of
+ [] -> ok;
+ Es -> {error,Es}
+ end.
+
+%% To be called by the compiler.
+module({Mod,Exp,Attr,Fs,Lc}=Code, _Opts)
+ when is_atom(Mod), is_list(Exp), is_list(Attr), is_integer(Lc) ->
+ case validate(Fs) of
+ [] -> {ok,Code};
+ Es0 ->
+ Es = [{?MODULE,E} || E <- Es0],
+ {error,[{atom_to_list(Mod),Es}]}
+ end.
+
+format_error([]) -> [];
+format_error([{{M,F,A},{I,Off,Desc}}|Es]) ->
+ [io_lib:format(" ~p:~p/~p+~p:~n ~p - ~p~n",
+ [M,F,A,Off,I,Desc])|format_error(Es)];
+format_error({{_M,F,A},{I,Off,Desc}}) ->
+ io_lib:format(
+ "function ~p/~p+~p:~n"
+ " Internal consistency check failed - please report this bug.~n"
+ " Instruction: ~p~n"
+ " Error: ~p:~n", [F,A,Off,I,Desc]).
+
+%%%
+%%% Local functions follow.
+%%%
+
+s_file(Name) ->
+ {ok,Is} = file:consult(Name),
+ Fs = find_functions(Is),
+ validate(Fs).
+
+find_functions(Fs) ->
+ find_functions_1(Fs, none, [], []).
+
+find_functions_1([{function,Name,Arity,Entry}|Is], Func, FuncAcc, Acc0) ->
+ Acc = add_func(Func, FuncAcc, Acc0),
+ find_functions_1(Is, {Name,Arity,Entry}, [], Acc);
+find_functions_1([I|Is], Func, FuncAcc, Acc) ->
+ find_functions_1(Is, Func, [I|FuncAcc], Acc);
+find_functions_1([], Func, FuncAcc, Acc) ->
+ reverse(add_func(Func, FuncAcc, Acc)).
+
+add_func(none, _, Acc) -> Acc;
+add_func({Name,Arity,Entry}, Is, Acc) ->
+ [{function,Name,Arity,Entry,reverse(Is)}|Acc].
+
+beam_file(Name) ->
+ try beam_disasm:file(Name) of
+ {error,beam_lib,Reason} -> [{beam_lib,Reason}];
+ {beam_file,L} ->
+ {value,{code,Code0}} = lists:keysearch(code, 1, L),
+ Code = beam_file_1(Code0, []),
+ validate(Code)
+ catch _:_ -> [disassembly_failed]
+ end.
+
+beam_file_1([F0|Fs], Acc) ->
+ F = conv_func(F0),
+ beam_file_1(Fs, [F|Acc]);
+beam_file_1([], Acc) -> reverse(Acc).
+
+%% Convert from the disassembly format to the internal format
+%% used by the compiler (as passed to the assembler).
+
+conv_func(Is) ->
+ conv_func_1(labels(Is)).
+
+conv_func_1({Ls,[{func_info,[{atom,M},{atom,F},Ar]},
+ {label,Entry}=Le|Is]}) ->
+ %% The entry label gets maybe not correct here
+ {function,F,Ar,Entry,
+ [{label,L}||L<-Ls]++[{func_info,{atom,M},{atom,F},Ar},Le|Is]}.
+
+%%%
+%%% The validator follows.
+%%%
+%%% The purpose of the validator is find errors in the generated code
+%%% that may cause the emulator to crash or behave strangely.
+%%% We don't care about type errors in the user's code that will
+%%% cause a proper exception at run-time.
+%%%
+
+%%% Things currently not checked. XXX
+%%%
+%%% - That floating point registers are initialized before used.
+%%% - That fclearerror and fcheckerror are used properly.
+%%% - Heap allocation for floating point numbers.
+%%% - Heap allocation for binaries.
+%%% - That a catchtag or trytag is not overwritten by the wrong
+%%% type of instruction (such as move/2).
+%%% - Make sure that all catchtags and trytags have been removed
+%%% from the stack at return/tail call.
+%%% - Verify get_list instructions.
+%%%
+
+%% validate([Function]) -> [] | [Error]
+%% A list of functions with their code. The code is in the same
+%% format as used in the compiler and in .S files.
+validate([]) -> [];
+validate([{function,Name,Ar,Entry,Code}|Fs]) ->
+ try validate_1(Code, Name, Ar, Entry) of
+ _ -> validate(Fs)
+ catch
+ Error ->
+ [Error|validate(Fs)];
+ error:Error ->
+ [validate_error(Error, Name, Ar)|validate(Fs)]
+ end.
+
+-ifdef(DEBUG).
+validate_error(Error, Name, Ar) ->
+ exit(validate_error_1(Error, Name, Ar)).
+-else.
+validate_error(Error, Name, Ar) ->
+ validate_error_1(Error, Name, Ar).
+-endif.
+validate_error_1(Error, Name, Ar) ->
+ {{'_',Name,Ar},
+ {internal_error,'_',{Error,erlang:get_stacktrace()}}}.
+
+-record(st, %Emulation state
+ {x=init_regs(0, term), %x register info.
+ y=init_regs(0, initialized), %y register info.
+ numy=none, %Number of y registers.
+ h=0, %Available heap size.
+ ct=[] %List of hot catch/try labels
+ }).
+
+-record(vst, %Validator state
+ {current=none, %Current state
+ branched=gb_trees:empty() %States at jumps
+ }).
+
+-ifdef(DEBUG).
+print_st(#st{x=Xs,y=Ys,numy=NumY,h=H,ct=Ct}) ->
+ io:format(" #st{x=~p~n"
+ " y=~p~n"
+ " numy=~p,h=~p,ct=~w~n",
+ [gb_trees:to_list(Xs),gb_trees:to_list(Ys),NumY,H,Ct]).
+-endif.
+
+validate_1(Is, Name, Arity, Entry) ->
+ validate_2(labels(Is), Name, Arity, Entry).
+
+validate_2({Ls1,[{func_info,{atom,Mod},{atom,Name},Arity}=_F|Is]},
+ Name, Arity, Entry) ->
+ lists:foreach(fun (_L) -> ?DBG_FORMAT(" ~p.~n", [_L]) end, Ls1),
+ ?DBG_FORMAT(" ~p.~n", [_F]),
+ validate_3(labels(Is), Name, Arity, Entry, Mod, Ls1);
+validate_2({Ls1,Is}, Name, Arity, _Entry) ->
+ error({{'_',Name,Arity},{first(Is),length(Ls1),illegal_instruction}}).
+
+validate_3({Ls2,Is}, Name, Arity, Entry, Mod, Ls1) ->
+ lists:foreach(fun (_L) -> ?DBG_FORMAT(" ~p.~n", [_L]) end, Ls2),
+ Offset = 1 + length(Ls2),
+ case lists:member(Entry, Ls2) of
+ true ->
+ St = init_state(Arity),
+ Vst = #vst{current=St,
+ branched=gb_trees_from_list([{L,St} || L <- Ls1])},
+ valfun(Is, {Mod,Name,Arity}, Offset, Vst);
+ false ->
+ error({{Mod,Name,Arity},{first(Is),Offset,no_entry_label}})
+ end.
+
+first([X|_]) -> X;
+first([]) -> [].
+
+labels(Is) ->
+ labels_1(Is, []).
+
+labels_1([{label,L}|Is], R) ->
+ labels_1(Is, [L|R]);
+labels_1(Is, R) ->
+ {lists:reverse(R),Is}.
+
+init_state(Arity) ->
+ Xs = init_regs(Arity, term),
+ Ys = init_regs(0, initialized),
+ #st{x=Xs,y=Ys,numy=none,h=0,ct=[]}.
+
+init_regs(0, _) ->
+ gb_trees:empty();
+init_regs(N, Type) ->
+ gb_trees_from_list([{R,Type} || R <- lists:seq(0, N-1)]).
+
+valfun([], _MFA, _Offset, Vst) -> Vst;
+valfun([I|Is], MFA, Offset, Vst) ->
+ ?DBG_FORMAT(" ~p.\n", [I]),
+ valfun(Is, MFA, Offset+1,
+ try valfun_1(I, Vst)
+ catch Error ->
+ error({MFA,{I,Offset,Error}})
+ end).
+
+%% Instructions that are allowed in dead code or when failing,
+%% that is while the state is undecided in some way.
+valfun_1({label,Lbl}, #vst{current=St0,branched=B}=Vst) ->
+ St = merge_states(Lbl, St0, B),
+ Vst#vst{current=St,branched=gb_trees:enter(Lbl, St, B)};
+valfun_1(_I, #vst{current=none}=Vst) ->
+ %% Ignore instructions after erlang:error/1,2, which
+ %% the original R10B compiler thought would return.
+ ?DBG_FORMAT("Ignoring ~p\n", [_I]),
+ Vst;
+valfun_1({badmatch,Src}, Vst) ->
+ assert_term(Src, Vst),
+ kill_state(Vst);
+valfun_1({case_end,Src}, Vst) ->
+ assert_term(Src, Vst),
+ kill_state(Vst);
+valfun_1(if_end, Vst) ->
+ kill_state(Vst);
+valfun_1({try_case_end,Src}, Vst) ->
+ assert_term(Src, Vst),
+ kill_state(Vst);
+%% Instructions that can not cause exceptions
+valfun_1({move,Src,Dst}, Vst) ->
+ Type = get_term_type(Src, Vst),
+ set_type_reg(Type, Dst, Vst);
+valfun_1({fmove,Src,{fr,_}}, Vst) ->
+ assert_type(float, Src, Vst);
+valfun_1({fmove,{fr,_},Dst}, Vst) ->
+ set_type_reg({float,[]}, Dst, Vst);
+valfun_1({kill,{y,_}=Reg}, Vst) ->
+ set_type_y(initialized, Reg, Vst);
+valfun_1({test_heap,Heap,Live}, Vst) ->
+ test_heap(Heap, Live, Vst);
+valfun_1({bif,_Op,nofail,Src,Dst}, Vst) ->
+ validate_src(Src, Vst),
+ set_type_reg(term, Dst, Vst);
+%% Put instructions.
+valfun_1({put_list,A,B,Dst}, Vst0) ->
+ assert_term(A, Vst0),
+ assert_term(B, Vst0),
+ Vst = eat_heap(2, Vst0),
+ set_type_reg(cons, Dst, Vst);
+valfun_1({put_tuple,Sz,Dst}, Vst0) when is_integer(Sz) ->
+ Vst = eat_heap(1, Vst0),
+ set_type_reg({tuple,Sz}, Dst, Vst);
+valfun_1({put,Src}, Vst) ->
+ assert_term(Src, Vst),
+ eat_heap(1, Vst);
+valfun_1({put_string,Sz,_,Dst}, Vst0) when is_integer(Sz) ->
+ Vst = eat_heap(2*Sz, Vst0),
+ set_type_reg(cons, Dst, Vst);
+%% Allocate and deallocate, et.al
+valfun_1({allocate,Stk,Live}, Vst) ->
+ allocate(false, Stk, 0, Live, Vst);
+valfun_1({allocate_heap,Stk,Heap,Live}, Vst) ->
+ allocate(false, Stk, Heap, Live, Vst);
+valfun_1({allocate_zero,Stk,Live}, Vst) ->
+ allocate(true, Stk, 0, Live, Vst);
+valfun_1({allocate_heap_zero,Stk,Heap,Live}, Vst) ->
+ allocate(true, Stk, Heap, Live, Vst);
+valfun_1({init,{y,_}=Reg}, Vst) ->
+ set_type_y(initialized, Reg, Vst);
+valfun_1({deallocate,StkSize}, #vst{current=#st{numy=StkSize,ct=[]}}=Vst) ->
+ deallocate(Vst);
+valfun_1({deallocate,_}, #vst{current=#st{numy=NumY,ct=[]}}) ->
+ error({allocated,NumY});
+valfun_1({deallocate,_}, #vst{current=#st{ct=Fails}}) ->
+ error({catch_try_stack,Fails});
+%% Catch & try.
+valfun_1({'catch',Dst,{f,Fail}}, Vst0) when Fail /= none ->
+ Vst = #vst{current=#st{ct=Fails}=St} =
+ set_type_y({catchtag,Fail}, Dst, Vst0),
+ Vst#vst{current=St#st{ct=[Fail|Fails]}};
+valfun_1({'try',Dst,{f,Fail}}, Vst0) ->
+ Vst = #vst{current=#st{ct=Fails}=St} =
+ set_type_y({trytag,Fail}, Dst, Vst0),
+ Vst#vst{current=St#st{ct=[Fail|Fails]}};
+%% Do a postponed state branch if necessary and try next set of instructions
+valfun_1(I, #vst{current=#st{ct=[]}}=Vst) ->
+ valfun_2(I, Vst);
+valfun_1(I, #vst{current=#st{ct=Fails}}=Vst0) ->
+ %% Perform a postponed state branch
+ Vst = #vst{current=St} = lists:foldl(fun branch_state/2, Vst0, Fails),
+ valfun_2(I, Vst#vst{current=St#st{ct=[]}}).
+
+%% Instructions that can cause exceptions.
+valfun_2({apply,Live}, Vst) ->
+ call(Live+2, Vst);
+valfun_2({apply_last,Live,_}, Vst) ->
+ tail_call(Live+2, Vst);
+valfun_2({call_fun,Live}, Vst) ->
+ call(Live, Vst);
+valfun_2({call,Live,_}, Vst) ->
+ call(Live, Vst);
+valfun_2({call_ext,Live,Func}, Vst) ->
+ call(Func, Live, Vst);
+valfun_2({call_only,Live,_}, Vst) ->
+ tail_call(Live, Vst);
+valfun_2({call_ext_only,Live,_}, Vst) ->
+ tail_call(Live, Vst);
+valfun_2({call_last,Live,_,_}, Vst) ->
+ tail_call(Live, Vst);
+valfun_2({call_ext_last,Live,_,_}, Vst) ->
+ tail_call(Live, Vst);
+valfun_2({make_fun,_,_,Live}, Vst) ->
+ call(Live, Vst);
+valfun_2({make_fun2,_,_,_,Live}, Vst) ->
+ call(Live, Vst);
+%% Floating point.
+valfun_2({fconv,Src,{fr,_}}, Vst) ->
+ assert_term(Src, Vst);
+valfun_2({bif,fadd,_,[{fr,_},{fr,_}],{fr,_}}, Vst) ->
+ Vst;
+valfun_2({bif,fdiv,_,[{fr,_},{fr,_}],{fr,_}}, Vst) ->
+ Vst;
+valfun_2({bif,fmul,_,[{fr,_},{fr,_}],{fr,_}}, Vst) ->
+ Vst;
+valfun_2({bif,fnegate,_,[{fr,_}],{fr,_}}, Vst) ->
+ Vst;
+valfun_2({bif,fsub,_,[{fr,_},{fr,_}],{fr,_}}, Vst) ->
+ Vst;
+valfun_2(fclearerror, Vst) ->
+ Vst;
+valfun_2({fcheckerror,_}, Vst) ->
+ Vst;
+%% Other BIFs
+valfun_2({bif,element,{f,Fail},[Pos,Tuple],Dst}, Vst0) ->
+ TupleType0 = get_term_type(Tuple, Vst0),
+ PosType = get_term_type(Pos, Vst0),
+ Vst1 = branch_state(Fail, Vst0),
+ TupleType = upgrade_type({tuple,[get_tuple_size(PosType)]}, TupleType0),
+ Vst = set_type(TupleType, Tuple, Vst1),
+ set_type_reg(term, Dst, Vst);
+valfun_2({bif,Op,{f,Fail},Src,Dst}, Vst0) ->
+ validate_src(Src, Vst0),
+ Vst = branch_state(Fail, Vst0),
+ Type = bif_type(Op, Src, Vst),
+ set_type_reg(Type, Dst, Vst);
+valfun_2(return, #vst{current=#st{numy=none}}=Vst) ->
+ kill_state(Vst);
+valfun_2(return, #vst{current=#st{numy=NumY}}) ->
+ error({stack_frame,NumY});
+valfun_2({jump,{f,_}}, #vst{current=none}=Vst) ->
+ %% Must be an unreachable jump which was not optimized away.
+ %% Do nothing.
+ Vst;
+valfun_2({jump,{f,Lbl}}, Vst) ->
+ kill_state(branch_state(Lbl, Vst));
+valfun_2({loop_rec,{f,Fail},Dst}, Vst0) ->
+ Vst = branch_state(Fail, Vst0),
+ set_type_reg(term, Dst, Vst);
+valfun_2(remove_message, Vst) ->
+ Vst;
+valfun_2({wait,_}, Vst) ->
+ kill_state(Vst);
+valfun_2({wait_timeout,_,Src}, Vst) ->
+ assert_term(Src, Vst);
+valfun_2({loop_rec_end,_}, Vst) ->
+ kill_state(Vst);
+valfun_2(timeout, #vst{current=St}=Vst) ->
+ Vst#vst{current=St#st{x=init_regs(0, term)}};
+valfun_2(send, Vst) ->
+ call(2, Vst);
+%% Catch & try.
+valfun_2({catch_end,Reg}, Vst0) ->
+ case get_type(Reg, Vst0) of
+ {catchtag,_} ->
+ Vst = #vst{current=St} = set_type_reg(initialized, Reg, Vst0),
+ Xs = gb_trees_from_list([{0,term}]),
+ Vst#vst{current=St#st{x=Xs}};
+ Type ->
+ error({bad_type,Type})
+ end;
+valfun_2({try_end,Reg}, Vst) ->
+ case get_type(Reg, Vst) of
+ {trytag,_} ->
+ set_type_reg(initialized, Reg, Vst);
+ Type ->
+ error({bad_type,Type})
+ end;
+valfun_2({try_case,Reg}, Vst0) ->
+ case get_type(Reg, Vst0) of
+ {trytag,_} ->
+ Vst = #vst{current=St} = set_type_reg(initialized, Reg, Vst0),
+ Xs = gb_trees_from_list([{0,{atom,[]}},{1,term},{2,term}]),
+ Vst#vst{current=St#st{x=Xs}};
+ Type ->
+ error({bad_type,Type})
+ end;
+valfun_2({set_tuple_element,Src,Tuple,I}, Vst) ->
+ assert_term(Src, Vst),
+ assert_type({tuple_element,I+1}, Tuple, Vst);
+%% Match instructions.
+valfun_2({select_val,Src,{f,Fail},{list,Choices}}, Vst) ->
+ assert_term(Src, Vst),
+ Lbls = [L || {f,L} <- Choices]++[Fail],
+ kill_state(foldl(fun(L, S) -> branch_state(L, S) end, Vst, Lbls));
+valfun_2({select_tuple_arity,Tuple,{f,Fail},{list,Choices}}, Vst) ->
+ assert_type(tuple, Tuple, Vst),
+ kill_state(branch_arities(Choices, Tuple, branch_state(Fail, Vst)));
+valfun_2({get_list,Src,D1,D2}, Vst0) ->
+ assert_term(Src, Vst0),
+ Vst = set_type_reg(term, D1, Vst0),
+ set_type_reg(term, D2, Vst);
+valfun_2({get_tuple_element,Src,I,Dst}, Vst) ->
+ assert_type({tuple_element,I+1}, Src, Vst),
+ set_type_reg(term, Dst, Vst);
+valfun_2({bs_restore,_}, Vst) ->
+ Vst;
+valfun_2({bs_save,_}, Vst) ->
+ Vst;
+valfun_2({bs_start_match,{f,Fail},Src}, Vst) ->
+ assert_term(Src, Vst),
+ branch_state(Fail, Vst);
+valfun_2({test,bs_skip_bits,{f,Fail},[Src,_,_]}, Vst) ->
+ assert_term(Src, Vst),
+ branch_state(Fail, Vst);
+valfun_2({test,_,{f,Fail},[_,_,_,Dst]}, Vst0) ->
+ Vst = branch_state(Fail, Vst0),
+ set_type_reg({integer,[]}, Dst, Vst);
+valfun_2({test,bs_test_tail,{f,Fail},_}, Vst) ->
+ branch_state(Fail, Vst);
+%% Other test instructions.
+valfun_2({test,is_float,{f,Lbl},[Float]}, Vst0) ->
+ assert_term(Float, Vst0),
+ Vst = branch_state(Lbl, Vst0),
+ set_type({float,[]}, Float, Vst);
+valfun_2({test,is_tuple,{f,Lbl},[Tuple]}, Vst0) ->
+ assert_term(Tuple, Vst0),
+ Vst = branch_state(Lbl, Vst0),
+ set_type({tuple,[0]}, Tuple, Vst);
+valfun_2({test,test_arity,{f,Lbl},[Tuple,Sz]}, Vst0) when is_integer(Sz) ->
+ assert_type(tuple, Tuple, Vst0),
+ Vst = branch_state(Lbl, Vst0),
+ set_type_reg({tuple,Sz}, Tuple, Vst);
+valfun_2({test,_Op,{f,Lbl},Src}, Vst) ->
+ validate_src(Src, Vst),
+ branch_state(Lbl, Vst);
+valfun_2({bs_add,{f,Fail},[A,B,_],Dst}, Vst0) ->
+ assert_term(A, Vst0),
+ assert_term(B, Vst0),
+ Vst = branch_state(Fail, Vst0),
+ set_type_reg({integer,[]}, Dst, Vst);
+valfun_2({bs_bits_to_bytes,{f,Fail},Src,Dst}, Vst0) ->
+ assert_term(Src, Vst0),
+ Vst = branch_state(Fail, Vst0),
+ set_type_reg({integer,[]}, Dst, Vst);
+valfun_2({bs_init2,{f,Fail},_,Heap,_,_,Dst}, Vst0) ->
+ Vst1 = heap_alloc(Heap, Vst0),
+ Vst = branch_state(Fail, Vst1),
+ set_type_reg(binary, Dst, Vst);
+valfun_2({bs_put_string,Sz,_}, Vst) when is_integer(Sz) ->
+ Vst;
+valfun_2({bs_put_binary,{f,Fail},_,_,_,Src}, Vst0) ->
+ assert_term(Src, Vst0),
+ branch_state(Fail, Vst0);
+valfun_2({bs_put_float,{f,Fail},_,_,_,Src}, Vst0) ->
+ assert_term(Src, Vst0),
+ branch_state(Fail, Vst0);
+valfun_2({bs_put_integer,{f,Fail},_,_,_,Src}, Vst0) ->
+ assert_term(Src, Vst0),
+ branch_state(Fail, Vst0);
+%% Old bit syntax construction (before R10B).
+valfun_2({bs_init,_,_}, Vst) -> Vst;
+valfun_2({bs_need_buf,_}, Vst) -> Vst;
+valfun_2({bs_final,{f,Fail},Dst}, Vst0) ->
+ Vst = branch_state(Fail, Vst0),
+ set_type_reg(binary, Dst, Vst);
+%% Misc.
+valfun_2({'%live',Live}, Vst) ->
+ verify_live(Live, Vst),
+ Vst;
+valfun_2(_, _) ->
+ error(unknown_instruction).
+
+kill_state(#vst{current=#st{ct=[]}}=Vst) ->
+ Vst#vst{current=none};
+kill_state(#vst{current=#st{ct=Fails}}=Vst0) ->
+ Vst = lists:foldl(fun branch_state/2, Vst0, Fails),
+ Vst#vst{current=none}.
+
+%% A "plain" call.
+%% The stackframe must have a known size and be initialized.
+%% The instruction will return to the instruction following the call.
+call(Live, #vst{current=St}=Vst) ->
+ verify_live(Live, Vst),
+ verify_y_init(Vst),
+ Xs = gb_trees_from_list([{0,term}]),
+ Vst#vst{current=St#st{x=Xs}}.
+
+%% A "plain" call.
+%% The stackframe must have a known size and be initialized.
+%% The instruction will return to the instruction following the call.
+call(Name, Live, #vst{current=St}=Vst) ->
+ verify_live(Live, Vst),
+ case return_type(Name, Vst) of
+ exception ->
+ kill_state(Vst);
+ Type ->
+ verify_y_init(Vst),
+ Xs = gb_trees_from_list([{0,Type}]),
+ Vst#vst{current=St#st{x=Xs}}
+ end.
+
+%% Tail call.
+%% The stackframe must have a known size and be initialized.
+%% Does not return to the instruction following the call.
+tail_call(Live, Vst) ->
+ kill_state(call(Live, Vst)).
+
+allocate(Zero, Stk, Heap, Live, #vst{current=#st{numy=none}=St}=Vst) ->
+ verify_live(Live, Vst),
+ Ys = init_regs(case Zero of
+ true -> Stk;
+ false -> 0
+ end, initialized),
+ Vst#vst{current=St#st{y=Ys,numy=Stk,h=heap_alloc_1(Heap)}};
+allocate(_, _, _, _, #vst{current=#st{numy=Numy}}) ->
+ error({existing_stack_frame,{size,Numy}}).
+
+deallocate(#vst{current=St}=Vst) ->
+ Vst#vst{current=St#st{y=init_regs(0, initialized),numy=none}}.
+
+test_heap(Heap, Live, Vst) ->
+ verify_live(Live, Vst),
+ heap_alloc(Heap, Vst).
+
+heap_alloc(Heap, #vst{current=St}=Vst) ->
+ Vst#vst{current=St#st{h=heap_alloc_1(Heap)}}.
+
+heap_alloc_1({alloc,Alloc}) ->
+ {value,{_,Heap}} = lists:keysearch(words, 1, Alloc),
+ Heap;
+heap_alloc_1(Heap) when is_integer(Heap) -> Heap.
+
+
+set_type(Type, {x,_}=Reg, Vst) -> set_type_reg(Type, Reg, Vst);
+set_type(Type, {y,_}=Reg, Vst) -> set_type_y(Type, Reg, Vst);
+set_type(_, _, #vst{}=Vst) -> Vst.
+
+set_type_reg(Type, {x,X}, #vst{current=#st{x=Xs}=St}=Vst)
+ when 0 =< X, X < ?MAXREG ->
+ Vst#vst{current=St#st{x=gb_trees:enter(X, Type, Xs)}};
+set_type_reg(Type, Reg, Vst) ->
+ set_type_y(Type, Reg, Vst).
+
+set_type_y(Type, {y,Y}=Reg, #vst{current=#st{y=Ys,numy=NumY}=St}=Vst)
+ when is_integer(Y), 0 =< Y, Y < ?MAXREG ->
+ case {Y,NumY} of
+ {_,none} ->
+ error({no_stack_frame,Reg});
+ {_,_} when Y > NumY ->
+ error({y_reg_out_of_range,Reg,NumY});
+ {_,_} ->
+ Vst#vst{current=St#st{y=gb_trees:enter(Y, Type, Ys)}}
+ end;
+set_type_y(Type, Reg, #vst{}) -> error({invalid_store,Reg,Type}).
+
+assert_term(Src, Vst) ->
+ get_term_type(Src, Vst),
+ Vst.
+
+%% The possible types.
+%%
+%% First non-term types:
+%%
+%% initialized Only for Y registers. Means that the Y register
+%% has been initialized with some valid term so that
+%% it is safe to pass to the garbage collector.
+%% NOT safe to use in any other way (will not crash the
+%% emulator, but clearly points to a bug in the compiler).
+%%
+%% {catchtag,Lbl} A special term used within a catch. Must only be used
+%% by the catch instructions; NOT safe to use in other
+%% instructions.
+%%
+%% {trytag,Lbl} A special term used within a try block. Must only be
+%% used by the catch instructions; NOT safe to use in other
+%% instructions.
+%%
+%% exception Can only be used as a type returned by return_type/2
+%% (which gives the type of the value returned by a BIF).
+%% Thus 'exception' is never stored as type descriptor
+%% for a register.
+%%
+%% Normal terms:
+%%
+%% term Any valid Erlang (but not of the special types above).
+%%
+%% bool The atom 'true' or the atom 'false'.
+%%
+%% cons Cons cell: [_|_]
+%%
+%% nil Empty list: []
+%%
+%% {tuple,[Sz]} Tuple. An element has been accessed using
+%% element/2 or setelement/3 so that it is known that
+%% the type is a tuple of size at least Sz.
+%%
+%% {tuple,Sz} Tuple. A test_arity instruction has been seen
+%% so that it is known that the size is exactly Sz.
+%%
+%% {atom,[]} Atom.
+%% {atom,Atom}
+%%
+%% {integer,[]} Integer.
+%% {integer,Integer}
+%%
+%% {float,[]} Float.
+%% {float,Float}
+%%
+%% number Integer or Float of unknown value
+%%
+
+assert_type(WantedType, Term, Vst) ->
+ assert_type(WantedType, get_type(Term, Vst)),
+ Vst.
+
+assert_type(float, {float,_}) -> ok;
+assert_type(tuple, {tuple,_}) -> ok;
+assert_type({tuple_element,I}, {tuple,[Sz]})
+ when 1 =< I, I =< Sz ->
+ ok;
+assert_type({tuple_element,I}, {tuple,Sz})
+ when is_integer(Sz), 1 =< I, I =< Sz ->
+ ok;
+assert_type(Needed, Actual) ->
+ error({bad_type,{needed,Needed},{actual,Actual}}).
+
+%% upgrade_type/2 is used when linear code finds out more and
+%% more information about a type, so the type gets "narrower"
+%% or perhaps inconsistent. In the case of inconsistency
+%% we mostly widen the type to 'term' to make subsequent
+%% code fail if it assumes anything about the type.
+
+upgrade_type(Same, Same) -> Same;
+upgrade_type(term, OldT) -> OldT;
+upgrade_type(NewT, term) -> NewT;
+upgrade_type({Type,New}=NewT, {Type,Old}=OldT)
+ when Type == atom; Type == integer; Type == float ->
+ if New =:= Old -> OldT;
+ New =:= [] -> OldT;
+ Old =:= [] -> NewT;
+ true -> term
+ end;
+upgrade_type({Type,_}=NewT, number)
+ when Type == integer; Type == float ->
+ NewT;
+upgrade_type(number, {Type,_}=OldT)
+ when Type == integer; Type == float ->
+ OldT;
+upgrade_type(bool, {atom,A}) ->
+ upgrade_bool(A);
+upgrade_type({atom,A}, bool) ->
+ upgrade_bool(A);
+upgrade_type({tuple,[Sz]}, {tuple,[OldSz]})
+ when is_integer(Sz) ->
+ {tuple,[max(Sz, OldSz)]};
+upgrade_type({tuple,Sz}=T, {tuple,[_]})
+ when is_integer(Sz) ->
+ %% This also takes care of the user error when a tuple element
+ %% is accesed outside the known exact tuple size; there is
+ %% no more type information, just a runtime error which is not
+ %% our problem.
+ T;
+upgrade_type({tuple,[Sz]}, {tuple,_}=T)
+ when is_integer(Sz) ->
+ %% Same as the previous clause but mirrored.
+ T;
+upgrade_type(_A, _B) ->
+ %%io:format("upgrade_type: ~p ~p\n", [_A,_B]),
+ term.
+
+upgrade_bool([]) -> bool;
+upgrade_bool(true) -> {atom,true};
+upgrade_bool(false) -> {atom,false};
+upgrade_bool(_) -> term.
+
+get_tuple_size({integer,[]}) -> 0;
+get_tuple_size({integer,Sz}) -> Sz;
+get_tuple_size(_) -> 0.
+
+validate_src(Ss, Vst) when is_list(Ss) ->
+ foldl(fun(S, _) -> get_type(S, Vst) end, ok, Ss).
+
+get_term_type(Src, Vst) ->
+ case get_type(Src, Vst) of
+ initialized -> error({not_assigned,Src});
+ exception -> error({exception,Src});
+ {catchtag,_} -> error({catchtag,Src});
+ {trytag,_} -> error({trytag,Src});
+ Type -> Type
+ end.
+
+get_type(nil=T, _) -> T;
+get_type({atom,A}=T, _) when is_atom(A) -> T;
+get_type({float,F}=T, _) when is_float(F) -> T;
+get_type({integer,I}=T, _) when is_integer(I) -> T;
+get_type({x,X}=Reg, #vst{current=#st{x=Xs}}) when is_integer(X) ->
+ case gb_trees:lookup(X, Xs) of
+ {value,Type} -> Type;
+ none -> error({uninitialized_reg,Reg})
+ end;
+get_type({y,Y}=Reg, #vst{current=#st{y=Ys}}) when is_integer(Y) ->
+ case gb_trees:lookup(Y, Ys) of
+ {value,initialized} -> error({unassigned_reg,Reg});
+ {value,Type} -> Type;
+ none -> error({uninitialized_reg,Reg})
+ end;
+get_type(Src, _) -> error({bad_source,Src}).
+
+branch_arities([], _, #vst{}=Vst) -> Vst;
+branch_arities([Sz,{f,L}|T], Tuple, #vst{current=St}=Vst0)
+ when is_integer(Sz) ->
+ Vst1 = set_type_reg({tuple,Sz}, Tuple, Vst0),
+ Vst = branch_state(L, Vst1),
+ branch_arities(T, Tuple, Vst#vst{current=St}).
+
+branch_state(0, #vst{}=Vst) -> Vst;
+branch_state(L, #vst{current=St,branched=B}=Vst) ->
+ Vst#vst{
+ branched=case gb_trees:is_defined(L, B) of
+ false ->
+ gb_trees:insert(L, St#st{ct=[]}, B);
+ true ->
+ MergedSt = merge_states(L, St, B),
+ gb_trees:update(L, MergedSt#st{ct=[]}, B)
+ end}.
+
+%% merge_states/3 is used when there are more than one way to arrive
+%% at this point, and the type states for the different paths has
+%% to be merged. The type states are downgraded to the least common
+%% subset for the subsequent code.
+
+merge_states(0, St, _Branched) -> St;
+merge_states(L, St, Branched) ->
+ case gb_trees:lookup(L, Branched) of
+ none -> St;
+ {value,OtherSt} when St == none -> OtherSt;
+ {value,OtherSt} ->
+ merge_states_1(St, OtherSt)
+ end.
+
+merge_states_1(#st{x=Xs0,y=Ys0,numy=NumY0,h=H0}=St,
+ #st{x=Xs1,y=Ys1,numy=NumY1,h=H1}) ->
+ NumY = merge_stk(NumY0, NumY1),
+ Xs = merge_regs(Xs0, Xs1),
+ Ys = merge_regs(Ys0, Ys1),
+ St#st{x=Xs,y=Ys,numy=NumY,h=min(H0, H1)}.
+
+merge_stk(S, S) -> S;
+merge_stk(_, _) -> undecided.
+
+merge_regs(Rs0, Rs1) ->
+ Rs = merge_regs_1(gb_trees:to_list(Rs0), gb_trees:to_list(Rs1)),
+ gb_trees_from_list(Rs).
+
+merge_regs_1([Same|Rs1], [Same|Rs2]) ->
+ [Same|merge_regs_1(Rs1, Rs2)];
+merge_regs_1([{R1,_}|Rs1], [{R2,_}|_]=Rs2) when R1 < R2 ->
+ merge_regs_1(Rs1, Rs2);
+merge_regs_1([{R1,_}|_]=Rs1, [{R2,_}|Rs2]) when R1 > R2 ->
+ merge_regs_1(Rs1, Rs2);
+merge_regs_1([{R,Type1}|Rs1], [{R,Type2}|Rs2]) ->
+ [{R,merge_types(Type1, Type2)}|merge_regs_1(Rs1, Rs2)];
+merge_regs_1([], []) -> [];
+merge_regs_1([], [_|_]) -> [];
+merge_regs_1([_|_], []) -> [].
+
+merge_types(T, T) -> T;
+merge_types(initialized=I, _) -> I;
+merge_types(_, initialized=I) -> I;
+merge_types({tuple,Same}=T, {tuple,Same}) -> T;
+merge_types({tuple,A}, {tuple,B}) ->
+ {tuple,[min(tuple_sz(A), tuple_sz(B))]};
+merge_types({Type,A}, {Type,B})
+ when Type == atom; Type == integer; Type == float ->
+ if A =:= B -> {Type,A};
+ true -> {Type,[]}
+ end;
+merge_types({Type,_}, number)
+ when Type == integer; Type == float ->
+ number;
+merge_types(number, {Type,_})
+ when Type == integer; Type == float ->
+ number;
+merge_types(bool, {atom,A}) ->
+ merge_bool(A);
+merge_types({atom,A}, bool) ->
+ merge_bool(A);
+merge_types(_, _) -> term.
+
+tuple_sz([Sz]) -> Sz;
+tuple_sz(Sz) -> Sz.
+
+merge_bool([]) -> {atom,[]};
+merge_bool(true) -> bool;
+merge_bool(false) -> bool;
+merge_bool(_) -> {atom,[]}.
+
+verify_y_init(#vst{current=#st{numy=none}}) -> ok;
+verify_y_init(#vst{current=#st{numy=undecided}}) ->
+ error(unknown_size_of_stackframe);
+verify_y_init(#vst{current=#st{y=Ys,numy=NumY}}) ->
+ verify_y_init_1(NumY, Ys).
+
+verify_y_init_1(0, _) -> ok;
+verify_y_init_1(N, Ys) ->
+ Y = N-1,
+ case gb_trees:is_defined(Y, Ys) of
+ false -> error({{y,Y},not_initialized});
+ true -> verify_y_init_1(Y, Ys)
+ end.
+
+verify_live(0, #vst{}) -> ok;
+verify_live(N, #vst{current=#st{x=Xs}}) ->
+ verify_live_1(N, Xs).
+
+verify_live_1(0, _) -> ok;
+verify_live_1(N, Xs) ->
+ X = N-1,
+ case gb_trees:is_defined(X, Xs) of
+ false -> error({{x,X},not_live});
+ true -> verify_live_1(X, Xs)
+ end.
+
+eat_heap(N, #vst{current=#st{h=Heap0}=St}=Vst) ->
+ case Heap0-N of
+ Neg when Neg < 0 ->
+ error({heap_overflow,{left,Heap0},{wanted,N}});
+ Heap ->
+ Vst#vst{current=St#st{h=Heap}}
+ end.
+
+bif_type('-', Src, Vst) ->
+ arith_type(Src, Vst);
+bif_type('+', Src, Vst) ->
+ arith_type(Src, Vst);
+bif_type('*', Src, Vst) ->
+ arith_type(Src, Vst);
+bif_type(abs, [Num], Vst) ->
+ case get_type(Num, Vst) of
+ {float,_}=T -> T;
+ {integer,_}=T -> T;
+ _ -> number
+ end;
+bif_type(float, _, _) -> {float,[]};
+bif_type('/', _, _) -> {float,[]};
+%% Integer operations.
+bif_type('div', [_,_], _) -> {integer,[]};
+bif_type('rem', [_,_], _) -> {integer,[]};
+bif_type(length, [_], _) -> {integer,[]};
+bif_type(size, [_], _) -> {integer,[]};
+bif_type(trunc, [_], _) -> {integer,[]};
+bif_type(round, [_], _) -> {integer,[]};
+bif_type('band', [_,_], _) -> {integer,[]};
+bif_type('bor', [_,_], _) -> {integer,[]};
+bif_type('bxor', [_,_], _) -> {integer,[]};
+bif_type('bnot', [_], _) -> {integer,[]};
+bif_type('bsl', [_,_], _) -> {integer,[]};
+bif_type('bsr', [_,_], _) -> {integer,[]};
+%% Booleans.
+bif_type('==', [_,_], _) -> bool;
+bif_type('/=', [_,_], _) -> bool;
+bif_type('=<', [_,_], _) -> bool;
+bif_type('<', [_,_], _) -> bool;
+bif_type('>=', [_,_], _) -> bool;
+bif_type('>', [_,_], _) -> bool;
+bif_type('=:=', [_,_], _) -> bool;
+bif_type('=/=', [_,_], _) -> bool;
+bif_type('not', [_], _) -> bool;
+bif_type('and', [_,_], _) -> bool;
+bif_type('or', [_,_], _) -> bool;
+bif_type('xor', [_,_], _) -> bool;
+bif_type(is_atom, [_], _) -> bool;
+bif_type(is_boolean, [_], _) -> bool;
+bif_type(is_binary, [_], _) -> bool;
+bif_type(is_constant, [_], _) -> bool;
+bif_type(is_float, [_], _) -> bool;
+bif_type(is_function, [_], _) -> bool;
+bif_type(is_integer, [_], _) -> bool;
+bif_type(is_list, [_], _) -> bool;
+bif_type(is_number, [_], _) -> bool;
+bif_type(is_pid, [_], _) -> bool;
+bif_type(is_port, [_], _) -> bool;
+bif_type(is_reference, [_], _) -> bool;
+bif_type(is_tuple, [_], _) -> bool;
+%% Misc.
+bif_type(node, [], _) -> {atom,[]};
+bif_type(node, [_], _) -> {atom,[]};
+bif_type(hd, [_], _) -> term;
+bif_type(tl, [_], _) -> term;
+bif_type(get, [_], _) -> term;
+bif_type(raise, [_,_], _) -> exception;
+bif_type(_, _, _) -> term.
+
+arith_type([A,B], Vst) ->
+ case {get_type(A, Vst),get_type(B, Vst)} of
+ {{float,_},_} -> {float,[]};
+ {_,{float,_}} -> {float,[]};
+ {_,_} -> number
+ end;
+arith_type(_, _) -> number.
+
+return_type({extfunc,M,F,A}, Vst) ->
+ return_type_1(M, F, A, Vst).
+
+return_type_1(erlang, setelement, 3, Vst) ->
+ Tuple = {x,1},
+ TupleType =
+ case get_type(Tuple, Vst) of
+ {tuple,_}=TT -> TT;
+ _ -> {tuple,[0]}
+ end,
+ case get_type({x,0}, Vst) of
+ {integer,[]} -> TupleType;
+ {integer,I} -> upgrade_type({tuple,[I]}, TupleType);
+ _ -> TupleType
+ end;
+return_type_1(erlang, F, A, _) ->
+ return_type_erl(F, A);
+return_type_1(math, F, A, _) ->
+ return_type_math(F, A);
+return_type_1(_, _, _, _) -> term.
+
+return_type_erl(exit, 1) -> exception;
+return_type_erl(throw, 1) -> exception;
+return_type_erl(fault, 1) -> exception;
+return_type_erl(fault, 2) -> exception;
+return_type_erl(error, 1) -> exception;
+return_type_erl(error, 2) -> exception;
+return_type_erl(_, _) -> term.
+
+return_type_math(cos, 1) -> {float,[]};
+return_type_math(cosh, 1) -> {float,[]};
+return_type_math(sin, 1) -> {float,[]};
+return_type_math(sinh, 1) -> {float,[]};
+return_type_math(tan, 1) -> {float,[]};
+return_type_math(tanh, 1) -> {float,[]};
+return_type_math(acos, 1) -> {float,[]};
+return_type_math(acosh, 1) -> {float,[]};
+return_type_math(asin, 1) -> {float,[]};
+return_type_math(asinh, 1) -> {float,[]};
+return_type_math(atan, 1) -> {float,[]};
+return_type_math(atanh, 1) -> {float,[]};
+return_type_math(erf, 1) -> {float,[]};
+return_type_math(erfc, 1) -> {float,[]};
+return_type_math(exp, 1) -> {float,[]};
+return_type_math(log, 1) -> {float,[]};
+return_type_math(log10, 1) -> {float,[]};
+return_type_math(sqrt, 1) -> {float,[]};
+return_type_math(atan2, 2) -> {float,[]};
+return_type_math(pow, 2) -> {float,[]};
+return_type_math(pi, 0) -> {float,[]};
+return_type_math(_, _) -> term.
+
+min(A, B) when is_integer(A), is_integer(B), A < B -> A;
+min(A, B) when is_integer(A), is_integer(B) -> B.
+
+max(A, B) when is_integer(A), is_integer(B), A > B -> A;
+max(A, B) when is_integer(A), is_integer(B) -> B.
+
+gb_trees_from_list(L) -> gb_trees:from_orddict(orddict:from_list(L)).
+
+-ifdef(DEBUG).
+error(Error) -> exit(Error).
+-else.
+error(Error) -> throw(Error).
+-endif.
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/cerl.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/cerl.erl
new file mode 100644
index 0000000000..e4bdfc7dbe
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/cerl.erl
@@ -0,0 +1,4169 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Richard Carlsson.
+%% Copyright (C) 1999-2002 Richard Carlsson.
+%% Portions created by Ericsson are Copyright 2001, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: cerl.erl,v 1.3 2010/03/04 13:54:20 maria Exp $
+
+%% =====================================================================
+%% @doc Core Erlang abstract syntax trees.
+%%
+%% This module defines an abstract data type for representing Core
+%% Erlang source code as syntax trees.
+%%
+%% A recommended starting point for the first-time user is the
+%% documentation of the function type/1
.
+%%
+%% NOTES:
+%%
+%% This module deals with the composition and decomposition of
+%% syntactic entities (as opposed to semantic ones); its
+%% purpose is to hide all direct references to the data structures
+%% used to represent these entities. With few exceptions, the
+%% functions in this module perform no semantic interpretation of
+%% their inputs, and in general, the user is assumed to pass
+%% type-correct arguments - if this is not done, the effects are not
+%% defined.
+%%
+%% The internal representations of abstract syntax trees are
+%% subject to change without notice, and should not be documented
+%% outside this module. Furthermore, we do not give any guarantees on
+%% how an abstract syntax tree may or may not be represented, with
+%% the following exceptions: no syntax tree is represented by a
+%% single atom, such as none
, by a list constructor
+%% [X | Y]
, or by the empty list []
. This
+%% can be relied on when writing functions that operate on syntax
+%% trees.
+%%
+%% @type cerl(). An abstract Core Erlang syntax tree.
+%%
+%% Every abstract syntax tree has a type, given by the
+%% function type/1
. In addition,
+%% each syntax tree has a list of user annotations (cf. get_ann/1
), which are included
+%% in the Core Erlang syntax.
+
+-module(cerl).
+
+-export([abstract/1, add_ann/2, alias_pat/1, alias_var/1,
+ ann_abstract/2, ann_c_alias/3, ann_c_apply/3, ann_c_atom/2,
+ ann_c_call/4, ann_c_case/3, ann_c_catch/2, ann_c_char/2,
+ ann_c_clause/3, ann_c_clause/4, ann_c_cons/3, ann_c_float/2,
+ ann_c_fname/3, ann_c_fun/3, ann_c_int/2, ann_c_let/4,
+ ann_c_letrec/3, ann_c_module/4, ann_c_module/5, ann_c_nil/1,
+ ann_c_cons_skel/3, ann_c_tuple_skel/2, ann_c_primop/3,
+ ann_c_receive/2, ann_c_receive/4, ann_c_seq/3, ann_c_string/2,
+ ann_c_try/6, ann_c_tuple/2, ann_c_values/2, ann_c_var/2,
+ ann_make_data/3, ann_make_list/2, ann_make_list/3,
+ ann_make_data_skel/3, ann_make_tree/3, apply_args/1,
+ apply_arity/1, apply_op/1, atom_lit/1, atom_name/1, atom_val/1,
+ c_alias/2, c_apply/2, c_atom/1, c_call/3, c_case/2, c_catch/1,
+ c_char/1, c_clause/2, c_clause/3, c_cons/2, c_float/1,
+ c_fname/2, c_fun/2, c_int/1, c_let/3, c_letrec/2, c_module/3,
+ c_module/4, c_nil/0, c_cons_skel/2, c_tuple_skel/1, c_primop/2,
+ c_receive/1, c_receive/3, c_seq/2, c_string/1, c_try/5,
+ c_tuple/1, c_values/1, c_var/1, call_args/1, call_arity/1,
+ call_module/1, call_name/1, case_arg/1, case_arity/1,
+ case_clauses/1, catch_body/1, char_lit/1, char_val/1,
+ clause_arity/1, clause_body/1, clause_guard/1, clause_pats/1,
+ clause_vars/1, concrete/1, cons_hd/1, cons_tl/1, copy_ann/2,
+ data_arity/1, data_es/1, data_type/1, float_lit/1, float_val/1,
+ fname_arity/1, fname_id/1, fold_literal/1, from_records/1,
+ fun_arity/1, fun_body/1, fun_vars/1, get_ann/1, int_lit/1,
+ int_val/1, is_c_alias/1, is_c_apply/1, is_c_atom/1,
+ is_c_call/1, is_c_case/1, is_c_catch/1, is_c_char/1,
+ is_c_clause/1, is_c_cons/1, is_c_float/1, is_c_fname/1,
+ is_c_fun/1, is_c_int/1, is_c_let/1, is_c_letrec/1, is_c_list/1,
+ is_c_module/1, is_c_nil/1, is_c_primop/1, is_c_receive/1,
+ is_c_seq/1, is_c_string/1, is_c_try/1, is_c_tuple/1,
+ is_c_values/1, is_c_var/1, is_data/1, is_leaf/1, is_literal/1,
+ is_literal_term/1, is_print_char/1, is_print_string/1,
+ let_arg/1, let_arity/1, let_body/1, let_vars/1, letrec_body/1,
+ letrec_defs/1, letrec_vars/1, list_elements/1, list_length/1,
+ make_data/2, make_list/1, make_list/2, make_data_skel/2,
+ make_tree/2, meta/1, module_attrs/1, module_defs/1,
+ module_exports/1, module_name/1, module_vars/1,
+ pat_list_vars/1, pat_vars/1, primop_args/1, primop_arity/1,
+ primop_name/1, receive_action/1, receive_clauses/1,
+ receive_timeout/1, seq_arg/1, seq_body/1, set_ann/2,
+ string_lit/1, string_val/1, subtrees/1, to_records/1,
+ try_arg/1, try_body/1, try_vars/1, try_evars/1, try_handler/1,
+ tuple_arity/1, tuple_es/1, type/1, unfold_literal/1,
+ update_c_alias/3, update_c_apply/3, update_c_call/4,
+ update_c_case/3, update_c_catch/2, update_c_clause/4,
+ update_c_cons/3, update_c_cons_skel/3, update_c_fname/2,
+ update_c_fname/3, update_c_fun/3, update_c_let/4,
+ update_c_letrec/3, update_c_module/5, update_c_primop/3,
+ update_c_receive/4, update_c_seq/3, update_c_try/6,
+ update_c_tuple/2, update_c_tuple_skel/2, update_c_values/2,
+ update_c_var/2, update_data/3, update_list/2, update_list/3,
+ update_data_skel/3, update_tree/2, update_tree/3,
+ values_arity/1, values_es/1, var_name/1, c_binary/1,
+ update_c_binary/2, ann_c_binary/2, is_c_binary/1,
+ binary_segments/1, c_bitstr/3, c_bitstr/4, c_bitstr/5,
+ update_c_bitstr/5, update_c_bitstr/6, ann_c_bitstr/5,
+ ann_c_bitstr/6, is_c_bitstr/1, bitstr_val/1, bitstr_size/1,
+ bitstr_bitsize/1, bitstr_unit/1, bitstr_type/1,
+ bitstr_flags/1]).
+
+-include("core_parse.hrl").
+
+
+%% =====================================================================
+%% Representation (general)
+%%
+%% All nodes are represented by tuples of arity 2 or (generally)
+%% greater, whose first element is an atom which uniquely identifies the
+%% type of the node, and whose second element is a (proper) list of
+%% annotation terms associated with the node - this is by default empty.
+%%
+%% For most node constructor functions, there are analogous functions
+%% named 'ann_...', taking one extra argument 'As' (always the first
+%% argument), specifying an annotation list at node creation time.
+%% Similarly, there are also functions named 'update_...', taking one
+%% extra argument 'Old', specifying a node from which all fields not
+%% explicitly given as arguments should be copied (generally, this is
+%% the annotation field only).
+%% =====================================================================
+
+%% This defines the general representation of constant literals:
+
+-record(literal, {ann = [], val}).
+
+
+%% @spec type(Node::cerl()) -> atom()
+%%
+%% @doc Returns the type tag of Node
. Current node types
+%% are:
+%%
+%%
+%%
+%% alias |
+%% apply |
+%% binary |
+%% bitstr |
+%% call |
+%% case |
+%% catch |
+%%
+%% clause |
+%% cons |
+%% fun |
+%% let |
+%% letrec |
+%% literal |
+%% module |
+%%
+%% primop |
+%% receive |
+%% seq |
+%% try |
+%% tuple |
+%% values |
+%% var |
+%%
+%%
+%%
+%% Note: The name of the primary constructor function for a node
+%% type is always the name of the type itself, prefixed by
+%% "c_
"; recognizer predicates are correspondingly
+%% prefixed by "is_c_
". Furthermore, to simplify
+%% preservation of annotations (cf. get_ann/1
), there are
+%% analogous constructor functions prefixed by "ann_c_
"
+%% and "update_c_
", for setting the annotation list of
+%% the new node to either a specific value or to the annotations of an
+%% existing node, respectively.
+%%
+%% @see abstract/1
+%% @see c_alias/2
+%% @see c_apply/2
+%% @see c_binary/1
+%% @see c_bitstr/5
+%% @see c_call/3
+%% @see c_case/2
+%% @see c_catch/1
+%% @see c_clause/3
+%% @see c_cons/2
+%% @see c_fun/2
+%% @see c_let/3
+%% @see c_letrec/2
+%% @see c_module/3
+%% @see c_primop/2
+%% @see c_receive/1
+%% @see c_seq/2
+%% @see c_try/3
+%% @see c_tuple/1
+%% @see c_values/1
+%% @see c_var/1
+%% @see get_ann/1
+%% @see to_records/1
+%% @see from_records/1
+%% @see data_type/1
+%% @see subtrees/1
+%% @see meta/1
+
+type(Node) ->
+ element(1, Node).
+
+
+%% @spec is_leaf(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is a leaf node,
+%% otherwise false
. The current leaf node types are
+%% literal
and var
.
+%%
+%% Note: all literals (cf. is_literal/1
) are leaf
+%% nodes, even if they represent structured (constant) values such as
+%% {foo, [bar, baz]}
. Also note that variables are leaf
+%% nodes but not literals.
+%%
+%% @see type/1
+%% @see is_literal/1
+
+is_leaf(Node) ->
+ case type(Node) of
+ literal -> true;
+ var -> true;
+ _ -> false
+ end.
+
+
+%% @spec get_ann(cerl()) -> [term()]
+%%
+%% @doc Returns the list of user annotations associated with a syntax
+%% tree node. For a newly created node, this is the empty list. The
+%% annotations may be any terms.
+%%
+%% @see set_ann/2
+
+get_ann(Node) ->
+ element(2, Node).
+
+
+%% @spec set_ann(Node::cerl(), Annotations::[term()]) -> cerl()
+%%
+%% @doc Sets the list of user annotations of Node
to
+%% Annotations
.
+%%
+%% @see get_ann/1
+%% @see add_ann/2
+%% @see copy_ann/2
+
+set_ann(Node, List) ->
+ setelement(2, Node, List).
+
+
+%% @spec add_ann(Annotations::[term()], Node::cerl()) -> cerl()
+%%
+%% @doc Appends Annotations
to the list of user
+%% annotations of Node
.
+%%
+%% Note: this is equivalent to set_ann(Node, Annotations ++
+%% get_ann(Node))
, but potentially more efficient.
+%%
+%% @see get_ann/1
+%% @see set_ann/2
+
+add_ann(Terms, Node) ->
+ set_ann(Node, Terms ++ get_ann(Node)).
+
+
+%% @spec copy_ann(Source::cerl(), Target::cerl()) -> cerl()
+%%
+%% @doc Copies the list of user annotations from Source
+%% to Target
.
+%%
+%% Note: this is equivalent to set_ann(Target,
+%% get_ann(Source))
, but potentially more efficient.
+%%
+%% @see get_ann/1
+%% @see set_ann/2
+
+copy_ann(Source, Target) ->
+ set_ann(Target, get_ann(Source)).
+
+
+%% @spec abstract(Term::term()) -> cerl()
+%%
+%% @doc Creates a syntax tree corresponding to an Erlang term.
+%% Term
must be a literal term, i.e., one that can be
+%% represented as a source code literal. Thus, it may not contain a
+%% process identifier, port, reference, binary or function value as a
+%% subterm.
+%%
+%% Note: This is a constant time operation.
+%%
+%% @see ann_abstract/2
+%% @see concrete/1
+%% @see is_literal/1
+%% @see is_literal_term/1
+
+abstract(T) ->
+ #literal{val = T}.
+
+
+%% @spec ann_abstract(Annotations::[term()], Term::term()) -> cerl()
+%% @see abstract/1
+
+ann_abstract(As, T) ->
+ #literal{val = T, ann = As}.
+
+
+%% @spec is_literal_term(Term::term()) -> boolean()
+%%
+%% @doc Returns true
if Term
can be
+%% represented as a literal, otherwise false
. This
+%% function takes time proportional to the size of Term
.
+%%
+%% @see abstract/1
+
+is_literal_term(T) when integer(T) -> true;
+is_literal_term(T) when float(T) -> true;
+is_literal_term(T) when atom(T) -> true;
+is_literal_term([]) -> true;
+is_literal_term([H | T]) ->
+ case is_literal_term(H) of
+ true ->
+ is_literal_term(T);
+ false ->
+ false
+ end;
+is_literal_term(T) when tuple(T) ->
+ is_literal_term_list(tuple_to_list(T));
+is_literal_term(_) ->
+ false.
+
+is_literal_term_list([T | Ts]) ->
+ case is_literal_term(T) of
+ true ->
+ is_literal_term_list(Ts);
+ false ->
+ false
+ end;
+is_literal_term_list([]) ->
+ true.
+
+
+%% @spec concrete(Node::cerl()) -> term()
+%%
+%% @doc Returns the Erlang term represented by a syntax tree. An
+%% exception is thrown if Node
does not represent a
+%% literal term.
+%%
+%% Note: This is a constant time operation.
+%%
+%% @see abstract/1
+%% @see is_literal/1
+
+%% Because the normal tuple and list constructor operations always
+%% return a literal if the arguments are literals, 'concrete' and
+%% 'is_literal' never need to traverse the structure.
+
+concrete(#literal{val = V}) ->
+ V.
+
+
+%% @spec is_literal(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
represents a
+%% literal term, otherwise false
. This function returns
+%% true
if and only if the value of
+%% concrete(Node)
is defined.
+%%
+%% Note: This is a constant time operation.
+%%
+%% @see abstract/1
+%% @see concrete/1
+%% @see fold_literal/1
+
+is_literal(#literal{}) ->
+ true;
+is_literal(_) ->
+ false.
+
+
+%% @spec fold_literal(Node::cerl()) -> cerl()
+%%
+%% @doc Assures that literals have a compact representation. This is
+%% occasionally useful if c_cons_skel/2
,
+%% c_tuple_skel/1
or unfold_literal/1
were
+%% used in the construction of Node
, and you want to revert
+%% to the normal "folded" representation of literals. If
+%% Node
represents a tuple or list constructor, its
+%% elements are rewritten recursively, and the node is reconstructed
+%% using c_cons/2
or c_tuple/1
, respectively;
+%% otherwise, Node
is not changed.
+%%
+%% @see is_literal/1
+%% @see c_cons_skel/2
+%% @see c_tuple_skel/1
+%% @see c_cons/2
+%% @see c_tuple/1
+%% @see unfold_literal/1
+
+fold_literal(Node) ->
+ case type(Node) of
+ tuple ->
+ update_c_tuple(Node, fold_literal_list(tuple_es(Node)));
+ cons ->
+ update_c_cons(Node, fold_literal(cons_hd(Node)),
+ fold_literal(cons_tl(Node)));
+ _ ->
+ Node
+ end.
+
+fold_literal_list([E | Es]) ->
+ [fold_literal(E) | fold_literal_list(Es)];
+fold_literal_list([]) ->
+ [].
+
+
+%% @spec unfold_literal(Node::cerl()) -> cerl()
+%%
+%% @doc Assures that literals have a fully expanded representation. If
+%% Node
represents a literal tuple or list constructor, its
+%% elements are rewritten recursively, and the node is reconstructed
+%% using c_cons_skel/2
or c_tuple_skel/1
,
+%% respectively; otherwise, Node
is not changed. The {@link
+%% fold_literal/1} can be used to revert to the normal compact
+%% representation.
+%%
+%% @see is_literal/1
+%% @see c_cons_skel/2
+%% @see c_tuple_skel/1
+%% @see c_cons/2
+%% @see c_tuple/1
+%% @see fold_literal/1
+
+unfold_literal(Node) ->
+ case type(Node) of
+ literal ->
+ copy_ann(Node, unfold_concrete(concrete(Node)));
+ _ ->
+ Node
+ end.
+
+unfold_concrete(Val) ->
+ case Val of
+ _ when tuple(Val) ->
+ c_tuple_skel(unfold_concrete_list(tuple_to_list(Val)));
+ [H|T] ->
+ c_cons_skel(unfold_concrete(H), unfold_concrete(T));
+ _ ->
+ abstract(Val)
+ end.
+
+unfold_concrete_list([E | Es]) ->
+ [unfold_concrete(E) | unfold_concrete_list(Es)];
+unfold_concrete_list([]) ->
+ [].
+
+
+%% ---------------------------------------------------------------------
+
+-record(module, {ann = [], name, exports, attrs, defs}).
+
+
+%% @spec c_module(Name::cerl(), Exports, Definitions) -> cerl()
+%%
+%% Exports = [cerl()]
+%% Definitions = [{cerl(), cerl()}]
+%%
+%% @equiv c_module(Name, Exports, [], Definitions)
+
+c_module(Name, Exports, Es) ->
+ #module{name = Name, exports = Exports, attrs = [], defs = Es}.
+
+
+%% @spec c_module(Name::cerl(), Exports, Attributes, Definitions) ->
+%% cerl()
+%%
+%% Exports = [cerl()]
+%% Attributes = [{cerl(), cerl()}]
+%% Definitions = [{cerl(), cerl()}]
+%%
+%% @doc Creates an abstract module definition. The result represents
+%%
+%% module Name [E1, ..., Ek]
+%% attributes [K1 = T1, ...,
+%% Km = Tm]
+%% V1 = F1
+%% ...
+%% Vn = Fn
+%% end
+%%
+%% if Exports
= [E1, ..., Ek]
,
+%% Attributes
= [{K1, T1}, ..., {Km, Tm}]
,
+%% and Definitions
= [{V1, F1}, ..., {Vn,
+%% Fn}]
.
+%%
+%% Name
and all the Ki
must be atom
+%% literals, and all the Ti
must be constant literals. All
+%% the Vi
and Ei
must have type
+%% var
and represent function names. All the
+%% Fi
must have type 'fun'
.
+%%
+%% @see c_module/3
+%% @see module_name/1
+%% @see module_exports/1
+%% @see module_attrs/1
+%% @see module_defs/1
+%% @see module_vars/1
+%% @see ann_c_module/4
+%% @see ann_c_module/5
+%% @see update_c_module/5
+%% @see c_atom/1
+%% @see c_var/1
+%% @see c_fun/2
+%% @see is_literal/1
+
+c_module(Name, Exports, Attrs, Es) ->
+ #module{name = Name, exports = Exports, attrs = Attrs, defs = Es}.
+
+
+%% @spec ann_c_module(As::[term()], Name::cerl(), Exports,
+%% Definitions) -> cerl()
+%%
+%% Exports = [cerl()]
+%% Definitions = [{cerl(), cerl()}]
+%%
+%% @see c_module/3
+%% @see ann_c_module/5
+
+ann_c_module(As, Name, Exports, Es) ->
+ #module{name = Name, exports = Exports, attrs = [], defs = Es,
+ ann = As}.
+
+
+%% @spec ann_c_module(As::[term()], Name::cerl(), Exports,
+%% Attributes, Definitions) -> cerl()
+%%
+%% Exports = [cerl()]
+%% Attributes = [{cerl(), cerl()}]
+%% Definitions = [{cerl(), cerl()}]
+%%
+%% @see c_module/4
+%% @see ann_c_module/4
+
+ann_c_module(As, Name, Exports, Attrs, Es) ->
+ #module{name = Name, exports = Exports, attrs = Attrs, defs = Es,
+ ann = As}.
+
+
+%% @spec update_c_module(Old::cerl(), Name::cerl(), Exports,
+%% Attributes, Definitions) -> cerl()
+%%
+%% Exports = [cerl()]
+%% Attributes = [{cerl(), cerl()}]
+%% Definitions = [{cerl(), cerl()}]
+%%
+%% @see c_module/4
+
+update_c_module(Node, Name, Exports, Attrs, Es) ->
+ #module{name = Name, exports = Exports, attrs = Attrs, defs = Es,
+ ann = get_ann(Node)}.
+
+
+%% @spec is_c_module(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% module definition, otherwise false
.
+%%
+%% @see type/1
+
+is_c_module(#module{}) ->
+ true;
+is_c_module(_) ->
+ false.
+
+
+%% @spec module_name(Node::cerl()) -> cerl()
+%%
+%% @doc Returns the name subtree of an abstract module definition.
+%%
+%% @see c_module/4
+
+module_name(Node) ->
+ Node#module.name.
+
+
+%% @spec module_exports(Node::cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of exports subtrees of an abstract module
+%% definition.
+%%
+%% @see c_module/4
+
+module_exports(Node) ->
+ Node#module.exports.
+
+
+%% @spec module_attrs(Node::cerl()) -> [{cerl(), cerl()}]
+%%
+%% @doc Returns the list of pairs of attribute key/value subtrees of
+%% an abstract module definition.
+%%
+%% @see c_module/4
+
+module_attrs(Node) ->
+ Node#module.attrs.
+
+
+%% @spec module_defs(Node::cerl()) -> [{cerl(), cerl()}]
+%%
+%% @doc Returns the list of function definitions of an abstract module
+%% definition.
+%%
+%% @see c_module/4
+
+module_defs(Node) ->
+ Node#module.defs.
+
+
+%% @spec module_vars(Node::cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of left-hand side function variable subtrees
+%% of an abstract module definition.
+%%
+%% @see c_module/4
+
+module_vars(Node) ->
+ [F || {F, _} <- module_defs(Node)].
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_int(Value::integer()) -> cerl()
+%%
+%%
+%% @doc Creates an abstract integer literal. The lexical
+%% representation is the canonical decimal numeral of
+%% Value
.
+%%
+%% @see ann_c_int/2
+%% @see is_c_int/1
+%% @see int_val/1
+%% @see int_lit/1
+%% @see c_char/1
+
+c_int(Value) ->
+ #literal{val = Value}.
+
+
+%% @spec ann_c_int(As::[term()], Value::integer()) -> cerl()
+%% @see c_int/1
+
+ann_c_int(As, Value) ->
+ #literal{val = Value, ann = As}.
+
+
+%% @spec is_c_int(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
represents an
+%% integer literal, otherwise false
.
+%% @see c_int/1
+
+is_c_int(#literal{val = V}) when integer(V) ->
+ true;
+is_c_int(_) ->
+ false.
+
+
+%% @spec int_val(cerl()) -> integer()
+%%
+%% @doc Returns the value represented by an integer literal node.
+%% @see c_int/1
+
+int_val(Node) ->
+ Node#literal.val.
+
+
+%% @spec int_lit(cerl()) -> string()
+%%
+%% @doc Returns the numeral string represented by an integer literal
+%% node.
+%% @see c_int/1
+
+int_lit(Node) ->
+ integer_to_list(int_val(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_float(Value::float()) -> cerl()
+%%
+%% @doc Creates an abstract floating-point literal. The lexical
+%% representation is the decimal floating-point numeral of
+%% Value
.
+%%
+%% @see ann_c_float/2
+%% @see is_c_float/1
+%% @see float_val/1
+%% @see float_lit/1
+
+%% Note that not all floating-point numerals can be represented with
+%% full precision.
+
+c_float(Value) ->
+ #literal{val = Value}.
+
+
+%% @spec ann_c_float(As::[term()], Value::float()) -> cerl()
+%% @see c_float/1
+
+ann_c_float(As, Value) ->
+ #literal{val = Value, ann = As}.
+
+
+%% @spec is_c_float(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
represents a
+%% floating-point literal, otherwise false
.
+%% @see c_float/1
+
+is_c_float(#literal{val = V}) when float(V) ->
+ true;
+is_c_float(_) ->
+ false.
+
+
+%% @spec float_val(cerl()) -> float()
+%%
+%% @doc Returns the value represented by a floating-point literal
+%% node.
+%% @see c_float/1
+
+float_val(Node) ->
+ Node#literal.val.
+
+
+%% @spec float_lit(cerl()) -> string()
+%%
+%% @doc Returns the numeral string represented by a floating-point
+%% literal node.
+%% @see c_float/1
+
+float_lit(Node) ->
+ float_to_list(float_val(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_atom(Name) -> cerl()
+%% Name = atom() | string()
+%%
+%% @doc Creates an abstract atom literal. The print name of the atom
+%% is the character sequence represented by Name
.
+%%
+%% Note: passing a string as argument to this function causes a
+%% corresponding atom to be created for the internal representation.
+%%
+%% @see ann_c_atom/2
+%% @see is_c_atom/1
+%% @see atom_val/1
+%% @see atom_name/1
+%% @see atom_lit/1
+
+c_atom(Name) when atom(Name) ->
+ #literal{val = Name};
+c_atom(Name) ->
+ #literal{val = list_to_atom(Name)}.
+
+
+%% @spec ann_c_atom(As::[term()], Name) -> cerl()
+%% Name = atom() | string()
+%% @see c_atom/1
+
+ann_c_atom(As, Name) when atom(Name) ->
+ #literal{val = Name, ann = As};
+ann_c_atom(As, Name) ->
+ #literal{val = list_to_atom(Name), ann = As}.
+
+
+%% @spec is_c_atom(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
represents an
+%% atom literal, otherwise false
.
+%%
+%% @see c_atom/1
+
+is_c_atom(#literal{val = V}) when atom(V) ->
+ true;
+is_c_atom(_) ->
+ false.
+
+%% @spec atom_val(cerl())-> atom()
+%%
+%% @doc Returns the value represented by an abstract atom.
+%%
+%% @see c_atom/1
+
+atom_val(Node) ->
+ Node#literal.val.
+
+
+%% @spec atom_name(cerl()) -> string()
+%%
+%% @doc Returns the printname of an abstract atom.
+%%
+%% @see c_atom/1
+
+atom_name(Node) ->
+ atom_to_list(atom_val(Node)).
+
+
+%% @spec atom_lit(cerl()) -> string()
+%%
+%% @doc Returns the literal string represented by an abstract
+%% atom. This always includes surrounding single-quote characters.
+%%
+%% Note that an abstract atom may have several literal
+%% representations, and that the representation yielded by this
+%% function is not fixed; e.g.,
+%% atom_lit(c_atom("a\012b"))
could yield the string
+%% "\'a\\nb\'"
.
+%%
+%% @see c_atom/1
+
+%% TODO: replace the use of the unofficial 'write_string/2'.
+
+atom_lit(Node) ->
+ io_lib:write_string(atom_name(Node), $'). %' stupid Emacs.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_char(Value) -> cerl()
+%%
+%% Value = char() | integer()
+%%
+%% @doc Creates an abstract character literal. If the local
+%% implementation of Erlang defines char()
as a subset of
+%% integer()
, this function is equivalent to
+%% c_int/1
. Otherwise, if the given value is an integer,
+%% it will be converted to the character with the corresponding
+%% code. The lexical representation of a character is
+%% "$Char
", where Char
is a single
+%% printing character or an escape sequence.
+%%
+%% @see c_int/1
+%% @see c_string/1
+%% @see ann_c_char/2
+%% @see is_c_char/1
+%% @see char_val/1
+%% @see char_lit/1
+%% @see is_print_char/1
+
+c_char(Value) when integer(Value), Value >= 0 ->
+ #literal{val = Value}.
+
+
+%% @spec ann_c_char(As::[term()], Value::char()) -> cerl()
+%% @see c_char/1
+
+ann_c_char(As, Value) ->
+ #literal{val = Value, ann = As}.
+
+
+%% @spec is_c_char(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
may represent a
+%% character literal, otherwise false
.
+%%
+%% If the local implementation of Erlang defines
+%% char()
as a subset of integer()
, then
+%% is_c_int(Node)
will also yield
+%% true
.
+%%
+%% @see c_char/1
+%% @see is_print_char/1
+
+is_c_char(#literal{val = V}) when integer(V), V >= 0 ->
+ is_char_value(V);
+is_c_char(_) ->
+ false.
+
+
+%% @spec is_print_char(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
may represent a
+%% "printing" character, otherwise false
. (Cf.
+%% is_c_char/1
.) A "printing" character has either a
+%% given graphical representation, or a "named" escape sequence such
+%% as "\n
". Currently, only ISO 8859-1 (Latin-1)
+%% character values are recognized.
+%%
+%% @see c_char/1
+%% @see is_c_char/1
+
+is_print_char(#literal{val = V}) when integer(V), V >= 0 ->
+ is_print_char_value(V);
+is_print_char(_) ->
+ false.
+
+
+%% @spec char_val(cerl()) -> char()
+%%
+%% @doc Returns the value represented by an abstract character literal.
+%%
+%% @see c_char/1
+
+char_val(Node) ->
+ Node#literal.val.
+
+
+%% @spec char_lit(cerl()) -> string()
+%%
+%% @doc Returns the literal string represented by an abstract
+%% character. This includes a leading $
+%% character. Currently, all characters that are not in the set of ISO
+%% 8859-1 (Latin-1) "printing" characters will be escaped.
+%%
+%% @see c_char/1
+
+char_lit(Node) ->
+ io_lib:write_char(char_val(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_string(Value::string()) -> cerl()
+%%
+%% @doc Creates an abstract string literal. Equivalent to creating an
+%% abstract list of the corresponding character literals
+%% (cf. is_c_string/1
), but is typically more
+%% efficient. The lexical representation of a string is
+%% ""Chars"
", where Chars
is a
+%% sequence of printing characters or spaces.
+%%
+%% @see c_char/1
+%% @see ann_c_string/2
+%% @see is_c_string/1
+%% @see string_val/1
+%% @see string_lit/1
+%% @see is_print_string/1
+
+c_string(Value) ->
+ #literal{val = Value}.
+
+
+%% @spec ann_c_string(As::[term()], Value::string()) -> cerl()
+%% @see c_string/1
+
+ann_c_string(As, Value) ->
+ #literal{val = Value, ann = As}.
+
+
+%% @spec is_c_string(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
may represent a
+%% string literal, otherwise false
. Strings are defined
+%% as lists of characters; see is_c_char/1
for details.
+%%
+%% @see c_string/1
+%% @see is_c_char/1
+%% @see is_print_string/1
+
+is_c_string(#literal{val = V}) ->
+ is_char_list(V);
+is_c_string(_) ->
+ false.
+
+
+%% @spec is_print_string(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
may represent a
+%% string literal containing only "printing" characters, otherwise
+%% false
. See is_c_string/1
and
+%% is_print_char/1
for details. Currently, only ISO
+%% 8859-1 (Latin-1) character values are recognized.
+%%
+%% @see c_string/1
+%% @see is_c_string/1
+%% @see is_print_char/1
+
+is_print_string(#literal{val = V}) ->
+ is_print_char_list(V);
+is_print_string(_) ->
+ false.
+
+
+%% @spec string_val(cerl()) -> string()
+%%
+%% @doc Returns the value represented by an abstract string literal.
+%%
+%% @see c_string/1
+
+string_val(Node) ->
+ Node#literal.val.
+
+
+%% @spec string_lit(cerl()) -> string()
+%%
+%% @doc Returns the literal string represented by an abstract string.
+%% This includes surrounding double-quote characters
+%% "..."
. Currently, characters that are not in the set
+%% of ISO 8859-1 (Latin-1) "printing" characters will be escaped,
+%% except for spaces.
+%%
+%% @see c_string/1
+
+string_lit(Node) ->
+ io_lib:write_string(string_val(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_nil() -> cerl()
+%%
+%% @doc Creates an abstract empty list. The result represents
+%% "[]
". The empty list is traditionally called "nil".
+%%
+%% @see ann_c_nil/1
+%% @see is_c_list/1
+%% @see c_cons/2
+
+c_nil() ->
+ #literal{val = []}.
+
+
+%% @spec ann_c_nil(As::[term()]) -> cerl()
+%% @see c_nil/0
+
+ann_c_nil(As) ->
+ #literal{val = [], ann = As}.
+
+
+%% @spec is_c_nil(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% empty list, otherwise false
.
+
+is_c_nil(#literal{val = []}) ->
+ true;
+is_c_nil(_) ->
+ false.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_cons(Head::cerl(), Tail::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract list constructor. The result represents
+%% "[Head | Tail]
". Note that if both
+%% Head
and Tail
have type
+%% literal
, then the result will also have type
+%% literal
, and annotations on Head
and
+%% Tail
are lost.
+%%
+%% Recall that in Erlang, the tail element of a list constructor is
+%% not necessarily a list.
+%%
+%% @see ann_c_cons/3
+%% @see update_c_cons/3
+%% @see c_cons_skel/2
+%% @see is_c_cons/1
+%% @see cons_hd/1
+%% @see cons_tl/1
+%% @see is_c_list/1
+%% @see c_nil/0
+%% @see list_elements/1
+%% @see list_length/1
+%% @see make_list/2
+
+-record(cons, {ann = [], hd, tl}).
+
+%% *Always* collapse literals.
+
+c_cons(#literal{val = Head}, #literal{val = Tail}) ->
+ #literal{val = [Head | Tail]};
+c_cons(Head, Tail) ->
+ #cons{hd = Head, tl = Tail}.
+
+
+%% @spec ann_c_cons(As::[term()], Head::cerl(), Tail::cerl()) -> cerl()
+%% @see c_cons/2
+
+ann_c_cons(As, #literal{val = Head}, #literal{val = Tail}) ->
+ #literal{val = [Head | Tail], ann = As};
+ann_c_cons(As, Head, Tail) ->
+ #cons{hd = Head, tl = Tail, ann = As}.
+
+
+%% @spec update_c_cons(Old::cerl(), Head::cerl(), Tail::cerl()) ->
+%% cerl()
+%% @see c_cons/2
+
+update_c_cons(Node, #literal{val = Head}, #literal{val = Tail}) ->
+ #literal{val = [Head | Tail], ann = get_ann(Node)};
+update_c_cons(Node, Head, Tail) ->
+ #cons{hd = Head, tl = Tail, ann = get_ann(Node)}.
+
+
+%% @spec c_cons_skel(Head::cerl(), Tail::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract list constructor skeleton. Does not fold
+%% constant literals, i.e., the result always has type
+%% cons
, representing "[Head |
+%% Tail]
".
+%%
+%% This function is occasionally useful when it is necessary to have
+%% annotations on the subnodes of a list constructor node, even when the
+%% subnodes are constant literals. Note however that
+%% is_literal/1
will yield false
and
+%% concrete/1
will fail if passed the result from this
+%% function.
+%%
+%% fold_literal/1
can be used to revert a node to the
+%% normal-form representation.
+%%
+%% @see ann_c_cons_skel/3
+%% @see update_c_cons_skel/3
+%% @see c_cons/2
+%% @see is_c_cons/1
+%% @see is_c_list/1
+%% @see c_nil/0
+%% @see is_literal/1
+%% @see fold_literal/1
+%% @see concrete/1
+
+%% *Never* collapse literals.
+
+c_cons_skel(Head, Tail) ->
+ #cons{hd = Head, tl = Tail}.
+
+
+%% @spec ann_c_cons_skel(As::[term()], Head::cerl(), Tail::cerl()) ->
+%% cerl()
+%% @see c_cons_skel/2
+
+ann_c_cons_skel(As, Head, Tail) ->
+ #cons{hd = Head, tl = Tail, ann = As}.
+
+
+%% @spec update_c_cons_skel(Old::cerl(), Head::cerl(), Tail::cerl()) ->
+%% cerl()
+%% @see c_cons_skel/2
+
+update_c_cons_skel(Node, Head, Tail) ->
+ #cons{hd = Head, tl = Tail, ann = get_ann(Node)}.
+
+
+%% @spec is_c_cons(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% list constructor, otherwise false
.
+
+is_c_cons(#cons{}) ->
+ true;
+is_c_cons(#literal{val = [_ | _]}) ->
+ true;
+is_c_cons(_) ->
+ false.
+
+
+%% @spec cons_hd(cerl()) -> cerl()
+%%
+%% @doc Returns the head subtree of an abstract list constructor.
+%%
+%% @see c_cons/2
+
+cons_hd(#cons{hd = Head}) ->
+ Head;
+cons_hd(#literal{val = [Head | _]}) ->
+ #literal{val = Head}.
+
+
+%% @spec cons_tl(cerl()) -> cerl()
+%%
+%% @doc Returns the tail subtree of an abstract list constructor.
+%%
+%% Recall that the tail does not necessarily represent a proper
+%% list.
+%%
+%% @see c_cons/2
+
+cons_tl(#cons{tl = Tail}) ->
+ Tail;
+cons_tl(#literal{val = [_ | Tail]}) ->
+ #literal{val = Tail}.
+
+
+%% @spec is_c_list(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
represents a
+%% proper list, otherwise false
. A proper list is either
+%% the empty list []
, or a cons cell [Head |
+%% Tail]
, where recursively Tail
is a
+%% proper list.
+%%
+%% Note: Because Node
is a syntax tree, the actual
+%% run-time values corresponding to its subtrees may often be partially
+%% or completely unknown. Thus, if Node
represents e.g.
+%% "[... | Ns]
" (where Ns
is a variable), then
+%% the function will return false
, because it is not known
+%% whether Ns
will be bound to a list at run-time. If
+%% Node
instead represents e.g. "[1, 2, 3]
" or
+%% "[A | []]
", then the function will return
+%% true
.
+%%
+%% @see c_cons/2
+%% @see c_nil/0
+%% @see list_elements/1
+%% @see list_length/1
+
+is_c_list(#cons{tl = Tail}) ->
+ is_c_list(Tail);
+is_c_list(#literal{val = V}) ->
+ is_proper_list(V);
+is_c_list(_) ->
+ false.
+
+is_proper_list([_ | Tail]) ->
+ is_proper_list(Tail);
+is_proper_list([]) ->
+ true;
+is_proper_list(_) ->
+ false.
+
+%% @spec list_elements(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of element subtrees of an abstract list.
+%% Node
must represent a proper list. E.g., if
+%% Node
represents "[X1, X2 |
+%% [X3, X4 | []]
", then
+%% list_elements(Node)
yields the list [X1, X2, X3,
+%% X4]
.
+%%
+%% @see c_cons/2
+%% @see c_nil/1
+%% @see is_c_list/1
+%% @see list_length/1
+%% @see make_list/2
+
+list_elements(#cons{hd = Head, tl = Tail}) ->
+ [Head | list_elements(Tail)];
+list_elements(#literal{val = V}) ->
+ abstract_list(V).
+
+abstract_list([X | Xs]) ->
+ [abstract(X) | abstract_list(Xs)];
+abstract_list([]) ->
+ [].
+
+
+%% @spec list_length(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of element subtrees of an abstract list.
+%% Node
must represent a proper list. E.g., if
+%% Node
represents "[X1 | [X2, X3 | [X4, X5,
+%% X6]]]
", then list_length(Node)
returns the
+%% integer 6.
+%%
+%% Note: this is equivalent to
+%% length(list_elements(Node))
, but potentially more
+%% efficient.
+%%
+%% @see c_cons/2
+%% @see c_nil/1
+%% @see is_c_list/1
+%% @see list_elements/1
+
+list_length(L) ->
+ list_length(L, 0).
+
+list_length(#cons{tl = Tail}, A) ->
+ list_length(Tail, A + 1);
+list_length(#literal{val = V}, A) ->
+ A + length(V).
+
+
+%% @spec make_list(List) -> Node
+%% @equiv make_list(List, none)
+
+make_list(List) ->
+ ann_make_list([], List).
+
+
+%% @spec make_list(List::[cerl()], Tail) -> cerl()
+%%
+%% Tail = cerl() | none
+%%
+%% @doc Creates an abstract list from the elements in List
+%% and the optional Tail
. If Tail
is
+%% none
, the result will represent a nil-terminated list,
+%% otherwise it represents "[... | Tail]
".
+%%
+%% @see c_cons/2
+%% @see c_nil/0
+%% @see ann_make_list/3
+%% @see update_list/3
+%% @see list_elements/1
+
+make_list(List, Tail) ->
+ ann_make_list([], List, Tail).
+
+
+%% @spec update_list(Old::cerl(), List::[cerl()]) -> cerl()
+%% @equiv update_list(Old, List, none)
+
+update_list(Node, List) ->
+ ann_make_list(get_ann(Node), List).
+
+
+%% @spec update_list(Old::cerl(), List::[cerl()], Tail) -> cerl()
+%%
+%% Tail = cerl() | none
+%%
+%% @see make_list/2
+%% @see update_list/2
+
+update_list(Node, List, Tail) ->
+ ann_make_list(get_ann(Node), List, Tail).
+
+
+%% @spec ann_make_list(As::[term()], List::[cerl()]) -> cerl()
+%% @equiv ann_make_list(As, List, none)
+
+ann_make_list(As, List) ->
+ ann_make_list(As, List, none).
+
+
+%% @spec ann_make_list(As::[term()], List::[cerl()], Tail) -> cerl()
+%%
+%% Tail = cerl() | none
+%%
+%% @see make_list/2
+%% @see ann_make_list/2
+
+ann_make_list(As, [H | T], Tail) ->
+ ann_c_cons(As, H, make_list(T, Tail)); % `c_cons' folds literals
+ann_make_list(As, [], none) ->
+ ann_c_nil(As);
+ann_make_list(_, [], Node) ->
+ Node.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_tuple(Elements::[cerl()]) -> cerl()
+%%
+%% @doc Creates an abstract tuple. If Elements
is
+%% [E1, ..., En]
, the result represents
+%% "{E1, ..., En}
". Note that if all
+%% nodes in Elements
have type literal
, or if
+%% Elements
is empty, then the result will also have type
+%% literal
and annotations on nodes in
+%% Elements
are lost.
+%%
+%% Recall that Erlang has distinct 1-tuples, i.e., {X}
+%% is always distinct from X
itself.
+%%
+%% @see ann_c_tuple/2
+%% @see update_c_tuple/2
+%% @see is_c_tuple/1
+%% @see tuple_es/1
+%% @see tuple_arity/1
+%% @see c_tuple_skel/1
+
+-record(tuple, {ann = [], es}).
+
+%% *Always* collapse literals.
+
+c_tuple(Es) ->
+ case is_lit_list(Es) of
+ false ->
+ #tuple{es = Es};
+ true ->
+ #literal{val = list_to_tuple(lit_list_vals(Es))}
+ end.
+
+
+%% @spec ann_c_tuple(As::[term()], Elements::[cerl()]) -> cerl()
+%% @see c_tuple/1
+
+ann_c_tuple(As, Es) ->
+ case is_lit_list(Es) of
+ false ->
+ #tuple{es = Es, ann = As};
+ true ->
+ #literal{val = list_to_tuple(lit_list_vals(Es)), ann = As}
+ end.
+
+
+%% @spec update_c_tuple(Old::cerl(), Elements::[cerl()]) -> cerl()
+%% @see c_tuple/1
+
+update_c_tuple(Node, Es) ->
+ case is_lit_list(Es) of
+ false ->
+ #tuple{es = Es, ann = get_ann(Node)};
+ true ->
+ #literal{val = list_to_tuple(lit_list_vals(Es)),
+ ann = get_ann(Node)}
+ end.
+
+
+%% @spec c_tuple_skel(Elements::[cerl()]) -> cerl()
+%%
+%% @doc Creates an abstract tuple skeleton. Does not fold constant
+%% literals, i.e., the result always has type tuple
,
+%% representing "{E1, ..., En}
", if
+%% Elements
is [E1, ..., En]
.
+%%
+%% This function is occasionally useful when it is necessary to have
+%% annotations on the subnodes of a tuple node, even when all the
+%% subnodes are constant literals. Note however that
+%% is_literal/1
will yield false
and
+%% concrete/1
will fail if passed the result from this
+%% function.
+%%
+%% fold_literal/1
can be used to revert a node to the
+%% normal-form representation.
+%%
+%% @see ann_c_tuple_skel/2
+%% @see update_c_tuple_skel/2
+%% @see c_tuple/1
+%% @see tuple_es/1
+%% @see is_c_tuple/1
+%% @see is_literal/1
+%% @see fold_literal/1
+%% @see concrete/1
+
+%% *Never* collapse literals.
+
+c_tuple_skel(Es) ->
+ #tuple{es = Es}.
+
+
+%% @spec ann_c_tuple_skel(As::[term()], Elements::[cerl()]) -> cerl()
+%% @see c_tuple_skel/1
+
+ann_c_tuple_skel(As, Es) ->
+ #tuple{es = Es, ann = As}.
+
+
+%% @spec update_c_tuple_skel(Old::cerl(), Elements::[cerl()]) -> cerl()
+%% @see c_tuple_skel/1
+
+update_c_tuple_skel(Old, Es) ->
+ #tuple{es = Es, ann = get_ann(Old)}.
+
+
+%% @spec is_c_tuple(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% tuple, otherwise false
.
+%%
+%% @see c_tuple/1
+
+is_c_tuple(#tuple{}) ->
+ true;
+is_c_tuple(#literal{val = V}) when tuple(V) ->
+ true;
+is_c_tuple(_) ->
+ false.
+
+
+%% @spec tuple_es(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of element subtrees of an abstract tuple.
+%%
+%% @see c_tuple/1
+
+tuple_es(#tuple{es = Es}) ->
+ Es;
+tuple_es(#literal{val = V}) ->
+ make_lit_list(tuple_to_list(V)).
+
+
+%% @spec tuple_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of element subtrees of an abstract tuple.
+%%
+%% Note: this is equivalent to length(tuple_es(Node))
,
+%% but potentially more efficient.
+%%
+%% @see tuple_es/1
+%% @see c_tuple/1
+
+tuple_arity(#tuple{es = Es}) ->
+ length(Es);
+tuple_arity(#literal{val = V}) when tuple(V) ->
+ size(V).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_var(Name::var_name()) -> cerl()
+%%
+%% var_name() = integer() | atom() | {atom(), integer()}
+%%
+%% @doc Creates an abstract variable. A variable is identified by its
+%% name, given by the Name
parameter.
+%%
+%% If a name is given by a single atom, it should either be a
+%% "simple" atom which does not need to be single-quoted in Erlang, or
+%% otherwise its print name should correspond to a proper Erlang
+%% variable, i.e., begin with an uppercase character or an
+%% underscore. Names on the form {A, N}
represent
+%% function name variables "A/N
"; these
+%% are special variables which may be bound only in the function
+%% definitions of a module or a letrec
. They may not be
+%% bound in let
expressions and cannot occur in clause
+%% patterns. The atom A
in a function name may be any
+%% atom; the integer N
must be nonnegative. The functions
+%% c_fname/2
etc. are utilities for handling function
+%% name variables.
+%%
+%% When printing variable names, they must have the form of proper
+%% Core Erlang variables and function names. E.g., a name represented
+%% by an integer such as 42
could be formatted as
+%% "_42
", an atom 'Xxx'
simply as
+%% "Xxx
", and an atom foo
as
+%% "_foo
". However, one must assure that any two valid
+%% distinct names are never mapped to the same strings. Tuples such
+%% as {foo, 2}
representing function names can simply by
+%% formatted as "'foo'/2
", with no risk of conflicts.
+%%
+%% @see ann_c_var/2
+%% @see update_c_var/2
+%% @see is_c_var/1
+%% @see var_name/1
+%% @see c_fname/2
+%% @see c_module/4
+%% @see c_letrec/2
+
+-record(var, {ann = [], name}).
+
+c_var(Name) ->
+ #var{name = Name}.
+
+
+%% @spec ann_c_var(As::[term()], Name::var_name()) -> cerl()
+%%
+%% @see c_var/1
+
+ann_c_var(As, Name) ->
+ #var{name = Name, ann = As}.
+
+%% @spec update_c_var(Old::cerl(), Name::var_name()) -> cerl()
+%%
+%% @see c_var/1
+
+update_c_var(Node, Name) ->
+ #var{name = Name, ann = get_ann(Node)}.
+
+
+%% @spec is_c_var(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% variable, otherwise false
.
+%%
+%% @see c_var/1
+
+is_c_var(#var{}) ->
+ true;
+is_c_var(_) ->
+ false.
+
+
+%% @spec c_fname(Name::atom(), Arity::integer()) -> cerl()
+%% @equiv c_var({Name, Arity})
+%% @see fname_id/1
+%% @see fname_arity/1
+%% @see is_c_fname/1
+%% @see ann_c_fname/3
+%% @see update_c_fname/3
+
+c_fname(Atom, Arity) ->
+ c_var({Atom, Arity}).
+
+
+%% @spec ann_c_fname(As::[term()], Name::atom(), Arity::integer()) ->
+%% cerl()
+%% @equiv ann_c_var(As, {Atom, Arity})
+%% @see c_fname/2
+
+ann_c_fname(As, Atom, Arity) ->
+ ann_c_var(As, {Atom, Arity}).
+
+
+%% @spec update_c_fname(Old::cerl(), Name::atom()) -> cerl()
+%% @doc Like update_c_fname/3
, but takes the arity from
+%% Node
.
+%% @see update_c_fname/3
+%% @see c_fname/2
+
+update_c_fname(#var{name = {_, Arity}, ann = As}, Atom) ->
+ #var{name = {Atom, Arity}, ann = As}.
+
+
+%% @spec update_c_fname(Old::cerl(), Name::atom(), Arity::integer()) ->
+%% cerl()
+%% @equiv update_c_var(Old, {Atom, Arity})
+%% @see update_c_fname/2
+%% @see c_fname/2
+
+update_c_fname(Node, Atom, Arity) ->
+ update_c_var(Node, {Atom, Arity}).
+
+
+%% @spec is_c_fname(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% function name variable, otherwise false
.
+%%
+%% @see c_fname/2
+%% @see c_var/1
+%% @see c_var_name/1
+
+is_c_fname(#var{name = {A, N}}) when atom(A), integer(N), N >= 0 ->
+ true;
+is_c_fname(_) ->
+ false.
+
+
+%% @spec var_name(cerl()) -> var_name()
+%%
+%% @doc Returns the name of an abstract variable.
+%%
+%% @see c_var/1
+
+var_name(Node) ->
+ Node#var.name.
+
+
+%% @spec fname_id(cerl()) -> atom()
+%%
+%% @doc Returns the identifier part of an abstract function name
+%% variable.
+%%
+%% @see fname_arity/1
+%% @see c_fname/2
+
+fname_id(#var{name={A,_}}) ->
+ A.
+
+
+%% @spec fname_arity(cerl()) -> integer()
+%%
+%% @doc Returns the arity part of an abstract function name variable.
+%%
+%% @see fname_id/1
+%% @see c_fname/2
+
+fname_arity(#var{name={_,N}}) ->
+ N.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_values(Elements::[cerl()]) -> cerl()
+%%
+%% @doc Creates an abstract value list. If Elements
is
+%% [E1, ..., En]
, the result represents
+%% "<E1, ..., En>
".
+%%
+%% @see ann_c_values/2
+%% @see update_c_values/2
+%% @see is_c_values/1
+%% @see values_es/1
+%% @see values_arity/1
+
+-record(values, {ann = [], es}).
+
+c_values(Es) ->
+ #values{es = Es}.
+
+
+%% @spec ann_c_values(As::[term()], Elements::[cerl()]) -> cerl()
+%% @see c_values/1
+
+ann_c_values(As, Es) ->
+ #values{es = Es, ann = As}.
+
+
+%% @spec update_c_values(Old::cerl(), Elements::[cerl()]) -> cerl()
+%% @see c_values/1
+
+update_c_values(Node, Es) ->
+ #values{es = Es, ann = get_ann(Node)}.
+
+
+%% @spec is_c_values(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% value list; otherwise false
.
+%%
+%% @see c_values/1
+
+is_c_values(#values{}) ->
+ true;
+is_c_values(_) ->
+ false.
+
+
+%% @spec values_es(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of element subtrees of an abstract value
+%% list.
+%%
+%% @see c_values/1
+%% @see values_arity/1
+
+values_es(Node) ->
+ Node#values.es.
+
+
+%% @spec values_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of element subtrees of an abstract value
+%% list.
+%%
+%% Note: This is equivalent to
+%% length(values_es(Node))
, but potentially more
+%% efficient.
+%%
+%% @see c_values/1
+%% @see values_es/1
+
+values_arity(Node) ->
+ length(values_es(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_binary(Segments::[cerl()]) -> cerl()
+%%
+%% @doc Creates an abstract binary-template. A binary object is a
+%% sequence of 8-bit bytes. It is specified by zero or more bit-string
+%% template segments of arbitrary lengths (in number of bits),
+%% such that the sum of the lengths is evenly divisible by 8. If
+%% Segments
is [S1, ..., Sn]
, the result
+%% represents "#{S1, ..., Sn}#
". All the
+%% Si
must have type bitstr
.
+%%
+%% @see ann_c_binary/2
+%% @see update_c_binary/2
+%% @see is_c_binary/1
+%% @see binary_segments/1
+%% @see c_bitstr/5
+
+-record(binary, {ann = [], segments}).
+
+c_binary(Segments) ->
+ #binary{segments = Segments}.
+
+
+%% @spec ann_c_binary(As::[term()], Segments::[cerl()]) -> cerl()
+%% @see c_binary/1
+
+ann_c_binary(As, Segments) ->
+ #binary{segments = Segments, ann = As}.
+
+
+%% @spec update_c_binary(Old::cerl(), Segments::[cerl()]) -> cerl()
+%% @see c_binary/1
+
+update_c_binary(Node, Segments) ->
+ #binary{segments = Segments, ann = get_ann(Node)}.
+
+
+%% @spec is_c_binary(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% binary-template; otherwise false
.
+%%
+%% @see c_binary/1
+
+is_c_binary(#binary{}) ->
+ true;
+is_c_binary(_) ->
+ false.
+
+
+%% @spec binary_segments(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of segment subtrees of an abstract
+%% binary-template.
+%%
+%% @see c_binary/1
+%% @see c_bitstr/5
+
+binary_segments(Node) ->
+ Node#binary.segments.
+
+
+%% @spec c_bitstr(Value::cerl(), Size::cerl(), Unit::cerl(),
+%% Type::cerl(), Flags::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract bit-string template. These can only occur as
+%% components of an abstract binary-template (see {@link c_binary/1}).
+%% The result represents "#<Value>(Size,
+%% Unit, Type, Flags)
", where
+%% Unit
must represent a positive integer constant,
+%% Type
must represent a constant atom (one of
+%% 'integer'
, 'float'
, or
+%% 'binary'
), and Flags
must represent a
+%% constant list "[F1, ..., Fn]"
where
+%% all the Fi
are atoms.
+%%
+%% @see c_binary/1
+%% @see ann_c_bitstr/6
+%% @see update_c_bitstr/6
+%% @see is_c_bitstr/1
+%% @see bitstr_val/1
+%% @see bitstr_size/1
+%% @see bitstr_unit/1
+%% @see bitstr_type/1
+%% @see bitstr_flags/1
+
+-record(bitstr, {ann = [], val, size, unit, type, flags}).
+
+c_bitstr(Val, Size, Unit, Type, Flags) ->
+ #bitstr{val = Val, size = Size, unit = Unit, type = Type,
+ flags = Flags}.
+
+
+%% @spec c_bitstr(Value::cerl(), Size::cerl(), Type::cerl(),
+%% Flags::cerl()) -> cerl()
+%% @equiv c_bitstr(Value, Size, abstract(1), Type, Flags)
+
+c_bitstr(Val, Size, Type, Flags) ->
+ c_bitstr(Val, Size, abstract(1), Type, Flags).
+
+
+%% @spec c_bitstr(Value::cerl(), Type::cerl(),
+%% Flags::cerl()) -> cerl()
+%% @equiv c_bitstr(Value, abstract(all), abstract(1), Type, Flags)
+
+c_bitstr(Val, Type, Flags) ->
+ c_bitstr(Val, abstract(all), abstract(1), Type, Flags).
+
+
+%% @spec ann_c_bitstr(As::[term()], Value::cerl(), Size::cerl(),
+%% Unit::cerl(), Type::cerl(), Flags::cerl()) -> cerl()
+%% @see c_bitstr/5
+%% @see ann_c_bitstr/5
+
+ann_c_bitstr(As, Val, Size, Unit, Type, Flags) ->
+ #bitstr{val = Val, size = Size, unit = Unit, type = Type,
+ flags = Flags, ann = As}.
+
+%% @spec ann_c_bitstr(As::[term()], Value::cerl(), Size::cerl(),
+%% Type::cerl(), Flags::cerl()) -> cerl()
+%% @equiv ann_c_bitstr(As, Value, Size, abstract(1), Type, Flags)
+
+ann_c_bitstr(As, Value, Size, Type, Flags) ->
+ ann_c_bitstr(As, Value, Size, abstract(1), Type, Flags).
+
+
+%% @spec update_c_bitstr(Old::cerl(), Value::cerl(), Size::cerl(),
+%% Unit::cerl(), Type::cerl(), Flags::cerl()) -> cerl()
+%% @see c_bitstr/5
+%% @see update_c_bitstr/5
+
+update_c_bitstr(Node, Val, Size, Unit, Type, Flags) ->
+ #bitstr{val = Val, size = Size, unit = Unit, type = Type,
+ flags = Flags, ann = get_ann(Node)}.
+
+
+%% @spec update_c_bitstr(Old::cerl(), Value::cerl(), Size::cerl(),
+%% Type::cerl(), Flags::cerl()) -> cerl()
+%% @equiv update_c_bitstr(Node, Value, Size, abstract(1), Type, Flags)
+
+update_c_bitstr(Node, Value, Size, Type, Flags) ->
+ update_c_bitstr(Node, Value, Size, abstract(1), Type, Flags).
+
+%% @spec is_c_bitstr(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% bit-string template; otherwise false
.
+%%
+%% @see c_bitstr/5
+
+is_c_bitstr(#bitstr{}) ->
+ true;
+is_c_bitstr(_) ->
+ false.
+
+
+%% @spec bitstr_val(cerl()) -> cerl()
+%%
+%% @doc Returns the value subtree of an abstract bit-string template.
+%%
+%% @see c_bitstr/5
+
+bitstr_val(Node) ->
+ Node#bitstr.val.
+
+
+%% @spec bitstr_size(cerl()) -> cerl()
+%%
+%% @doc Returns the size subtree of an abstract bit-string template.
+%%
+%% @see c_bitstr/5
+
+bitstr_size(Node) ->
+ Node#bitstr.size.
+
+
+%% @spec bitstr_bitsize(cerl()) -> integer() | any | all
+%%
+%% @doc Returns the total size in bits of an abstract bit-string
+%% template. If the size field is an integer literal, the result is the
+%% product of the size and unit values; if the size field is the atom
+%% literal all
, the atom all
is returned; in
+%% all other cases, the atom any
is returned.
+%%
+%% @see c_bitstr/5
+
+bitstr_bitsize(Node) ->
+ Size = Node#bitstr.size,
+ case is_literal(Size) of
+ true ->
+ case concrete(Size) of
+ all ->
+ all;
+ S when integer(S) ->
+ S*concrete(Node#bitstr.unit);
+ true ->
+ any
+ end;
+ false ->
+ any
+ end.
+
+
+%% @spec bitstr_unit(cerl()) -> cerl()
+%%
+%% @doc Returns the unit subtree of an abstract bit-string template.
+%%
+%% @see c_bitstr/5
+
+bitstr_unit(Node) ->
+ Node#bitstr.unit.
+
+
+%% @spec bitstr_type(cerl()) -> cerl()
+%%
+%% @doc Returns the type subtree of an abstract bit-string template.
+%%
+%% @see c_bitstr/5
+
+bitstr_type(Node) ->
+ Node#bitstr.type.
+
+
+%% @spec bitstr_flags(cerl()) -> cerl()
+%%
+%% @doc Returns the flags subtree of an abstract bit-string template.
+%%
+%% @see c_bitstr/5
+
+bitstr_flags(Node) ->
+ Node#bitstr.flags.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_fun(Variables::[cerl()], Body::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract fun-expression. If Variables
+%% is [V1, ..., Vn]
, the result represents "fun
+%% (V1, ..., Vn) -> Body
". All the
+%% Vi
must have type var
.
+%%
+%% @see ann_c_fun/3
+%% @see update_c_fun/3
+%% @see is_c_fun/1
+%% @see fun_vars/1
+%% @see fun_body/1
+%% @see fun_arity/1
+
+-record('fun', {ann = [], vars, body}).
+
+c_fun(Variables, Body) ->
+ #'fun'{vars = Variables, body = Body}.
+
+
+%% @spec ann_c_fun(As::[term()], Variables::[cerl()], Body::cerl()) ->
+%% cerl()
+%% @see c_fun/2
+
+ann_c_fun(As, Variables, Body) ->
+ #'fun'{vars = Variables, body = Body, ann = As}.
+
+
+%% @spec update_c_fun(Old::cerl(), Variables::[cerl()],
+%% Body::cerl()) -> cerl()
+%% @see c_fun/2
+
+update_c_fun(Node, Variables, Body) ->
+ #'fun'{vars = Variables, body = Body, ann = get_ann(Node)}.
+
+
+%% @spec is_c_fun(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% fun-expression, otherwise false
.
+%%
+%% @see c_fun/2
+
+is_c_fun(#'fun'{}) ->
+ true; % Now this is fun!
+is_c_fun(_) ->
+ false.
+
+
+%% @spec fun_vars(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of parameter subtrees of an abstract
+%% fun-expression.
+%%
+%% @see c_fun/2
+%% @see fun_arity/1
+
+fun_vars(Node) ->
+ Node#'fun'.vars.
+
+
+%% @spec fun_body(cerl()) -> cerl()
+%%
+%% @doc Returns the body subtree of an abstract fun-expression.
+%%
+%% @see c_fun/2
+
+fun_body(Node) ->
+ Node#'fun'.body.
+
+
+%% @spec fun_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of parameter subtrees of an abstract
+%% fun-expression.
+%%
+%% Note: this is equivalent to length(fun_vars(Node))
,
+%% but potentially more efficient.
+%%
+%% @see c_fun/2
+%% @see fun_vars/1
+
+fun_arity(Node) ->
+ length(fun_vars(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_seq(Argument::cerl(), Body::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract sequencing expression. The result
+%% represents "do Argument Body
".
+%%
+%% @see ann_c_seq/3
+%% @see update_c_seq/3
+%% @see is_c_seq/1
+%% @see seq_arg/1
+%% @see seq_body/1
+
+-record(seq, {ann = [], arg, body}).
+
+c_seq(Argument, Body) ->
+ #seq{arg = Argument, body = Body}.
+
+
+%% @spec ann_c_seq(As::[term()], Argument::cerl(), Body::cerl()) ->
+%% cerl()
+%% @see c_seq/2
+
+ann_c_seq(As, Argument, Body) ->
+ #seq{arg = Argument, body = Body, ann = As}.
+
+
+%% @spec update_c_seq(Old::cerl(), Argument::cerl(), Body::cerl()) ->
+%% cerl()
+%% @see c_seq/2
+
+update_c_seq(Node, Argument, Body) ->
+ #seq{arg = Argument, body = Body, ann = get_ann(Node)}.
+
+
+%% @spec is_c_seq(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% sequencing expression, otherwise false
.
+%%
+%% @see c_seq/2
+
+is_c_seq(#seq{}) ->
+ true;
+is_c_seq(_) ->
+ false.
+
+
+%% @spec seq_arg(cerl()) -> cerl()
+%%
+%% @doc Returns the argument subtree of an abstract sequencing
+%% expression.
+%%
+%% @see c_seq/2
+
+seq_arg(Node) ->
+ Node#seq.arg.
+
+
+%% @spec seq_body(cerl()) -> cerl()
+%%
+%% @doc Returns the body subtree of an abstract sequencing expression.
+%%
+%% @see c_seq/2
+
+seq_body(Node) ->
+ Node#seq.body.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_let(Variables::[cerl()], Argument::cerl(), Body::cerl()) ->
+%% cerl()
+%%
+%% @doc Creates an abstract let-expression. If Variables
+%% is [V1, ..., Vn]
, the result represents "let
+%% <V1, ..., Vn> = Argument in
+%% Body
". All the Vi
must have type
+%% var
.
+%%
+%% @see ann_c_let/4
+%% @see update_c_let/4
+%% @see is_c_let/1
+%% @see let_vars/1
+%% @see let_arg/1
+%% @see let_body/1
+%% @see let_arity/1
+
+-record('let', {ann = [], vars, arg, body}).
+
+c_let(Variables, Argument, Body) ->
+ #'let'{vars = Variables, arg = Argument, body = Body}.
+
+
+%% ann_c_let(As, Variables, Argument, Body) -> Node
+%% @see c_let/3
+
+ann_c_let(As, Variables, Argument, Body) ->
+ #'let'{vars = Variables, arg = Argument, body = Body, ann = As}.
+
+
+%% update_c_let(Old, Variables, Argument, Body) -> Node
+%% @see c_let/3
+
+update_c_let(Node, Variables, Argument, Body) ->
+ #'let'{vars = Variables, arg = Argument, body = Body,
+ ann = get_ann(Node)}.
+
+
+%% @spec is_c_let(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% let-expression, otherwise false
.
+%%
+%% @see c_let/3
+
+is_c_let(#'let'{}) ->
+ true;
+is_c_let(_) ->
+ false.
+
+
+%% @spec let_vars(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of left-hand side variables of an abstract
+%% let-expression.
+%%
+%% @see c_let/3
+%% @see let_arity/1
+
+let_vars(Node) ->
+ Node#'let'.vars.
+
+
+%% @spec let_arg(cerl()) -> cerl()
+%%
+%% @doc Returns the argument subtree of an abstract let-expression.
+%%
+%% @see c_let/3
+
+let_arg(Node) ->
+ Node#'let'.arg.
+
+
+%% @spec let_body(cerl()) -> cerl()
+%%
+%% @doc Returns the body subtree of an abstract let-expression.
+%%
+%% @see c_let/3
+
+let_body(Node) ->
+ Node#'let'.body.
+
+
+%% @spec let_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of left-hand side variables of an abstract
+%% let-expression.
+%%
+%% Note: this is equivalent to length(let_vars(Node))
,
+%% but potentially more efficient.
+%%
+%% @see c_let/3
+%% @see let_vars/1
+
+let_arity(Node) ->
+ length(let_vars(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_letrec(Definitions::[{cerl(), cerl()}], Body::cerl()) ->
+%% cerl()
+%%
+%% @doc Creates an abstract letrec-expression. If
+%% Definitions
is [{V1, F1}, ..., {Vn, Fn}]
,
+%% the result represents "letrec V1 = F1
+%% ... Vn = Fn in Body
. All the
+%% Vi
must have type var
and represent
+%% function names. All the Fi
must have type
+%% 'fun'
.
+%%
+%% @see ann_c_letrec/3
+%% @see update_c_letrec/3
+%% @see is_c_letrec/1
+%% @see letrec_defs/1
+%% @see letrec_body/1
+%% @see letrec_vars/1
+
+-record(letrec, {ann = [], defs, body}).
+
+c_letrec(Defs, Body) ->
+ #letrec{defs = Defs, body = Body}.
+
+
+%% @spec ann_c_letrec(As::[term()], Definitions::[{cerl(), cerl()}],
+%% Body::cerl()) -> cerl()
+%% @see c_letrec/2
+
+ann_c_letrec(As, Defs, Body) ->
+ #letrec{defs = Defs, body = Body, ann = As}.
+
+
+%% @spec update_c_letrec(Old::cerl(),
+%% Definitions::[{cerl(), cerl()}],
+%% Body::cerl()) -> cerl()
+%% @see c_letrec/2
+
+update_c_letrec(Node, Defs, Body) ->
+ #letrec{defs = Defs, body = Body, ann = get_ann(Node)}.
+
+
+%% @spec is_c_letrec(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% letrec-expression, otherwise false
.
+%%
+%% @see c_letrec/2
+
+is_c_letrec(#letrec{}) ->
+ true;
+is_c_letrec(_) ->
+ false.
+
+
+%% @spec letrec_defs(Node::cerl()) -> [{cerl(), cerl()}]
+%%
+%% @doc Returns the list of definitions of an abstract
+%% letrec-expression. If Node
represents "letrec
+%% V1 = F1 ... Vn = Fn in
+%% Body
", the returned value is [{V1, F1}, ...,
+%% {Vn, Fn}]
.
+%%
+%% @see c_letrec/2
+
+letrec_defs(Node) ->
+ Node#letrec.defs.
+
+
+%% @spec letrec_body(cerl()) -> cerl()
+%%
+%% @doc Returns the body subtree of an abstract letrec-expression.
+%%
+%% @see c_letrec/2
+
+letrec_body(Node) ->
+ Node#letrec.body.
+
+
+%% @spec letrec_vars(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of left-hand side function variable subtrees
+%% of a letrec-expression. If Node
represents
+%% "letrec V1 = F1 ... Vn =
+%% Fn in Body
", the returned value is
+%% [V1, ..., Vn]
.
+%%
+%% @see c_letrec/2
+
+letrec_vars(Node) ->
+ [F || {F, _} <- letrec_defs(Node)].
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_case(Argument::cerl(), Clauses::[cerl()]) -> cerl()
+%%
+%% @doc Creates an abstract case-expression. If Clauses
+%% is [C1, ..., Cn]
, the result represents "case
+%% Argument of C1 ... Cn
+%% end
". Clauses
must not be empty.
+%%
+%% @see ann_c_case/3
+%% @see update_c_case/3
+%% @see is_c_case/1
+%% @see c_clause/3
+%% @see case_arg/1
+%% @see case_clauses/1
+%% @see case_arity/1
+
+-record('case', {ann = [], arg, clauses}).
+
+c_case(Expr, Clauses) ->
+ #'case'{arg = Expr, clauses = Clauses}.
+
+
+%% @spec ann_c_case(As::[term()], Argument::cerl(),
+%% Clauses::[cerl()]) -> cerl()
+%% @see c_case/2
+
+ann_c_case(As, Expr, Clauses) ->
+ #'case'{arg = Expr, clauses = Clauses, ann = As}.
+
+
+%% @spec update_c_case(Old::cerl(), Argument::cerl(),
+%% Clauses::[cerl()]) -> cerl()
+%% @see c_case/2
+
+update_c_case(Node, Expr, Clauses) ->
+ #'case'{arg = Expr, clauses = Clauses, ann = get_ann(Node)}.
+
+
+%% is_c_case(Node) -> boolean()
+%%
+%% Node = cerl()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% case-expression; otherwise false
.
+%%
+%% @see c_case/2
+
+is_c_case(#'case'{}) ->
+ true;
+is_c_case(_) ->
+ false.
+
+
+%% @spec case_arg(cerl()) -> cerl()
+%%
+%% @doc Returns the argument subtree of an abstract case-expression.
+%%
+%% @see c_case/2
+
+case_arg(Node) ->
+ Node#'case'.arg.
+
+
+%% @spec case_clauses(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of clause subtrees of an abstract
+%% case-expression.
+%%
+%% @see c_case/2
+%% @see case_arity/1
+
+case_clauses(Node) ->
+ Node#'case'.clauses.
+
+
+%% @spec case_arity(Node::cerl()) -> integer()
+%%
+%% @doc Equivalent to
+%% clause_arity(hd(case_clauses(Node)))
, but potentially
+%% more efficient.
+%%
+%% @see c_case/2
+%% @see case_clauses/1
+%% @see clause_arity/1
+
+case_arity(Node) ->
+ clause_arity(hd(case_clauses(Node))).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_clause(Patterns::[cerl()], Body::cerl()) -> cerl()
+%% @equiv c_clause(Patterns, c_atom(true), Body)
+%% @see c_atom/1
+
+c_clause(Patterns, Body) ->
+ c_clause(Patterns, c_atom(true), Body).
+
+
+%% @spec c_clause(Patterns::[cerl()], Guard::cerl(), Body::cerl()) ->
+%% cerl()
+%%
+%% @doc Creates an an abstract clause. If Patterns
is
+%% [P1, ..., Pn]
, the result represents
+%% "<P1, ..., Pn> when Guard ->
+%% Body
".
+%%
+%% @see c_clause/2
+%% @see ann_c_clause/4
+%% @see update_c_clause/4
+%% @see is_c_clause/1
+%% @see c_case/2
+%% @see c_receive/3
+%% @see clause_pats/1
+%% @see clause_guard/1
+%% @see clause_body/1
+%% @see clause_arity/1
+%% @see clause_vars/1
+
+-record(clause, {ann = [], pats, guard, body}).
+
+c_clause(Patterns, Guard, Body) ->
+ #clause{pats = Patterns, guard = Guard, body = Body}.
+
+
+%% @spec ann_c_clause(As::[term()], Patterns::[cerl()],
+%% Body::cerl()) -> cerl()
+%% @equiv ann_c_clause(As, Patterns, c_atom(true), Body)
+%% @see c_clause/3
+ann_c_clause(As, Patterns, Body) ->
+ ann_c_clause(As, Patterns, c_atom(true), Body).
+
+
+%% @spec ann_c_clause(As::[term()], Patterns::[cerl()], Guard::cerl(),
+%% Body::cerl()) -> cerl()
+%% @see ann_c_clause/3
+%% @see c_clause/3
+
+ann_c_clause(As, Patterns, Guard, Body) ->
+ #clause{pats = Patterns, guard = Guard, body = Body, ann = As}.
+
+
+%% @spec update_c_clause(Old::cerl(), Patterns::[cerl()],
+%% Guard::cerl(), Body::cerl()) -> cerl()
+%% @see c_clause/3
+
+update_c_clause(Node, Patterns, Guard, Body) ->
+ #clause{pats = Patterns, guard = Guard, body = Body,
+ ann = get_ann(Node)}.
+
+
+%% @spec is_c_clause(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% clause, otherwise false
.
+%%
+%% @see c_clause/3
+
+is_c_clause(#clause{}) ->
+ true;
+is_c_clause(_) ->
+ false.
+
+
+%% @spec clause_pats(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of pattern subtrees of an abstract clause.
+%%
+%% @see c_clause/3
+%% @see clause_arity/1
+
+clause_pats(Node) ->
+ Node#clause.pats.
+
+
+%% @spec clause_guard(cerl()) -> cerl()
+%%
+%% @doc Returns the guard subtree of an abstract clause.
+%%
+%% @see c_clause/3
+
+clause_guard(Node) ->
+ Node#clause.guard.
+
+
+%% @spec clause_body(cerl()) -> cerl()
+%%
+%% @doc Returns the body subtree of an abstract clause.
+%%
+%% @see c_clause/3
+
+clause_body(Node) ->
+ Node#clause.body.
+
+
+%% @spec clause_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of pattern subtrees of an abstract clause.
+%%
+%% Note: this is equivalent to
+%% length(clause_pats(Node))
, but potentially more
+%% efficient.
+%%
+%% @see c_clause/3
+%% @see clause_pats/1
+
+clause_arity(Node) ->
+ length(clause_pats(Node)).
+
+
+%% @spec clause_vars(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of all abstract variables in the patterns of
+%% an abstract clause. The order of listing is not defined.
+%%
+%% @see c_clause/3
+%% @see pat_list_vars/1
+
+clause_vars(Clause) ->
+ pat_list_vars(clause_pats(Clause)).
+
+
+%% @spec pat_vars(Pattern::cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of all abstract variables in a pattern. An
+%% exception is thrown if Node
does not represent a
+%% well-formed Core Erlang clause pattern. The order of listing is not
+%% defined.
+%%
+%% @see pat_list_vars/1
+%% @see clause_vars/1
+
+pat_vars(Node) ->
+ pat_vars(Node, []).
+
+pat_vars(Node, Vs) ->
+ case type(Node) of
+ var ->
+ [Node | Vs];
+ literal ->
+ Vs;
+ cons ->
+ pat_vars(cons_hd(Node), pat_vars(cons_tl(Node), Vs));
+ tuple ->
+ pat_list_vars(tuple_es(Node), Vs);
+ binary ->
+ pat_list_vars(binary_segments(Node), Vs);
+ bitstr ->
+ pat_vars(bitstr_val(Node), Vs);
+ alias ->
+ pat_vars(alias_pat(Node), [alias_var(Node) | Vs])
+ end.
+
+
+%% @spec pat_list_vars(Patterns::[cerl()]) -> [cerl()]
+%%
+%% @doc Returns the list of all abstract variables in the given
+%% patterns. An exception is thrown if some element in
+%% Patterns
does not represent a well-formed Core Erlang
+%% clause pattern. The order of listing is not defined.
+%%
+%% @see pat_vars/1
+%% @see clause_vars/1
+
+pat_list_vars(Ps) ->
+ pat_list_vars(Ps, []).
+
+pat_list_vars([P | Ps], Vs) ->
+ pat_list_vars(Ps, pat_vars(P, Vs));
+pat_list_vars([], Vs) ->
+ Vs.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_alias(Variable::cerl(), Pattern::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract pattern alias. The result represents
+%% "Variable = Pattern
".
+%%
+%% @see ann_c_alias/3
+%% @see update_c_alias/3
+%% @see is_c_alias/1
+%% @see alias_var/1
+%% @see alias_pat/1
+%% @see c_clause/3
+
+-record(alias, {ann = [], var, pat}).
+
+c_alias(Var, Pattern) ->
+ #alias{var = Var, pat = Pattern}.
+
+
+%% @spec ann_c_alias(As::[term()], Variable::cerl(),
+%% Pattern::cerl()) -> cerl()
+%% @see c_alias/2
+
+ann_c_alias(As, Var, Pattern) ->
+ #alias{var = Var, pat = Pattern, ann = As}.
+
+
+%% @spec update_c_alias(Old::cerl(), Variable::cerl(),
+%% Pattern::cerl()) -> cerl()
+%% @see c_alias/2
+
+update_c_alias(Node, Var, Pattern) ->
+ #alias{var = Var, pat = Pattern, ann = get_ann(Node)}.
+
+
+%% @spec is_c_alias(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% pattern alias, otherwise false
.
+%%
+%% @see c_alias/2
+
+is_c_alias(#alias{}) ->
+ true;
+is_c_alias(_) ->
+ false.
+
+
+%% @spec alias_var(cerl()) -> cerl()
+%%
+%% @doc Returns the variable subtree of an abstract pattern alias.
+%%
+%% @see c_alias/2
+
+alias_var(Node) ->
+ Node#alias.var.
+
+
+%% @spec alias_pat(cerl()) -> cerl()
+%%
+%% @doc Returns the pattern subtree of an abstract pattern alias.
+%%
+%% @see c_alias/2
+
+alias_pat(Node) ->
+ Node#alias.pat.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_receive(Clauses::[cerl()]) -> cerl()
+%% @equiv c_receive(Clauses, c_atom(infinity), c_atom(true))
+%% @see c_atom/1
+
+c_receive(Clauses) ->
+ c_receive(Clauses, c_atom(infinity), c_atom(true)).
+
+
+%% @spec c_receive(Clauses::[cerl()], Timeout::cerl(),
+%% Action::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract receive-expression. If
+%% Clauses
is [C1, ..., Cn]
, the result
+%% represents "receive C1 ... Cn after
+%% Timeout -> Action end
".
+%%
+%% @see c_receive/1
+%% @see ann_c_receive/4
+%% @see update_c_receive/4
+%% @see is_c_receive/1
+%% @see receive_clauses/1
+%% @see receive_timeout/1
+%% @see receive_action/1
+
+-record('receive', {ann = [], clauses, timeout, action}).
+
+c_receive(Clauses, Timeout, Action) ->
+ #'receive'{clauses = Clauses, timeout = Timeout, action = Action}.
+
+
+%% @spec ann_c_receive(As::[term()], Clauses::[cerl()]) -> cerl()
+%% @equiv ann_c_receive(As, Clauses, c_atom(infinity), c_atom(true))
+%% @see c_receive/3
+%% @see c_atom/1
+
+ann_c_receive(As, Clauses) ->
+ ann_c_receive(As, Clauses, c_atom(infinity), c_atom(true)).
+
+
+%% @spec ann_c_receive(As::[term()], Clauses::[cerl()],
+%% Timeout::cerl(), Action::cerl()) -> cerl()
+%% @see ann_c_receive/2
+%% @see c_receive/3
+
+ann_c_receive(As, Clauses, Timeout, Action) ->
+ #'receive'{clauses = Clauses, timeout = Timeout, action = Action,
+ ann = As}.
+
+
+%% @spec update_c_receive(Old::cerl(), Clauses::[cerl()],
+%% Timeout::cerl(), Action::cerl()) -> cerl()
+%% @see c_receive/3
+
+update_c_receive(Node, Clauses, Timeout, Action) ->
+ #'receive'{clauses = Clauses, timeout = Timeout, action = Action,
+ ann = get_ann(Node)}.
+
+
+%% @spec is_c_receive(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% receive-expression, otherwise false
.
+%%
+%% @see c_receive/3
+
+is_c_receive(#'receive'{}) ->
+ true;
+is_c_receive(_) ->
+ false.
+
+
+%% @spec receive_clauses(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of clause subtrees of an abstract
+%% receive-expression.
+%%
+%% @see c_receive/3
+
+receive_clauses(Node) ->
+ Node#'receive'.clauses.
+
+
+%% @spec receive_timeout(cerl()) -> cerl()
+%%
+%% @doc Returns the timeout subtree of an abstract receive-expression.
+%%
+%% @see c_receive/3
+
+receive_timeout(Node) ->
+ Node#'receive'.timeout.
+
+
+%% @spec receive_action(cerl()) -> cerl()
+%%
+%% @doc Returns the action subtree of an abstract receive-expression.
+%%
+%% @see c_receive/3
+
+receive_action(Node) ->
+ Node#'receive'.action.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_apply(Operator::cerl(), Arguments::[cerl()]) -> cerl()
+%%
+%% @doc Creates an abstract function application. If
+%% Arguments
is [A1, ..., An]
, the result
+%% represents "apply Operator(A1, ...,
+%% An)
".
+%%
+%% @see ann_c_apply/3
+%% @see update_c_apply/3
+%% @see is_c_apply/1
+%% @see apply_op/1
+%% @see apply_args/1
+%% @see apply_arity/1
+%% @see c_call/3
+%% @see c_primop/2
+
+-record(apply, {ann = [], op, args}).
+
+c_apply(Operator, Arguments) ->
+ #apply{op = Operator, args = Arguments}.
+
+
+%% @spec ann_c_apply(As::[term()], Operator::cerl(),
+%% Arguments::[cerl()]) -> cerl()
+%% @see c_apply/2
+
+ann_c_apply(As, Operator, Arguments) ->
+ #apply{op = Operator, args = Arguments, ann = As}.
+
+
+%% @spec update_c_apply(Old::cerl(), Operator::cerl(),
+%% Arguments::[cerl()]) -> cerl()
+%% @see c_apply/2
+
+update_c_apply(Node, Operator, Arguments) ->
+ #apply{op = Operator, args = Arguments, ann = get_ann(Node)}.
+
+
+%% @spec is_c_apply(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% function application, otherwise false
.
+%%
+%% @see c_apply/2
+
+is_c_apply(#apply{}) ->
+ true;
+is_c_apply(_) ->
+ false.
+
+
+%% @spec apply_op(cerl()) -> cerl()
+%%
+%% @doc Returns the operator subtree of an abstract function
+%% application.
+%%
+%% @see c_apply/2
+
+apply_op(Node) ->
+ Node#apply.op.
+
+
+%% @spec apply_args(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of argument subtrees of an abstract function
+%% application.
+%%
+%% @see c_apply/2
+%% @see apply_arity/1
+
+apply_args(Node) ->
+ Node#apply.args.
+
+
+%% @spec apply_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of argument subtrees of an abstract
+%% function application.
+%%
+%% Note: this is equivalent to
+%% length(apply_args(Node))
, but potentially more
+%% efficient.
+%%
+%% @see c_apply/2
+%% @see apply_args/1
+
+apply_arity(Node) ->
+ length(apply_args(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_call(Module::cerl(), Name::cerl(), Arguments::[cerl()]) ->
+%% cerl()
+%%
+%% @doc Creates an abstract inter-module call. If
+%% Arguments
is [A1, ..., An]
, the result
+%% represents "call Module:Name(A1,
+%% ..., An)
".
+%%
+%% @see ann_c_call/4
+%% @see update_c_call/4
+%% @see is_c_call/1
+%% @see call_module/1
+%% @see call_name/1
+%% @see call_args/1
+%% @see call_arity/1
+%% @see c_apply/2
+%% @see c_primop/2
+
+-record(call, {ann = [], module, name, args}).
+
+c_call(Module, Name, Arguments) ->
+ #call{module = Module, name = Name, args = Arguments}.
+
+
+%% @spec ann_c_call(As::[term()], Module::cerl(), Name::cerl(),
+%% Arguments::[cerl()]) -> cerl()
+%% @see c_call/3
+
+ann_c_call(As, Module, Name, Arguments) ->
+ #call{module = Module, name = Name, args = Arguments, ann = As}.
+
+
+%% @spec update_c_call(Old::cerl(), Module::cerl(), Name::cerl(),
+%% Arguments::[cerl()]) -> cerl()
+%% @see c_call/3
+
+update_c_call(Node, Module, Name, Arguments) ->
+ #call{module = Module, name = Name, args = Arguments,
+ ann = get_ann(Node)}.
+
+
+%% @spec is_c_call(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% inter-module call expression; otherwise false
.
+%%
+%% @see c_call/3
+
+is_c_call(#call{}) ->
+ true;
+is_c_call(_) ->
+ false.
+
+
+%% @spec call_module(cerl()) -> cerl()
+%%
+%% @doc Returns the module subtree of an abstract inter-module call.
+%%
+%% @see c_call/3
+
+call_module(Node) ->
+ Node#call.module.
+
+
+%% @spec call_name(cerl()) -> cerl()
+%%
+%% @doc Returns the name subtree of an abstract inter-module call.
+%%
+%% @see c_call/3
+
+call_name(Node) ->
+ Node#call.name.
+
+
+%% @spec call_args(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of argument subtrees of an abstract
+%% inter-module call.
+%%
+%% @see c_call/3
+%% @see call_arity/1
+
+call_args(Node) ->
+ Node#call.args.
+
+
+%% @spec call_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of argument subtrees of an abstract
+%% inter-module call.
+%%
+%% Note: this is equivalent to
+%% length(call_args(Node))
, but potentially more
+%% efficient.
+%%
+%% @see c_call/3
+%% @see call_args/1
+
+call_arity(Node) ->
+ length(call_args(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_primop(Name::cerl(), Arguments::[cerl()]) -> cerl()
+%%
+%% @doc Creates an abstract primitive operation call. If
+%% Arguments
is [A1, ..., An]
, the result
+%% represents "primop Name(A1, ...,
+%% An)
". Name
must be an atom literal.
+%%
+%% @see ann_c_primop/3
+%% @see update_c_primop/3
+%% @see is_c_primop/1
+%% @see primop_name/1
+%% @see primop_args/1
+%% @see primop_arity/1
+%% @see c_apply/2
+%% @see c_call/3
+
+-record(primop, {ann = [], name, args}).
+
+c_primop(Name, Arguments) ->
+ #primop{name = Name, args = Arguments}.
+
+
+%% @spec ann_c_primop(As::[term()], Name::cerl(),
+%% Arguments::[cerl()]) -> cerl()
+%% @see c_primop/2
+
+ann_c_primop(As, Name, Arguments) ->
+ #primop{name = Name, args = Arguments, ann = As}.
+
+
+%% @spec update_c_primop(Old::cerl(), Name::cerl(),
+%% Arguments::[cerl()]) -> cerl()
+%% @see c_primop/2
+
+update_c_primop(Node, Name, Arguments) ->
+ #primop{name = Name, args = Arguments, ann = get_ann(Node)}.
+
+
+%% @spec is_c_primop(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% primitive operation call, otherwise false
.
+%%
+%% @see c_primop/2
+
+is_c_primop(#primop{}) ->
+ true;
+is_c_primop(_) ->
+ false.
+
+
+%% @spec primop_name(cerl()) -> cerl()
+%%
+%% @doc Returns the name subtree of an abstract primitive operation
+%% call.
+%%
+%% @see c_primop/2
+
+primop_name(Node) ->
+ Node#primop.name.
+
+
+%% @spec primop_args(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of argument subtrees of an abstract primitive
+%% operation call.
+%%
+%% @see c_primop/2
+%% @see primop_arity/1
+
+primop_args(Node) ->
+ Node#primop.args.
+
+
+%% @spec primop_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of argument subtrees of an abstract
+%% primitive operation call.
+%%
+%% Note: this is equivalent to
+%% length(primop_args(Node))
, but potentially more
+%% efficient.
+%%
+%% @see c_primop/2
+%% @see primop_args/1
+
+primop_arity(Node) ->
+ length(primop_args(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_try(Argument::cerl(), Variables::[cerl()], Body::cerl(),
+%% ExceptionVars::[cerl()], Handler::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract try-expression. If Variables
is
+%% [V1, ..., Vn]
and ExceptionVars
is
+%% [X1, ..., Xm]
, the result represents "try
+%% Argument of <V1, ..., Vn> ->
+%% Body catch <X1, ..., Xm> ->
+%% Handler
". All the Vi
and Xi
+%% must have type var
.
+%%
+%% @see ann_c_try/6
+%% @see update_c_try/6
+%% @see is_c_try/1
+%% @see try_arg/1
+%% @see try_vars/1
+%% @see try_body/1
+%% @see c_catch/1
+
+-record('try', {ann = [], arg, vars, body, evars, handler}).
+
+c_try(Expr, Vs, Body, Evs, Handler) ->
+ #'try'{arg = Expr, vars = Vs, body = Body,
+ evars = Evs, handler = Handler}.
+
+
+%% @spec ann_c_try(As::[term()], Expression::cerl(),
+%% Variables::[cerl()], Body::cerl(),
+%% EVars::[cerl()], EBody::[cerl()]) -> cerl()
+%% @see c_try/3
+
+ann_c_try(As, Expr, Vs, Body, Evs, Handler) ->
+ #'try'{arg = Expr, vars = Vs, body = Body,
+ evars = Evs, handler = Handler, ann = As}.
+
+
+%% @spec update_c_try(Old::cerl(), Expression::cerl(),
+%% Variables::[cerl()], Body::cerl(),
+%% EVars::[cerl()], EBody::[cerl()]) -> cerl()
+%% @see c_try/3
+
+update_c_try(Node, Expr, Vs, Body, Evs, Handler) ->
+ #'try'{arg = Expr, vars = Vs, body = Body,
+ evars = Evs, handler = Handler, ann = get_ann(Node)}.
+
+
+%% @spec is_c_try(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% try-expression, otherwise false
.
+%%
+%% @see c_try/3
+
+is_c_try(#'try'{}) ->
+ true;
+is_c_try(_) ->
+ false.
+
+
+%% @spec try_arg(cerl()) -> cerl()
+%%
+%% @doc Returns the expression subtree of an abstract try-expression.
+%%
+%% @see c_try/3
+
+try_arg(Node) ->
+ Node#'try'.arg.
+
+
+%% @spec try_vars(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of success variable subtrees of an abstract
+%% try-expression.
+%%
+%% @see c_try/3
+
+try_vars(Node) ->
+ Node#'try'.vars.
+
+
+%% @spec try_body(cerl()) -> cerl()
+%%
+%% @doc Returns the success body subtree of an abstract try-expression.
+%%
+%% @see c_try/3
+
+try_body(Node) ->
+ Node#'try'.body.
+
+
+%% @spec try_evars(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of exception variable subtrees of an abstract
+%% try-expression.
+%%
+%% @see c_try/3
+
+try_evars(Node) ->
+ Node#'try'.evars.
+
+
+%% @spec try_handler(cerl()) -> cerl()
+%%
+%% @doc Returns the exception body subtree of an abstract
+%% try-expression.
+%%
+%% @see c_try/3
+
+try_handler(Node) ->
+ Node#'try'.handler.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_catch(Body::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract catch-expression. The result represents
+%% "catch Body
".
+%%
+%% Note: catch-expressions can be rewritten as try-expressions, and
+%% will eventually be removed from Core Erlang.
+%%
+%% @see ann_c_catch/2
+%% @see update_c_catch/2
+%% @see is_c_catch/1
+%% @see catch_body/1
+%% @see c_try/3
+
+-record('catch', {ann = [], body}).
+
+c_catch(Body) ->
+ #'catch'{body = Body}.
+
+
+%% @spec ann_c_catch(As::[term()], Body::cerl()) -> cerl()
+%% @see c_catch/1
+
+ann_c_catch(As, Body) ->
+ #'catch'{body = Body, ann = As}.
+
+
+%% @spec update_c_catch(Old::cerl(), Body::cerl()) -> cerl()
+%% @see c_catch/1
+
+update_c_catch(Node, Body) ->
+ #'catch'{body = Body, ann = get_ann(Node)}.
+
+
+%% @spec is_c_catch(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
is an abstract
+%% catch-expression, otherwise false
.
+%%
+%% @see c_catch/1
+
+is_c_catch(#'catch'{}) ->
+ true;
+is_c_catch(_) ->
+ false.
+
+
+%% @spec catch_body(Node::cerl()) -> cerl()
+%%
+%% @doc Returns the body subtree of an abstract catch-expression.
+%%
+%% @see c_catch/1
+
+catch_body(Node) ->
+ Node#'catch'.body.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec to_records(Tree::cerl()) -> record(record_types())
+%%
+%% @doc Translates an abstract syntax tree to a corresponding explicit
+%% record representation. The records are defined in the file
+%% "cerl.hrl
".
+%%
+%% Note: Compound constant literals are always unfolded in the
+%% record representation.
+%%
+%% @see type/1
+%% @see from_records/1
+
+to_records(Node) ->
+ A = get_ann(Node),
+ case type(Node) of
+ literal ->
+ lit_to_records(concrete(Node), A);
+ binary ->
+ #c_binary{anno = A,
+ segments =
+ list_to_records(binary_segments(Node))};
+ bitstr ->
+ #c_bitstr{anno = A,
+ val = to_records(bitstr_val(Node)),
+ size = to_records(bitstr_size(Node)),
+ unit = to_records(bitstr_unit(Node)),
+ type = to_records(bitstr_type(Node)),
+ flags = to_records(bitstr_flags(Node))};
+ cons ->
+ #c_cons{anno = A,
+ hd = to_records(cons_hd(Node)),
+ tl = to_records(cons_tl(Node))};
+ tuple ->
+ #c_tuple{anno = A,
+ es = list_to_records(tuple_es(Node))};
+ var ->
+ case is_c_fname(Node) of
+ true ->
+ #c_fname{anno = A,
+ id = fname_id(Node),
+ arity = fname_arity(Node)};
+ false ->
+ #c_var{anno = A, name = var_name(Node)}
+ end;
+ values ->
+ #c_values{anno = A,
+ es = list_to_records(values_es(Node))};
+ 'fun' ->
+ #c_fun{anno = A,
+ vars = list_to_records(fun_vars(Node)),
+ body = to_records(fun_body(Node))};
+ seq ->
+ #c_seq{anno = A,
+ arg = to_records(seq_arg(Node)),
+ body = to_records(seq_body(Node))};
+ 'let' ->
+ #c_let{anno = A,
+ vars = list_to_records(let_vars(Node)),
+ arg = to_records(let_arg(Node)),
+ body = to_records(let_body(Node))};
+ letrec ->
+ #c_letrec{anno = A,
+ defs = [#c_def{name = to_records(N),
+ val = to_records(F)}
+ || {N, F} <- letrec_defs(Node)],
+ body = to_records(letrec_body(Node))};
+ 'case' ->
+ #c_case{anno = A,
+ arg = to_records(case_arg(Node)),
+ clauses =
+ list_to_records(case_clauses(Node))};
+ clause ->
+ #c_clause{anno = A,
+ pats = list_to_records(clause_pats(Node)),
+ guard = to_records(clause_guard(Node)),
+ body = to_records(clause_body(Node))};
+ alias ->
+ #c_alias{anno = A,
+ var = to_records(alias_var(Node)),
+ pat = to_records(alias_pat(Node))};
+ 'receive' ->
+ #c_receive{anno = A,
+ clauses =
+ list_to_records(receive_clauses(Node)),
+ timeout =
+ to_records(receive_timeout(Node)),
+ action =
+ to_records(receive_action(Node))};
+ apply ->
+ #c_apply{anno = A,
+ op = to_records(apply_op(Node)),
+ args = list_to_records(apply_args(Node))};
+ call ->
+ #c_call{anno = A,
+ module = to_records(call_module(Node)),
+ name = to_records(call_name(Node)),
+ args = list_to_records(call_args(Node))};
+ primop ->
+ #c_primop{anno = A,
+ name = to_records(primop_name(Node)),
+ args = list_to_records(primop_args(Node))};
+ 'try' ->
+ #c_try{anno = A,
+ arg = to_records(try_arg(Node)),
+ vars = list_to_records(try_vars(Node)),
+ body = to_records(try_body(Node)),
+ evars = list_to_records(try_evars(Node)),
+ handler = to_records(try_handler(Node))};
+ 'catch' ->
+ #c_catch{anno = A,
+ body = to_records(catch_body(Node))};
+ module ->
+ #c_module{anno = A,
+ name = to_records(module_name(Node)),
+ exports = list_to_records(
+ module_exports(Node)),
+ attrs = [#c_def{name = to_records(K),
+ val = to_records(V)}
+ || {K, V} <- module_attrs(Node)],
+ defs = [#c_def{name = to_records(N),
+ val = to_records(F)}
+ || {N, F} <- module_defs(Node)]}
+ end.
+
+list_to_records([T | Ts]) ->
+ [to_records(T) | list_to_records(Ts)];
+list_to_records([]) ->
+ [].
+
+lit_to_records(V, A) when integer(V) ->
+ #c_int{anno = A, val = V};
+lit_to_records(V, A) when float(V) ->
+ #c_float{anno = A, val = V};
+lit_to_records(V, A) when atom(V) ->
+ #c_atom{anno = A, val = V};
+lit_to_records([H | T] = V, A) ->
+ case is_print_char_list(V) of
+ true ->
+ #c_string{anno = A, val = V};
+ false ->
+ #c_cons{anno = A,
+ hd = lit_to_records(H, []),
+ tl = lit_to_records(T, [])}
+ end;
+lit_to_records([], A) ->
+ #c_nil{anno = A};
+lit_to_records(V, A) when tuple(V) ->
+ #c_tuple{anno = A, es = lit_list_to_records(tuple_to_list(V))}.
+
+lit_list_to_records([T | Ts]) ->
+ [lit_to_records(T, []) | lit_list_to_records(Ts)];
+lit_list_to_records([]) ->
+ [].
+
+
+%% @spec from_records(Tree::record(record_types())) -> cerl()
+%%
+%% record_types() = c_alias | c_apply | c_call | c_case | c_catch |
+%% c_clause | c_cons | c_def| c_fun | c_let |
+%% c_letrec |c_lit | c_module | c_primop |
+%% c_receive | c_seq | c_try | c_tuple |
+%% c_values | c_var
+%%
+%% @doc Translates an explicit record representation to a
+%% corresponding abstract syntax tree. The records are defined in the
+%% file "cerl.hrl
".
+%%
+%% Note: Compound constant literals are folded, discarding
+%% annotations on subtrees. There are no c_def
nodes in
+%% the abstract representation; annotations on c_def
+%% records are discarded.
+%%
+%% @see type/1
+%% @see to_records/1
+
+from_records(#c_int{val = V, anno = As}) ->
+ ann_c_int(As, V);
+from_records(#c_float{val = V, anno = As}) ->
+ ann_c_float(As, V);
+from_records(#c_atom{val = V, anno = As}) ->
+ ann_c_atom(As, V);
+from_records(#c_char{val = V, anno = As}) ->
+ ann_c_char(As, V);
+from_records(#c_string{val = V, anno = As}) ->
+ ann_c_string(As, V);
+from_records(#c_nil{anno = As}) ->
+ ann_c_nil(As);
+from_records(#c_binary{segments = Ss, anno = As}) ->
+ ann_c_binary(As, from_records_list(Ss));
+from_records(#c_bitstr{val = V, size = S, unit = U, type = T,
+ flags = Fs, anno = As}) ->
+ ann_c_bitstr(As, from_records(V), from_records(S), from_records(U),
+ from_records(T), from_records(Fs));
+from_records(#c_cons{hd = H, tl = T, anno = As}) ->
+ ann_c_cons(As, from_records(H), from_records(T));
+from_records(#c_tuple{es = Es, anno = As}) ->
+ ann_c_tuple(As, from_records_list(Es));
+from_records(#c_var{name = Name, anno = As}) ->
+ ann_c_var(As, Name);
+from_records(#c_fname{id = Id, arity = Arity, anno = As}) ->
+ ann_c_fname(As, Id, Arity);
+from_records(#c_values{es = Es, anno = As}) ->
+ ann_c_values(As, from_records_list(Es));
+from_records(#c_fun{vars = Vs, body = B, anno = As}) ->
+ ann_c_fun(As, from_records_list(Vs), from_records(B));
+from_records(#c_seq{arg = A, body = B, anno = As}) ->
+ ann_c_seq(As, from_records(A), from_records(B));
+from_records(#c_let{vars = Vs, arg = A, body = B, anno = As}) ->
+ ann_c_let(As, from_records_list(Vs), from_records(A),
+ from_records(B));
+from_records(#c_letrec{defs = Fs, body = B, anno = As}) ->
+ ann_c_letrec(As, [{from_records(N), from_records(F)}
+ || #c_def{name = N, val = F} <- Fs],
+ from_records(B));
+from_records(#c_case{arg = A, clauses = Cs, anno = As}) ->
+ ann_c_case(As, from_records(A), from_records_list(Cs));
+from_records(#c_clause{pats = Ps, guard = G, body = B, anno = As}) ->
+ ann_c_clause(As, from_records_list(Ps), from_records(G),
+ from_records(B));
+from_records(#c_alias{var = V, pat = P, anno = As}) ->
+ ann_c_alias(As, from_records(V), from_records(P));
+from_records(#c_receive{clauses = Cs, timeout = T, action = A,
+ anno = As}) ->
+ ann_c_receive(As, from_records_list(Cs), from_records(T),
+ from_records(A));
+from_records(#c_apply{op = Op, args = Es, anno = As}) ->
+ ann_c_apply(As, from_records(Op), from_records_list(Es));
+from_records(#c_call{module = M, name = N, args = Es, anno = As}) ->
+ ann_c_call(As, from_records(M), from_records(N),
+ from_records_list(Es));
+from_records(#c_primop{name = N, args = Es, anno = As}) ->
+ ann_c_primop(As, from_records(N), from_records_list(Es));
+from_records(#c_try{arg = E, vars = Vs, body = B,
+ evars = Evs, handler = H, anno = As}) ->
+ ann_c_try(As, from_records(E), from_records_list(Vs),
+ from_records(B), from_records_list(Evs), from_records(H));
+from_records(#c_catch{body = B, anno = As}) ->
+ ann_c_catch(As, from_records(B));
+from_records(#c_module{name = N, exports = Es, attrs = Ds, defs = Fs,
+ anno = As}) ->
+ ann_c_module(As, from_records(N),
+ from_records_list(Es),
+ [{from_records(K), from_records(V)}
+ || #c_def{name = K, val = V} <- Ds],
+ [{from_records(V), from_records(F)}
+ || #c_def{name = V, val = F} <- Fs]).
+
+from_records_list([T | Ts]) ->
+ [from_records(T) | from_records_list(Ts)];
+from_records_list([]) ->
+ [].
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec is_data(Node::cerl()) -> boolean()
+%%
+%% @doc Returns true
if Node
represents a
+%% data constructor, otherwise false
. Data constructors
+%% are cons cells, tuples, and atomic literals.
+%%
+%% @see data_type/1
+%% @see data_es/1
+%% @see data_arity/1
+
+is_data(#literal{}) ->
+ true;
+is_data(#cons{}) ->
+ true;
+is_data(#tuple{}) ->
+ true;
+is_data(_) ->
+ false.
+
+
+%% @spec data_type(Node::cerl()) -> dtype()
+%%
+%% dtype() = cons | tuple | {'atomic', Value}
+%% Value = integer() | float() | atom() | []
+%%
+%% @doc Returns a type descriptor for a data constructor
+%% node. (Cf. is_data/1
.) This is mainly useful for
+%% comparing types and for constructing new nodes of the same type
+%% (cf. make_data/2
). If Node
represents an
+%% integer, floating-point number, atom or empty list, the result is
+%% {'atomic', Value}
, where Value
is the value
+%% of concrete(Node)
, otherwise the result is either
+%% cons
or tuple
.
+%%
+%% Type descriptors can be compared for equality or order (in the
+%% Erlang term order), but remember that floating-point values should
+%% in general never be tested for equality.
+%%
+%% @see is_data/1
+%% @see make_data/2
+%% @see type/1
+%% @see concrete/1
+
+data_type(#literal{val = V}) ->
+ case V of
+ [_ | _] ->
+ cons;
+ _ when tuple(V) ->
+ tuple;
+ _ ->
+ {'atomic', V}
+ end;
+data_type(#cons{}) ->
+ cons;
+data_type(#tuple{}) ->
+ tuple.
+
+
+%% @spec data_es(Node::cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of subtrees of a data constructor node. If
+%% the arity of the constructor is zero, the result is the empty list.
+%%
+%% Note: if data_type(Node)
is cons
, the
+%% number of subtrees is exactly two. If data_type(Node)
+%% is {'atomic', Value}
, the number of subtrees is
+%% zero.
+%%
+%% @see is_data/1
+%% @see data_type/1
+%% @see data_arity/1
+%% @see make_data/2
+
+data_es(#literal{val = V}) ->
+ case V of
+ [Head | Tail] ->
+ [#literal{val = Head}, #literal{val = Tail}];
+ _ when tuple(V) ->
+ make_lit_list(tuple_to_list(V));
+ _ ->
+ []
+ end;
+data_es(#cons{hd = H, tl = T}) ->
+ [H, T];
+data_es(#tuple{es = Es}) ->
+ Es.
+
+
+%% @spec data_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of subtrees of a data constructor
+%% node. This is equivalent to length(data_es(Node))
, but
+%% potentially more efficient.
+%%
+%% @see is_data/1
+%% @see data_es/1
+
+data_arity(#literal{val = V}) ->
+ case V of
+ [_ | _] ->
+ 2;
+ _ when tuple(V) ->
+ size(V);
+ _ ->
+ 0
+ end;
+data_arity(#cons{}) ->
+ 2;
+data_arity(#tuple{es = Es}) ->
+ length(Es).
+
+
+%% @spec make_data(Type::dtype(), Elements::[cerl()]) -> cerl()
+%%
+%% @doc Creates a data constructor node with the specified type and
+%% subtrees. (Cf. data_type/1
.) An exception is thrown
+%% if the length of Elements
is invalid for the given
+%% Type
; see data_es/1
for arity constraints
+%% on constructor types.
+%%
+%% @see data_type/1
+%% @see data_es/1
+%% @see ann_make_data/3
+%% @see update_data/3
+%% @see make_data_skel/2
+
+make_data(CType, Es) ->
+ ann_make_data([], CType, Es).
+
+
+%% @spec ann_make_data(As::[term()], Type::dtype(),
+%% Elements::[cerl()]) -> cerl()
+%% @see make_data/2
+
+ann_make_data(As, {'atomic', V}, []) -> #literal{val = V, ann = As};
+ann_make_data(As, cons, [H, T]) -> ann_c_cons(As, H, T);
+ann_make_data(As, tuple, Es) -> ann_c_tuple(As, Es).
+
+
+%% @spec update_data(Old::cerl(), Type::dtype(),
+%% Elements::[cerl()]) -> cerl()
+%% @see make_data/2
+
+update_data(Node, CType, Es) ->
+ ann_make_data(get_ann(Node), CType, Es).
+
+
+%% @spec make_data_skel(Type::dtype(), Elements::[cerl()]) -> cerl()
+%%
+%% @doc Like make_data/2
, but analogous to
+%% c_tuple_skel/1
and c_cons_skel/2
.
+%%
+%% @see ann_make_data_skel/3
+%% @see update_data_skel/3
+%% @see make_data/2
+%% @see c_tuple_skel/1
+%% @see c_cons_skel/2
+
+make_data_skel(CType, Es) ->
+ ann_make_data_skel([], CType, Es).
+
+
+%% @spec ann_make_data_skel(As::[term()], Type::dtype(),
+%% Elements::[cerl()]) -> cerl()
+%% @see make_data_skel/2
+
+ann_make_data_skel(As, {'atomic', V}, []) -> #literal{val = V, ann = As};
+ann_make_data_skel(As, cons, [H, T]) -> ann_c_cons_skel(As, H, T);
+ann_make_data_skel(As, tuple, Es) -> ann_c_tuple_skel(As, Es).
+
+
+%% @spec update_data_skel(Old::cerl(), Type::dtype(),
+%% Elements::[cerl()]) -> cerl()
+%% @see make_data_skel/2
+
+update_data_skel(Node, CType, Es) ->
+ ann_make_data_skel(get_ann(Node), CType, Es).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec subtrees(Node::cerl()) -> [[cerl()]]
+%%
+%% @doc Returns the grouped list of all subtrees of a node. If
+%% Node
is a leaf node (cf. is_leaf/1
), this
+%% is the empty list, otherwise the result is always a nonempty list,
+%% containing the lists of subtrees of Node
, in
+%% left-to-right order as they occur in the printed program text, and
+%% grouped by category. Often, each group contains only a single
+%% subtree.
+%%
+%% Depending on the type of Node
, the size of some
+%% groups may be variable (e.g., the group consisting of all the
+%% elements of a tuple), while others always contain the same number
+%% of elements - usually exactly one (e.g., the group containing the
+%% argument expression of a case-expression). Note, however, that the
+%% exact structure of the returned list (for a given node type) should
+%% in general not be depended upon, since it might be subject to
+%% change without notice.
+%%
+%% The function subtrees/1
and the constructor functions
+%% make_tree/2
and update_tree/2
can be a
+%% great help if one wants to traverse a syntax tree, visiting all its
+%% subtrees, but treat nodes of the tree in a uniform way in most or all
+%% cases. Using these functions makes this simple, and also assures that
+%% your code is not overly sensitive to extensions of the syntax tree
+%% data type, because any node types not explicitly handled by your code
+%% can be left to a default case.
+%%
+%% For example:
+%%
+%% postorder(F, Tree) ->
+%% F(case subtrees(Tree) of
+%% [] -> Tree;
+%% List -> update_tree(Tree,
+%% [[postorder(F, Subtree)
+%% || Subtree <- Group]
+%% || Group <- List])
+%% end).
+%%
+%% maps the function F
on Tree
and all its
+%% subtrees, doing a post-order traversal of the syntax tree. (Note
+%% the use of update_tree/2
to preserve annotations.) For
+%% a simple function like:
+%%
+%% f(Node) ->
+%% case type(Node) of
+%% atom -> atom("a_" ++ atom_name(Node));
+%% _ -> Node
+%% end.
+%%
+%% the call postorder(fun f/1, Tree)
will yield a new
+%% representation of Tree
in which all atom names have
+%% been extended with the prefix "a_", but nothing else (including
+%% annotations) has been changed.
+%%
+%% @see is_leaf/1
+%% @see make_tree/2
+%% @see update_tree/2
+
+subtrees(T) ->
+ case is_leaf(T) of
+ true ->
+ [];
+ false ->
+ case type(T) of
+ values ->
+ [values_es(T)];
+ binary ->
+ [binary_segments(T)];
+ bitstr ->
+ [[bitstr_val(T)], [bitstr_size(T)],
+ [bitstr_unit(T)], [bitstr_type(T)],
+ [bitstr_flags(T)]];
+ cons ->
+ [[cons_hd(T)], [cons_tl(T)]];
+ tuple ->
+ [tuple_es(T)];
+ 'let' ->
+ [let_vars(T), [let_arg(T)], [let_body(T)]];
+ seq ->
+ [[seq_arg(T)], [seq_body(T)]];
+ apply ->
+ [[apply_op(T)], apply_args(T)];
+ call ->
+ [[call_module(T)], [call_name(T)],
+ call_args(T)];
+ primop ->
+ [[primop_name(T)], primop_args(T)];
+ 'case' ->
+ [[case_arg(T)], case_clauses(T)];
+ clause ->
+ [clause_pats(T), [clause_guard(T)],
+ [clause_body(T)]];
+ alias ->
+ [[alias_var(T)], [alias_pat(T)]];
+ 'fun' ->
+ [fun_vars(T), [fun_body(T)]];
+ 'receive' ->
+ [receive_clauses(T), [receive_timeout(T)],
+ [receive_action(T)]];
+ 'try' ->
+ [[try_arg(T)], try_vars(T), [try_body(T)],
+ try_evars(T), [try_handler(T)]];
+ 'catch' ->
+ [[catch_body(T)]];
+ letrec ->
+ Es = unfold_tuples(letrec_defs(T)),
+ [Es, [letrec_body(T)]];
+ module ->
+ As = unfold_tuples(module_attrs(T)),
+ Es = unfold_tuples(module_defs(T)),
+ [[module_name(T)], module_exports(T), As, Es]
+ end
+ end.
+
+
+%% @spec update_tree(Old::cerl(), Groups::[[cerl()]]) -> cerl()
+%%
+%% @doc Creates a syntax tree with the given subtrees, and the same
+%% type and annotations as the Old
node. This is
+%% equivalent to ann_make_tree(get_ann(Node), type(Node),
+%% Groups)
, but potentially more efficient.
+%%
+%% @see update_tree/3
+%% @see ann_make_tree/3
+%% @see get_ann/1
+%% @see type/1
+
+update_tree(Node, Gs) ->
+ ann_make_tree(get_ann(Node), type(Node), Gs).
+
+
+%% @spec update_tree(Old::cerl(), Type::atom(), Groups::[[cerl()]]) ->
+%% cerl()
+%%
+%% @doc Creates a syntax tree with the given type and subtrees, and
+%% the same annotations as the Old
node. This is
+%% equivalent to ann_make_tree(get_ann(Node), Type,
+%% Groups)
, but potentially more efficient.
+%%
+%% @see update_tree/2
+%% @see ann_make_tree/3
+%% @see get_ann/1
+
+update_tree(Node, Type, Gs) ->
+ ann_make_tree(get_ann(Node), Type, Gs).
+
+
+%% @spec make_tree(Type::atom(), Groups::[[cerl()]]) -> cerl()
+%%
+%% @doc Creates a syntax tree with the given type and subtrees.
+%% Type
must be a node type name
+%% (cf. type/1
) that does not denote a leaf node type
+%% (cf. is_leaf/1
). Groups
must be a
+%% nonempty list of groups of syntax trees, representing the
+%% subtrees of a node of the given type, in left-to-right order as
+%% they would occur in the printed program text, grouped by category
+%% as done by subtrees/1
.
+%%
+%% The result of ann_make_tree(get_ann(Node), type(Node),
+%% subtrees(Node))
(cf. update_tree/2
) represents
+%% the same source code text as the original Node
,
+%% assuming that subtrees(Node)
yields a nonempty
+%% list. However, it does not necessarily have the exact same data
+%% representation as Node
.
+%%
+%% @see ann_make_tree/3
+%% @see type/1
+%% @see is_leaf/1
+%% @see subtrees/1
+%% @see update_tree/2
+
+make_tree(Type, Gs) ->
+ ann_make_tree([], Type, Gs).
+
+
+%% @spec ann_make_tree(As::[term()], Type::atom(),
+%% Groups::[[cerl()]]) -> cerl()
+%%
+%% @doc Creates a syntax tree with the given annotations, type and
+%% subtrees. See make_tree/2
for details.
+%%
+%% @see make_tree/2
+
+ann_make_tree(As, values, [Es]) -> ann_c_values(As, Es);
+ann_make_tree(As, binary, [Ss]) -> ann_c_binary(As, Ss);
+ann_make_tree(As, bitstr, [[V],[S],[U],[T],[Fs]]) ->
+ ann_c_bitstr(As, V, S, U, T, Fs);
+ann_make_tree(As, cons, [[H], [T]]) -> ann_c_cons(As, H, T);
+ann_make_tree(As, tuple, [Es]) -> ann_c_tuple(As, Es);
+ann_make_tree(As, 'let', [Vs, [A], [B]]) -> ann_c_let(As, Vs, A, B);
+ann_make_tree(As, seq, [[A], [B]]) -> ann_c_seq(As, A, B);
+ann_make_tree(As, apply, [[Op], Es]) -> ann_c_apply(As, Op, Es);
+ann_make_tree(As, call, [[M], [N], Es]) -> ann_c_call(As, M, N, Es);
+ann_make_tree(As, primop, [[N], Es]) -> ann_c_primop(As, N, Es);
+ann_make_tree(As, 'case', [[A], Cs]) -> ann_c_case(As, A, Cs);
+ann_make_tree(As, clause, [Ps, [G], [B]]) -> ann_c_clause(As, Ps, G, B);
+ann_make_tree(As, alias, [[V], [P]]) -> ann_c_alias(As, V, P);
+ann_make_tree(As, 'fun', [Vs, [B]]) -> ann_c_fun(As, Vs, B);
+ann_make_tree(As, 'receive', [Cs, [T], [A]]) ->
+ ann_c_receive(As, Cs, T, A);
+ann_make_tree(As, 'try', [[E], Vs, [B], Evs, [H]]) ->
+ ann_c_try(As, E, Vs, B, Evs, H);
+ann_make_tree(As, 'catch', [[B]]) -> ann_c_catch(As, B);
+ann_make_tree(As, letrec, [Es, [B]]) ->
+ ann_c_letrec(As, fold_tuples(Es), B);
+ann_make_tree(As, module, [[N], Xs, Es, Ds]) ->
+ ann_c_module(As, N, Xs, fold_tuples(Es), fold_tuples(Ds)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec meta(Tree::cerl()) -> cerl()
+%%
+%% @doc Creates a meta-representation of a syntax tree. The result
+%% represents an Erlang expression "MetaTree
"
+%% which, if evaluated, will yield a new syntax tree representing the
+%% same source code text as Tree
(although the actual
+%% data representation may be different). The expression represented
+%% by MetaTree
is implementation independent
+%% with regard to the data structures used by the abstract syntax tree
+%% implementation.
+%%
+%% Any node in Tree
whose node type is
+%% var
(cf. type/1
), and whose list of
+%% annotations (cf. get_ann/1
) contains the atom
+%% meta_var
, will remain unchanged in the resulting tree,
+%% except that exactly one occurrence of meta_var
is
+%% removed from its annotation list.
+%%
+%% The main use of the function meta/1
is to transform
+%% a data structure Tree
, which represents a piece of
+%% program code, into a form that is representation independent
+%% when printed. E.g., suppose Tree
represents a
+%% variable named "V". Then (assuming a function print/1
+%% for printing syntax trees), evaluating
+%% print(abstract(Tree))
- simply using
+%% abstract/1
to map the actual data structure onto a
+%% syntax tree representation - would output a string that might look
+%% something like "{var, ..., 'V'}
", which is obviously
+%% dependent on the implementation of the abstract syntax trees. This
+%% could e.g. be useful for caching a syntax tree in a file. However,
+%% in some situations like in a program generator generator (with two
+%% "generator"), it may be unacceptable. Using
+%% print(meta(Tree))
instead would output a
+%% representation independent syntax tree generating
+%% expression; in the above case, something like
+%% "cerl:c_var('V')
".
+%%
+%% The implementation tries to generate compact code with respect
+%% to literals and lists.
+%%
+%% @see abstract/1
+%% @see type/1
+%% @see get_ann/1
+
+meta(Node) ->
+ %% First of all we check for metavariables:
+ case type(Node) of
+ var ->
+ case lists:member(meta_var, get_ann(Node)) of
+ false ->
+ meta_0(var, Node);
+ true ->
+ %% A meta-variable: remove the first found
+ %% 'meta_var' annotation, but otherwise leave
+ %% the node unchanged.
+ set_ann(Node, lists:delete(meta_var, get_ann(Node)))
+ end;
+ Type ->
+ meta_0(Type, Node)
+ end.
+
+meta_0(Type, Node) ->
+ case get_ann(Node) of
+ [] ->
+ meta_1(Type, Node);
+ As ->
+ meta_call(set_ann, [meta_1(Type, Node), abstract(As)])
+ end.
+
+meta_1(literal, Node) ->
+ %% We handle atomic literals separately, to get a bit
+ %% more compact code. For the rest, we use 'abstract'.
+ case concrete(Node) of
+ V when atom(V) ->
+ meta_call(c_atom, [Node]);
+ V when integer(V) ->
+ meta_call(c_int, [Node]);
+ V when float(V) ->
+ meta_call(c_float, [Node]);
+ [] ->
+ meta_call(c_nil, []);
+ _ ->
+ meta_call(abstract, [Node])
+ end;
+meta_1(var, Node) ->
+ %% A normal variable or function name.
+ meta_call(c_var, [abstract(var_name(Node))]);
+meta_1(values, Node) ->
+ meta_call(c_values,
+ [make_list(meta_list(values_es(Node)))]);
+meta_1(binary, Node) ->
+ meta_call(c_binary,
+ [make_list(meta_list(binary_segments(Node)))]);
+meta_1(bitstr, Node) ->
+ meta_call(c_bitstr,
+ [meta(bitstr_val(Node)),
+ meta(bitstr_size(Node)),
+ meta(bitstr_unit(Node)),
+ meta(bitstr_type(Node)),
+ meta(bitstr_flags(Node))]);
+meta_1(cons, Node) ->
+ %% The list is split up if some sublist has annotatations. If
+ %% we get exactly one element, we generate a 'c_cons' call
+ %% instead of 'make_list' to reconstruct the node.
+ case split_list(Node) of
+ {[H], none} ->
+ meta_call(c_cons, [meta(H), meta(c_nil())]);
+ {[H], Node1} ->
+ meta_call(c_cons, [meta(H), meta(Node1)]);
+ {L, none} ->
+ meta_call(make_list, [make_list(meta_list(L))]);
+ {L, Node1} ->
+ meta_call(make_list,
+ [make_list(meta_list(L)), meta(Node1)])
+ end;
+meta_1(tuple, Node) ->
+ meta_call(c_tuple,
+ [make_list(meta_list(tuple_es(Node)))]);
+meta_1('let', Node) ->
+ meta_call(c_let,
+ [make_list(meta_list(let_vars(Node))),
+ meta(let_arg(Node)), meta(let_body(Node))]);
+meta_1(seq, Node) ->
+ meta_call(c_seq,
+ [meta(seq_arg(Node)), meta(seq_body(Node))]);
+meta_1(apply, Node) ->
+ meta_call(c_apply,
+ [meta(apply_op(Node)),
+ make_list(meta_list(apply_args(Node)))]);
+meta_1(call, Node) ->
+ meta_call(c_call,
+ [meta(call_module(Node)), meta(call_name(Node)),
+ make_list(meta_list(call_args(Node)))]);
+meta_1(primop, Node) ->
+ meta_call(c_primop,
+ [meta(primop_name(Node)),
+ make_list(meta_list(primop_args(Node)))]);
+meta_1('case', Node) ->
+ meta_call(c_case,
+ [meta(case_arg(Node)),
+ make_list(meta_list(case_clauses(Node)))]);
+meta_1(clause, Node) ->
+ meta_call(c_clause,
+ [make_list(meta_list(clause_pats(Node))),
+ meta(clause_guard(Node)),
+ meta(clause_body(Node))]);
+meta_1(alias, Node) ->
+ meta_call(c_alias,
+ [meta(alias_var(Node)), meta(alias_pat(Node))]);
+meta_1('fun', Node) ->
+ meta_call(c_fun,
+ [make_list(meta_list(fun_vars(Node))),
+ meta(fun_body(Node))]);
+meta_1('receive', Node) ->
+ meta_call(c_receive,
+ [make_list(meta_list(receive_clauses(Node))),
+ meta(receive_timeout(Node)),
+ meta(receive_action(Node))]);
+meta_1('try', Node) ->
+ meta_call(c_try,
+ [meta(try_arg(Node)),
+ make_list(meta_list(try_vars(Node))),
+ meta(try_body(Node)),
+ make_list(meta_list(try_evars(Node))),
+ meta(try_handler(Node))]);
+meta_1('catch', Node) ->
+ meta_call(c_catch, [meta(catch_body(Node))]);
+meta_1(letrec, Node) ->
+ meta_call(c_letrec,
+ [make_list([c_tuple([meta(N), meta(F)])
+ || {N, F} <- letrec_defs(Node)]),
+ meta(letrec_body(Node))]);
+meta_1(module, Node) ->
+ meta_call(c_module,
+ [meta(module_name(Node)),
+ make_list(meta_list(module_exports(Node))),
+ make_list([c_tuple([meta(A), meta(V)])
+ || {A, V} <- module_attrs(Node)]),
+ make_list([c_tuple([meta(N), meta(F)])
+ || {N, F} <- module_defs(Node)])]).
+
+meta_call(F, As) ->
+ c_call(c_atom(?MODULE), c_atom(F), As).
+
+meta_list([T | Ts]) ->
+ [meta(T) | meta_list(Ts)];
+meta_list([]) ->
+ [].
+
+split_list(Node) ->
+ split_list(set_ann(Node, []), []).
+
+split_list(Node, L) ->
+ A = get_ann(Node),
+ case type(Node) of
+ cons when A == [] ->
+ split_list(cons_tl(Node), [cons_hd(Node) | L]);
+ nil when A == [] ->
+ {lists:reverse(L), none};
+ _ ->
+ {lists:reverse(L), Node}
+ end.
+
+
+%% ---------------------------------------------------------------------
+
+%% General utilities
+
+is_lit_list([#literal{} | Es]) ->
+ is_lit_list(Es);
+is_lit_list([_ | _]) ->
+ false;
+is_lit_list([]) ->
+ true.
+
+lit_list_vals([#literal{val = V} | Es]) ->
+ [V | lit_list_vals(Es)];
+lit_list_vals([]) ->
+ [].
+
+make_lit_list([V | Vs]) ->
+ [#literal{val = V} | make_lit_list(Vs)];
+make_lit_list([]) ->
+ [].
+
+%% The following tests are the same as done by 'io_lib:char_list' and
+%% 'io_lib:printable_list', respectively, but for a single character.
+
+is_char_value(V) when V >= $\000, V =< $\377 -> true;
+is_char_value(_) -> false.
+
+is_print_char_value(V) when V >= $\040, V =< $\176 -> true;
+is_print_char_value(V) when V >= $\240, V =< $\377 -> true;
+is_print_char_value(V) when V =:= $\b -> true;
+is_print_char_value(V) when V =:= $\d -> true;
+is_print_char_value(V) when V =:= $\e -> true;
+is_print_char_value(V) when V =:= $\f -> true;
+is_print_char_value(V) when V =:= $\n -> true;
+is_print_char_value(V) when V =:= $\r -> true;
+is_print_char_value(V) when V =:= $\s -> true;
+is_print_char_value(V) when V =:= $\t -> true;
+is_print_char_value(V) when V =:= $\v -> true;
+is_print_char_value(V) when V =:= $\" -> true;
+is_print_char_value(V) when V =:= $\' -> true;
+is_print_char_value(V) when V =:= $\\ -> true;
+is_print_char_value(_) -> false.
+
+is_char_list([V | Vs]) when integer(V) ->
+ case is_char_value(V) of
+ true ->
+ is_char_list(Vs);
+ false ->
+ false
+ end;
+is_char_list([]) ->
+ true;
+is_char_list(_) ->
+ false.
+
+is_print_char_list([V | Vs]) when integer(V) ->
+ case is_print_char_value(V) of
+ true ->
+ is_print_char_list(Vs);
+ false ->
+ false
+ end;
+is_print_char_list([]) ->
+ true;
+is_print_char_list(_) ->
+ false.
+
+unfold_tuples([{X, Y} | Ps]) ->
+ [X, Y | unfold_tuples(Ps)];
+unfold_tuples([]) ->
+ [].
+
+fold_tuples([X, Y | Es]) ->
+ [{X, Y} | fold_tuples(Es)];
+fold_tuples([]) ->
+ [].
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/cerl_clauses.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/cerl_clauses.erl
new file mode 100644
index 0000000000..16e4b37a10
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/cerl_clauses.erl
@@ -0,0 +1,409 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Richard Carlsson.
+%% Copyright (C) 1999-2002 Richard Carlsson.
+%% Portions created by Ericsson are Copyright 2001, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: cerl_clauses.erl,v 1.2 2009/09/17 09:46:19 kostis Exp $
+
+%% @doc Utility functions for Core Erlang case/receive clauses.
+%%
+%% Syntax trees are defined in the module cerl
.
+%%
+%% @type cerl() = cerl:cerl()
+
+-module(cerl_clauses).
+
+-export([any_catchall/1, eval_guard/1, is_catchall/1, match/2,
+ match_list/2, reduce/1, reduce/2]).
+
+-import(cerl, [alias_pat/1, alias_var/1, data_arity/1, data_es/1,
+ data_type/1, clause_guard/1, clause_pats/1, concrete/1,
+ is_data/1, is_c_var/1, let_body/1, letrec_body/1,
+ seq_body/1, try_arg/1, type/1, values_es/1]).
+
+-import(lists, [reverse/1]).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec is_catchall(Clause::cerl()) -> boolean()
+%%
+%% @doc Returns true
if an abstract clause is a
+%% catch-all, otherwise false
. A clause is a catch-all if
+%% all its patterns are variables, and its guard expression always
+%% evaluates to true
; cf. eval_guard/1
.
+%%
+%% Note: Clause
must have type
+%% clause
.
+%%
+%% @see eval_guard/1
+%% @see any_catchall/1
+
+is_catchall(C) ->
+ case all_vars(clause_pats(C)) of
+ true ->
+ case eval_guard(clause_guard(C)) of
+ {value, true} ->
+ true;
+ _ ->
+ false
+ end;
+ false ->
+ false
+ end.
+
+all_vars([C | Cs]) ->
+ case is_c_var(C) of
+ true ->
+ all_vars(Cs);
+ false ->
+ false
+ end;
+all_vars([]) ->
+ true.
+
+
+%% @spec any_catchall(Clauses::[cerl()]) -> boolean()
+%%
+%% @doc Returns true
if any of the abstract clauses in
+%% the list is a catch-all, otherwise false
. See
+%% is_catchall/1
for details.
+%%
+%% Note: each node in Clauses
must have type
+%% clause
.
+%%
+%% @see is_catchall/1
+
+any_catchall([C | Cs]) ->
+ case is_catchall(C) of
+ true ->
+ true;
+ false ->
+ any_catchall(Cs)
+ end;
+any_catchall([]) ->
+ false.
+
+
+%% @spec eval_guard(Expr::cerl()) -> none | {value, term()}
+%%
+%% @doc Tries to reduce a guard expression to a single constant value,
+%% if possible. The returned value is {value, Term}
if the
+%% guard expression Expr
always yields the constant value
+%% Term
, and is otherwise none
.
+%%
+%% Note that although guard expressions should only yield boolean
+%% values, this function does not guarantee that Term
is
+%% either true
or false
. Also note that only
+%% simple constructs like let-expressions are examined recursively;
+%% general constant folding is not performed.
+%%
+%% @see is_catchall/1
+
+%% This function could possibly be improved further, but constant
+%% folding should in general be performed elsewhere.
+
+eval_guard(E) ->
+ case type(E) of
+ literal ->
+ {value, concrete(E)};
+ values ->
+ case values_es(E) of
+ [E1] ->
+ eval_guard(E1);
+ _ ->
+ none
+ end;
+ 'try' ->
+ eval_guard(try_arg(E));
+ seq ->
+ eval_guard(seq_body(E));
+ 'let' ->
+ eval_guard(let_body(E));
+ 'letrec' ->
+ eval_guard(letrec_body(E));
+ _ ->
+ none
+ end.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec reduce(Clauses) -> {true, {Clauses, Bindings}}
+%% | {false, Clauses}
+%%
+%% @equiv reduce(Cs, [])
+
+reduce(Cs) ->
+ reduce(Cs, []).
+
+%% @spec reduce(Clauses::[Clause], Exprs::[Expr]) ->
+%% {true, {Clause, Bindings}}
+%% | {false, [Clause]}
+%%
+%% Clause = cerl()
+%% Expr = any | cerl()
+%% Bindings = [{cerl(), cerl()}]
+%%
+%% @doc Selects a single clause, if possible, or otherwise reduces the
+%% list of selectable clauses. The input is a list Clauses
+%% of abstract clauses (i.e., syntax trees of type clause
),
+%% and a list of switch expressions Exprs
. The function
+%% tries to uniquely select a single clause or discard unselectable
+%% clauses, with respect to the switch expressions. All abstract clauses
+%% in the list must have the same number of patterns. If
+%% Exprs
is not the empty list, it must have the same
+%% length as the number of patterns in each clause; see
+%% match_list/2
for details.
+%%
+%% A clause can only be selected if its guard expression always
+%% yields the atom true
, and a clause whose guard
+%% expression always yields the atom false
can never be
+%% selected. Other guard expressions are considered to have unknown
+%% value; cf. eval_guard/1
.
+%%
+%% If a particular clause can be selected, the function returns
+%% {true, {Clause, Bindings}}
, where Clause
is
+%% the selected clause and Bindings
is a list of pairs
+%% {Var, SubExpr}
associating the variables occurring in
+%% the patterns of Clause
with the corresponding
+%% subexpressions in Exprs
. The list of bindings is given
+%% in innermost-first order; see the match/2
function for
+%% details.
+%%
+%% If no clause could be definitely selected, the function returns
+%% {false, NewClauses}
, where NewClauses
is
+%% the list of entries in Clauses
that remain after
+%% eliminating unselectable clauses, preserving the relative order.
+%%
+%% @see eval_guard/1
+%% @see match/2
+%% @see match_list/2
+
+reduce(Cs, Es) ->
+ reduce(Cs, Es, []).
+
+reduce([C | Cs], Es, Cs1) ->
+ Ps = clause_pats(C),
+ case match_list(Ps, Es) of
+ none ->
+ %% Here, we know that the current clause cannot possibly be
+ %% selected, so we drop it and visit the rest.
+ reduce(Cs, Es, Cs1);
+ {false, _} ->
+ %% We are not sure if this clause might be selected, so we
+ %% save it and visit the rest.
+ reduce(Cs, Es, [C | Cs1]);
+ {true, Bs} ->
+ case eval_guard(clause_guard(C)) of
+ {value, true} when Cs1 == [] ->
+ %% We have a definite match - we return the residual
+ %% expression and signal that a selection has been
+ %% made. All other clauses are dropped.
+ {true, {C, Bs}};
+ {value, true} ->
+ %% Unless one of the previous clauses is selected,
+ %% this clause will definitely be, so we can drop
+ %% the rest.
+ {false, reverse([C | Cs1])};
+ {value, false} ->
+ %% This clause can never be selected, since its
+ %% guard is never 'true', so we drop it.
+ reduce(Cs, Es, Cs1);
+ _ ->
+ %% We are not sure if this clause might be selected
+ %% (or might even cause a crash), so we save it and
+ %% visit the rest.
+ reduce(Cs, Es, [C | Cs1])
+ end
+ end;
+reduce([], _, Cs) ->
+ %% All clauses visited, without a complete match. Signal "not
+ %% reduced" and return the saved clauses, in the correct order.
+ {false, reverse(Cs)}.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec match(Pattern::cerl(), Expr) ->
+%% none | {true, Bindings} | {false, Bindings}
+%%
+%% Expr = any | cerl()
+%% Bindings = [{cerl(), Expr}]
+%%
+%% @doc Matches a pattern against an expression. The returned value is
+%% none
if a match is impossible, {true,
+%% Bindings}
if Pattern
definitely matches
+%% Expr
, and {false, Bindings}
if a match is
+%% not definite, but cannot be excluded. Bindings
is then
+%% a list of pairs {Var, SubExpr}
, associating each
+%% variable in the pattern with either the corresponding subexpression
+%% of Expr
, or with the atom any
if no
+%% matching subexpression exists. (Recall that variables may not be
+%% repeated in a Core Erlang pattern.) The list of bindings is given
+%% in innermost-first order; this should only be of interest if
+%% Pattern
contains one or more alias patterns. If the
+%% returned value is {true, []}
, it implies that the
+%% pattern and the expression are syntactically identical.
+%%
+%% Instead of a syntax tree, the atom any
can be
+%% passed for Expr
(or, more generally, be used for any
+%% subtree of Expr
, in as much the abstract syntax tree
+%% implementation allows it); this means that it cannot be decided
+%% whether the pattern will match or not, and the corresponding
+%% variable bindings will all map to any
. The typical use
+%% is for producing bindings for receive
clauses.
+%%
+%% Note: Binary-syntax patterns are never structurally matched
+%% against binary-syntax expressions by this function.
+%%
+%% Examples:
+%%
+%% - Matching a pattern "
{X, Y}
" against the
+%% expression "{foo, f(Z)}
" yields {true,
+%% Bindings}
where Bindings
associates
+%% "X
" with the subtree "foo
" and
+%% "Y
" with the subtree "f(Z)
".
+%%
+%% - Matching pattern "
{X, {bar, Y}}
" against
+%% expression "{foo, f(Z)}
" yields {false,
+%% Bindings}
where Bindings
associates
+%% "X
" with the subtree "foo
" and
+%% "Y
" with any
(because it is not known
+%% if "{foo, Y}
" might match the run-time value of
+%% "f(Z)
" or not).
+%%
+%% - Matching pattern "
{foo, bar}
" against expression
+%% "{foo, f()}
" yields {false, []}
,
+%% telling us that there might be a match, but we cannot deduce any
+%% bindings.
+%%
+%% - Matching
{foo, X = {bar, Y}}
against expression
+%% "{foo, {bar, baz}}
" yields {true,
+%% Bindings}
where Bindings
associates
+%% "Y
" with "baz
", and "X
"
+%% with "{bar, baz}
".
+%%
+%% - Matching a pattern "
{X, Y}
" against
+%% any
yields {false, Bindings}
where
+%% Bindings
associates both "X
" and
+%% "Y
" with any
.
+%%
+
+match(P, E) ->
+ match(P, E, []).
+
+match(P, E, Bs) ->
+ case type(P) of
+ var ->
+ %% Variables always match, since they cannot have repeated
+ %% occurrences in a pattern.
+ {true, [{P, E} | Bs]};
+ alias ->
+ %% All variables in P1 will be listed before the alias
+ %% variable in the result.
+ match(alias_pat(P), E, [{alias_var(P), E} | Bs]);
+ binary ->
+ %% The most we can do is to say "definitely no match" if a
+ %% binary pattern is matched against non-binary data.
+ if E == any ->
+ {false, Bs};
+ true ->
+ case is_data(E) of
+ true ->
+ none;
+ false ->
+ {false, Bs}
+ end
+ end;
+ _ ->
+ match_1(P, E, Bs)
+ end.
+
+match_1(P, E, Bs) ->
+ case is_data(P) of
+ true when E == any ->
+ %% If we don't know the structure of the value of E at this
+ %% point, we just match the subpatterns against 'any', and
+ %% make sure the result is a "maybe".
+ Ps = data_es(P),
+ Es = lists:duplicate(length(Ps), any),
+ case match_list(Ps, Es, Bs) of
+ {_, Bs1} ->
+ {false, Bs1};
+ none ->
+ none
+ end;
+ true ->
+ %% Test if the expression represents a constructor
+ case is_data(E) of
+ true ->
+ T1 = {data_type(E), data_arity(E)},
+ T2 = {data_type(P), data_arity(P)},
+ %% Note that we must test for exact equality.
+ if T1 =:= T2 ->
+ match_list(data_es(P), data_es(E), Bs);
+ true ->
+ none
+ end;
+ false ->
+ %% We don't know the run-time structure of E, and P
+ %% is not a variable or an alias pattern, so we
+ %% match against 'any' instead.
+ match_1(P, any, Bs)
+ end;
+ false ->
+ %% Strange pattern - give up, but don't say "no match".
+ {false, Bs}
+ end.
+
+
+%% @spec match_list(Patterns::[cerl()], Exprs::[Expr]) ->
+%% none | {true, Bindings} | {false, Bindings}
+%%
+%% Expr = any | cerl()
+%% Bindings = [{cerl(), cerl()}]
+%%
+%% @doc Like match/2
, but matching a sequence of patterns
+%% against a sequence of expressions. Passing an empty list for
+%% Exprs
is equivalent to passing a list of
+%% any
atoms of the same length as Patterns
.
+%%
+%% @see match/2
+
+match_list([], []) ->
+ {true, []}; % no patterns always match
+match_list(Ps, []) ->
+ match_list(Ps, lists:duplicate(length(Ps), any), []);
+match_list(Ps, Es) ->
+ match_list(Ps, Es, []).
+
+match_list([P | Ps], [E | Es], Bs) ->
+ case match(P, E, Bs) of
+ {true, Bs1} ->
+ match_list(Ps, Es, Bs1);
+ {false, Bs1} ->
+ %% Make sure "maybe" is preserved
+ case match_list(Ps, Es, Bs1) of
+ {_, Bs2} ->
+ {false, Bs2};
+ none ->
+ none
+ end;
+ none ->
+ none
+ end;
+match_list([], [], Bs) ->
+ {true, Bs}.
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/cerl_inline.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/cerl_inline.erl
new file mode 100644
index 0000000000..cd332279d1
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/cerl_inline.erl
@@ -0,0 +1,2762 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Richard Carlsson.
+%% Copyright (C) 1999-2002 Richard Carlsson.
+%% Portions created by Ericsson are Copyright 2001, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: cerl_inline.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%
+%% Core Erlang inliner.
+
+%% =====================================================================
+%%
+%% This is an implementation of the algorithm by Waddell and Dybvig
+%% ("Fast and Effective Procedure Inlining", International Static
+%% Analysis Symposium 1997), adapted to the Core Erlang language.
+%%
+%% Instead of always renaming variables and function variables, this
+%% implementation uses the "no-shadowing strategy" of Peyton Jones and
+%% Marlow ("Secrets of the Glasgow Haskell Compiler Inliner", 1999).
+%%
+%% =====================================================================
+
+%% TODO: inline single-source-reference operands without size limit.
+
+-module(cerl_inline).
+
+-export([core_transform/2, transform/1, transform/2]).
+
+-import(cerl, [abstract/1, alias_pat/1, alias_var/1, apply_args/1,
+ apply_op/1, atom_name/1, atom_val/1, bitstr_val/1,
+ bitstr_size/1, bitstr_unit/1, bitstr_type/1,
+ bitstr_flags/1, binary_segments/1, update_c_alias/3,
+ update_c_apply/3, update_c_binary/2, update_c_bitstr/6,
+ update_c_call/4, update_c_case/3, update_c_catch/2,
+ update_c_clause/4, c_fun/2, c_int/1, c_let/3,
+ update_c_let/4, update_c_letrec/3, update_c_module/5,
+ update_c_primop/3, update_c_receive/4, update_c_seq/3,
+ c_seq/2, update_c_try/6, c_tuple/1, update_c_values/2,
+ c_values/1, c_var/1, call_args/1, call_module/1,
+ call_name/1, case_arity/1, case_arg/1, case_clauses/1,
+ catch_body/1, clause_body/1, clause_guard/1,
+ clause_pats/1, clause_vars/1, concrete/1, cons_hd/1,
+ cons_tl/1, data_arity/1, data_es/1, data_type/1,
+ fun_body/1, fun_vars/1, get_ann/1, int_val/1,
+ is_c_atom/1, is_c_cons/1, is_c_fun/1, is_c_int/1,
+ is_c_list/1, is_c_seq/1, is_c_tuple/1, is_c_var/1,
+ is_data/1, is_literal/1, is_literal_term/1, let_arg/1,
+ let_body/1, let_vars/1, letrec_body/1, letrec_defs/1,
+ list_length/1, list_elements/1, update_data/3,
+ make_list/1, make_data_skel/2, module_attrs/1,
+ module_defs/1, module_exports/1, module_name/1,
+ primop_args/1, primop_name/1, receive_action/1,
+ receive_clauses/1, receive_timeout/1, seq_arg/1,
+ seq_body/1, set_ann/2, try_arg/1, try_body/1, try_vars/1,
+ try_evars/1, try_handler/1, tuple_es/1, tuple_arity/1,
+ type/1, values_es/1, var_name/1]).
+
+-import(lists, [foldl/3, foldr/3, mapfoldl/3, reverse/1]).
+
+%%
+%% Constants
+%%
+
+debug_runtime() -> false.
+debug_counters() -> false.
+
+%% Normal execution times for inlining are between 0.1 and 0.3 seconds
+%% (on the author's current equipment). The default effort limit of 150
+%% is high enough that most normal programs never hit the limit even
+%% once, and for difficult programs, it generally keeps the execution
+%% times below 2-5 seconds. Using an effort counter of 1000 will thus
+%% have no further effect on most programs, but some programs may take
+%% as much as 10 seconds or more. Effort counts larger than 2500 have
+%% never been observed even on very ill-conditioned programs.
+%%
+%% Size limits between 6 and 18 tend to actually shrink the code,
+%% because of the simplifications made possible by inlining. A limit of
+%% 16 seems to be optimal for this purpose, often shrinking the
+%% executable code by up to 10%. Size limits between 18 and 30 generally
+%% give the same code size as if no inlining was done (i.e., code
+%% duplication balances out the simplifications at these levels). A size
+%% limit between 1 and 5 tends to inline small functions and propagate
+%% constants, but does not cause much simplifications do be done, so the
+%% net effect will be a slight increase in code size. For size limits
+%% above 30, the executable code size tends to increase with about 10%
+%% per 100 units, with some variations depending on the sizes of
+%% functions in the source code.
+%%
+%% Typically, about 90% of the maximum speedup achievable is already
+%% reached using a size limit of 30, and 98% is reached at limits around
+%% 100-150; there is rarely any point in letting the code size increase
+%% by more than 10-15%. If too large functions are inlined, cache
+%% effects will slow the program down.
+
+default_effort() -> 150.
+default_size() -> 24.
+
+%% Base costs/weights for different kinds of expressions. If these are
+%% modified, the size limits above may have to be adjusted.
+
+weight(var) -> 0; % We count no cost for variable accesses.
+weight(values) -> 0; % Value aggregates have no cost in themselves.
+weight(literal) -> 1; % We assume efficient handling of constants.
+weight(data) -> 1; % Base cost; add 1 per element.
+weight(element) -> 1; % Cost of storing/fetching an element.
+weight(argument) -> 1; % Cost of passing a function argument.
+weight('fun') -> 6; % Base cost + average number of free vars.
+weight('let') -> 0; % Count no cost for let-bindings.
+weight(letrec) -> 0; % Like a let-binding.
+weight('case') -> 0; % Case switches have no base cost.
+weight(clause) -> 1; % Count one jump at the end of each clause body.
+weight('receive') -> 9; % Initialization/cleanup cost.
+weight('try') -> 1; % Assume efficient implementation.
+weight('catch') -> 1; % See `try'.
+weight(apply) -> 3; % Average base cost: call/return.
+weight(call) -> 3; % Assume remote-calls as efficient as `apply'.
+weight(primop) -> 2; % Assume more efficient than `apply'.
+weight(binary) -> 4; % Initialisation base cost.
+weight(bitstr) -> 3; % Coding/decoding a value; like a primop.
+weight(module) -> 1. % Like a letrec with a constant body
+
+%% These "reference" structures are used for variables and function
+%% variables. They keep track of the variable name, any bound operand,
+%% and the associated store location.
+
+-record(ref, {name, opnd, loc}).
+
+%% Operand structures contain the operand expression, the renaming and
+%% environment, the state location, and the effort counter at the call
+%% site (cf. `visit').
+
+-record(opnd, {expr, ren, env, loc, effort}).
+
+%% Since expressions are only visited in `effect' context when they are
+%% not bound to a referenced variable, only expressions visited in
+%% 'value' context are cached.
+
+-record(cache, {expr, size}).
+
+%% The context flags for an application structure are kept separate from
+%% the structure itself. Note that the original algorithm had exactly
+%% one operand in each application context structure, while we can have
+%% several, or none.
+
+-record(app, {opnds, ctxt, loc}).
+
+
+%%
+%% Interface functions
+%%
+
+%% Use compile option `{core_transform, inline}' to insert this as a
+%% compilation pass.
+
+core_transform(Code, Opts) ->
+ cerl:to_records(transform(cerl:from_records(Code), Opts)).
+
+transform(Tree) ->
+ transform(Tree, []).
+
+transform(Tree, Opts) ->
+ main(Tree, value, Opts).
+
+main(Tree, Ctxt, Opts) ->
+ %% We spawn a new process to do the work, so we don't have to worry
+ %% about cluttering the process dictionary with debugging info, or
+ %% proper deallocation of ets-tables.
+ Opts1 = Opts ++ [{inline_size, default_size()},
+ {inline_effort, default_effort()}],
+ Reply = self(),
+ Pid = spawn_link(fun () -> start(Reply, Tree, Ctxt, Opts1) end),
+ receive
+ {Pid1, Tree1} when Pid1 == Pid ->
+ Tree1
+ end.
+
+start(Reply, Tree, Ctxt, Opts) ->
+ init_debug(),
+ case debug_runtime() of
+ true ->
+ put(inline_start_time,
+ element(1, erlang:statistics(runtime)));
+ _ ->
+ ok
+ end,
+ Size = max(1, proplists:get_value(inline_size, Opts)),
+ Effort = max(1, proplists:get_value(inline_effort, Opts)),
+ case proplists:get_bool(verbose, Opts) of
+ true ->
+ io:fwrite("Inlining: inline_size=~w inline_effort=~w\n",
+ [Size, Effort]);
+ false ->
+ ok
+ end,
+
+ %% Note that the counters of the new state are passive.
+ S = st__new(Effort, Size),
+
+%%% Initialization is not needed at present. Note that the code in
+%%% `inline_init' is not up-to-date with this module.
+%%% {Tree1, S1} = inline_init:init(Tree, S),
+%%% {Tree2, _S2} = i(Tree1, Ctxt, S1),
+ {Tree2, _S2} = i(Tree, Ctxt, S),
+ report_debug(),
+ Reply ! {self(), Tree2}.
+
+init_debug() ->
+ case debug_counters() of
+ true ->
+ put(counter_effort_triggers, 0),
+ put(counter_effort_max, 0),
+ put(counter_size_triggers, 0),
+ put(counter_size_max, 0);
+ _ ->
+ ok
+ end.
+
+report_debug() ->
+ case debug_runtime() of
+ true ->
+ {Time, _} = erlang:statistics(runtime),
+ report("Total run time for inlining: ~.2.0f s.\n",
+ [(Time - get(inline_start_time))/1000]);
+ _ ->
+ ok
+ end,
+ case debug_counters() of
+ true ->
+ counter_stats();
+ _ ->
+ ok
+ end.
+
+counter_stats() ->
+ T1 = get(counter_effort_triggers),
+ T2 = get(counter_size_triggers),
+ E = get(counter_effort_max),
+ S = get(counter_size_max),
+ M1 = io_lib:fwrite("\tNumber of triggered "
+ "effort counters: ~p.\n", [T1]),
+ M2 = io_lib:fwrite("\tNumber of triggered "
+ "size counters: ~p.\n", [T2]),
+ M3 = io_lib:fwrite("\tLargest active effort counter: ~p.\n",
+ [E]),
+ M4 = io_lib:fwrite("\tLargest active size counter: ~p.\n",
+ [S]),
+ report("Counter statistics:\n~s", [[M1, M2, M3, M4]]).
+
+
+%% =====================================================================
+%% The main inlining function
+%%
+%% i(E :: coreErlang(),
+%% Ctxt :: value | effect | #app{}
+%% Ren :: renaming(),
+%% Env :: environment(),
+%% S :: state())
+%% -> {E', S'}
+%%
+%% Note: It is expected that the input source code ('E') does not
+%% contain free variables. If it does, there is a risk of accidental
+%% name capture, in case a generated "new" variable name happens to be
+%% the same as the name of a variable that is free further below in the
+%% tree; the algorithm only consults the current environment to check if
+%% a name already exists.
+%%
+%% The renaming maps names of source-code variable and function
+%% variables to new names as necessary to avoid clashes, according to
+%% the "no-shadowing" strategy. The environment maps *residual-code*
+%% variables and function variables to operands and global information.
+%% Separating the renaming from the environment, and using the
+%% residual-code variables instead of the source-code variables as its
+%% domain, improves the behaviour of the algorithm when code needs to be
+%% traversed more than once.
+%%
+%% Note that there is no such thing as a `test' context for expressions
+%% in (Core) Erlang (see `i_case' below for details).
+
+i(E, Ctxt, S) ->
+ i(E, Ctxt, ren__identity(), env__empty(), S).
+
+i(E, Ctxt, Ren, Env, S0) ->
+ %% Count one unit of effort on each pass.
+ S = count_effort(1, S0),
+ case is_data(E) of
+ true ->
+ i_data(E, Ctxt, Ren, Env, S);
+ false ->
+ case type(E) of
+ var ->
+ i_var(E, Ctxt, Ren, Env, S);
+ values ->
+ i_values(E, Ctxt, Ren, Env, S);
+ 'fun' ->
+ i_fun(E, Ctxt, Ren, Env, S);
+ seq ->
+ i_seq(E, Ctxt, Ren, Env, S);
+ 'let' ->
+ i_let(E, Ctxt, Ren, Env, S);
+ letrec ->
+ i_letrec(E, Ctxt, Ren, Env, S);
+ 'case' ->
+ i_case(E, Ctxt, Ren, Env, S);
+ 'receive' ->
+ i_receive(E, Ctxt, Ren, Env, S);
+ apply ->
+ i_apply(E, Ctxt, Ren, Env, S);
+ call ->
+ i_call(E, Ctxt, Ren, Env, S);
+ primop ->
+ i_primop(E, Ren, Env, S);
+ 'try' ->
+ i_try(E, Ctxt, Ren, Env, S);
+ 'catch' ->
+ i_catch(E, Ctxt, Ren, Env, S);
+ binary ->
+ i_binary(E, Ren, Env, S);
+ module ->
+ i_module(E, Ctxt, Ren, Env, S)
+ end
+ end.
+
+i_data(E, Ctxt, Ren, Env, S) ->
+ case is_literal(E) of
+ true ->
+ %% This is the `(const c)' case of the original algorithm:
+ %% literal terms which (regardless of size) do not need to
+ %% be constructed dynamically at runtime - boldly assuming
+ %% that the compiler/runtime system can handle this.
+ case Ctxt of
+ effect ->
+ %% Reduce useless constants to a simple value.
+ {void(), count_size(weight(literal), S)};
+ _ ->
+ %% (In Erlang, we cannot set all non-`false'
+ %% constants to `true' in a `test' context, like we
+ %% could do in Lisp or C, so the above is the only
+ %% special case to be handled here.)
+ {E, count_size(weight(literal), S)}
+ end;
+ false ->
+ %% Data constructors are like to calls to safe built-in
+ %% functions, for which we can "decide to inline"
+ %% immediately; there is no need to create operand
+ %% structures. In `effect' context, we can simply make a
+ %% sequence of the argument expressions, also visited in
+ %% `effect' context. In all other cases, the arguments are
+ %% visited for value.
+ case Ctxt of
+ effect ->
+ %% Note that this will count the sizes of the
+ %% subexpressions, even though some or all of them
+ %% might be discarded by the sequencing afterwards.
+ {Es1, S1} = mapfoldl(fun (E, S) ->
+ i(E, effect, Ren, Env,
+ S)
+ end,
+ S, data_es(E)),
+ E1 = foldl(fun (E1, E2) -> make_seq(E1, E2) end,
+ void(), Es1),
+ {E1, S1};
+ _ ->
+ {Es1, S1} = mapfoldl(fun (E, S) ->
+ i(E, value, Ren, Env,
+ S)
+ end,
+ S, data_es(E)),
+ %% The total size/cost is the base cost for a data
+ %% constructor plus the cost for storing each
+ %% element.
+ N = weight(data) + length(Es1) * weight(element),
+ S2 = count_size(N, S1),
+ {update_data(E, data_type(E), Es1), S2}
+ end
+ end.
+
+%% This is the `(ref x)' (variable use) case of the original algorithm.
+%% Note that binding occurrences are always handled in the respective
+%% cases of the binding constructs.
+
+i_var(E, Ctxt, Ren, Env, S) ->
+ case Ctxt of
+ effect ->
+ %% Reduce useless variable references to a simple constant.
+ %% This also avoids useless visiting of bound operands.
+ {void(), count_size(weight(literal), S)};
+ _ ->
+ Name = var_name(E),
+ case env__lookup(ren__map(Name, Ren), Env) of
+ {ok, R} ->
+ case R#ref.opnd of
+ undefined ->
+ %% The variable is not associated with an
+ %% argument expression; just residualize it.
+ residualize_var(R, S);
+ Opnd ->
+ i_var_1(R, Opnd, Ctxt, Env, S)
+ end;
+ error ->
+ %% The variable is unbound. (It has not been
+ %% accidentally captured, however, or it would have
+ %% been in the environment.) We leave it as it is,
+ %% without any warning.
+ {E, count_size(weight(var), S)}
+ end
+ end.
+
+%% This first visits the bound operand and then does copy propagation.
+%% Note that we must first set the "inner-pending" flag, and clear the
+%% flag afterwards.
+
+i_var_1(R, Opnd, Ctxt, Env, S) ->
+ %% If the operand is already "inner-pending", it is residualised.
+ %% (In Lisp/C, if the variable might be assigned to, it should also
+ %% be residualised.)
+ L = Opnd#opnd.loc,
+ case st__test_inner_pending(L, S) of
+ true ->
+ residualize_var(R, S);
+ false ->
+ S1 = st__mark_inner_pending(L, S),
+ case catch {ok, visit(Opnd, S1)} of
+ {ok, {E, S2}} ->
+ %% Note that we pass the current environment and
+ %% context to `copy', but not the current renaming.
+ S3 = st__clear_inner_pending(L, S2),
+ copy(R, Opnd, E, Ctxt, Env, S3);
+ {'EXIT', X} ->
+ exit(X);
+ X ->
+ %% If we use destructive update for the
+ %% `inner-pending' flag, we must make sure to clear
+ %% it also if we make a nonlocal return.
+ st__clear_inner_pending(Opnd#opnd.loc, S1),
+ throw(X)
+ end
+ end.
+
+%% A multiple-value aggregate `'. This is very much like a
+%% tuple data constructor `{e1, ..., en}'; cf. `i_data' for details.
+
+i_values(E, Ctxt, Ren, Env, S) ->
+ case values_es(E) of
+ [E1] ->
+ %% Single-value aggregates can be dropped; they are simply
+ %% notation.
+ i(E1, Ctxt, Ren, Env, S);
+ Es ->
+ %% In `effect' context, we can simply make a sequence of the
+ %% argument expressions, also visited in `effect' context.
+ %% In all other cases, the arguments are visited for value.
+ case Ctxt of
+ effect ->
+ {Es1, S1} =
+ mapfoldl(fun (E, S) ->
+ i(E, effect, Ren, Env, S)
+ end,
+ S, Es),
+ E1 = foldl(fun (E1, E2) ->
+ make_seq(E1, E2)
+ end,
+ void(), Es1),
+ {E1, S1}; % drop annotations on E
+ _ ->
+ {Es1, S1} = mapfoldl(fun (E, S) ->
+ i(E, value, Ren, Env,
+ S)
+ end,
+ S, Es),
+ %% Aggregating values does not write them to memory,
+ %% so we count no extra cost per element.
+ S2 = count_size(weight(values), S1),
+ {update_c_values(E, Es1), S2}
+ end
+ end.
+
+%% A let-expression `let = e0 in e1' is semantically
+%% equivalent to a case-expression `case e0 of when 'true'
+%% -> e1 end'. As a special case, `let = e0 in e1' is also
+%% equivalent to `apply fun (v) -> e0 (e1)'. However, for efficiency,
+%% and in order to allow the handling of `case' clauses to introduce new
+%% let-expressions without entering an infinite rewrite loop, we handle
+%% these directly.
+
+%%% %% Rewriting a `let' to an equivalent expression.
+%%% i_let(E, Ctxt, Ren, Env, S) ->
+%%% case let_vars(E) of
+%%% [V] ->
+%%% E1 = update_c_apply(E, c_fun([V], let_body(E)), [let_arg(E)]),
+%%% i(E1, Ctxt, Ren, Env, S);
+%%% Vs ->
+%%% C = c_clause(Vs, abstract(true), let_body(E)),
+%%% E1 = update_c_case(E, let_arg(E), [C]),
+%%% i(E1, Ctxt, Ren, Env, S)
+%%% end.
+
+i_let(E, Ctxt, Ren, Env, S) ->
+ case let_vars(E) of
+ [V] ->
+ i_let_1(V, E, Ctxt, Ren, Env, S);
+ Vs ->
+ %% Visit the argument expression in `value' context, to
+ %% simplify it as far as possible.
+ {A, S1} = i(let_arg(E), value, Ren, Env, S),
+ case get_components(length(Vs), result(A)) of
+ {true, As} ->
+ %% Note that only the components of the result of
+ %% `A' are passed on; any effects are hoisted.
+ {E1, S2} = i_let_2(Vs, As, E, Ctxt, Ren, Env, S1),
+ {hoist_effects(A, E1), S2};
+ false ->
+ %% We cannot do anything with this `let', since the
+ %% variables cannot be matched against the argument
+ %% components. Just visit the variables for renaming
+ %% and visit the body for value (cf. `i_fun').
+ {_, Ren1, Env1, S2} = bind_locals(Vs, Ren, Env, S1),
+ Vs1 = i_params(Vs, Ren1, Env1),
+ %% The body is always visited for value here.
+ {B, S3} = i(let_body(E), value, Ren1, Env1, S2),
+ S4 = count_size(weight('let'), S3),
+ {update_c_let(E, Vs1, A, B), S4}
+ end
+ end.
+
+%% Single-variable `let' binding.
+
+i_let_1(V, E, Ctxt, Ren, Env, S) ->
+ %% Make an operand structure for the argument expression, create a
+ %% local binding from the parameter to the operand structure, and
+ %% visit the body. Finally create necessary bindings and/or set
+ %% flags.
+ {Opnd, S1} = make_opnd(let_arg(E), Ren, Env, S),
+ {[R], Ren1, Env1, S2} = bind_locals([V], [Opnd], Ren, Env, S1),
+ {E1, S3} = i(let_body(E), Ctxt, Ren1, Env1, S2),
+ i_let_3([R], [Opnd], E1, S3).
+
+%% Multi-variable `let' binding.
+
+i_let_2(Vs, As, E, Ctxt, Ren, Env, S) ->
+ %% Make operand structures for the argument components. Note that
+ %% since the argument has already been visited at this point, we use
+ %% the identity renaming for the operands.
+ {Opnds, S1} = mapfoldl(fun (E, S) ->
+ make_opnd(E, ren__identity(), Env, S)
+ end,
+ S, As),
+ %% Create local bindings from the parameters to their respective
+ %% operand structures, and visit the body.
+ {Rs, Ren1, Env1, S2} = bind_locals(Vs, Opnds, Ren, Env, S1),
+ {E1, S3} = i(let_body(E), Ctxt, Ren1, Env1, S2),
+ i_let_3(Rs, Opnds, E1, S3).
+
+i_let_3(Rs, Opnds, E, S) ->
+ %% Create necessary bindings and/or set flags.
+ {E1, S1} = make_let_bindings(Rs, E, S),
+
+ %% We must also create evaluation for effect, for any unused
+ %% operands, as after an application expression.
+ residualize_operands(Opnds, E1, S1).
+
+%% A sequence `do e1 e2', written `(seq e1 e2)' in the original
+%% algorithm, where `e1' is evaluated for effect only (since its value
+%% is not used), and `e2' yields the final value. Note that we use
+%% `make_seq' to recompose the sequence after visiting the parts.
+
+i_seq(E, Ctxt, Ren, Env, S) ->
+ {E1, S1} = i(seq_arg(E), effect, Ren, Env, S),
+ {E2, S2} = i(seq_body(E), Ctxt, Ren, Env, S1),
+ %% A sequence has no cost in itself.
+ {make_seq(E1, E2), S2}.
+
+
+%% The `case' switch of Core Erlang is rather different from the boolean
+%% `(if e1 e2 e3)' case of the original algorithm, but the central idea
+%% is the same: if, given the simplified switch expression (which is
+%% visited in `value' context - a boolean `test' context would not be
+%% generally useful), there is a clause which could definitely be
+%% selected, such that no clause before it can possibly be selected,
+%% then we can eliminate all other clauses. (And even if this is not the
+%% case, some clauses can often be eliminated.) Furthermore, if a clause
+%% can be selected, we can replace the case-expression (including the
+%% switch expression) with the body of the clause and a set of zero or
+%% more let-bindings of subexpressions of the switch expression. (In the
+%% simplest case, the switch expression is evaluated only for effect.)
+
+i_case(E, Ctxt, Ren, Env, S) ->
+ %% First visit the switch expression in `value' context, to simplify
+ %% it as far as possible. Note that only the result part is passed
+ %% on to the clause matching below; any effects are hoisted.
+ {A, S1} = i(case_arg(E), value, Ren, Env, S),
+ A1 = result(A),
+
+ %% Propagating an application context into the branches could cause
+ %% the arguments of the application to be evaluated *after* the
+ %% switch expression, but *before* the body of the selected clause.
+ %% Such interleaving is not allowed in general, and it does not seem
+ %% worthwile to make a more powerful transformation here. Therefore,
+ %% the clause bodies are conservatively visited for value if the
+ %% context is `application'.
+ Ctxt1 = safe_context(Ctxt),
+ {E1, S2} = case get_components(case_arity(E), A1) of
+ {true, As} ->
+ i_case_1(As, E, Ctxt1, Ren, Env, S1);
+ false ->
+ i_case_1([], E, Ctxt1, Ren, Env, S1)
+ end,
+ {hoist_effects(A, E1), S2}.
+
+i_case_1(As, E, Ctxt, Ren, Env, S) ->
+ case i_clauses(As, case_clauses(E), Ctxt, Ren, Env, S) of
+ {false, {As1, Vs, Env1, Cs}, S1} ->
+ %% We still have a list of clauses. Sanity check:
+ if Cs == [] ->
+ report_warning("empty list of clauses "
+ "in residual program!.\n");
+ true ->
+ ok
+ end,
+ {A, S2} = i(c_values(As1), value, ren__identity(), Env1,
+ S1),
+ {E1, S3} = i_case_2(Cs, A, E, S2),
+ i_case_3(Vs, Env1, E1, S3);
+ {true, {_, Vs, Env1, [C]}, S1} ->
+ %% A single clause was selected; we just take the body.
+ i_case_3(Vs, Env1, clause_body(C), S1)
+ end.
+
+%% Check if all clause bodies are actually equivalent expressions that
+%% do not depent on pattern variables (this sometimes occurs as a
+%% consequence of inlining, e.g., all branches might yield 'true'), and
+%% if so, replace the `case' with a sequence, first evaluating the
+%% clause selection for effect, then evaluating one of the clause bodies
+%% for its value. (Unless the switch contains a catch-all clause, the
+%% clause selection must be evaluated for effect, since there is no
+%% guarantee that any of the clauses will actually match. Assuming that
+%% some clause always matches could make an undefined program produce a
+%% value.) This makes the final size less than what was accounted for
+%% when visiting the clauses, but currently we don't try to adjust for
+%% this.
+
+i_case_2(Cs, A, E, S) ->
+ case equivalent_clauses(Cs) of
+ false ->
+ %% Count the base sizes for the remaining clauses; pattern
+ %% and guard sizes are already counted.
+ N = weight('case') + weight(clause) * length(Cs),
+ S1 = count_size(N, S),
+ {update_c_case(E, A, Cs), S1};
+ true ->
+ case cerl_clauses:any_catchall(Cs) of
+ true ->
+ %% We know that some clause must be selected, so we
+ %% can drop all the testing as well.
+ E1 = make_seq(A, clause_body(hd(Cs))),
+ {E1, S};
+ false ->
+ %% The clause selection must be performed for
+ %% effect.
+ E1 = update_c_case(E, A,
+ set_clause_bodies(Cs, void())),
+ {make_seq(E1, clause_body(hd(Cs))), S}
+ end
+ end.
+
+i_case_3(Vs, Env, E, S) ->
+ %% For the variables bound to the switch expression subexpressions,
+ %% make let bindings or create evaluation for effect.
+ Rs = [env__get(var_name(V), Env) || V <- Vs],
+ {E1, S1} = make_let_bindings(Rs, E, S),
+ Opnds = [R#ref.opnd || R <- Rs],
+ residualize_operands(Opnds, E1, S1).
+
+%% This function takes a sequence of switch expressions `Es' (which can
+%% be the empty list if these are unknown) and a list `Cs' of clauses,
+%% and returns `{Match, {As, Vs, Env1, Cs1}, S1}' where `As' is a list
+%% of residual switch expressions, `Vs' the list of variables used in
+%% the templates, `Env1' the environment for the templates, and `Cs1'
+%% the list of residual clauses. `Match' is `true' if some clause could
+%% be shown to definitely match (in this case, `Cs1' contains exactly
+%% one element), and `false' otherwise. `S1' is the new state. The given
+%% `Ctxt' is the context to be used for visiting the body of clauses.
+%%
+%% Visiting a clause basically amounts to extending the environment for
+%% all variables in the pattern, as for a `fun' (cf. `i_fun'),
+%% propagating match information if possible, and visiting the guard and
+%% body in the new environment.
+%%
+%% To make it cheaper to do handle a set of clauses, and to avoid
+%% unnecessarily exceeding the size limit, we avoid visiting the bodies
+%% of clauses which are subsequently removed, by dividing the visiting
+%% of a clause into two stages: first construct the environment(s) and
+%% visit the pattern (for renaming) and the guard (for value), then
+%% reduce the switch as much as possible, and lastly visit the body.
+
+i_clauses(Cs, Ctxt, Ren, Env, S) ->
+ i_clauses([], Cs, Ctxt, Ren, Env, S).
+
+i_clauses(Es, Cs, Ctxt, Ren, Env, S) ->
+ %% Create templates for the switch expressions.
+ {Ts, {Vs, Env0}} = mapfoldl(fun (E, {Vs, Env}) ->
+ {T, Vs1, Env1} =
+ make_template(E, Env),
+ {T, {Vs1 ++ Vs, Env1}}
+ end,
+ {[], Env}, Es),
+
+ %% Make operand structures for the switch subexpression templates
+ %% (found in `Env0') and add proper ref-structure bindings to the
+ %% environment. Since the subexpressions in general can be
+ %% interdependent (Vs is in reverse-dependency order), the
+ %% environment (and renaming) must be created incrementally. Note
+ %% that since the switch expressions have been visited already, the
+ %% identity renaming is used for the operands.
+ Vs1 = lists:reverse(Vs),
+ {Ren1, Env1, S1} =
+ foldl(fun (V, {Ren, Env, S}) ->
+ E = env__get(var_name(V), Env0),
+ {Opnd, S_1} = make_opnd(E, ren__identity(), Env,
+ S),
+ {_, Ren1, Env1, S_2} = bind_locals([V], [Opnd],
+ Ren, Env, S_1),
+ {Ren1, Env1, S_2}
+ end,
+ {Ren, Env, S}, Vs1),
+
+ %% First we visit the head of each individual clause, renaming
+ %% pattern variables, inserting let-bindings in the guard and body,
+ %% and visiting the guard. The information used for visiting the
+ %% clause body will be prefixed to the clause annotations.
+ {Cs1, S2} = mapfoldl(fun (C, S) ->
+ i_clause_head(C, Ts, Ren1, Env1, S)
+ end,
+ S1, Cs),
+
+ %% Now that the clause guards have been reduced as far as possible,
+ %% we can attempt to reduce the clauses.
+ As = [hd(get_ann(T)) || T <- Ts],
+ case cerl_clauses:reduce(Cs1, Ts) of
+ {false, Cs2} ->
+ %% We still have one or more clauses (with associated
+ %% extended environments). Their bodies have not yet been
+ %% visited, so we do that (in the respective safe
+ %% environments, adding the sizes of the visited heads to
+ %% the current size counter) and return the final list of
+ %% clauses.
+ {Cs3, S3} = mapfoldl(
+ fun (C, S) ->
+ i_clause_body(C, Ctxt, S)
+ end,
+ S2, Cs2),
+ {false, {As, Vs1, Env1, Cs3}, S3};
+ {true, {C, _}} ->
+ %% A clause C could be selected (the bindings have already
+ %% been added to the guard/body). Note that since the clause
+ %% head will probably be discarded, its size is not counted.
+ {C1, Ren2, Env2, _} = get_clause_extras(C),
+ {B, S3} = i(clause_body(C), Ctxt, Ren2, Env2, S2),
+ C2 = update_c_clause(C1, clause_pats(C1), clause_guard(C1), B),
+ {true, {As, Vs1, Env1, [C2]}, S3}
+ end.
+
+%% This visits the head of a clause, renames pattern variables, inserts
+%% let-bindings in the guard and body, and does inlining on the guard
+%% expression. Returns a list of pairs `{NewClause, Data}', where `Data'
+%% is `{Renaming, Environment, Size}' used for visiting the body of the
+%% new clause.
+
+i_clause_head(C, Ts, Ren, Env, S) ->
+ %% Match the templates against the (non-renamed) patterns to get the
+ %% available information about matching subexpressions. We don't
+ %% care at this point whether an exact match/nomatch is detected.
+ Ps = clause_pats(C),
+ Bs = case cerl_clauses:match_list(Ps, Ts) of
+ {_, Bs1} -> Bs1;
+ none -> []
+ end,
+
+ %% The patterns must be visited for renaming; cf. `i_pattern'. We
+ %% use a passive size counter for visiting the patterns and the
+ %% guard (cf. `visit'), because we do not know at this stage whether
+ %% the clause will be kept or not; the final value of the counter is
+ %% included in the returned value below.
+ {_, Ren1, Env1, S1} = bind_locals(clause_vars(C), Ren, Env, S),
+ S2 = new_passive_size(get_size_limit(S1), S1),
+ {Ps1, S3} = mapfoldl(fun (P, S) ->
+ i_pattern(P, Ren1, Env1, Ren, Env, S)
+ end,
+ S2, Ps),
+
+ %% Rewrite guard and body and visit the guard for value. Discard the
+ %% latter size count if the guard turns out to be a constant.
+ G = add_match_bindings(Bs, clause_guard(C)),
+ B = add_match_bindings(Bs, clause_body(C)),
+ {G1, S4} = i(G, value, Ren1, Env1, S3),
+ S5 = case is_literal(G1) of
+ true ->
+ revert_size(S3, S4);
+ false ->
+ S4
+ end,
+
+ %% Revert to the size counter we had on entry to this function. The
+ %% environment and renaming, together with the size of the clause
+ %% head, are prefixed to the annotations for later use.
+ Size = get_size_value(S5),
+ C1 = update_c_clause(C, Ps1, G1, B),
+ {set_clause_extras(C1, Ren1, Env1, Size), revert_size(S, S5)}.
+
+add_match_bindings(Bs, E) ->
+ %% Don't waste time if the variables definitely cannot be used.
+ %% (Most guards are simply `true'.)
+ case is_literal(E) of
+ true ->
+ E;
+ false ->
+ Vs = [V || {V, E} <- Bs, E /= any],
+ Es = [hd(get_ann(E)) || {_V, E} <- Bs, E /= any],
+ c_let(Vs, c_values(Es), E)
+ end.
+
+i_clause_body(C0, Ctxt, S) ->
+ {C, Ren, Env, Size} = get_clause_extras(C0),
+ S1 = count_size(Size, S),
+ {B, S2} = i(clause_body(C), Ctxt, Ren, Env, S1),
+ C1 = update_c_clause(C, clause_pats(C), clause_guard(C), B),
+ {C1, S2}.
+
+get_clause_extras(C) ->
+ [{Ren, Env, Size} | As] = get_ann(C),
+ {set_ann(C, As), Ren, Env, Size}.
+
+set_clause_extras(C, Ren, Env, Size) ->
+ As = [{Ren, Env, Size} | get_ann(C)],
+ set_ann(C, As).
+
+%% This is the `(lambda x e)' case of the original algorithm. A
+%% `fun' is like a lambda expression, but with a varying number of
+%% parameters; possibly zero.
+
+i_fun(E, Ctxt, Ren, Env, S) ->
+ case Ctxt of
+ effect ->
+ %% Reduce useless `fun' expressions to a simple constant;
+ %% visiting the body would be a waste of time, and could
+ %% needlessly mark variables as referenced.
+ {void(), count_size(weight(literal), S)};
+ value ->
+ %% Note that the variables are visited as patterns.
+ Vs = fun_vars(E),
+ {_, Ren1, Env1, S1} = bind_locals(Vs, Ren, Env, S),
+ Vs1 = i_params(Vs, Ren1, Env1),
+
+ %% The body is always visited for value.
+ {B, S2} = i(fun_body(E), value, Ren1, Env1, S1),
+
+ %% We don't bother to include the exact number of free
+ %% variables in the cost for creating a fun-value.
+ S3 = count_size(weight('fun'), S2),
+
+ %% Inlining might have duplicated code, so we must remove
+ %% any 'id'-annotations from the original fun-expression.
+ %% (This forces a later stage to invent new id:s.) This is
+ %% necessary as long as fun:s may still need to be
+ %% identified the old way. Function variables that are not
+ %% in application context also have such annotations, but
+ %% the inlining will currently lose all annotations on
+ %% variable references (I think), so that's not a problem.
+ {set_ann(c_fun(Vs1, B), kill_id_anns(get_ann(E))), S3};
+ #app{} ->
+ %% An application of a fun-expression (in the source code)
+ %% is handled by going directly to `inline'; this is never
+ %% residualised, and we don't set up new counters here. Note
+ %% that inlining of copy-propagated fun-expressions is done
+ %% in `copy'; not here.
+ inline(E, Ctxt, Ren, Env, S)
+ end.
+
+%% A `letrec' requires a circular environment, but is otherwise like a
+%% `let', i.e. like a direct lambda application. Note that only
+%% fun-expressions (lambda abstractions) may occur in the right-hand
+%% side of each definition.
+
+i_letrec(E, Ctxt, Ren, Env, S) ->
+ %% Note that we pass an empty list for the auto-referenced
+ %% (exported) functions here.
+ {Es, B, _, S1} = i_letrec(letrec_defs(E), letrec_body(E), [], Ctxt,
+ Ren, Env, S),
+
+ %% If no bindings remain, only the body is returned.
+ case Es of
+ [] ->
+ {B, S1}; % drop annotations on E
+ _ ->
+ S2 = count_size(weight(letrec), S1),
+ {update_c_letrec(E, Es, B), S2}
+ end.
+
+%% The major part of this is shared by letrec-expressions and module
+%% definitions alike.
+
+i_letrec(Es, B, Xs, Ctxt, Ren, Env, S) ->
+ %% First, we create operands with dummy renamings and environments,
+ %% and with fresh store locations for cached expressions and operand
+ %% info.
+ {Opnds, S1} = mapfoldl(fun ({_, E}, S) ->
+ make_opnd(E, undefined, undefined, S)
+ end,
+ S, Es),
+
+ %% Then we make recursive bindings for the definitions.
+ {Rs, Ren1, Env1, S2} = bind_recursive([F || {F, _} <- Es],
+ Opnds, Ren, Env, S1),
+
+ %% For the function variables listed in Xs (none for a
+ %% letrec-expression), we must make sure that the corresponding
+ %% operand expressions are visited and that the definitions are
+ %% marked as referenced; we also need to return the possibly renamed
+ %% function variables.
+ {Xs1, S3} =
+ mapfoldl(
+ fun (X, S) ->
+ Name = ren__map(var_name(X), Ren1),
+ case env__lookup(Name, Env1) of
+ {ok, R} ->
+ S_1 = i_letrec_export(R, S),
+ {ref_to_var(R), S_1};
+ error ->
+ %% We just skip any exports that are not
+ %% actually defined here, and generate a
+ %% warning message.
+ {N, A} = var_name(X),
+ report_warning("export `~w'/~w "
+ "not defined.\n", [N, A]),
+ {X, S}
+ end
+ end,
+ S2, Xs),
+
+ %% At last, we can then visit the body.
+ {B1, S4} = i(B, Ctxt, Ren1, Env1, S3),
+
+ %% Finally, we create new letrec-bindings for any and all
+ %% residualised definitions. All referenced functions should have
+ %% been visited; the call to `visit' below is expected to retreive a
+ %% cached expression.
+ Rs1 = keep_referenced(Rs, S4),
+ {Es1, S5} = mapfoldl(fun (R, S) ->
+ {E_1, S_1} = visit(R#ref.opnd, S),
+ {{ref_to_var(R), E_1}, S_1}
+ end,
+ S4, Rs1),
+ {Es1, B1, Xs1, S5}.
+
+%% This visits the operand for a function definition exported by a
+%% `letrec' (which is really a `module' module definition, since normal
+%% letrecs have no export declarations). Only the updated state is
+%% returned. We must handle the "inner-pending" flag when doing this;
+%% cf. `i_var'.
+
+i_letrec_export(R, S) ->
+ Opnd = R#ref.opnd,
+ S1 = st__mark_inner_pending(Opnd#opnd.loc, S),
+ {_, S2} = visit(Opnd, S1),
+ {_, S3} = residualize_var(R, st__clear_inner_pending(Opnd#opnd.loc,
+ S2)),
+ S3.
+
+%% This is the `(call e1 e2)' case of the original algorithm. The only
+%% difference is that we must handle multiple (or no) operand
+%% expressions.
+
+i_apply(E, Ctxt, Ren, Env, S) ->
+ {Opnds, S1} = mapfoldl(fun (E, S) ->
+ make_opnd(E, Ren, Env, S)
+ end,
+ S, apply_args(E)),
+
+ %% Allocate a new app-context location and set up an application
+ %% context structure containing the surrounding context.
+ {L, S2} = st__new_app_loc(S1),
+ Ctxt1 = #app{opnds = Opnds, ctxt = Ctxt, loc = L},
+
+ %% Visit the operator expression in the new call context.
+ {E1, S3} = i(apply_op(E), Ctxt1, Ren, Env, S2),
+
+ %% Check the "inlined" flag to find out what to do next. (The store
+ %% location could be recycled after the flag has been tested, but
+ %% there is no real advantage to that, because in practice, only
+ %% 4-5% of all created store locations will ever be reused, while
+ %% there will be a noticable overhead for managing the free list.)
+ case st__get_app_inlined(L, S3) of
+ true ->
+ %% The application was inlined, so we have the final
+ %% expression in `E1'. We just have to handle any operands
+ %% that need to be residualized for effect only (i.e., those
+ %% the values of which are not used).
+ residualize_operands(Opnds, E1, S3);
+ false ->
+ %% Otherwise, `E1' is the residual operator expression. We
+ %% make sure all operands are visited, and rebuild the
+ %% application.
+ {Es, S4} = mapfoldl(fun (Opnd, S) ->
+ visit_and_count_size(Opnd, S)
+ end,
+ S3, Opnds),
+ N = apply_size(length(Es)),
+ {update_c_apply(E, E1, Es), count_size(N, S4)}
+ end.
+
+apply_size(A) ->
+ weight(apply) + weight(argument) * A.
+
+%% Since it is not the task of this transformation to handle
+%% cross-module inlining, all inter-module calls are handled by visiting
+%% the components (the module and function name, and the arguments of
+%% the call) for value. In `effect' context, if the function itself is
+%% known to be completely effect free, the call can be discarded and the
+%% arguments evaluated for effect. Otherwise, if all the visited
+%% arguments are to constants, and the function is known to be safe to
+%% execute at compile time, then we try to evaluate the call. If
+%% evaluation completes normally, the call is replaced by the result;
+%% otherwise the call is residualised.
+
+i_call(E, Ctxt, Ren, Env, S) ->
+ {M, S1} = i(call_module(E), value, Ren, Env, S),
+ {F, S2} = i(call_name(E), value, Ren, Env, S1),
+ As = call_args(E),
+ Arity = length(As),
+
+ %% Check if the name of the called function is static. If so,
+ %% discard the size counts performed above, since the values will
+ %% not cause any runtime cost.
+ Static = is_c_atom(M) and is_c_atom(F),
+ S3 = case Static of
+ true ->
+ revert_size(S, S2);
+ false ->
+ S2
+ end,
+ case Ctxt of
+ effect when Static == true ->
+ case is_safe_call(atom_val(M), atom_val(F), Arity) of
+ true ->
+ %% The result will not be used, and the call is
+ %% effect free, so we create a multiple-value
+ %% aggregate containing the (not yet visited)
+ %% arguments and process that instead.
+ i(c_values(As), effect, Ren, Env, S3);
+ false ->
+ %% We are not allowed to simply discard the call,
+ %% but we can try to evaluate it.
+ i_call_1(Static, M, F, Arity, As, E, Ctxt, Ren, Env,
+ S3)
+ end;
+ _ ->
+ i_call_1(Static, M, F, Arity, As, E, Ctxt, Ren, Env, S3)
+ end.
+
+i_call_1(Static, M, F, Arity, As, E, Ctxt, Ren, Env, S) ->
+ %% Visit the arguments for value.
+ {As1, S1} = mapfoldl(fun (X, A) -> i(X, value, Ren, Env, A) end,
+ S, As),
+ case Static of
+ true ->
+ case erl_bifs:is_pure(atom_val(M), atom_val(F), Arity) of
+ true ->
+ %% It is allowed to evaluate this at compile time.
+ case all_static(As1) of
+ true ->
+ i_call_3(M, F, As1, E, Ctxt, Env, S1);
+ false ->
+ %% See if the call can be rewritten instead.
+ i_call_4(M, F, As1, E, Ctxt, Env, S1)
+ end;
+ false ->
+ i_call_2(M, F, As1, E, S1)
+ end;
+ false ->
+ i_call_2(M, F, As1, E, S1)
+ end.
+
+%% Residualise the call.
+
+i_call_2(M, F, As, E, S) ->
+ N = weight(call) + weight(argument) * length(As),
+ {update_c_call(E, M, F, As), count_size(N, S)}.
+
+%% Attempt to evaluate the call to yield a literal; if that fails, try
+%% to rewrite the expression.
+
+i_call_3(M, F, As, E, Ctxt, Env, S) ->
+ %% Note that we extract the results of argument expessions here; the
+ %% expressions could still be sequences with side effects.
+ Vs = [concrete(result(A)) || A <- As],
+ case catch {ok, apply(atom_val(M), atom_val(F), Vs)} of
+ {ok, V} ->
+ %% Evaluation completed normally - try to turn the result
+ %% back into a syntax tree (representing a literal).
+ case is_literal_term(V) of
+ true ->
+ %% Make a sequence of the arguments (as a
+ %% multiple-value aggregate) and the final value.
+ S1 = count_size(weight(values), S),
+ S2 = count_size(weight(literal), S1),
+ {make_seq(c_values(As), abstract(V)), S2};
+ false ->
+ %% The result could not be represented as a literal.
+ i_call_4(M, F, As, E, Ctxt, Env, S)
+ end;
+ _ ->
+ %% The evaluation attempt did not complete normally.
+ i_call_4(M, F, As, E, Ctxt, Env, S)
+ end.
+
+%% Rewrite the expression, if possible, otherwise residualise it.
+
+i_call_4(M, F, As, E, Ctxt, Env, S) ->
+ case reduce_bif_call(atom_val(M), atom_val(F), As, Env) of
+ false ->
+ %% Nothing more to be done - residualise the call.
+ i_call_2(M, F, As, E, S);
+ {true, E1} ->
+ %% We revisit the result, because the rewriting might have
+ %% opened possibilities for further inlining. Since the
+ %% parts have already been visited once, we use the identity
+ %% renaming here.
+ i(E1, Ctxt, ren__identity(), Env, S)
+ end.
+
+%% For now, we assume that primops cannot be evaluated at compile time,
+%% probably being too special. Also, we have no knowledge about their
+%% side effects.
+
+i_primop(E, Ren, Env, S) ->
+ %% Visit the arguments for value.
+ {As, S1} = mapfoldl(fun (E, S) ->
+ i(E, value, Ren, Env, S)
+ end,
+ S, primop_args(E)),
+ N = weight(primop) + weight(argument) * length(As),
+ {update_c_primop(E, primop_name(E), As), count_size(N, S1)}.
+
+%% This is like having an expression with an extra fun-expression
+%% attached for "exceptional cases"; actually, there are exactly two
+%% parameter variables for the body, but they are easiest handled as if
+%% their number might vary, just as for a `fun'.
+
+i_try(E, Ctxt, Ren, Env, S) ->
+ %% The argument expression is evaluated in `value' context, and the
+ %% surrounding context is propagated into both branches. We do not
+ %% try to recognize cases when the protected expression will
+ %% actually raise an exception. Note that the variables are visited
+ %% as patterns.
+ {A, S1} = i(try_arg(E), value, Ren, Env, S),
+ Vs = try_vars(E),
+ {_, Ren1, Env1, S2} = bind_locals(Vs, Ren, Env, S1),
+ Vs1 = i_params(Vs, Ren1, Env1),
+ {B, S3} = i(try_body(E), Ctxt, Ren1, Env1, S2),
+ case is_safe(A) of
+ true ->
+ %% The `try' wrapper can be dropped in this case. Since the
+ %% expressions have been visited already, the identity
+ %% renaming is used when we revisit the new let-expression.
+ i(c_let(Vs1, A, B), Ctxt, ren__identity(), Env, S3);
+ false ->
+ Evs = try_evars(E),
+ {_, Ren2, Env2, S4} = bind_locals(Evs, Ren, Env, S3),
+ Evs1 = i_params(Evs, Ren2, Env2),
+ {H, S5} = i(try_handler(E), Ctxt, Ren2, Env2, S4),
+ S6 = count_size(weight('try'), S5),
+ {update_c_try(E, A, Vs1, B, Evs1, H), S6}
+ end.
+
+%% A special case of try-expressions:
+
+i_catch(E, Ctxt, Ren, Env, S) ->
+ %% We cannot propagate application contexts into the catch.
+ {E1, S1} = i(catch_body(E), safe_context(Ctxt), Ren, Env, S),
+ case is_safe(E1) of
+ true ->
+ %% The `catch' wrapper can be dropped in this case.
+ {E1, S1};
+ false ->
+ S2 = count_size(weight('catch'), S1),
+ {update_c_catch(E, E1), S2}
+ end.
+
+%% A receive-expression is very much like a case-expression, with the
+%% difference that we do not have access to a switch expression, since
+%% the value being switched on is taken from the mailbox. The fact that
+%% the receive-expression may iterate over an arbitrary number of
+%% messages is not of interest to us. All we can do here is to visit its
+%% subexpressions, and possibly eliminate definitely unselectable
+%% clauses.
+
+i_receive(E, Ctxt, Ren, Env, S) ->
+ %% We first visit the expiry expression (for value) and the expiry
+ %% body (in the surrounding context).
+ {T, S1} = i(receive_timeout(E), value, Ren, Env, S),
+ {B, S2} = i(receive_action(E), Ctxt, Ren, Env, S1),
+
+ %% Then we visit the clauses. Note that application contexts may not
+ %% in general be propagated into the branches (and the expiry body),
+ %% because the execution of the `receive' may remove a message from
+ %% the mailbox as a side effect; the situation is thus analogous to
+ %% that in a `case' expression.
+ Ctxt1 = safe_context(Ctxt),
+ case i_clauses(receive_clauses(E), Ctxt1, Ren, Env, S2) of
+ {false, {[], _, _, Cs}, S3} ->
+ %% We still have a list of clauses. If the list is empty,
+ %% and the expiry expression is the integer zero, the
+ %% expression reduces to the expiry body.
+ if Cs == [] ->
+ case is_c_int(T) andalso (int_val(T) == 0) of
+ true ->
+ {B, S3};
+ false ->
+ i_receive_1(E, Cs, T, B, S3)
+ end;
+ true ->
+ i_receive_1(E, Cs, T, B, S3)
+ end;
+ {true, {_, _, _, Cs}, S3} ->
+ %% Cs is a single clause that will always be matched (if a
+ %% message exists), but we must keep the `receive' statement
+ %% in order to fetch the message from the mailbox.
+ i_receive_1(E, Cs, T, B, S3)
+ end.
+
+i_receive_1(E, Cs, T, B, S) ->
+ %% Here, we just add the base sizes for the receive-expression
+ %% itself and for each remaining clause; cf. `case'.
+ N = weight('receive') + weight(clause) * length(Cs),
+ {update_c_receive(E, Cs, T, B), count_size(N, S)}.
+
+%% A module definition is like a `letrec', with some add-ons (export and
+%% attribute declarations) but without an explicit body. Actually, the
+%% exporting of function names has the same effect as if there was a
+%% body consisting of the list of references to the exported functions.
+%% Thus, the exported functions are exactly those which can be
+%% referenced from outside the module.
+
+i_module(E, Ctxt, Ren, Env, S) ->
+ %% Cf. `i_letrec'. Note that we pass a dummy constant value for the
+ %% "body" parameter.
+ {Es, _, Xs1, S1} = i_letrec(module_defs(E), void(),
+ module_exports(E), Ctxt, Ren, Env, S),
+ %% Sanity check:
+ case Es of
+ [] ->
+ report_warning("no function definitions remaining "
+ "in module `~s'.\n",
+ [atom_name(module_name(E))]);
+ _ ->
+ ok
+ end,
+ E1 = update_c_module(E, module_name(E), Xs1, module_attrs(E), Es),
+ {E1, count_size(weight(module), S1)}.
+
+%% Binary-syntax expressions are too complicated to do anything
+%% interesting with here - that is beyond the scope of this program;
+%% also, their construction could have side effects, so even in effect
+%% context we can't remove them. (We don't bother to identify cases of
+%% "safe" unused binaries which could be removed.)
+
+i_binary(E, Ren, Env, S) ->
+ %% Visit the segments for value.
+ {Es, S1} = mapfoldl(fun (E, S) ->
+ i_bitstr(E, Ren, Env, S)
+ end,
+ S, binary_segments(E)),
+ S2 = count_size(weight(binary), S1),
+ {update_c_binary(E, Es), S2}.
+
+i_bitstr(E, Ren, Env, S) ->
+ %% It is not necessary to visit the Unit, Type and Flags fields,
+ %% since these are always literals.
+ {Val, S1} = i(bitstr_val(E), value, Ren, Env, S),
+ {Size, S2} = i(bitstr_size(E), value, Ren, Env, S1),
+ Unit = bitstr_unit(E),
+ Type = bitstr_type(E),
+ Flags = bitstr_flags(E),
+ S3 = count_size(weight(bitstr), S2),
+ {update_c_bitstr(E, Val, Size, Unit, Type, Flags), S3}.
+
+%% This is a simplified version of `i_pattern', for lists of parameter
+%% variables only. It does not modify the state.
+
+i_params([V | Vs], Ren, Env) ->
+ Name = ren__map(var_name(V), Ren),
+ case env__lookup(Name, Env) of
+ {ok, R} ->
+ [ref_to_var(R) | i_params(Vs, Ren, Env)];
+ error ->
+ report_internal_error("variable `~w' not bound "
+ "in pattern.\n", [Name]),
+ exit(error)
+ end;
+i_params([], _, _) ->
+ [].
+
+%% For ordinary patterns, we just visit to rename variables and count
+%% the size/cost. All occurring binding instances of variables should
+%% already have been added to the renaming and environment; however, to
+%% handle the size expressions of binary-syntax patterns, we must pass
+%% the renaming and environment of the containing expression
+
+i_pattern(E, Ren, Env, Ren0, Env0, S) ->
+ case type(E) of
+ var ->
+ %% Count no size.
+ Name = ren__map(var_name(E), Ren),
+ case env__lookup(Name, Env) of
+ {ok, R} ->
+ {ref_to_var(R), S};
+ error ->
+ report_internal_error("variable `~w' not bound "
+ "in pattern.\n", [Name]),
+ exit(error)
+ end;
+ alias ->
+ %% Count no size.
+ V = alias_var(E),
+ Name = ren__map(var_name(V), Ren),
+ case env__lookup(Name, Env) of
+ {ok, R} ->
+ %% Visit the subpattern and recompose.
+ V1 = ref_to_var(R),
+ {P, S1} = i_pattern(alias_pat(E), Ren, Env, Ren0,
+ Env0, S),
+ {update_c_alias(E, V1, P), S1};
+ error ->
+ report_internal_error("variable `~w' not bound "
+ "in pattern.\n", [Name]),
+ exit(error)
+ end;
+ binary ->
+ {Es, S1} = mapfoldl(fun (E, S) ->
+ i_bitstr_pattern(E, Ren, Env,
+ Ren0, Env0, S)
+ end,
+ S, binary_segments(E)),
+ S2 = count_size(weight(binary), S1),
+ {update_c_binary(E, Es), S2};
+ _ ->
+ case is_literal(E) of
+ true ->
+ {E, count_size(weight(literal), S)};
+ false ->
+ {Es1, S1} = mapfoldl(fun (E, S) ->
+ i_pattern(E, Ren, Env,
+ Ren0, Env0,
+ S)
+ end,
+ S, data_es(E)),
+ %% We assume that in general, the elements of the
+ %% constructor will all be fetched.
+ N = weight(data) + length(Es1) * weight(element),
+ S2 = count_size(N, S1),
+ {update_data(E, data_type(E), Es1), S2}
+ end
+ end.
+
+i_bitstr_pattern(E, Ren, Env, Ren0, Env0, S) ->
+ %% It is not necessary to visit the Unit, Type and Flags fields,
+ %% since these are always literals. The Value field is a limited
+ %% pattern - either a literal or an unbound variable. The Size field
+ %% is a limited expression - either a literal or a variable bound in
+ %% the environment of the containing expression.
+ {Val, S1} = i_pattern(bitstr_val(E), Ren, Env, Ren0, Env0, S),
+ {Size, S2} = i(bitstr_size(E), value, Ren0, Env0, S1),
+ Unit = bitstr_unit(E),
+ Type = bitstr_type(E),
+ Flags = bitstr_flags(E),
+ S3 = count_size(weight(bitstr), S2),
+ {update_c_bitstr(E, Val, Size, Unit, Type, Flags), S3}.
+
+
+%% ---------------------------------------------------------------------
+%% Other central inlining functions
+
+%% It is assumed here that `E' is a fun-expression and the context is an
+%% app-structure. If the inlining might be aborted for some reason, a
+%% corresponding catch should have been set up before entering `inline'.
+%%
+%% Note: if the inlined body is a lambda abstraction, and the
+%% surrounding context of the app-context is also an app-context, the
+%% `inlined' flag of the outermost context will be set before that of
+%% the inner context is set. E.g.: `let F = fun (X) -> fun (Y) -> E in
+%% apply apply F(A)(B)' will propagate the body of F, which is a lambda
+%% abstraction, into the outer application context, which will be
+%% inlined to produce expression `E', and the flag of the outer context
+%% will be set. Upon return, the flag of the inner context will also be
+%% set. However, the flags are then tested in innermost-first order.
+%% Thus, if some inlining attempt is aborted, the `inlined' flags of any
+%% nested app-contexts must be cleared.
+%%
+%% This implementation does nothing to handle inlining of calls to
+%% recursive functions in a smart way. This means that as long as the
+%% size and effort counters do not prevent it, the function body will be
+%% inlined (i.e., the first iteration will be unrolled), and the
+%% recursive calls will be residualized.
+
+inline(E, #app{opnds = Opnds, ctxt = Ctxt, loc = L}, Ren, Env, S) ->
+ %% Check that the arities match:
+ Vs = fun_vars(E),
+ if length(Opnds) /= length(Vs) ->
+ report_error("function called with wrong number "
+ "of arguments!\n"),
+ %% TODO: should really just residualise the call...
+ exit(error);
+ true ->
+ ok
+ end,
+ %% Create local bindings for the parameters to their respective
+ %% operand structures from the app-structure, and visit the body in
+ %% the context saved in the structure.
+ {Rs, Ren1, Env1, S1} = bind_locals(Vs, Opnds, Ren, Env, S),
+ {E1, S2} = i(fun_body(E), Ctxt, Ren1, Env1, S1),
+
+ %% Create necessary bindings and/or set flags.
+ {E2, S3} = make_let_bindings(Rs, E1, S2),
+
+ %% Lastly, flag the application as inlined, since the inlining
+ %% attempt was not aborted before we reached this point.
+ {E2, st__set_app_inlined(L, S3)}.
+
+%% For the (possibly renamed) argument variables to an inlined call,
+%% either create `let' bindings for them, if they are still referenced
+%% in the residual expression (in C/Lisp, also if they are assigned to),
+%% or otherwise (if they are not referenced or assigned) mark them for
+%% evaluation for side effects.
+
+make_let_bindings([R | Rs], E, S) ->
+ {E1, S1} = make_let_bindings(Rs, E, S),
+ make_let_binding(R, E1, S1);
+make_let_bindings([], E, S) ->
+ {E, S}.
+
+make_let_binding(R, E, S) ->
+ %% The `referenced' flag is conservatively computed. We therefore
+ %% first check some simple cases where parameter R is definitely not
+ %% referenced in the resulting body E.
+ case is_literal(E) of
+ true ->
+ %% A constant contains no variable references.
+ make_let_binding_1(R, E, S);
+ false ->
+ case is_c_var(E) of
+ true ->
+ case var_name(E) =:= R#ref.name of
+ true ->
+ %% The body is simply the parameter variable
+ %% itself. Visit the operand for value and
+ %% substitute the result for the body.
+ visit_and_count_size(R#ref.opnd, S);
+ false ->
+ %% Not the same variable, so the parameter
+ %% is not referenced at all.
+ make_let_binding_1(R, E, S)
+ end;
+ false ->
+ %% Proceed to check the `referenced' flag.
+ case st__get_var_referenced(R#ref.loc, S) of
+ true ->
+ %% The parameter is probably referenced in
+ %% the residual code (although it might not
+ %% be). Visit the operand for value and
+ %% create a let-binding.
+ {E1, S1} = visit_and_count_size(R#ref.opnd,
+ S),
+ S2 = count_size(weight('let'), S1),
+ {c_let([ref_to_var(R)], E1, E), S2};
+ false ->
+ %% The parameter is definitely not
+ %% referenced.
+ make_let_binding_1(R, E, S)
+ end
+ end
+ end.
+
+%% This marks the operand for evaluation for effect.
+
+make_let_binding_1(R, E, S) ->
+ Opnd = R#ref.opnd,
+ {E, st__set_opnd_effect(Opnd#opnd.loc, S)}.
+
+%% Here, `R' is the ref-structure which is the target of the copy
+%% propagation, and `Opnd' is a visited operand structure, to be
+%% propagated through `R' if possible - if not, `R' is residualised.
+%% `Opnd' is normally the operand that `R' is bound to, and `E' is the
+%% result of visiting `Opnd' for value; we pass this as an argument so
+%% we don't have to fetch it multiple times (because we don't have
+%% constant time access).
+%%
+%% We also pass the environment of the site of the variable reference,
+%% for use when inlining a propagated fun-expression. In the original
+%% algorithm by Waddell, the environment used for inlining such cases is
+%% the identity mapping, because the fun-expression body has already
+%% been visited for value, and their algorithm combines renaming of
+%% source-code variables with the looking up of information about
+%% residual-code variables. We, however, need to check the environment
+%% of the call site when creating new non-shadowed variables, but we
+%% must avoid repeated renaming. We therefore separate the renaming and
+%% the environment (as in the renaming algorithm of Peyton-Jones and
+%% Marlow). This also makes our implementation more general, compared to
+%% the original algorithm, because we do not give up on propagating
+%% variables that were free in the fun-body.
+%%
+%% Example:
+%%
+%% let F = fun (X) -> {'foo', X} in
+%% let G = fun (H) -> apply H(F) % F is free in the fun G
+%% in apply G(fun (F) -> apply F(42))
+%% =>
+%% let F = fun (X) -> {'foo', X} in
+%% apply (fun (H) -> apply H(F))(fun (F) -> apply F(42))
+%% =>
+%% let F = fun (X) -> {'foo', X} in
+%% apply (fun (F) -> apply F(42))(F)
+%% =>
+%% let F = fun (X) -> {'foo', X} in
+%% apply F(42)
+%% =>
+%% apply (fun (X) -> {'foo', X})(2)
+%% =>
+%% {'foo', 42}
+%%
+%% The original algorithm would give up at stage 4, because F was free
+%% in the propagated fun-expression. Our version inlines this example
+%% completely.
+
+copy(R, Opnd, E, Ctxt, Env, S) ->
+ case is_c_var(E) of
+ true ->
+ %% The operand reduces to another variable - get its
+ %% ref-structure and attempt to propagate further.
+ copy_var(env__get(var_name(E), Opnd#opnd.env), Ctxt, Env,
+ S);
+ false ->
+ %% Apart from variables and functional values (the latter
+ %% are handled by `copy_1' below), only constant literals
+ %% are copyable in general; other things, including e.g.
+ %% tuples `{foo, X}', could cause duplication of work, and
+ %% are not copy propagated.
+ case is_literal(E) of
+ true ->
+ {E, count_size(weight(literal), S)};
+ false ->
+ copy_1(R, Opnd, E, Ctxt, Env, S)
+ end
+ end.
+
+copy_var(R, Ctxt, Env, S) ->
+ %% (In Lisp or C, if this other variable might be assigned to, we
+ %% should residualize the "parent" instead, so we don't bypass any
+ %% destructive updates.)
+ case R#ref.opnd of
+ undefined ->
+ %% This variable is not bound to an expression, so just
+ %% residualize it.
+ residualize_var(R, S);
+ Opnd ->
+ %% Note that because operands are always visited before
+ %% copied, all copyable operand expressions will be
+ %% propagated through any number of bindings. If `R' was
+ %% bound to a constant literal, we would never have reached
+ %% this point.
+ case st__lookup_opnd_cache(Opnd#opnd.loc, S) of
+ error ->
+ %% The result for this operand is not yet ready
+ %% (which should mean that it is a recursive
+ %% reference). Thus, we must residualise the
+ %% variable.
+ residualize_var(R, S);
+ {ok, #cache{expr = E1}} ->
+ %% The result for the operand is ready, so we can
+ %% proceed to propagate it.
+ copy_1(R, Opnd, E1, Ctxt, Env, S)
+ end
+ end.
+
+copy_1(R, Opnd, E, Ctxt, Env, S) ->
+ %% Fun-expression (lambdas) are a bit special; they are copyable,
+ %% but should preferably not be duplicated, so they should not be
+ %% copy propagated except into application contexts, where they can
+ %% be inlined.
+ case is_c_fun(E) of
+ true ->
+ case Ctxt of
+ #app{} ->
+ %% First test if the operand is "outer-pending"; if
+ %% so, don't inline.
+ case st__test_outer_pending(Opnd#opnd.loc, S) of
+ false ->
+ copy_inline(R, Opnd, E, Ctxt, Env, S);
+ true ->
+ %% Cyclic reference forced inlining to stop
+ %% (avoiding infinite unfolding).
+ residualize_var(R, S)
+ end;
+ _ ->
+ residualize_var(R, S)
+ end;
+ false ->
+ %% We have no other cases to handle here
+ residualize_var(R, S)
+ end.
+
+%% This inlines a function value that was propagated to an application
+%% context. The inlining is done with an identity renaming (since the
+%% expression is already visited) but in the environment of the call
+%% site (which is OK because of the no-shadowing strategy for renaming,
+%% and because the domain of our environments are the residual-program
+%% variables instead of the source-program variables). Note that we must
+%% first set the "outer-pending" flag, and clear it afterwards.
+
+copy_inline(R, Opnd, E, Ctxt, Env, S) ->
+ S1 = st__mark_outer_pending(Opnd#opnd.loc, S),
+ case catch {ok, copy_inline_1(R, E, Ctxt, Env, S1)} of
+ {ok, {E1, S2}} ->
+ {E1, st__clear_outer_pending(Opnd#opnd.loc, S2)};
+ {'EXIT', X} ->
+ exit(X);
+ X ->
+ %% If we use destructive update for the `outer-pending'
+ %% flag, we must make sure to clear it upon a nonlocal
+ %% return.
+ st__clear_outer_pending(Opnd#opnd.loc, S1),
+ throw(X)
+ end.
+
+%% If the current effort counter was passive, we use a new active effort
+%% counter with the inherited limit for this particular inlining.
+
+copy_inline_1(R, E, Ctxt, Env, S) ->
+ case effort_is_active(S) of
+ true ->
+ copy_inline_2(R, E, Ctxt, Env, S);
+ false ->
+ S1 = new_active_effort(get_effort_limit(S), S),
+ case catch {ok, copy_inline_2(R, E, Ctxt, Env, S1)} of
+ {ok, {E1, S2}} ->
+ %% Revert to the old effort counter.
+ {E1, revert_effort(S, S2)};
+ {counter_exceeded, effort, _} ->
+ %% Aborted this inlining attempt because too much
+ %% effort was spent. Residualize the variable and
+ %% revert to the previous state.
+ residualize_var(R, S);
+ {'EXIT', X} ->
+ exit(X);
+ X ->
+ throw(X)
+ end
+ end.
+
+%% Regardless of whether the current size counter is active or not, we
+%% use a new active size counter for each inlining. If the current
+%% counter was passive, the new counter gets the inherited size limit;
+%% if it was active, the size limit of the new counter will be equal to
+%% the remaining budget of the current counter (which itself is not
+%% affected by the inlining). This distributes the size budget more
+%% evenly over "inlinings within inlinings", so that the whole size
+%% budget is not spent on the first few call sites (in an inlined
+%% function body) forcing the remaining call sites to be residualised.
+
+copy_inline_2(R, E, Ctxt, Env, S) ->
+ Limit = case size_is_active(S) of
+ true ->
+ get_size_limit(S) - get_size_value(S);
+ false ->
+ get_size_limit(S)
+ end,
+ %% Add the cost of the application to the new size limit, so we
+ %% always inline functions that are small enough, even if `Limit' is
+ %% close to zero at this point. (This is an extension to the
+ %% original algorithm.)
+ S1 = new_active_size(Limit + apply_size(length(Ctxt#app.opnds)), S),
+ case catch {ok, inline(E, Ctxt, ren__identity(), Env, S1)} of
+ {ok, {E1, S2}} ->
+ %% Revert to the old size counter.
+ {E1, revert_size(S, S2)};
+ {counter_exceeded, size, S2} ->
+ %% Aborted this inlining attempt because it got too big.
+ %% Residualize the variable and revert to the old size
+ %% counter. (It is important that we do not also revert the
+ %% effort counter here. Because the effort and size counters
+ %% are always set up together, we know that the effort
+ %% counter returned in S2 is the same that was passed to
+ %% `inline'.)
+ S3 = revert_size(S, S2),
+ %% If we use destructive update for the `inlined' flag, we
+ %% must make sure to clear the flags of any nested
+ %% app-contexts upon aborting; see `inline' for details.
+ reset_nested_apps(Ctxt, S3), % for effect
+ residualize_var(R, S3);
+ {'EXIT', X} ->
+ exit(X);
+ X ->
+ throw(X)
+ end.
+
+reset_nested_apps(#app{ctxt = Ctxt, loc = L}, S) ->
+ reset_nested_apps(Ctxt, st__clear_app_inlined(L, S));
+reset_nested_apps(_, S) ->
+ S.
+
+
+%% ---------------------------------------------------------------------
+%% Support functions
+
+new_var(Env) ->
+ Name = env__new_vname(Env),
+ c_var(Name).
+
+residualize_var(R, S) ->
+ S1 = count_size(weight(var), S),
+ {ref_to_var(R), st__set_var_referenced(R#ref.loc, S1)}.
+
+%% This function returns the value-producing subexpression of any
+%% expression. (Except for sequencing expressions, this is the
+%% expression itself.)
+
+result(E) ->
+ case is_c_seq(E) of
+ true ->
+ %% Also see `make_seq', which is used in all places to build
+ %% sequences so that they are always nested in the first
+ %% position.
+ seq_body(E);
+ false ->
+ E
+ end.
+
+%% This function rewrites E to `do A1 E' if A is `do A1 A2', and
+%% otherwise returns E unchanged.
+
+hoist_effects(A, E) ->
+ case type(A) of
+ seq -> make_seq(seq_arg(A), E);
+ _ -> E
+ end.
+
+%% This "build sequencing expression" operation assures that sequences
+%% are always nested in the first position, which makes it easy to find
+%% the actual value-producing expression of a sequence (cf. `result').
+
+make_seq(E1, E2) ->
+ case is_safe(E1) of
+ true ->
+ %% The first expression can safely be dropped.
+ E2;
+ false ->
+ %% If `E1' is a sequence whose final expression has no side
+ %% effects, then we can lose *that* expression when we
+ %% compose the new sequence, since its value will not be
+ %% used.
+ E3 = case is_c_seq(E1) of
+ true ->
+ case is_safe(seq_body(E1)) of
+ true ->
+ %% Drop the final expression.
+ seq_arg(E1);
+ false ->
+ E1
+ end;
+ false ->
+ E1
+ end,
+ case is_c_seq(E2) of
+ true ->
+ %% `E2' is a sequence (E2' E2''), so we must
+ %% rearrange the nesting to ((E1, E2') E2''), to
+ %% preserve the invariant. Annotations on `E2' are
+ %% lost.
+ c_seq(c_seq(E3, seq_arg(E2)), seq_body(E2));
+ false ->
+ c_seq(E3, E2)
+ end
+ end.
+
+%% Currently, safe expressions include variables, lambda expressions,
+%% constructors with safe subexpressions (this includes atoms, integers,
+%% empty lists, etc.), seq-, let- and letrec-expressions with safe
+%% subexpressions, try- and catch-expressions with safe subexpressions
+%% and calls to safe functions with safe argument subexpressions.
+%% Binaries seem too tricky to be considered.
+
+is_safe(E) ->
+ case is_data(E) of
+ true ->
+ is_safe_list(data_es(E));
+ false ->
+ case type(E) of
+ var ->
+ true;
+ 'fun' ->
+ true;
+ values ->
+ is_safe_list(values_es(E));
+ 'seq' ->
+ case is_safe(seq_arg(E)) of
+ true ->
+ is_safe(seq_body(E));
+ false ->
+ false
+ end;
+ 'let' ->
+ case is_safe(let_arg(E)) of
+ true ->
+ is_safe(let_body(E));
+ false ->
+ false
+ end;
+ letrec ->
+ is_safe(letrec_body(E));
+ 'try' ->
+ %% If the argument expression is not safe, it could
+ %% be modifying the state; thus, even if the body is
+ %% safe, the try-expression as a whole would not be.
+ %% If the argument is safe, the handler is not used.
+ case is_safe(try_arg(E)) of
+ true ->
+ is_safe(try_body(E));
+ false ->
+ false
+ end;
+ 'catch' ->
+ is_safe(catch_body(E));
+ call ->
+ M = call_module(E),
+ F = call_name(E),
+ case is_c_atom(M) and is_c_atom(F) of
+ true ->
+ As = call_args(E),
+ case is_safe_list(As) of
+ true ->
+ is_safe_call(atom_val(M),
+ atom_val(F),
+ length(As));
+ false ->
+ false
+ end;
+ false ->
+ false
+ end;
+ _ ->
+ false
+ end
+ end.
+
+is_safe_list([E | Es]) ->
+ case is_safe(E) of
+ true ->
+ is_safe_list(Es);
+ false ->
+ false
+ end;
+is_safe_list([]) ->
+ true.
+
+is_safe_call(M, F, A) ->
+ erl_bifs:is_safe(M, F, A).
+
+%% When setting up local variables, we only create new names if we have
+%% to, according to the "no-shadowing" strategy.
+
+make_locals(Vs, Ren, Env) ->
+ make_locals(Vs, [], Ren, Env).
+
+make_locals([V | Vs], As, Ren, Env) ->
+ Name = var_name(V),
+ case env__is_defined(Name, Env) of
+ false ->
+ %% The variable need not be renamed. Just make sure that the
+ %% renaming will map it to itself.
+ Name1 = Name,
+ Ren1 = ren__add_identity(Name, Ren);
+ true ->
+ %% The variable must be renamed to maintain the no-shadowing
+ %% invariant. Do the right thing for function variables.
+ Name1 = case Name of
+ {A, N} ->
+ env__new_fname(A, N, Env);
+ _ ->
+ env__new_vname(Env)
+ end,
+ Ren1 = ren__add(Name, Name1, Ren)
+ end,
+ %% This temporary binding is added for correct new-key generation.
+ Env1 = env__bind(Name1, dummy, Env),
+ make_locals(Vs, [Name1 | As], Ren1, Env1);
+make_locals([], As, Ren, Env) ->
+ {reverse(As), Ren, Env}.
+
+%% This adds let-bindings for the source code variables in `Es' to the
+%% environment `Env'.
+%%
+%% Note that we always assign a new state location for the
+%% residual-program variable, since we cannot know when a location for a
+%% particular variable in the source code can be reused.
+
+bind_locals(Vs, Ren, Env, S) ->
+ Opnds = lists:duplicate(length(Vs), undefined),
+ bind_locals(Vs, Opnds, Ren, Env, S).
+
+bind_locals(Vs, Opnds, Ren, Env, S) ->
+ {Ns, Ren1, Env1} = make_locals(Vs, Ren, Env),
+ {Rs, Env2, S1} = bind_locals_1(Ns, Opnds, [], Env1, S),
+ {Rs, Ren1, Env2, S1}.
+
+%% Note that the `Vs' are currently not used for anything except the
+%% number of variables. If we were maintaining "source-referenced"
+%% flags, then the flag in the new variable should be initialized to the
+%% current value of the (residual-) referenced-flag of the "parent".
+
+bind_locals_1([N | Ns], [Opnd | Opnds], Rs, Env, S) ->
+ {R, S1} = new_ref(N, Opnd, S),
+ Env1 = env__bind(N, R, Env),
+ bind_locals_1(Ns, Opnds, [R | Rs], Env1, S1);
+bind_locals_1([], [], Rs, Env, S) ->
+ {lists:reverse(Rs), Env, S}.
+
+new_refs(Ns, Opnds, S) ->
+ new_refs(Ns, Opnds, [], S).
+
+new_refs([N | Ns], [Opnd | Opnds], Rs, S) ->
+ {R, S1} = new_ref(N, Opnd, S),
+ new_refs(Ns, Opnds, [R | Rs], S1);
+new_refs([], [], Rs, S) ->
+ {lists:reverse(Rs), S}.
+
+new_ref(N, Opnd, S) ->
+ {L, S1} = st__new_ref_loc(S),
+ {#ref{name = N, opnd = Opnd, loc = L}, S1}.
+
+%% This adds recursive bindings for the source code variables in `Es' to
+%% the environment `Env'. Note that recursive binding of a set of
+%% variables is an atomic operation on the environment - they cannot be
+%% added one at a time.
+
+bind_recursive(Vs, Opnds, Ren, Env, S) ->
+ {Ns, Ren1, Env1} = make_locals(Vs, Ren, Env),
+ {Rs, S1} = new_refs(Ns, Opnds, S),
+
+ %% When this fun-expression is evaluated, it updates the operand
+ %% structure in the ref-structure to contain the recursively defined
+ %% environment and the correct renaming.
+ Fun = fun (R, Env) ->
+ Opnd = R#ref.opnd,
+ R#ref{opnd = Opnd#opnd{ren = Ren1, env = Env}}
+ end,
+ {Rs, Ren1, env__bind_recursive(Ns, Rs, Fun, Env1), S1}.
+
+safe_context(Ctxt) ->
+ case Ctxt of
+ #app{} ->
+ value;
+ _ ->
+ Ctxt
+ end.
+
+%% Note that the name of a variable encodes its type: a "plain" variable
+%% or a function variable. The latter kind also contains an arity number
+%% which should be preserved upon renaming.
+
+ref_to_var(#ref{name = Name}) ->
+ %% If we were maintaining "source-referenced" flags, the annotation
+ %% `add_ann([#source_ref{loc = L}], E)' should also be done here, to
+ %% make the algorithm reapplicable. This is however not necessary
+ %% since there are no destructive variable assignments in Erlang.
+ c_var(Name).
+
+%% Including the effort counter of the call site assures that the cost
+%% of processing an operand via `visit' is charged to the correct
+%% counter. In particular, if the effort counter of the call site was
+%% passive, the operands will also be processed with a passive counter.
+
+make_opnd(E, Ren, Env, S) ->
+ {L, S1} = st__new_opnd_loc(S),
+ C = st__get_effort(S1),
+ Opnd = #opnd{expr = E, ren = Ren, env = Env, loc = L, effort = C},
+ {Opnd, S1}.
+
+keep_referenced(Rs, S) ->
+ [R || R <- Rs, st__get_var_referenced(R#ref.loc, S)].
+
+residualize_operands(Opnds, E, S) ->
+ foldr(fun (Opnd, {E, S}) -> residualize_operand(Opnd, E, S) end,
+ {E, S}, Opnds).
+
+%% This is the only case where an operand expression can be visited in
+%% `effect' context instead of `value' context.
+
+residualize_operand(Opnd, E, S) ->
+ case st__get_opnd_effect(Opnd#opnd.loc, S) of
+ true ->
+ %% The operand has not been visited, so we do that now, but
+ %% in `effect' context. (Waddell's algoritm does some stuff
+ %% here to account specially for the operand size, which
+ %% appears unnecessary.)
+ {E1, S1} = i(Opnd#opnd.expr, effect, Opnd#opnd.ren,
+ Opnd#opnd.env, S),
+ {make_seq(E1, E), S1};
+ false ->
+ {E, S}
+ end.
+
+%% The `visit' function always visits the operand expression in `value'
+%% context (`residualize_operand' visits an unreferenced operand
+%% expression in `effect' context when necessary). A new passive size
+%% counter is used for visiting the operand, the final value of which is
+%% then cached along with the resulting expression.
+%%
+%% Note that the effort counter of the call site, included in the
+%% operand structure, is not a shared object. Thus, the effort budget is
+%% actually reused over all occurrences of the operands of a single
+%% application. This does not appear to be a problem; just a
+%% modification of the algorithm.
+
+visit(Opnd, S) ->
+ {C, S1} = visit_1(Opnd, S),
+ {C#cache.expr, S1}.
+
+visit_and_count_size(Opnd, S) ->
+ {C, S1} = visit_1(Opnd, S),
+ {C#cache.expr, count_size(C#cache.size, S1)}.
+
+visit_1(Opnd, S) ->
+ case st__lookup_opnd_cache(Opnd#opnd.loc, S) of
+ error ->
+ %% Use a new, passive, size counter for visiting operands,
+ %% and use the effort counter of the context of the operand.
+ %% It turns out that if the latter is active, it must be the
+ %% same object as the one currently used, and if it is
+ %% passive, it does not matter if it is the same object as
+ %% any other counter.
+ Effort = Opnd#opnd.effort,
+ Active = counter__is_active(Effort),
+ S1 = case Active of
+ true ->
+ S; % don't change effort counter
+ false ->
+ st__set_effort(Effort, S)
+ end,
+ S2 = new_passive_size(get_size_limit(S1), S1),
+
+ %% Visit the expression and cache the result, along with the
+ %% final value of the size counter.
+ {E, S3} = i(Opnd#opnd.expr, value, Opnd#opnd.ren,
+ Opnd#opnd.env, S2),
+ Size = get_size_value(S3),
+ C = #cache{expr = E, size = Size},
+ S4 = revert_size(S, st__set_opnd_cache(Opnd#opnd.loc, C,
+ S3)),
+ case Active of
+ true ->
+ {C, S4}; % keep using the same effort counter
+ false ->
+ {C, revert_effort(S, S4)}
+ end;
+ {ok, C} ->
+ {C, S}
+ end.
+
+%% Create a pattern matching template for an expression. A template
+%% contains only data constructors (including atomic ones) and
+%% variables, and compound literals are not folded into a single node.
+%% Each node in the template is annotated with the variable which holds
+%% the corresponding subexpression; these are new, unique variables not
+%% existing in the given `Env'. Returns `{Template, Variables, NewEnv}',
+%% where `Variables' is the list of all variables corresponding to nodes
+%% in the template *listed in reverse dependency order*, and `NewEnv' is
+%% `Env' augmented with mappings from the variable names to
+%% subexpressions of `E' (not #ref{} structures!) rewritten so that no
+%% computations are duplicated. `Variables' is guaranteed to be nonempty
+%% - at least the root node will always be bound to a new variable.
+
+make_template(E, Env) ->
+ make_template(E, [], Env).
+
+make_template(E, Vs0, Env0) ->
+ case is_data(E) of
+ true ->
+ {Ts, {Vs1, Env1}} = mapfoldl(
+ fun (E, {Vs0, Env0}) ->
+ {T, Vs1, Env1} =
+ make_template(E, Vs0,
+ Env0),
+ {T, {Vs1, Env1}}
+ end,
+ {Vs0, Env0}, data_es(E)),
+ T = make_data_skel(data_type(E), Ts),
+ E1 = update_data(E, data_type(E),
+ [hd(get_ann(T)) || T <- Ts]),
+ V = new_var(Env1),
+ Env2 = env__bind(var_name(V), E1, Env1),
+ {set_ann(T, [V]), [V | Vs1], Env2};
+ false ->
+ case type(E) of
+ seq ->
+ %% For a sequencing, we can rebind the variable used
+ %% for the body, and pass on the template as it is.
+ {T, Vs1, Env1} = make_template(seq_body(E), Vs0,
+ Env0),
+ V = var_name(hd(get_ann(T))),
+ E1 = update_c_seq(E, seq_arg(E), env__get(V, Env1)),
+ Env2 = env__bind(V, E1, Env1),
+ {T, Vs1, Env2};
+ _ ->
+ V = new_var(Env0),
+ Env1 = env__bind(var_name(V), E, Env0),
+ {set_ann(V, [V]), [V | Vs0], Env1}
+ end
+ end.
+
+%% Two clauses are equivalent if their bodies are equivalent expressions
+%% given that the respective pattern variables are local.
+
+equivalent_clauses([]) ->
+ true;
+equivalent_clauses([C | Cs]) ->
+ Env = cerl_trees:variables(c_values(clause_pats(C))),
+ equivalent_clauses_1(clause_body(C), Cs, Env).
+
+equivalent_clauses_1(E, [C | Cs], Env) ->
+ Env1 = cerl_trees:variables(c_values(clause_pats(C))),
+ case equivalent(E, clause_body(C), ordsets:union(Env, Env1)) of
+ true ->
+ equivalent_clauses_1(E, Cs, Env);
+ false ->
+ false
+ end;
+equivalent_clauses_1(_, [], _Env) ->
+ true.
+
+%% Two expressions are equivalent if and only if they yield the same
+%% value and has the same side effects in the same order. Currently, we
+%% only accept equality between constructors (constants) and nonlocal
+%% variables, since this should cover most cases of interest. If a
+%% variable is locally bound in one expression, it cannot be equivalent
+%% to one with the same name in the other expression, so we need not
+%% keep track of two environments.
+
+equivalent(E1, E2, Env) ->
+ case is_data(E1) of
+ true ->
+ case is_data(E2) of
+ true ->
+ T1 = {data_type(E1), data_arity(E1)},
+ T2 = {data_type(E2), data_arity(E2)},
+ %% Note that we must test for exact equality.
+ if T1 =:= T2 ->
+ equivalent_lists(data_es(E1), data_es(E2),
+ Env);
+ true ->
+ false
+ end;
+ false ->
+ false
+ end;
+ false ->
+ case type(E1) of
+ var ->
+ case is_c_var(E2) of
+ true ->
+ N1 = var_name(E1),
+ N2 = var_name(E2),
+ if N1 =:= N2 ->
+ not ordsets:is_element(N1, Env);
+ true ->
+ false
+ end;
+ false ->
+ false
+ end;
+ _ ->
+ %% Other constructs are not being considered.
+ false
+ end
+ end.
+
+equivalent_lists([E1 | Es1], [E2 | Es2], Env) ->
+ equivalent(E1, E2, Env) and equivalent_lists(Es1, Es2, Env);
+equivalent_lists([], [], _) ->
+ true;
+equivalent_lists(_, _, _) ->
+ false.
+
+%% Return `false' or `{true, EffectExpr, ValueExpr}'. The environment is
+%% passed for new-variable generation.
+
+reduce_bif_call(M, F, As, Env) ->
+ reduce_bif_call_1(M, F, length(As), As, Env).
+
+reduce_bif_call_1(erlang, element, 2, [X, Y], _Env) ->
+ case is_c_int(X) and is_c_tuple(Y) of
+ true ->
+ %% We are free to change the relative evaluation order of
+ %% the elements, so lifting out a particular element is OK.
+ T = list_to_tuple(tuple_es(Y)),
+ N = int_val(X),
+ if integer(N), N > 0, N =< size(T) ->
+ E = element(N, T),
+ Es = tuple_to_list(setelement(N, T, void())),
+ {true, make_seq(c_tuple(Es), E)};
+ true ->
+ false
+ end;
+ false ->
+ false
+ end;
+reduce_bif_call_1(erlang, hd, 1, [X], _Env) ->
+ case is_c_cons(X) of
+ true ->
+ %% Cf. `element/2' above.
+ {true, make_seq(cons_tl(X), cons_hd(X))};
+ false ->
+ false
+ end;
+reduce_bif_call_1(erlang, length, 1, [X], _Env) ->
+ case is_c_list(X) of
+ true ->
+ %% Cf. `erlang:size/1' below.
+ {true, make_seq(X, c_int(list_length(X)))};
+ false ->
+ false
+ end;
+reduce_bif_call_1(erlang, list_to_tuple, 1, [X], _Env) ->
+ case is_c_list(X) of
+ true ->
+ %% This does not actually preserve all the evaluation order
+ %% constraints of the list, but I don't imagine that it will
+ %% be a problem.
+ {true, c_tuple(list_elements(X))};
+ false ->
+ false
+ end;
+reduce_bif_call_1(erlang, setelement, 3, [X, Y, Z], Env) ->
+ case is_c_int(X) and is_c_tuple(Y) of
+ true ->
+ %% Here, unless `Z' is a simple expression, we must bind it
+ %% to a new variable, because in that case, `Z' must be
+ %% evaluated before any part of `Y'.
+ T = list_to_tuple(tuple_es(Y)),
+ N = int_val(X),
+ if integer(N), N > 0, N =< size(T) ->
+ E = element(N, T),
+ case is_simple(Z) of
+ true ->
+ Es = tuple_to_list(setelement(N, T, Z)),
+ {true, make_seq(E, c_tuple(Es))};
+ false ->
+ V = new_var(Env),
+ Es = tuple_to_list(setelement(N, T, V)),
+ E1 = make_seq(E, c_tuple(Es)),
+ {true, c_let([V], Z, E1)}
+ end;
+ true ->
+ false
+ end;
+ false ->
+ false
+ end;
+reduce_bif_call_1(erlang, size, 1, [X], _Env) ->
+ case is_c_tuple(X) of
+ true ->
+ %% Just evaluate the tuple for effect and use the size (the
+ %% arity) as the result.
+ {true, make_seq(X, c_int(tuple_arity(X)))};
+ false ->
+ false
+ end;
+reduce_bif_call_1(erlang, tl, 1, [X], _Env) ->
+ case is_c_cons(X) of
+ true ->
+ %% Cf. `element/2' above.
+ {true, make_seq(cons_hd(X), cons_tl(X))};
+ false ->
+ false
+ end;
+reduce_bif_call_1(erlang, tuple_to_list, 1, [X], _Env) ->
+ case is_c_tuple(X) of
+ true ->
+ %% This actually introduces slightly stronger constraints on
+ %% the evaluation order of the subexpressions.
+ {true, make_list(tuple_es(X))};
+ false ->
+ false
+ end;
+reduce_bif_call_1(_M, _F, _A, _As, _Env) ->
+ false.
+
+effort_is_active(S) ->
+ counter__is_active(st__get_effort(S)).
+
+size_is_active(S) ->
+ counter__is_active(st__get_size(S)).
+
+get_effort_limit(S) ->
+ counter__limit(st__get_effort(S)).
+
+new_active_effort(Limit, S) ->
+ st__set_effort(counter__new_active(Limit), S).
+
+revert_effort(S1, S2) ->
+ st__set_effort(st__get_effort(S1), S2).
+
+new_active_size(Limit, S) ->
+ st__set_size(counter__new_active(Limit), S).
+
+new_passive_size(Limit, S) ->
+ st__set_size(counter__new_passive(Limit), S).
+
+revert_size(S1, S2) ->
+ st__set_size(st__get_size(S1), S2).
+
+count_effort(N, S) ->
+ C = st__get_effort(S),
+ C1 = counter__add(N, C, effort, S),
+ case debug_counters() of
+ true ->
+ case counter__is_active(C1) of
+ true ->
+ V = counter__value(C1),
+ case V > get(counter_effort_max) of
+ true ->
+ put(counter_effort_max, V);
+ false ->
+ ok
+ end;
+ false ->
+ ok
+ end;
+ _ ->
+ ok
+ end,
+ st__set_effort(C1, S).
+
+count_size(N, S) ->
+ C = st__get_size(S),
+ C1 = counter__add(N, C, size, S),
+ case debug_counters() of
+ true ->
+ case counter__is_active(C1) of
+ true ->
+ V = counter__value(C1),
+ case V > get(counter_size_max) of
+ true ->
+ put(counter_size_max, V);
+ false ->
+ ok
+ end;
+ false ->
+ ok
+ end;
+ _ ->
+ ok
+ end,
+ st__set_size(C1, S).
+
+get_size_value(S) ->
+ counter__value(st__get_size(S)).
+
+get_size_limit(S) ->
+ counter__limit(st__get_size(S)).
+
+kill_id_anns([{'id',_} | As]) ->
+ kill_id_anns(As);
+kill_id_anns([A | As]) ->
+ [A | kill_id_anns(As)];
+kill_id_anns([]) ->
+ [].
+
+
+%% =====================================================================
+%% General utilities
+
+max(X, Y) when X > Y -> X;
+max(_, Y) -> Y.
+
+%% The atom `ok', is widely used in Erlang for "void" values.
+
+void() -> abstract(ok).
+
+is_simple(E) ->
+ case type(E) of
+ literal -> true;
+ var -> true;
+ 'fun' -> true;
+ _ -> false
+ end.
+
+get_components(N, E) ->
+ case type(E) of
+ values ->
+ Es = values_es(E),
+ if length(Es) == N ->
+ {true, Es};
+ true ->
+ false
+ end;
+ _ when N == 1 ->
+ {true, [E]};
+ _ ->
+ false
+ end.
+
+all_static([E | Es]) ->
+ case is_literal(result(E)) of
+ true ->
+ all_static(Es);
+ false ->
+ false
+ end;
+all_static([]) ->
+ true.
+
+set_clause_bodies([C | Cs], B) ->
+ [update_c_clause(C, clause_pats(C), clause_guard(C), B)
+ | set_clause_bodies(Cs, B)];
+set_clause_bodies([], _) ->
+ [].
+
+filename([C | T]) when integer(C), C > 0, C =< 255 ->
+ [C | filename(T)];
+filename([H|T]) ->
+ filename(H) ++ filename(T);
+filename([]) ->
+ [];
+filename(N) when atom(N) ->
+ atom_to_list(N);
+filename(N) ->
+ report_error("bad filename: `~P'.", [N, 25]),
+ exit(error).
+
+
+%% =====================================================================
+%% Abstract datatype: renaming()
+
+ren__identity() ->
+ dict:new().
+
+ren__add(X, Y, Ren) ->
+ dict:store(X, Y, Ren).
+
+ren__map(X, Ren) ->
+ case dict:find(X, Ren) of
+ {ok, Y} ->
+ Y;
+ error ->
+ X
+ end.
+
+ren__add_identity(X, Ren) ->
+ dict:erase(X, Ren).
+
+
+%% =====================================================================
+%% Abstract datatype: environment()
+
+env__empty() ->
+ rec_env:empty().
+
+env__bind(Key, Val, Env) ->
+ rec_env:bind(Key, Val, Env).
+
+%% `Es' should have type `[{Key, Val}]', and `Fun' should have type
+%% `(Val, Env) -> T', mapping a value together with the recursive
+%% environment itself to some term `T' to be returned when the entry is
+%% looked up.
+
+env__bind_recursive(Ks, Vs, F, Env) ->
+ rec_env:bind_recursive(Ks, Vs, F, Env).
+
+env__lookup(Key, Env) ->
+ rec_env:lookup(Key, Env).
+
+env__get(Key, Env) ->
+ rec_env:get(Key, Env).
+
+env__is_defined(Key, Env) ->
+ rec_env:is_defined(Key, Env).
+
+env__new_vname(Env) ->
+ rec_env:new_key(Env).
+
+env__new_fname(A, N, Env) ->
+ rec_env:new_key(fun (X) ->
+ S = integer_to_list(X),
+ {list_to_atom(atom_to_list(A) ++ "_" ++ S),
+ N}
+ end, Env).
+
+
+%% =====================================================================
+%% Abstract datatype: state()
+
+-record(state, {free, % next free location
+ size, % size counter
+ effort, % effort counter
+ cache, % operand expression cache
+ var_flags, % flags for variables (#ref-structures)
+ opnd_flags, % flags for operands
+ app_flags}). % flags for #app-structures
+
+%% Note that we do not have a `var_assigned' flag, since there is no
+%% destructive assignment in Erlang. In the original algorithm, the
+%% "residual-referenced"-flags of the previous inlining pass (or
+%% initialization pass) are used as the "source-referenced"-flags for
+%% the subsequent pass. The latter may then be used as a safe
+%% approximation whenever we need to base a decision on whether or not a
+%% particular variable or function variable could be referenced in the
+%% program being generated, and computation of the new
+%% "residual-referenced" flag for that variable is not yet finished. In
+%% the present algorithm, this can only happen in the presence of
+%% variable assignments, which do not exist in Erlang. Therefore, we do
+%% not keep "source-referenced" flags for residual-code references in
+%% our implementation.
+%%
+%% The "inner-pending" flag tells us whether we are already in the
+%% process of visiting a particular operand, and the "outer-pending"
+%% flag whether we are in the process of inlining a propagated
+%% functional value. The "pending flags" are really counters limiting
+%% the number of times an operand may be inlined recursively, causing
+%% loop unrolling; however, unrolling more than one iteration does not
+%% work offhand in the present implementation. (TODO: find out why.)
+%% Note that the initial value must be greater than zero in order for
+%% any inlining at all to be done.
+
+%% Flags are stored in ETS-tables, one table for each class. The second
+%% element in each stored tuple is the key (the "label").
+
+-record(var_flags, {lab, referenced = false}).
+-record(opnd_flags, {lab, inner_pending = 1, outer_pending = 1,
+ effect = false}).
+-record(app_flags, {lab, inlined = false}).
+
+st__new(Effort, Size) ->
+ #state{free = 0,
+ size = counter__new_passive(Size),
+ effort = counter__new_passive(Effort),
+ cache = dict:new(),
+ var_flags = ets:new(var, [set, private, {keypos, 2}]),
+ opnd_flags = ets:new(opnd, [set, private, {keypos, 2}]),
+ app_flags = ets:new(app, [set, private, {keypos, 2}])}.
+
+st__new_loc(S) ->
+ N = S#state.free,
+ {N, S#state{free = N + 1}}.
+
+st__get_effort(S) ->
+ S#state.effort.
+
+st__set_effort(C, S) ->
+ S#state{effort = C}.
+
+st__get_size(S) ->
+ S#state.size.
+
+st__set_size(C, S) ->
+ S#state{size = C}.
+
+st__set_var_referenced(L, S) ->
+ T = S#state.var_flags,
+ [F] = ets:lookup(T, L),
+ ets:insert(T, F#var_flags{referenced = true}),
+ S.
+
+st__get_var_referenced(L, S) ->
+ ets:lookup_element(S#state.var_flags, L, #var_flags.referenced).
+
+st__lookup_opnd_cache(L, S) ->
+ dict:find(L, S#state.cache).
+
+%% Note that setting the cache should only be done once.
+
+st__set_opnd_cache(L, C, S) ->
+ S#state{cache = dict:store(L, C, S#state.cache)}.
+
+st__set_opnd_effect(L, S) ->
+ T = S#state.opnd_flags,
+ [F] = ets:lookup(T, L),
+ ets:insert(T, F#opnd_flags{effect = true}),
+ S.
+
+st__get_opnd_effect(L, S) ->
+ ets:lookup_element(S#state.opnd_flags, L, #opnd_flags.effect).
+
+st__set_app_inlined(L, S) ->
+ T = S#state.app_flags,
+ [F] = ets:lookup(T, L),
+ ets:insert(T, F#app_flags{inlined = true}),
+ S.
+
+st__clear_app_inlined(L, S) ->
+ T = S#state.app_flags,
+ [F] = ets:lookup(T, L),
+ ets:insert(T, F#app_flags{inlined = false}),
+ S.
+
+st__get_app_inlined(L, S) ->
+ ets:lookup_element(S#state.app_flags, L, #app_flags.inlined).
+
+%% The pending-flags are initialized by `st__new_opnd_loc' below.
+
+st__test_inner_pending(L, S) ->
+ T = S#state.opnd_flags,
+ P = ets:lookup_element(T, L, #opnd_flags.inner_pending),
+ P =< 0.
+
+st__mark_inner_pending(L, S) ->
+ ets:update_counter(S#state.opnd_flags, L,
+ {#opnd_flags.inner_pending, -1}),
+ S.
+
+st__clear_inner_pending(L, S) ->
+ ets:update_counter(S#state.opnd_flags, L,
+ {#opnd_flags.inner_pending, 1}),
+ S.
+
+st__test_outer_pending(L, S) ->
+ T = S#state.opnd_flags,
+ P = ets:lookup_element(T, L, #opnd_flags.outer_pending),
+ P =< 0.
+
+st__mark_outer_pending(L, S) ->
+ ets:update_counter(S#state.opnd_flags, L,
+ {#opnd_flags.outer_pending, -1}),
+ S.
+
+st__clear_outer_pending(L, S) ->
+ ets:update_counter(S#state.opnd_flags, L,
+ {#opnd_flags.outer_pending, 1}),
+ S.
+
+st__new_app_loc(S) ->
+ V = {L, _S1} = st__new_loc(S),
+ ets:insert(S#state.app_flags, #app_flags{lab = L}),
+ V.
+
+st__new_ref_loc(S) ->
+ V = {L, _S1} = st__new_loc(S),
+ ets:insert(S#state.var_flags, #var_flags{lab = L}),
+ V.
+
+st__new_opnd_loc(S) ->
+ V = {L, _S1} = st__new_loc(S),
+ ets:insert(S#state.opnd_flags, #opnd_flags{lab = L}),
+ V.
+
+
+%% =====================================================================
+%% Abstract datatype: counter()
+%%
+%% `counter__add' throws `{counter_exceeded, Type, Data}' if the
+%% resulting counter value would exceed the limit for the counter in
+%% question (`Type' and `Data' are given by the user).
+
+-record(counter, {active, value, limit}).
+
+counter__new_passive(Limit) when Limit > 0 ->
+ {0, Limit}.
+
+counter__new_active(Limit) when Limit > 0 ->
+ {Limit, Limit}.
+
+%% Active counters have values > 0 internally; passive counters start at
+%% zero. The 'limit' field is only accessed by the 'counter__limit'
+%% function.
+
+counter__is_active({C, _}) ->
+ C > 0.
+
+counter__limit({_, L}) ->
+ L.
+
+counter__value({N, L}) ->
+ if N > 0 ->
+ L - N;
+ true ->
+ -N
+ end.
+
+counter__add(N, {V, L}, Type, Data) ->
+ N1 = V - N,
+ if V > 0, N1 =< 0 ->
+ case debug_counters() of
+ true ->
+ case Type of
+ effort ->
+ put(counter_effort_triggers,
+ get(counter_effort_triggers) + 1);
+ size ->
+ put(counter_size_triggers,
+ get(counter_size_triggers) + 1)
+ end;
+ _ ->
+ ok
+ end,
+ throw({counter_exceeded, Type, Data});
+ true ->
+ {N1, L}
+ end.
+
+
+%% =====================================================================
+%% Reporting
+
+% report_internal_error(S) ->
+% report_internal_error(S, []).
+
+report_internal_error(S, Vs) ->
+ report_error("internal error: " ++ S, Vs).
+
+report_error(D) ->
+ report_error(D, []).
+
+report_error({F, L, D}, Vs) ->
+ report({F, L, {error, D}}, Vs);
+report_error(D, Vs) ->
+ report({error, D}, Vs).
+
+report_warning(D) ->
+ report_warning(D, []).
+
+report_warning({F, L, D}, Vs) ->
+ report({F, L, {warning, D}}, Vs);
+report_warning(D, Vs) ->
+ report({warning, D}, Vs).
+
+report(D, Vs) ->
+ io:put_chars(format(D, Vs)).
+
+format({error, D}, Vs) ->
+ ["error: ", format(D, Vs)];
+format({warning, D}, Vs) ->
+ ["warning: ", format(D, Vs)];
+format({"", L, D}, Vs) when integer(L), L > 0 ->
+ [io_lib:fwrite("~w: ", [L]), format(D, Vs)];
+format({"", _L, D}, Vs) ->
+ format(D, Vs);
+format({F, L, D}, Vs) when integer(L), L > 0 ->
+ [io_lib:fwrite("~s:~w: ", [filename(F), L]), format(D, Vs)];
+format({F, _L, D}, Vs) ->
+ [io_lib:fwrite("~s: ", [filename(F)]), format(D, Vs)];
+format(S, Vs) when list(S) ->
+ [io_lib:fwrite(S, Vs), $\n].
+
+
+%% =====================================================================
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/cerl_trees.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/cerl_trees.erl
new file mode 100644
index 0000000000..afe7c8708b
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/cerl_trees.erl
@@ -0,0 +1,801 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Richard Carlsson.
+%% Copyright (C) 1999-2002 Richard Carlsson.
+%% Portions created by Ericsson are Copyright 2001, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: cerl_trees.erl,v 1.2 2010/06/07 06:32:39 kostis Exp $
+
+%% @doc Basic functions on Core Erlang abstract syntax trees.
+%%
+%% Syntax trees are defined in the module cerl
.
+%%
+%% @type cerl() = cerl:cerl()
+
+-module(cerl_trees).
+
+-export([depth/1, fold/3, free_variables/1, label/1, label/2, map/2,
+ mapfold/3, size/1, variables/1]).
+
+-import(cerl, [alias_pat/1, alias_var/1, ann_c_alias/3, ann_c_apply/3,
+ ann_c_binary/2, ann_c_bitstr/6, ann_c_call/4,
+ ann_c_case/3, ann_c_catch/2, ann_c_clause/4,
+ ann_c_cons_skel/3, ann_c_fun/3, ann_c_let/4,
+ ann_c_letrec/3, ann_c_module/5, ann_c_primop/3,
+ ann_c_receive/4, ann_c_seq/3, ann_c_try/6,
+ ann_c_tuple_skel/2, ann_c_values/2, apply_args/1,
+ apply_op/1, binary_segments/1, bitstr_val/1,
+ bitstr_size/1, bitstr_unit/1, bitstr_type/1,
+ bitstr_flags/1, call_args/1, call_module/1, call_name/1,
+ case_arg/1, case_clauses/1, catch_body/1, clause_body/1,
+ clause_guard/1, clause_pats/1, clause_vars/1, concrete/1,
+ cons_hd/1, cons_tl/1, fun_body/1, fun_vars/1, get_ann/1,
+ let_arg/1, let_body/1, let_vars/1, letrec_body/1,
+ letrec_defs/1, letrec_vars/1, module_attrs/1,
+ module_defs/1, module_exports/1, module_name/1,
+ module_vars/1, primop_args/1, primop_name/1,
+ receive_action/1, receive_clauses/1, receive_timeout/1,
+ seq_arg/1, seq_body/1, set_ann/2, subtrees/1, try_arg/1,
+ try_body/1, try_vars/1, try_evars/1, try_handler/1,
+ tuple_es/1, type/1, update_c_alias/3, update_c_apply/3,
+ update_c_binary/2, update_c_bitstr/6, update_c_call/4,
+ update_c_case/3, update_c_catch/2, update_c_clause/4,
+ update_c_cons/3, update_c_cons_skel/3, update_c_fun/3,
+ update_c_let/4, update_c_letrec/3, update_c_module/5,
+ update_c_primop/3, update_c_receive/4, update_c_seq/3,
+ update_c_try/6, update_c_tuple/2, update_c_tuple_skel/2,
+ update_c_values/2, values_es/1, var_name/1]).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec depth(Tree::cerl) -> integer()
+%%
+%% @doc Returns the length of the longest path in the tree. A leaf
+%% node has depth zero, the tree representing "{foo,
+%% bar}
" has depth one, etc.
+
+depth(T) ->
+ case subtrees(T) of
+ [] ->
+ 0;
+ Gs ->
+ 1 + lists:foldl(fun (G, A) -> erlang:max(depth_1(G), A) end, 0, Gs)
+ end.
+
+depth_1(Ts) ->
+ lists:foldl(fun (T, A) -> erlang:max(depth(T), A) end, 0, Ts).
+
+%% max(X, Y) when X > Y -> X;
+%% max(_, Y) -> Y.
+
+
+%% @spec size(Tree::cerl()) -> integer()
+%%
+%% @doc Returns the number of nodes in Tree
.
+
+size(T) ->
+ fold(fun (_, S) -> S + 1 end, 0, T).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec map(Function, Tree::cerl()) -> cerl()
+%%
+%% Function = (cerl()) -> cerl()
+%%
+%% @doc Maps a function onto the nodes of a tree. This replaces each
+%% node in the tree by the result of applying the given function on
+%% the original node, bottom-up.
+%%
+%% @see mapfold/3
+
+map(F, T) ->
+ F(map_1(F, T)).
+
+map_1(F, T) ->
+ case type(T) of
+ literal ->
+ case concrete(T) of
+ [_ | _] ->
+ update_c_cons(T, map(F, cons_hd(T)),
+ map(F, cons_tl(T)));
+ V when tuple_size(V) > 0 ->
+ update_c_tuple(T, map_list(F, tuple_es(T)));
+ _ ->
+ T
+ end;
+ var ->
+ T;
+ values ->
+ update_c_values(T, map_list(F, values_es(T)));
+ cons ->
+ update_c_cons_skel(T, map(F, cons_hd(T)),
+ map(F, cons_tl(T)));
+ tuple ->
+ update_c_tuple_skel(T, map_list(F, tuple_es(T)));
+ 'let' ->
+ update_c_let(T, map_list(F, let_vars(T)),
+ map(F, let_arg(T)),
+ map(F, let_body(T)));
+ seq ->
+ update_c_seq(T, map(F, seq_arg(T)),
+ map(F, seq_body(T)));
+ apply ->
+ update_c_apply(T, map(F, apply_op(T)),
+ map_list(F, apply_args(T)));
+ call ->
+ update_c_call(T, map(F, call_module(T)),
+ map(F, call_name(T)),
+ map_list(F, call_args(T)));
+ primop ->
+ update_c_primop(T, map(F, primop_name(T)),
+ map_list(F, primop_args(T)));
+ 'case' ->
+ update_c_case(T, map(F, case_arg(T)),
+ map_list(F, case_clauses(T)));
+ clause ->
+ update_c_clause(T, map_list(F, clause_pats(T)),
+ map(F, clause_guard(T)),
+ map(F, clause_body(T)));
+ alias ->
+ update_c_alias(T, map(F, alias_var(T)),
+ map(F, alias_pat(T)));
+ 'fun' ->
+ update_c_fun(T, map_list(F, fun_vars(T)),
+ map(F, fun_body(T)));
+ 'receive' ->
+ update_c_receive(T, map_list(F, receive_clauses(T)),
+ map(F, receive_timeout(T)),
+ map(F, receive_action(T)));
+ 'try' ->
+ update_c_try(T, map(F, try_arg(T)),
+ map_list(F, try_vars(T)),
+ map(F, try_body(T)),
+ map_list(F, try_evars(T)),
+ map(F, try_handler(T)));
+ 'catch' ->
+ update_c_catch(T, map(F, catch_body(T)));
+ binary ->
+ update_c_binary(T, map_list(F, binary_segments(T)));
+ bitstr ->
+ update_c_bitstr(T, map(F, bitstr_val(T)),
+ map(F, bitstr_size(T)),
+ map(F, bitstr_unit(T)),
+ map(F, bitstr_type(T)),
+ map(F, bitstr_flags(T)));
+ letrec ->
+ update_c_letrec(T, map_pairs(F, letrec_defs(T)),
+ map(F, letrec_body(T)));
+ module ->
+ update_c_module(T, map(F, module_name(T)),
+ map_list(F, module_exports(T)),
+ map_pairs(F, module_attrs(T)),
+ map_pairs(F, module_defs(T)))
+ end.
+
+map_list(F, [T | Ts]) ->
+ [map(F, T) | map_list(F, Ts)];
+map_list(_, []) ->
+ [].
+
+map_pairs(F, [{T1, T2} | Ps]) ->
+ [{map(F, T1), map(F, T2)} | map_pairs(F, Ps)];
+map_pairs(_, []) ->
+ [].
+
+
+%% @spec fold(Function, Unit::term(), Tree::cerl()) -> term()
+%%
+%% Function = (cerl(), term()) -> term()
+%%
+%% @doc Does a fold operation over the nodes of the tree. The result
+%% is the value of Function(X1, Function(X2, ... Function(Xn,
+%% Unit) ... ))
, where X1, ..., Xn
are the nodes
+%% of Tree
in a post-order traversal.
+%%
+%% @see mapfold/3
+
+fold(F, S, T) ->
+ F(T, fold_1(F, S, T)).
+
+fold_1(F, S, T) ->
+ case type(T) of
+ literal ->
+ case concrete(T) of
+ [_ | _] ->
+ fold(F, fold(F, S, cons_hd(T)), cons_tl(T));
+ V when tuple_size(V) > 0 ->
+ fold_list(F, S, tuple_es(T));
+ _ ->
+ S
+ end;
+ var ->
+ S;
+ values ->
+ fold_list(F, S, values_es(T));
+ cons ->
+ fold(F, fold(F, S, cons_hd(T)), cons_tl(T));
+ tuple ->
+ fold_list(F, S, tuple_es(T));
+ 'let' ->
+ fold(F, fold(F, fold_list(F, S, let_vars(T)),
+ let_arg(T)),
+ let_body(T));
+ seq ->
+ fold(F, fold(F, S, seq_arg(T)), seq_body(T));
+ apply ->
+ fold_list(F, fold(F, S, apply_op(T)), apply_args(T));
+ call ->
+ fold_list(F, fold(F, fold(F, S, call_module(T)),
+ call_name(T)),
+ call_args(T));
+ primop ->
+ fold_list(F, fold(F, S, primop_name(T)), primop_args(T));
+ 'case' ->
+ fold_list(F, fold(F, S, case_arg(T)), case_clauses(T));
+ clause ->
+ fold(F, fold(F, fold_list(F, S, clause_pats(T)),
+ clause_guard(T)),
+ clause_body(T));
+ alias ->
+ fold(F, fold(F, S, alias_var(T)), alias_pat(T));
+ 'fun' ->
+ fold(F, fold_list(F, S, fun_vars(T)), fun_body(T));
+ 'receive' ->
+ fold(F, fold(F, fold_list(F, S, receive_clauses(T)),
+ receive_timeout(T)),
+ receive_action(T));
+ 'try' ->
+ fold(F, fold_list(F, fold(F, fold_list(F, fold(F, S, try_arg(T)),
+ try_vars(T)),
+ try_body(T)),
+ try_evars(T)),
+ try_handler(T));
+ 'catch' ->
+ fold(F, S, catch_body(T));
+ binary ->
+ fold_list(F, S, binary_segments(T));
+ bitstr ->
+ fold(F,
+ fold(F,
+ fold(F,
+ fold(F,
+ fold(F, S, bitstr_val(T)),
+ bitstr_size(T)),
+ bitstr_unit(T)),
+ bitstr_type(T)),
+ bitstr_flags(T));
+ letrec ->
+ fold(F, fold_pairs(F, S, letrec_defs(T)), letrec_body(T));
+ module ->
+ fold_pairs(F,
+ fold_pairs(F,
+ fold_list(F,
+ fold(F, S, module_name(T)),
+ module_exports(T)),
+ module_attrs(T)),
+ module_defs(T))
+ end.
+
+fold_list(F, S, [T | Ts]) ->
+ fold_list(F, fold(F, S, T), Ts);
+fold_list(_, S, []) ->
+ S.
+
+fold_pairs(F, S, [{T1, T2} | Ps]) ->
+ fold_pairs(F, fold(F, fold(F, S, T1), T2), Ps);
+fold_pairs(_, S, []) ->
+ S.
+
+
+%% @spec mapfold(Function, Initial::term(), Tree::cerl()) ->
+%% {cerl(), term()}
+%%
+%% Function = (cerl(), term()) -> {cerl(), term()}
+%%
+%% @doc Does a combined map/fold operation on the nodes of the
+%% tree. This is similar to map/2
, but also propagates a
+%% value from each application of Function
to the next,
+%% starting with the given value Initial
, while doing a
+%% post-order traversal of the tree, much like fold/3
.
+%%
+%% @see map/2
+%% @see fold/3
+
+mapfold(F, S0, T) ->
+ case type(T) of
+ literal ->
+ case concrete(T) of
+ [_ | _] ->
+ {T1, S1} = mapfold(F, S0, cons_hd(T)),
+ {T2, S2} = mapfold(F, S1, cons_tl(T)),
+ F(update_c_cons(T, T1, T2), S2);
+ V when tuple_size(V) > 0 ->
+ {Ts, S1} = mapfold_list(F, S0, tuple_es(T)),
+ F(update_c_tuple(T, Ts), S1);
+ _ ->
+ F(T, S0)
+ end;
+ var ->
+ F(T, S0);
+ values ->
+ {Ts, S1} = mapfold_list(F, S0, values_es(T)),
+ F(update_c_values(T, Ts), S1);
+ cons ->
+ {T1, S1} = mapfold(F, S0, cons_hd(T)),
+ {T2, S2} = mapfold(F, S1, cons_tl(T)),
+ F(update_c_cons_skel(T, T1, T2), S2);
+ tuple ->
+ {Ts, S1} = mapfold_list(F, S0, tuple_es(T)),
+ F(update_c_tuple_skel(T, Ts), S1);
+ 'let' ->
+ {Vs, S1} = mapfold_list(F, S0, let_vars(T)),
+ {A, S2} = mapfold(F, S1, let_arg(T)),
+ {B, S3} = mapfold(F, S2, let_body(T)),
+ F(update_c_let(T, Vs, A, B), S3);
+ seq ->
+ {A, S1} = mapfold(F, S0, seq_arg(T)),
+ {B, S2} = mapfold(F, S1, seq_body(T)),
+ F(update_c_seq(T, A, B), S2);
+ apply ->
+ {E, S1} = mapfold(F, S0, apply_op(T)),
+ {As, S2} = mapfold_list(F, S1, apply_args(T)),
+ F(update_c_apply(T, E, As), S2);
+ call ->
+ {M, S1} = mapfold(F, S0, call_module(T)),
+ {N, S2} = mapfold(F, S1, call_name(T)),
+ {As, S3} = mapfold_list(F, S2, call_args(T)),
+ F(update_c_call(T, M, N, As), S3);
+ primop ->
+ {N, S1} = mapfold(F, S0, primop_name(T)),
+ {As, S2} = mapfold_list(F, S1, primop_args(T)),
+ F(update_c_primop(T, N, As), S2);
+ 'case' ->
+ {A, S1} = mapfold(F, S0, case_arg(T)),
+ {Cs, S2} = mapfold_list(F, S1, case_clauses(T)),
+ F(update_c_case(T, A, Cs), S2);
+ clause ->
+ {Ps, S1} = mapfold_list(F, S0, clause_pats(T)),
+ {G, S2} = mapfold(F, S1, clause_guard(T)),
+ {B, S3} = mapfold(F, S2, clause_body(T)),
+ F(update_c_clause(T, Ps, G, B), S3);
+ alias ->
+ {V, S1} = mapfold(F, S0, alias_var(T)),
+ {P, S2} = mapfold(F, S1, alias_pat(T)),
+ F(update_c_alias(T, V, P), S2);
+ 'fun' ->
+ {Vs, S1} = mapfold_list(F, S0, fun_vars(T)),
+ {B, S2} = mapfold(F, S1, fun_body(T)),
+ F(update_c_fun(T, Vs, B), S2);
+ 'receive' ->
+ {Cs, S1} = mapfold_list(F, S0, receive_clauses(T)),
+ {E, S2} = mapfold(F, S1, receive_timeout(T)),
+ {A, S3} = mapfold(F, S2, receive_action(T)),
+ F(update_c_receive(T, Cs, E, A), S3);
+ 'try' ->
+ {E, S1} = mapfold(F, S0, try_arg(T)),
+ {Vs, S2} = mapfold_list(F, S1, try_vars(T)),
+ {B, S3} = mapfold(F, S2, try_body(T)),
+ {Evs, S4} = mapfold_list(F, S3, try_evars(T)),
+ {H, S5} = mapfold(F, S4, try_handler(T)),
+ F(update_c_try(T, E, Vs, B, Evs, H), S5);
+ 'catch' ->
+ {B, S1} = mapfold(F, S0, catch_body(T)),
+ F(update_c_catch(T, B), S1);
+ binary ->
+ {Ds, S1} = mapfold_list(F, S0, binary_segments(T)),
+ F(update_c_binary(T, Ds), S1);
+ bitstr ->
+ {Val, S1} = mapfold(F, S0, bitstr_val(T)),
+ {Size, S2} = mapfold(F, S1, bitstr_size(T)),
+ {Unit, S3} = mapfold(F, S2, bitstr_unit(T)),
+ {Type, S4} = mapfold(F, S3, bitstr_type(T)),
+ {Flags, S5} = mapfold(F, S4, bitstr_flags(T)),
+ F(update_c_bitstr(T, Val, Size, Unit, Type, Flags), S5);
+ letrec ->
+ {Ds, S1} = mapfold_pairs(F, S0, letrec_defs(T)),
+ {B, S2} = mapfold(F, S1, letrec_body(T)),
+ F(update_c_letrec(T, Ds, B), S2);
+ module ->
+ {N, S1} = mapfold(F, S0, module_name(T)),
+ {Es, S2} = mapfold_list(F, S1, module_exports(T)),
+ {As, S3} = mapfold_pairs(F, S2, module_attrs(T)),
+ {Ds, S4} = mapfold_pairs(F, S3, module_defs(T)),
+ F(update_c_module(T, N, Es, As, Ds), S4)
+ end.
+
+mapfold_list(F, S0, [T | Ts]) ->
+ {T1, S1} = mapfold(F, S0, T),
+ {Ts1, S2} = mapfold_list(F, S1, Ts),
+ {[T1 | Ts1], S2};
+mapfold_list(_, S, []) ->
+ {[], S}.
+
+mapfold_pairs(F, S0, [{T1, T2} | Ps]) ->
+ {T3, S1} = mapfold(F, S0, T1),
+ {T4, S2} = mapfold(F, S1, T2),
+ {Ps1, S3} = mapfold_pairs(F, S2, Ps),
+ {[{T3, T4} | Ps1], S3};
+mapfold_pairs(_, S, []) ->
+ {[], S}.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec variables(Tree::cerl()) -> [var_name()]
+%%
+%% var_name() = integer() | atom() | {atom(), integer()}
+%%
+%% @doc Returns an ordered-set list of the names of all variables in
+%% the syntax tree. (This includes function name variables.) An
+%% exception is thrown if Tree
does not represent a
+%% well-formed Core Erlang syntax tree.
+%%
+%% @see free_variables/1
+
+variables(T) ->
+ variables(T, false).
+
+
+%% @spec free_variables(Tree::cerl()) -> [var_name()]
+%%
+%% @doc Like variables/1
, but only includes variables
+%% that are free in the tree.
+%%
+%% @see variables/1
+
+free_variables(T) ->
+ variables(T, true).
+
+
+%% This is not exported
+
+variables(T, S) ->
+ case type(T) of
+ literal ->
+ [];
+ var ->
+ [var_name(T)];
+ values ->
+ vars_in_list(values_es(T), S);
+ cons ->
+ ordsets:union(variables(cons_hd(T), S),
+ variables(cons_tl(T), S));
+ tuple ->
+ vars_in_list(tuple_es(T), S);
+ 'let' ->
+ Vs = variables(let_body(T), S),
+ Vs1 = var_list_names(let_vars(T)),
+ Vs2 = case S of
+ true ->
+ ordsets:subtract(Vs, Vs1);
+ false ->
+ ordsets:union(Vs, Vs1)
+ end,
+ ordsets:union(variables(let_arg(T), S), Vs2);
+ seq ->
+ ordsets:union(variables(seq_arg(T), S),
+ variables(seq_body(T), S));
+ apply ->
+ ordsets:union(
+ variables(apply_op(T), S),
+ vars_in_list(apply_args(T), S));
+ call ->
+ ordsets:union(variables(call_module(T), S),
+ ordsets:union(
+ variables(call_name(T), S),
+ vars_in_list(call_args(T), S)));
+ primop ->
+ vars_in_list(primop_args(T), S);
+ 'case' ->
+ ordsets:union(variables(case_arg(T), S),
+ vars_in_list(case_clauses(T), S));
+ clause ->
+ Vs = ordsets:union(variables(clause_guard(T), S),
+ variables(clause_body(T), S)),
+ Vs1 = vars_in_list(clause_pats(T), S),
+ case S of
+ true ->
+ ordsets:subtract(Vs, Vs1);
+ false ->
+ ordsets:union(Vs, Vs1)
+ end;
+ alias ->
+ ordsets:add_element(var_name(alias_var(T)),
+ variables(alias_pat(T)));
+ 'fun' ->
+ Vs = variables(fun_body(T), S),
+ Vs1 = var_list_names(fun_vars(T)),
+ case S of
+ true ->
+ ordsets:subtract(Vs, Vs1);
+ false ->
+ ordsets:union(Vs, Vs1)
+ end;
+ 'receive' ->
+ ordsets:union(
+ vars_in_list(receive_clauses(T), S),
+ ordsets:union(variables(receive_timeout(T), S),
+ variables(receive_action(T), S)));
+ 'try' ->
+ Vs = variables(try_body(T), S),
+ Vs1 = var_list_names(try_vars(T)),
+ Vs2 = case S of
+ true ->
+ ordsets:subtract(Vs, Vs1);
+ false ->
+ ordsets:union(Vs, Vs1)
+ end,
+ Vs3 = variables(try_handler(T), S),
+ Vs4 = var_list_names(try_evars(T)),
+ Vs5 = case S of
+ true ->
+ ordsets:subtract(Vs3, Vs4);
+ false ->
+ ordsets:union(Vs3, Vs4)
+ end,
+ ordsets:union(variables(try_arg(T), S),
+ ordsets:union(Vs2, Vs5));
+ 'catch' ->
+ variables(catch_body(T), S);
+ binary ->
+ vars_in_list(binary_segments(T), S);
+ bitstr ->
+ ordsets:union(variables(bitstr_val(T), S),
+ variables(bitstr_size(T), S));
+ letrec ->
+ Vs = vars_in_defs(letrec_defs(T), S),
+ Vs1 = ordsets:union(variables(letrec_body(T), S), Vs),
+ Vs2 = var_list_names(letrec_vars(T)),
+ case S of
+ true ->
+ ordsets:subtract(Vs1, Vs2);
+ false ->
+ ordsets:union(Vs1, Vs2)
+ end;
+ module ->
+ Vs = vars_in_defs(module_defs(T), S),
+ Vs1 = ordsets:union(vars_in_list(module_exports(T), S), Vs),
+ Vs2 = var_list_names(module_vars(T)),
+ case S of
+ true ->
+ ordsets:subtract(Vs1, Vs2);
+ false ->
+ ordsets:union(Vs1, Vs2)
+ end
+ end.
+
+vars_in_list(Ts, S) ->
+ vars_in_list(Ts, S, []).
+
+vars_in_list([T | Ts], S, A) ->
+ vars_in_list(Ts, S, ordsets:union(variables(T, S), A));
+vars_in_list([], _, A) ->
+ A.
+
+%% Note that this function only visits the right-hand side of function
+%% definitions.
+
+vars_in_defs(Ds, S) ->
+ vars_in_defs(Ds, S, []).
+
+vars_in_defs([{_, F} | Ds], S, A) ->
+ vars_in_defs(Ds, S, ordsets:union(variables(F, S), A));
+vars_in_defs([], _, A) ->
+ A.
+
+%% This amounts to insertion sort. Since the lists are generally short,
+%% it is hardly worthwhile to use an asymptotically better sort.
+
+var_list_names(Vs) ->
+ var_list_names(Vs, []).
+
+var_list_names([V | Vs], A) ->
+ var_list_names(Vs, ordsets:add_element(var_name(V), A));
+var_list_names([], A) ->
+ A.
+
+
+%% ---------------------------------------------------------------------
+
+%% label(Tree::cerl()) -> {cerl(), integer()}
+%%
+%% @equiv label(Tree, 0)
+
+label(T) ->
+ label(T, 0).
+
+%% @spec label(Tree::cerl(), N::integer()) -> {cerl(), integer()}
+%%
+%% @doc Labels each expression in the tree. A term {label,
+%% L}
is prefixed to the annotation list of each expression node,
+%% where L is a unique number for every node, except for variables (and
+%% function name variables) which get the same label if they represent
+%% the same variable. Constant literal nodes are not labeled.
+%%
+%% The returned value is a tuple {NewTree, Max}
, where
+%% NewTree
is the labeled tree and Max
is 1
+%% plus the largest label value used. All previous annotation terms on
+%% the form {label, X}
are deleted.
+%%
+%% The values of L used in the tree is a dense range from
+%% N
to Max - 1
, where N =< Max
+%% =< N + size(Tree)
. Note that it is possible that no
+%% labels are used at all, i.e., N = Max
.
+%%
+%% Note: All instances of free variables will be given distinct
+%% labels.
+%%
+%% @see label/1
+%% @see size/1
+
+label(T, N) ->
+ label(T, N, dict:new()).
+
+label(T, N, Env) ->
+ case type(T) of
+ literal ->
+ %% Constant literals are not labeled.
+ {T, N};
+ var ->
+ case dict:find(var_name(T), Env) of
+ {ok, L} ->
+ {As, _} = label_ann(T, L),
+ N1 = N;
+ error ->
+ {As, N1} = label_ann(T, N)
+ end,
+ {set_ann(T, As), N1};
+ values ->
+ {Ts, N1} = label_list(values_es(T), N, Env),
+ {As, N2} = label_ann(T, N1),
+ {ann_c_values(As, Ts), N2};
+ cons ->
+ {T1, N1} = label(cons_hd(T), N, Env),
+ {T2, N2} = label(cons_tl(T), N1, Env),
+ {As, N3} = label_ann(T, N2),
+ {ann_c_cons_skel(As, T1, T2), N3};
+ tuple ->
+ {Ts, N1} = label_list(tuple_es(T), N, Env),
+ {As, N2} = label_ann(T, N1),
+ {ann_c_tuple_skel(As, Ts), N2};
+ 'let' ->
+ {A, N1} = label(let_arg(T), N, Env),
+ {Vs, N2, Env1} = label_vars(let_vars(T), N1, Env),
+ {B, N3} = label(let_body(T), N2, Env1),
+ {As, N4} = label_ann(T, N3),
+ {ann_c_let(As, Vs, A, B), N4};
+ seq ->
+ {A, N1} = label(seq_arg(T), N, Env),
+ {B, N2} = label(seq_body(T), N1, Env),
+ {As, N3} = label_ann(T, N2),
+ {ann_c_seq(As, A, B), N3};
+ apply ->
+ {E, N1} = label(apply_op(T), N, Env),
+ {Es, N2} = label_list(apply_args(T), N1, Env),
+ {As, N3} = label_ann(T, N2),
+ {ann_c_apply(As, E, Es), N3};
+ call ->
+ {M, N1} = label(call_module(T), N, Env),
+ {F, N2} = label(call_name(T), N1, Env),
+ {Es, N3} = label_list(call_args(T), N2, Env),
+ {As, N4} = label_ann(T, N3),
+ {ann_c_call(As, M, F, Es), N4};
+ primop ->
+ {F, N1} = label(primop_name(T), N, Env),
+ {Es, N2} = label_list(primop_args(T), N1, Env),
+ {As, N3} = label_ann(T, N2),
+ {ann_c_primop(As, F, Es), N3};
+ 'case' ->
+ {A, N1} = label(case_arg(T), N, Env),
+ {Cs, N2} = label_list(case_clauses(T), N1, Env),
+ {As, N3} = label_ann(T, N2),
+ {ann_c_case(As, A, Cs), N3};
+ clause ->
+ {_, N1, Env1} = label_vars(clause_vars(T), N, Env),
+ {Ps, N2} = label_list(clause_pats(T), N1, Env1),
+ {G, N3} = label(clause_guard(T), N2, Env1),
+ {B, N4} = label(clause_body(T), N3, Env1),
+ {As, N5} = label_ann(T, N4),
+ {ann_c_clause(As, Ps, G, B), N5};
+ alias ->
+ {V, N1} = label(alias_var(T), N, Env),
+ {P, N2} = label(alias_pat(T), N1, Env),
+ {As, N3} = label_ann(T, N2),
+ {ann_c_alias(As, V, P), N3};
+ 'fun' ->
+ {Vs, N1, Env1} = label_vars(fun_vars(T), N, Env),
+ {B, N2} = label(fun_body(T), N1, Env1),
+ {As, N3} = label_ann(T, N2),
+ {ann_c_fun(As, Vs, B), N3};
+ 'receive' ->
+ {Cs, N1} = label_list(receive_clauses(T), N, Env),
+ {E, N2} = label(receive_timeout(T), N1, Env),
+ {A, N3} = label(receive_action(T), N2, Env),
+ {As, N4} = label_ann(T, N3),
+ {ann_c_receive(As, Cs, E, A), N4};
+ 'try' ->
+ {E, N1} = label(try_arg(T), N, Env),
+ {Vs, N2, Env1} = label_vars(try_vars(T), N1, Env),
+ {B, N3} = label(try_body(T), N2, Env1),
+ {Evs, N4, Env2} = label_vars(try_evars(T), N3, Env),
+ {H, N5} = label(try_handler(T), N4, Env2),
+ {As, N6} = label_ann(T, N5),
+ {ann_c_try(As, E, Vs, B, Evs, H), N6};
+ 'catch' ->
+ {B, N1} = label(catch_body(T), N, Env),
+ {As, N2} = label_ann(T, N1),
+ {ann_c_catch(As, B), N2};
+ binary ->
+ {Ds, N1} = label_list(binary_segments(T), N, Env),
+ {As, N2} = label_ann(T, N1),
+ {ann_c_binary(As, Ds), N2};
+ bitstr ->
+ {Val, N1} = label(bitstr_val(T), N, Env),
+ {Size, N2} = label(bitstr_size(T), N1, Env),
+ {Unit, N3} = label(bitstr_unit(T), N2, Env),
+ {Type, N4} = label(bitstr_type(T), N3, Env),
+ {Flags, N5} = label(bitstr_flags(T), N4, Env),
+ {As, N6} = label_ann(T, N5),
+ {ann_c_bitstr(As, Val, Size, Unit, Type, Flags), N6};
+ letrec ->
+ {_, N1, Env1} = label_vars(letrec_vars(T), N, Env),
+ {Ds, N2} = label_defs(letrec_defs(T), N1, Env1),
+ {B, N3} = label(letrec_body(T), N2, Env1),
+ {As, N4} = label_ann(T, N3),
+ {ann_c_letrec(As, Ds, B), N4};
+ module ->
+ %% The module name is not labeled.
+ {_, N1, Env1} = label_vars(module_vars(T), N, Env),
+ {Ts, N2} = label_defs(module_attrs(T), N1, Env1),
+ {Ds, N3} = label_defs(module_defs(T), N2, Env1),
+ {Es, N4} = label_list(module_exports(T), N3, Env1),
+ {As, N5} = label_ann(T, N4),
+ {ann_c_module(As, module_name(T), Es, Ts, Ds), N5}
+ end.
+
+label_list([T | Ts], N, Env) ->
+ {T1, N1} = label(T, N, Env),
+ {Ts1, N2} = label_list(Ts, N1, Env),
+ {[T1 | Ts1], N2};
+label_list([], N, _Env) ->
+ {[], N}.
+
+label_vars([T | Ts], N, Env) ->
+ Env1 = dict:store(var_name(T), N, Env),
+ {As, N1} = label_ann(T, N),
+ T1 = set_ann(T, As),
+ {Ts1, N2, Env2} = label_vars(Ts, N1, Env1),
+ {[T1 | Ts1], N2, Env2};
+label_vars([], N, Env) ->
+ {[], N, Env}.
+
+label_defs([{F, T} | Ds], N, Env) ->
+ {F1, N1} = label(F, N, Env),
+ {T1, N2} = label(T, N1, Env),
+ {Ds1, N3} = label_defs(Ds, N2, Env),
+ {[{F1, T1} | Ds1], N3};
+label_defs([], N, _Env) ->
+ {[], N}.
+
+label_ann(T, N) ->
+ {[{label, N} | filter_labels(get_ann(T))], N + 1}.
+
+filter_labels([{label, _} | As]) ->
+ filter_labels(As);
+filter_labels([A | As]) ->
+ [A | filter_labels(As)];
+filter_labels([]) ->
+ [].
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/compile.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/compile.erl
new file mode 100644
index 0000000000..2b6d14e300
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/compile.erl
@@ -0,0 +1,1109 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: compile.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose: Run the Erlang compiler.
+
+-module(compile).
+-include("erl_compile.hrl").
+-include("core_parse.hrl").
+
+%% High-level interface.
+-export([file/1,file/2,format_error/1,iofile/1]).
+-export([forms/1,forms/2]).
+-export([output_generated/1]).
+-export([options/0]).
+
+%% Erlc interface.
+-export([compile/3,compile_beam/3,compile_asm/3,compile_core/3]).
+
+
+-import(lists, [member/2,reverse/1,keysearch/3,last/1,
+ map/2,flatmap/2,foreach/2,foldr/3,any/2,filter/2]).
+
+%% file(FileName)
+%% file(FileName, Options)
+%% Compile the module in file FileName.
+
+-define(DEFAULT_OPTIONS, [verbose,report_errors,report_warnings]).
+
+-define(pass(P), {P,fun P/1}).
+
+file(File) -> file(File, ?DEFAULT_OPTIONS).
+
+file(File, Opts) when list(Opts) ->
+ do_compile({file,File}, Opts++env_default_opts());
+file(File, Opt) ->
+ file(File, [Opt|?DEFAULT_OPTIONS]).
+
+forms(File) -> forms(File, ?DEFAULT_OPTIONS).
+
+forms(Forms, Opts) when list(Opts) ->
+ do_compile({forms,Forms}, [binary|Opts++env_default_opts()]);
+forms(Forms, Opts) when atom(Opts) ->
+ forms(Forms, [Opts|?DEFAULT_OPTIONS]).
+
+env_default_opts() ->
+ Key = "ERL_COMPILER_OPTIONS",
+ case os:getenv(Key) of
+ false -> [];
+ Str when list(Str) ->
+ case erl_scan:string(Str) of
+ {ok,Tokens,_} ->
+ case erl_parse:parse_term(Tokens ++ [{dot, 1}]) of
+ {ok,List} when list(List) -> List;
+ {ok,Term} -> [Term];
+ {error,_Reason} ->
+ io:format("Ignoring bad term in ~s\n", [Key]),
+ []
+ end;
+ {error, {_,_,_Reason}, _} ->
+ io:format("Ignoring bad term in ~s\n", [Key]),
+ []
+ end
+ end.
+
+do_compile(Input, Opts0) ->
+ Opts = expand_opts(Opts0),
+ Self = self(),
+ Serv = spawn_link(fun() -> internal(Self, Input, Opts) end),
+ receive
+ {Serv,Rep} -> Rep
+ end.
+
+%% Given a list of compilation options, returns true if compile:file/2
+%% would have generated a Beam file, false otherwise (if only a binary or a
+%% listing file would have been generated).
+
+output_generated(Opts) ->
+ any(fun ({save_binary,_F}) -> true;
+ (_Other) -> false
+ end, passes(file, expand_opts(Opts))).
+
+expand_opts(Opts) ->
+ foldr(fun expand_opt/2, [], Opts).
+
+expand_opt(basic_validation, Os) ->
+ [no_code_generation,to_pp,binary|Os];
+expand_opt(strong_validation, Os) ->
+ [no_code_generation,to_kernel,binary|Os];
+expand_opt(report, Os) ->
+ [report_errors,report_warnings|Os];
+expand_opt(return, Os) ->
+ [return_errors,return_warnings|Os];
+expand_opt(r7, Os) ->
+ [no_float_opt,no_new_funs,no_new_binaries,no_new_apply|Os];
+expand_opt(O, Os) -> [O|Os].
+
+filter_opts(Opts0) ->
+ %% Native code generation is not supported if no_new_funs is given.
+ case member(no_new_funs, Opts0) of
+ false -> Opts0;
+ true -> Opts0 -- [native]
+ end.
+
+%% format_error(ErrorDescriptor) -> string()
+
+format_error(no_native_support) ->
+ "this system is not configured for native-code compilation.";
+format_error({native, E}) ->
+ io_lib:fwrite("native-code compilation failed with reason: ~P.",
+ [E, 25]);
+format_error({native_crash, E}) ->
+ io_lib:fwrite("native-code compilation crashed with reason: ~P.",
+ [E, 25]);
+format_error({open,E}) ->
+ io_lib:format("open error '~s'", [file:format_error(E)]);
+format_error({epp,E}) ->
+ epp:format_error(E);
+format_error(write_error) ->
+ "error writing file";
+format_error({rename,S}) ->
+ io_lib:format("error renaming ~s", [S]);
+format_error({parse_transform,M,R}) ->
+ io_lib:format("error in parse transform '~s': ~p", [M, R]);
+format_error({core_transform,M,R}) ->
+ io_lib:format("error in core transform '~s': ~p", [M, R]);
+format_error({crash,Pass,Reason}) ->
+ io_lib:format("internal error in ~p;\ncrash reason: ~p", [Pass,Reason]);
+format_error({bad_return,Pass,Reason}) ->
+ io_lib:format("internal error in ~p;\nbad return value: ~p", [Pass,Reason]).
+
+%% The compile state record.
+-record(compile, {filename="",
+ dir="",
+ base="",
+ ifile="",
+ ofile="",
+ module=[],
+ code=[],
+ core_code=[],
+ abstract_code=[], %Abstract code for debugger.
+ options=[],
+ errors=[],
+ warnings=[]}).
+
+internal(Master, Input, Opts) ->
+ Master ! {self(),
+ case catch internal(Input, Opts) of
+ {'EXIT', Reason} ->
+ {error, Reason};
+ Other ->
+ Other
+ end}.
+
+internal({forms,Forms}, Opts) ->
+ Ps = passes(forms, Opts),
+ internal_comp(Ps, "", "", #compile{code=Forms,options=Opts});
+internal({file,File}, Opts) ->
+ Ps = passes(file, Opts),
+ Compile = #compile{options=Opts},
+ case member(from_core, Opts) of
+ true -> internal_comp(Ps, File, ".core", Compile);
+ false ->
+ case member(from_beam, Opts) of
+ true ->
+ internal_comp(Ps, File, ".beam", Compile);
+ false ->
+ case member(from_asm, Opts) orelse member(asm, Opts) of
+ true ->
+ internal_comp(Ps, File, ".S", Compile);
+ false ->
+ internal_comp(Ps, File, ".erl", Compile)
+ end
+ end
+ end.
+
+internal_comp(Passes, File, Suffix, St0) ->
+ Dir = filename:dirname(File),
+ Base = filename:basename(File, Suffix),
+ St1 = St0#compile{filename=File, dir=Dir, base=Base,
+ ifile=erlfile(Dir, Base, Suffix),
+ ofile=objfile(Base, St0)},
+ Run = case member(time, St1#compile.options) of
+ true ->
+ io:format("Compiling ~p\n", [File]),
+ fun run_tc/2;
+ false -> fun({_Name,Fun}, St) -> catch Fun(St) end
+ end,
+ case fold_comp(Passes, Run, St1) of
+ {ok,St2} -> comp_ret_ok(St2);
+ {error,St2} -> comp_ret_err(St2)
+ end.
+
+fold_comp([{Name,Test,Pass}|Ps], Run, St) ->
+ case Test(St) of
+ false -> %Pass is not needed.
+ fold_comp(Ps, Run, St);
+ true -> %Run pass in the usual way.
+ fold_comp([{Name,Pass}|Ps], Run, St)
+ end;
+fold_comp([{Name,Pass}|Ps], Run, St0) ->
+ case Run({Name,Pass}, St0) of
+ {ok,St1} -> fold_comp(Ps, Run, St1);
+ {error,St1} -> {error,St1};
+ {'EXIT',Reason} ->
+ Es = [{St0#compile.ifile,[{none,?MODULE,{crash,Name,Reason}}]}],
+ {error,St0#compile{errors=St0#compile.errors ++ Es}};
+ Other ->
+ Es = [{St0#compile.ifile,[{none,?MODULE,{bad_return,Name,Other}}]}],
+ {error,St0#compile{errors=St0#compile.errors ++ Es}}
+ end;
+fold_comp([], _Run, St) -> {ok,St}.
+
+os_process_size() ->
+ case os:type() of
+ {unix, sunos} ->
+ Size = os:cmd("ps -o vsz -p " ++ os:getpid() ++ " | tail -1"),
+ list_to_integer(lib:nonl(Size));
+ _ ->
+ 0
+ end.
+
+run_tc({Name,Fun}, St) ->
+ Before0 = statistics(runtime),
+ Val = (catch Fun(St)),
+ After0 = statistics(runtime),
+ {Before_c, _} = Before0,
+ {After_c, _} = After0,
+ io:format(" ~-30s: ~10.3f s (~w k)\n",
+ [Name, (After_c-Before_c) / 1000, os_process_size()]),
+ Val.
+
+comp_ret_ok(#compile{code=Code,warnings=Warn,module=Mod,options=Opts}=St) ->
+ report_warnings(St),
+ Ret1 = case member(binary, Opts) andalso not member(no_code_generation, Opts) of
+ true -> [Code];
+ false -> []
+ end,
+ Ret2 = case member(return_warnings, Opts) of
+ true -> Ret1 ++ [Warn];
+ false -> Ret1
+ end,
+ list_to_tuple([ok,Mod|Ret2]).
+
+comp_ret_err(St) ->
+ report_errors(St),
+ report_warnings(St),
+ case member(return_errors, St#compile.options) of
+ true -> {error,St#compile.errors,St#compile.warnings};
+ false -> error
+ end.
+
+%% passes(form|file, [Option]) -> [{Name,PassFun}]
+%% Figure out which passes that need to be run.
+
+passes(forms, Opts) ->
+ select_passes(standard_passes(), Opts);
+passes(file, Opts) ->
+ case member(from_beam, Opts) of
+ true ->
+ Ps = [?pass(read_beam_file)|binary_passes()],
+ select_passes(Ps, Opts);
+ false ->
+ Ps = case member(from_asm, Opts) orelse member(asm, Opts) of
+ true ->
+ [?pass(beam_consult_asm)|asm_passes()];
+ false ->
+ case member(from_core, Opts) of
+ true ->
+ [?pass(parse_core)|core_passes()];
+ false ->
+ [?pass(parse_module)|standard_passes()]
+ end
+ end,
+ Fs = select_passes(Ps, Opts),
+
+ %% If the last pass saves the resulting binary to a file,
+ %% insert a first pass to remove the file.
+ case last(Fs) of
+ {save_binary,_Fun} -> [?pass(remove_file)|Fs];
+ _Other -> Fs
+ end
+ end.
+
+%% select_passes([Command], Opts) -> [{Name,Function}]
+%% Interpret the lists of commands to return a pure list of passes.
+%%
+%% Command can be one of:
+%%
+%% {pass,Mod} Will be expanded to a call to the external
+%% function Mod:module(Code, Options). This
+%% function must transform the code and return
+%% {ok,NewCode} or {error,Term}.
+%% Example: {pass,beam_codegen}
+%%
+%% {Name,Fun} Name is an atom giving the name of the pass.
+%% Fun is an 'fun' taking one argument: a compile record.
+%% The fun should return {ok,NewCompileRecord} or
+%% {error,NewCompileRecord}.
+%% Note: ?pass(Name) is equvivalent to {Name,fun Name/1}.
+%% Example: ?pass(parse_module)
+%%
+%% {Name,Test,Fun} Like {Name,Fun} above, but the pass will be run
+%% (and listed by the `time' option) only if Test(St)
+%% returns true.
+%%
+%% {src_listing,Ext} Produces an Erlang source listing with the
+%% the file extension Ext. (Ext should not contain
+%% a period.) No more passes will be run.
+%%
+%% {listing,Ext} Produce an listing of the terms in the internal
+%% representation. The extension of the listing
+%% file will be Ext. (Ext should not contain
+%% a period.) No more passes will be run.
+%%
+%% {done,Ext} End compilation at this point. Produce a listing
+%% as with {listing,Ext}, unless 'binary' is
+%% specified, in which case the current
+%% representation of the code is returned without
+%% creating an output file.
+%%
+%% {iff,Flag,Cmd} If the given Flag is given in the option list,
+%% Cmd will be interpreted as a command.
+%% Otherwise, Cmd will be ignored.
+%% Example: {iff,dcg,{listing,"codegen}}
+%%
+%% {unless,Flag,Cmd} If the given Flag is NOT given in the option list,
+%% Cmd will be interpreted as a command.
+%% Otherwise, Cmd will be ignored.
+%% Example: {unless,no_kernopt,{pass,sys_kernopt}}
+%%
+
+select_passes([{pass,Mod}|Ps], Opts) ->
+ F = fun(St) ->
+ case catch Mod:module(St#compile.code, St#compile.options) of
+ {ok,Code} ->
+ {ok,St#compile{code=Code}};
+ {error,Es} ->
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end
+ end,
+ [{Mod,F}|select_passes(Ps, Opts)];
+select_passes([{src_listing,Ext}|_], _Opts) ->
+ [{listing,fun (St) -> src_listing(Ext, St) end}];
+select_passes([{listing,Ext}|_], _Opts) ->
+ [{listing,fun (St) -> listing(Ext, St) end}];
+select_passes([{done,Ext}|_], Opts) ->
+ select_passes([{unless,binary,{listing,Ext}}], Opts);
+select_passes([{iff,Flag,Pass}|Ps], Opts) ->
+ select_cond(Flag, true, Pass, Ps, Opts);
+select_passes([{unless,Flag,Pass}|Ps], Opts) ->
+ select_cond(Flag, false, Pass, Ps, Opts);
+select_passes([{_,Fun}=P|Ps], Opts) when is_function(Fun) ->
+ [P|select_passes(Ps, Opts)];
+select_passes([{_,Test,Fun}=P|Ps], Opts) when is_function(Test),
+ is_function(Fun) ->
+ [P|select_passes(Ps, Opts)];
+select_passes([], _Opts) ->
+ [];
+select_passes([List|Ps], Opts) when is_list(List) ->
+ case select_passes(List, Opts) of
+ [] -> select_passes(Ps, Opts);
+ Nested ->
+ case last(Nested) of
+ {listing,_Fun} -> Nested;
+ _Other -> Nested ++ select_passes(Ps, Opts)
+ end
+ end.
+
+select_cond(Flag, ShouldBe, Pass, Ps, Opts) ->
+ ShouldNotBe = not ShouldBe,
+ case member(Flag, Opts) of
+ ShouldBe -> select_passes([Pass|Ps], Opts);
+ ShouldNotBe -> select_passes(Ps, Opts)
+ end.
+
+%% The standard passes (almost) always run.
+
+standard_passes() ->
+ [?pass(transform_module),
+ {iff,'dpp',{listing,"pp"}},
+ ?pass(lint_module),
+ {iff,'P',{src_listing,"P"}},
+ {iff,'to_pp',{done,"P"}},
+
+ {iff,'dabstr',{listing,"abstr"}},
+ {iff,debug_info,?pass(save_abstract_code)},
+
+ ?pass(expand_module),
+ {iff,'dexp',{listing,"expand"}},
+ {iff,'E',{src_listing,"E"}},
+ {iff,'to_exp',{done,"E"}},
+
+ %% Conversion to Core Erlang.
+ ?pass(core_module),
+ {iff,'dcore',{listing,"core"}},
+ {iff,'to_core0',{done,"core"}}
+ | core_passes()].
+
+core_passes() ->
+ %% Optimization and transforms of Core Erlang code.
+ [{unless,no_copt,
+ [{core_old_inliner,fun test_old_inliner/1,fun core_old_inliner/1},
+ ?pass(core_fold_module),
+ {core_inline_module,fun test_core_inliner/1,fun core_inline_module/1},
+ {core_fold_after_inline,fun test_core_inliner/1,fun core_fold_module/1},
+ ?pass(core_transforms)]},
+ {iff,dcopt,{listing,"copt"}},
+ {iff,'to_core',{done,"core"}}
+ | kernel_passes()].
+
+kernel_passes() ->
+ %% Destructive setelement/3 optimization and core lint.
+ [?pass(core_dsetel_module),
+ {iff,clint,?pass(core_lint_module)},
+ {iff,core,?pass(save_core_code)},
+
+ %% Kernel Erlang and code generation.
+ ?pass(kernel_module),
+ {iff,dkern,{listing,"kernel"}},
+ {iff,'to_kernel',{done,"kernel"}},
+ {pass,v3_life},
+ {iff,dlife,{listing,"life"}},
+ {pass,v3_codegen},
+ {iff,dcg,{listing,"codegen"}}
+ | asm_passes()].
+
+asm_passes() ->
+ %% Assembly level optimisations.
+ [{unless,no_postopt,
+ [{pass,beam_block},
+ {iff,dblk,{listing,"block"}},
+ {unless,no_bopt,{pass,beam_bool}},
+ {iff,dbool,{listing,"bool"}},
+ {unless,no_topt,{pass,beam_type}},
+ {iff,dtype,{listing,"type"}},
+ {pass,beam_dead}, %Must always run since it splits blocks.
+ {iff,ddead,{listing,"dead"}},
+ {unless,no_jopt,{pass,beam_jump}},
+ {iff,djmp,{listing,"jump"}},
+ {pass,beam_clean},
+ {iff,dclean,{listing,"clean"}},
+ {pass,beam_flatten}]},
+
+ %% If post optimizations are turned off, we still coalesce
+ %% adjacent labels and remove unused labels to keep the
+ %% HiPE compiler happy.
+ {iff,no_postopt,
+ [?pass(beam_unused_labels),
+ {pass,beam_clean}]},
+
+ {iff,dopt,{listing,"optimize"}},
+ {iff,'S',{listing,"S"}},
+ {iff,'to_asm',{done,"S"}},
+
+ {pass,beam_validator},
+ ?pass(beam_asm)
+ | binary_passes()].
+
+binary_passes() ->
+ [{native_compile,fun test_native/1,fun native_compile/1},
+ {unless,binary,?pass(save_binary)}].
+
+%%%
+%%% Compiler passes.
+%%%
+
+%% Remove the target file so we don't have an old one if the compilation fail.
+remove_file(St) ->
+ file:delete(St#compile.ofile),
+ {ok,St}.
+
+-record(asm_module, {module,
+ exports,
+ labels,
+ functions=[],
+ cfun,
+ code,
+ attributes=[]}).
+
+preprocess_asm_forms(Forms) ->
+ R = #asm_module{},
+ R1 = collect_asm(Forms, R),
+ {R1#asm_module.module,
+ {R1#asm_module.module,
+ R1#asm_module.exports,
+ R1#asm_module.attributes,
+ R1#asm_module.functions,
+ R1#asm_module.labels}}.
+
+collect_asm([], R) ->
+ case R#asm_module.cfun of
+ undefined ->
+ R;
+ {A,B,C} ->
+ R#asm_module{functions=R#asm_module.functions++
+ [{function,A,B,C,R#asm_module.code}]}
+ end;
+collect_asm([{module,M} | Rest], R) ->
+ collect_asm(Rest, R#asm_module{module=M});
+collect_asm([{exports,M} | Rest], R) ->
+ collect_asm(Rest, R#asm_module{exports=M});
+collect_asm([{labels,M} | Rest], R) ->
+ collect_asm(Rest, R#asm_module{labels=M});
+collect_asm([{function,A,B,C} | Rest], R) ->
+ R1 = case R#asm_module.cfun of
+ undefined ->
+ R;
+ {A0,B0,C0} ->
+ R#asm_module{functions=R#asm_module.functions++
+ [{function,A0,B0,C0,R#asm_module.code}]}
+ end,
+ collect_asm(Rest, R1#asm_module{cfun={A,B,C}, code=[]});
+collect_asm([{attributes, Attr} | Rest], R) ->
+ collect_asm(Rest, R#asm_module{attributes=Attr});
+collect_asm([X | Rest], R) ->
+ collect_asm(Rest, R#asm_module{code=R#asm_module.code++[X]}).
+
+beam_consult_asm(St) ->
+ case file:consult(St#compile.ifile) of
+ {ok, Forms0} ->
+ {Module, Forms} = preprocess_asm_forms(Forms0),
+ {ok,St#compile{module=Module, code=Forms}};
+ {error,E} ->
+ Es = [{St#compile.ifile,[{none,?MODULE,{open,E}}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end.
+
+read_beam_file(St) ->
+ case file:read_file(St#compile.ifile) of
+ {ok,Beam} ->
+ Infile = St#compile.ifile,
+ case is_too_old(Infile) of
+ true ->
+ {ok,St#compile{module=none,code=none}};
+ false ->
+ Mod0 = filename:rootname(filename:basename(Infile)),
+ Mod = list_to_atom(Mod0),
+ {ok,St#compile{module=Mod,code=Beam,ofile=Infile}}
+ end;
+ {error,E} ->
+ Es = [{St#compile.ifile,[{none,?MODULE,{open,E}}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end.
+
+is_too_old(BeamFile) ->
+ case beam_lib:chunks(BeamFile, ["CInf"]) of
+ {ok,{_,[{"CInf",Term0}]}} ->
+ Term = binary_to_term(Term0),
+ Opts = proplists:get_value(options, Term, []),
+ lists:member(no_new_funs, Opts);
+ _ -> false
+ end.
+
+parse_module(St) ->
+ Opts = St#compile.options,
+ Cwd = ".",
+ IncludePath = [Cwd, St#compile.dir|inc_paths(Opts)],
+ Tab = ets:new(compiler__tab, [protected,named_table]),
+ ets:insert(Tab, {compiler_options,Opts}),
+ R = epp:parse_file(St#compile.ifile, IncludePath, pre_defs(Opts)),
+ ets:delete(Tab),
+ case R of
+ {ok,Forms} ->
+ {ok,St#compile{code=Forms}};
+ {error,E} ->
+ Es = [{St#compile.ifile,[{none,?MODULE,{epp,E}}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end.
+
+parse_core(St) ->
+ case file:read_file(St#compile.ifile) of
+ {ok,Bin} ->
+ case core_scan:string(binary_to_list(Bin)) of
+ {ok,Toks,_} ->
+ case core_parse:parse(Toks) of
+ {ok,Mod} ->
+ Name = (Mod#c_module.name)#c_atom.val,
+ {ok,St#compile{module=Name,code=Mod}};
+ {error,E} ->
+ Es = [{St#compile.ifile,[E]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end;
+ {error,E,_} ->
+ Es = [{St#compile.ifile,[E]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end;
+ {error,E} ->
+ Es = [{St#compile.ifile,[{none,compile,{open,E}}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end.
+
+compile_options([{attribute,_L,compile,C}|Fs]) when is_list(C) ->
+ C ++ compile_options(Fs);
+compile_options([{attribute,_L,compile,C}|Fs]) ->
+ [C|compile_options(Fs)];
+compile_options([_F|Fs]) -> compile_options(Fs);
+compile_options([]) -> [].
+
+transforms(Os) -> [ M || {parse_transform,M} <- Os ].
+
+transform_module(St) ->
+ %% Extract compile options from code into options field.
+ Ts = transforms(St#compile.options ++ compile_options(St#compile.code)),
+ foldl_transform(St, Ts).
+
+foldl_transform(St, [T|Ts]) ->
+ Name = "transform " ++ atom_to_list(T),
+ Fun = fun(S) -> T:parse_transform(S#compile.code, S#compile.options) end,
+ Run = case member(time, St#compile.options) of
+ true -> fun run_tc/2;
+ false -> fun({_Name,F}, S) -> catch F(S) end
+ end,
+ case Run({Name, Fun}, St) of
+ {error,Es,Ws} ->
+ {error,St#compile{warnings=St#compile.warnings ++ Ws,
+ errors=St#compile.errors ++ Es}};
+ {'EXIT',R} ->
+ Es = [{St#compile.ifile,[{none,compile,{parse_transform,T,R}}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}};
+ Forms ->
+ foldl_transform(St#compile{code=Forms}, Ts)
+ end;
+foldl_transform(St, []) -> {ok,St}.
+
+get_core_transforms(Opts) -> [M || {core_transform,M} <- Opts].
+
+core_transforms(St) ->
+ %% The options field holds the complete list of options at this
+
+ Ts = get_core_transforms(St#compile.options),
+ foldl_core_transforms(St, Ts).
+
+foldl_core_transforms(St, [T|Ts]) ->
+ Name = "core transform " ++ atom_to_list(T),
+ Fun = fun(S) -> T:core_transform(S#compile.code, S#compile.options) end,
+ Run = case member(time, St#compile.options) of
+ true -> fun run_tc/2;
+ false -> fun({_Name,F}, S) -> catch F(S) end
+ end,
+ case Run({Name, Fun}, St) of
+ {'EXIT',R} ->
+ Es = [{St#compile.ifile,[{none,compile,{core_transform,T,R}}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}};
+ Forms ->
+ foldl_core_transforms(St#compile{code=Forms}, Ts)
+ end;
+foldl_core_transforms(St, []) -> {ok,St}.
+
+%%% Fetches the module name from a list of forms. The module attribute must
+%%% be present.
+get_module([{attribute,_,module,{M,_As}} | _]) -> M;
+get_module([{attribute,_,module,M} | _]) -> M;
+get_module([_ | Rest]) ->
+ get_module(Rest).
+
+%%% A #compile state is returned, where St.base has been filled in
+%%% with the module name from Forms, as a string, in case it wasn't
+%%% set in St (i.e., it was "").
+add_default_base(St, Forms) ->
+ F = St#compile.filename,
+ case F of
+ "" ->
+ M = get_module(Forms),
+ St#compile{base = atom_to_list(M)};
+ _ ->
+ St
+ end.
+
+lint_module(St) ->
+ case erl_lint:module(St#compile.code,
+ St#compile.ifile, St#compile.options) of
+ {ok,Ws} ->
+ %% Insert name of module as base name, if needed. This is
+ %% for compile:forms to work with listing files.
+ St1 = add_default_base(St, St#compile.code),
+ {ok,St1#compile{warnings=St1#compile.warnings ++ Ws}};
+ {error,Es,Ws} ->
+ {error,St#compile{warnings=St#compile.warnings ++ Ws,
+ errors=St#compile.errors ++ Es}}
+ end.
+
+core_lint_module(St) ->
+ case core_lint:module(St#compile.code, St#compile.options) of
+ {ok,Ws} ->
+ {ok,St#compile{warnings=St#compile.warnings ++ Ws}};
+ {error,Es,Ws} ->
+ {error,St#compile{warnings=St#compile.warnings ++ Ws,
+ errors=St#compile.errors ++ Es}}
+ end.
+
+%% expand_module(State) -> State'
+%% Do the common preprocessing of the input forms.
+
+expand_module(#compile{code=Code,options=Opts0}=St0) ->
+ {Mod,Exp,Forms,Opts1} = sys_pre_expand:module(Code, Opts0),
+ Opts2 = expand_opts(Opts1),
+ Opts = filter_opts(Opts2),
+ {ok,St0#compile{module=Mod,options=Opts,code={Mod,Exp,Forms}}}.
+
+core_module(#compile{code=Code0,options=Opts,ifile=File}=St) ->
+ {ok,Code,Ws} = v3_core:module(Code0, Opts),
+ {ok,St#compile{code=Code,warnings=St#compile.warnings ++ [{File,Ws}]}}.
+
+core_fold_module(#compile{code=Code0,options=Opts,ifile=File}=St) ->
+ {ok,Code,Ws} = sys_core_fold:module(Code0, Opts),
+ {ok,St#compile{code=Code,warnings=St#compile.warnings ++ [{File,Ws}]}}.
+
+test_old_inliner(#compile{options=Opts}) ->
+ %% The point of this test is to avoid loading the old inliner
+ %% if we know that it will not be used.
+ case any(fun(no_inline) -> true;
+ (_) -> false
+ end, Opts) of
+ true -> false;
+ false ->
+ any(fun({inline,_}) -> true;
+ (_) -> false
+ end, Opts)
+ end.
+
+test_core_inliner(#compile{options=Opts}) ->
+ case any(fun(no_inline) -> true;
+ (_) -> false
+ end, Opts) of
+ true -> false;
+ false ->
+ any(fun(inline) -> true;
+ (_) -> false
+ end, Opts)
+ end.
+
+core_old_inliner(#compile{code=Code0,options=Opts}=St) ->
+ case catch sys_core_inline:module(Code0, Opts) of
+ {ok,Code} ->
+ {ok,St#compile{code=Code}};
+ {error,Es} ->
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end.
+
+core_inline_module(#compile{code=Code0,options=Opts}=St) ->
+ Code = cerl_inline:core_transform(Code0, Opts),
+ {ok,St#compile{code=Code}}.
+
+core_dsetel_module(#compile{code=Code0,options=Opts}=St) ->
+ {ok,Code} = sys_core_dsetel:module(Code0, Opts),
+ {ok,St#compile{code=Code}}.
+
+kernel_module(#compile{code=Code0,options=Opts,ifile=File}=St) ->
+ {ok,Code,Ws} = v3_kernel:module(Code0, Opts),
+ {ok,St#compile{code=Code,warnings=St#compile.warnings ++ [{File,Ws}]}}.
+
+save_abstract_code(St) ->
+ {ok,St#compile{abstract_code=abstract_code(St)}}.
+
+abstract_code(#compile{code=Code}) ->
+ Abstr = {raw_abstract_v1,Code},
+ case catch erlang:term_to_binary(Abstr, [compressed]) of
+ {'EXIT',_} -> term_to_binary(Abstr);
+ Other -> Other
+ end.
+
+save_core_code(St) ->
+ {ok,St#compile{core_code=cerl:from_records(St#compile.code)}}.
+
+beam_unused_labels(#compile{code=Code0}=St) ->
+ Code = beam_jump:module_labels(Code0),
+ {ok,St#compile{code=Code}}.
+
+beam_asm(#compile{ifile=File,code=Code0,abstract_code=Abst,options=Opts0}=St) ->
+ Source = filename:absname(File),
+ Opts = filter(fun is_informative_option/1, Opts0),
+ case beam_asm:module(Code0, Abst, Source, Opts) of
+ {ok,Code} -> {ok,St#compile{code=Code,abstract_code=[]}};
+ {error,Es} -> {error,St#compile{errors=St#compile.errors ++ Es}}
+ end.
+
+test_native(#compile{options=Opts}) ->
+ %% This test must be made late, because the r7 or no_new_funs options
+ %% will turn off the native option.
+ member(native, Opts).
+
+native_compile(#compile{code=none}=St) -> {ok,St};
+native_compile(St) ->
+ case erlang:system_info(hipe_architecture) of
+ undefined ->
+ Ws = [{St#compile.ifile,[{none,compile,no_native_support}]}],
+ {ok,St#compile{warnings=St#compile.warnings ++ Ws}};
+ _ ->
+ native_compile_1(St)
+ end.
+
+native_compile_1(St) ->
+ Opts0 = [no_new_binaries|St#compile.options],
+ IgnoreErrors = member(ignore_native_errors, Opts0),
+ Opts = case keysearch(hipe, 1, Opts0) of
+ {value,{hipe,L}} when list(L) -> L;
+ {value,{hipe,X}} -> [X];
+ _ -> []
+ end,
+ case catch hipe:compile(St#compile.module,
+ St#compile.core_code,
+ St#compile.code,
+ Opts) of
+ {ok, {Type,Bin}} when binary(Bin) ->
+ {ok, embed_native_code(St, {Type,Bin})};
+ {error, R} ->
+ case IgnoreErrors of
+ true ->
+ Ws = [{St#compile.ifile,[{none,?MODULE,{native,R}}]}],
+ {ok,St#compile{warnings=St#compile.warnings ++ Ws}};
+ false ->
+ Es = [{St#compile.ifile,[{none,?MODULE,{native,R}}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end;
+ {'EXIT',R} ->
+ case IgnoreErrors of
+ true ->
+ Ws = [{St#compile.ifile,[{none,?MODULE,{native_crash,R}}]}],
+ {ok,St#compile{warnings=St#compile.warnings ++ Ws}};
+ false ->
+ exit(R)
+ end
+ end.
+
+embed_native_code(St, {Architecture,NativeCode}) ->
+ {ok, _, Chunks0} = beam_lib:all_chunks(St#compile.code),
+ ChunkName = hipe_unified_loader:chunk_name(Architecture),
+ Chunks1 = lists:keydelete(ChunkName, 1, Chunks0),
+ Chunks = Chunks1 ++ [{ChunkName,NativeCode}],
+ {ok, BeamPlusNative} = beam_lib:build_module(Chunks),
+ St#compile{code=BeamPlusNative}.
+
+%% Returns true if the option is informative and therefore should be included
+%% in the option list of the compiled module.
+
+is_informative_option(beam) -> false;
+is_informative_option(report_warnings) -> false;
+is_informative_option(report_errors) -> false;
+is_informative_option(binary) -> false;
+is_informative_option(verbose) -> false;
+is_informative_option(_) -> true.
+
+save_binary(#compile{code=none}=St) -> {ok,St};
+save_binary(St) ->
+ Tfile = tmpfile(St#compile.ofile), %Temp working file
+ case write_binary(Tfile, St#compile.code, St) of
+ ok ->
+ case file:rename(Tfile, St#compile.ofile) of
+ ok ->
+ {ok,St};
+ {error,_Error} ->
+ file:delete(Tfile),
+ Es = [{St#compile.ofile,[{none,?MODULE,{rename,Tfile}}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end;
+ {error,_Error} ->
+ Es = [{Tfile,[{compile,write_error}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end.
+
+write_binary(Name, Bin, St) ->
+ Opts = case member(compressed, St#compile.options) of
+ true -> [compressed];
+ false -> []
+ end,
+ case file:write_file(Name, Bin, Opts) of
+ ok -> ok;
+ {error,_}=Error -> Error
+ end.
+
+%% report_errors(State) -> ok
+%% report_warnings(State) -> ok
+
+report_errors(St) ->
+ case member(report_errors, St#compile.options) of
+ true ->
+ foreach(fun ({{F,_L},Eds}) -> list_errors(F, Eds);
+ ({F,Eds}) -> list_errors(F, Eds) end,
+ St#compile.errors);
+ false -> ok
+ end.
+
+report_warnings(#compile{options=Opts,warnings=Ws0}) ->
+ case member(report_warnings, Opts) of
+ true ->
+ Ws1 = flatmap(fun({{F,_L},Eds}) -> format_message(F, Eds);
+ ({F,Eds}) -> format_message(F, Eds) end,
+ Ws0),
+ Ws = ordsets:from_list(Ws1),
+ foreach(fun({_,Str}) -> io:put_chars(Str) end, Ws);
+ false -> ok
+ end.
+
+format_message(F, [{Line,Mod,E}|Es]) ->
+ M = {Line,io_lib:format("~s:~w: Warning: ~s\n", [F,Line,Mod:format_error(E)])},
+ [M|format_message(F, Es)];
+format_message(F, [{Mod,E}|Es]) ->
+ M = {none,io_lib:format("~s: Warning: ~s\n", [F,Mod:format_error(E)])},
+ [M|format_message(F, Es)];
+format_message(_, []) -> [].
+
+%% list_errors(File, ErrorDescriptors) -> ok
+
+list_errors(F, [{Line,Mod,E}|Es]) ->
+ io:fwrite("~s:~w: ~s\n", [F,Line,Mod:format_error(E)]),
+ list_errors(F, Es);
+list_errors(F, [{Mod,E}|Es]) ->
+ io:fwrite("~s: ~s\n", [F,Mod:format_error(E)]),
+ list_errors(F, Es);
+list_errors(_F, []) -> ok.
+
+%% erlfile(Dir, Base) -> ErlFile
+%% outfile(Base, Extension, Options) -> OutputFile
+%% objfile(Base, Target, Options) -> ObjFile
+%% tmpfile(ObjFile) -> TmpFile
+%% Work out the correct input and output file names.
+
+iofile(File) when atom(File) ->
+ iofile(atom_to_list(File));
+iofile(File) ->
+ {filename:dirname(File), filename:basename(File, ".erl")}.
+
+erlfile(Dir, Base, Suffix) ->
+ filename:join(Dir, Base++Suffix).
+
+outfile(Base, Ext, Opts) when atom(Ext) ->
+ outfile(Base, atom_to_list(Ext), Opts);
+outfile(Base, Ext, Opts) ->
+ Obase = case keysearch(outdir, 1, Opts) of
+ {value, {outdir, Odir}} -> filename:join(Odir, Base);
+ _Other -> Base % Not found or bad format
+ end,
+ Obase++"."++Ext.
+
+objfile(Base, St) ->
+ outfile(Base, "beam", St#compile.options).
+
+tmpfile(Ofile) ->
+ reverse([$#|tl(reverse(Ofile))]).
+
+%% pre_defs(Options)
+%% inc_paths(Options)
+%% Extract the predefined macros and include paths from the option list.
+
+pre_defs([{d,M,V}|Opts]) ->
+ [{M,V}|pre_defs(Opts)];
+pre_defs([{d,M}|Opts]) ->
+ [M|pre_defs(Opts)];
+pre_defs([_|Opts]) ->
+ pre_defs(Opts);
+pre_defs([]) -> [].
+
+inc_paths(Opts) ->
+ [ P || {i,P} <- Opts, list(P) ].
+
+src_listing(Ext, St) ->
+ listing(fun (Lf, {_Mod,_Exp,Fs}) -> do_src_listing(Lf, Fs);
+ (Lf, Fs) -> do_src_listing(Lf, Fs) end,
+ Ext, St).
+
+do_src_listing(Lf, Fs) ->
+ foreach(fun (F) -> io:put_chars(Lf, [erl_pp:form(F),"\n"]) end,
+ Fs).
+
+listing(Ext, St) ->
+ listing(fun(Lf, Fs) -> beam_listing:module(Lf, Fs) end, Ext, St).
+
+listing(LFun, Ext, St) ->
+ Lfile = outfile(St#compile.base, Ext, St#compile.options),
+ case file:open(Lfile, [write,delayed_write]) of
+ {ok,Lf} ->
+ LFun(Lf, St#compile.code),
+ ok = file:close(Lf),
+ {ok,St};
+ {error,_Error} ->
+ Es = [{Lfile,[{none,compile,write_error}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end.
+
+options() ->
+ help(standard_passes()).
+
+help([{iff,Flag,{src_listing,Ext}}|T]) ->
+ io:fwrite("~p - Generate .~s source listing file\n", [Flag,Ext]),
+ help(T);
+help([{iff,Flag,{listing,Ext}}|T]) ->
+ io:fwrite("~p - Generate .~s file\n", [Flag,Ext]),
+ help(T);
+help([{iff,Flag,{Name,Fun}}|T]) when function(Fun) ->
+ io:fwrite("~p - Run ~s\n", [Flag,Name]),
+ help(T);
+help([{iff,_Flag,Action}|T]) ->
+ help(Action),
+ help(T);
+help([{unless,Flag,{pass,Pass}}|T]) ->
+ io:fwrite("~p - Skip the ~s pass\n", [Flag,Pass]),
+ help(T);
+help([{unless,no_postopt=Flag,List}|T]) when list(List) ->
+ %% Hard-coded knowledgde here.
+ io:fwrite("~p - Skip all post optimisation\n", [Flag]),
+ help(List),
+ help(T);
+help([{unless,_Flag,Action}|T]) ->
+ help(Action),
+ help(T);
+help([_|T]) ->
+ help(T);
+help(_) ->
+ ok.
+
+
+%% compile(AbsFileName, Outfilename, Options)
+%% Compile entry point for erl_compile.
+
+compile(File0, _OutFile, Options) ->
+ File = shorten_filename(File0),
+ case file(File, make_erl_options(Options)) of
+ {ok,_Mod} -> ok;
+ Other -> Other
+ end.
+
+compile_beam(File0, _OutFile, Opts) ->
+ File = shorten_filename(File0),
+ case file(File, [from_beam|make_erl_options(Opts)]) of
+ {ok,_Mod} -> ok;
+ Other -> Other
+ end.
+
+compile_asm(File0, _OutFile, Opts) ->
+ File = shorten_filename(File0),
+ case file(File, [asm|make_erl_options(Opts)]) of
+ {ok,_Mod} -> ok;
+ Other -> Other
+ end.
+
+compile_core(File0, _OutFile, Opts) ->
+ File = shorten_filename(File0),
+ case file(File, [from_core|make_erl_options(Opts)]) of
+ {ok,_Mod} -> ok;
+ Other -> Other
+ end.
+
+shorten_filename(Name0) ->
+ {ok,Cwd} = file:get_cwd(),
+ case lists:prefix(Cwd, Name0) of
+ false -> Name0;
+ true ->
+ Name = case lists:nthtail(length(Cwd), Name0) of
+ "/"++N -> N;
+ N -> N
+ end,
+ Name
+ end.
+
+%% Converts generic compiler options to specific options.
+
+make_erl_options(Opts) ->
+
+ %% This way of extracting will work even if the record passed
+ %% has more fields than known during compilation.
+
+ Includes = Opts#options.includes,
+ Defines = Opts#options.defines,
+ Outdir = Opts#options.outdir,
+ Warning = Opts#options.warning,
+ Verbose = Opts#options.verbose,
+ Specific = Opts#options.specific,
+ OutputType = Opts#options.output_type,
+ Cwd = Opts#options.cwd,
+
+ Options =
+ case Verbose of
+ true -> [verbose];
+ false -> []
+ end ++
+ case Warning of
+ 0 -> [];
+ _ -> [report_warnings]
+ end ++
+ map(
+ fun ({Name, Value}) ->
+ {d, Name, Value};
+ (Name) ->
+ {d, Name}
+ end,
+ Defines) ++
+ case OutputType of
+ undefined -> [];
+ jam -> [jam];
+ beam -> [beam];
+ native -> [native]
+ end,
+
+ Options++[report_errors, {cwd, Cwd}, {outdir, Outdir}|
+ map(fun(Dir) -> {i, Dir} end, Includes)]++Specific.
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_lib.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_lib.erl
new file mode 100644
index 0000000000..1fe45d5308
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_lib.erl
@@ -0,0 +1,509 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: core_lib.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose: Core Erlang abstract syntax functions.
+
+-module(core_lib).
+
+-export([get_anno/1,set_anno/2]).
+-export([is_atomic/1,is_literal/1,is_literal_list/1,
+ is_simple/1,is_simple_list/1,is_simple_top/1]).
+-export([literal_value/1,make_literal/1]).
+-export([make_values/1]).
+-export([map/2, fold/3, mapfold/3]).
+-export([is_var_used/2]).
+
+%% -compile([export_all]).
+
+-include("core_parse.hrl").
+
+%% get_anno(Core) -> Anno.
+%% set_anno(Core, Anno) -> Core.
+%% Generic get/set annotation.
+
+get_anno(C) -> element(2, C).
+set_anno(C, A) -> setelement(2, C, A).
+
+%% is_atomic(Expr) -> true | false.
+
+is_atomic(#c_char{}) -> true;
+is_atomic(#c_int{}) -> true;
+is_atomic(#c_float{}) -> true;
+is_atomic(#c_atom{}) -> true;
+is_atomic(#c_string{}) -> true;
+is_atomic(#c_nil{}) -> true;
+is_atomic(#c_fname{}) -> true;
+is_atomic(_) -> false.
+
+%% is_literal(Expr) -> true | false.
+
+is_literal(#c_cons{hd=H,tl=T}) ->
+ case is_literal(H) of
+ true -> is_literal(T);
+ false -> false
+ end;
+is_literal(#c_tuple{es=Es}) -> is_literal_list(Es);
+is_literal(#c_binary{segments=Es}) -> is_lit_bin(Es);
+is_literal(E) -> is_atomic(E).
+
+is_literal_list(Es) -> lists:all(fun is_literal/1, Es).
+
+is_lit_bin(Es) ->
+ lists:all(fun (#c_bitstr{val=E,size=S}) ->
+ is_literal(E) and is_literal(S)
+ end, Es).
+
+%% is_simple(Expr) -> true | false.
+
+is_simple(#c_var{}) -> true;
+is_simple(#c_cons{hd=H,tl=T}) ->
+ case is_simple(H) of
+ true -> is_simple(T);
+ false -> false
+ end;
+is_simple(#c_tuple{es=Es}) -> is_simple_list(Es);
+is_simple(#c_binary{segments=Es}) -> is_simp_bin(Es);
+is_simple(E) -> is_atomic(E).
+
+is_simple_list(Es) -> lists:all(fun is_simple/1, Es).
+
+is_simp_bin(Es) ->
+ lists:all(fun (#c_bitstr{val=E,size=S}) ->
+ is_simple(E) and is_simple(S)
+ end, Es).
+
+%% is_simple_top(Expr) -> true | false.
+%% Only check if the top-level is a simple.
+
+is_simple_top(#c_var{}) -> true;
+is_simple_top(#c_cons{}) -> true;
+is_simple_top(#c_tuple{}) -> true;
+is_simple_top(#c_binary{}) -> true;
+is_simple_top(E) -> is_atomic(E).
+
+%% literal_value(LitExpr) -> Value.
+%% Return the value of LitExpr.
+
+literal_value(#c_char{val=C}) -> C;
+literal_value(#c_int{val=I}) -> I;
+literal_value(#c_float{val=F}) -> F;
+literal_value(#c_atom{val=A}) -> A;
+literal_value(#c_string{val=S}) -> S;
+literal_value(#c_nil{}) -> [];
+literal_value(#c_cons{hd=H,tl=T}) ->
+ [literal_value(H)|literal_value(T)];
+literal_value(#c_tuple{es=Es}) ->
+ list_to_tuple(literal_value_list(Es)).
+
+literal_value_list(Vals) -> lists:map(fun literal_value/1, Vals).
+
+%% make_literal(Value) -> LitExpr.
+%% Make a literal expression from an Erlang value.
+
+make_literal(I) when integer(I) -> #c_int{val=I};
+make_literal(F) when float(F) -> #c_float{val=F};
+make_literal(A) when atom(A) -> #c_atom{val=A};
+make_literal([]) -> #c_nil{};
+make_literal([H|T]) ->
+ #c_cons{hd=make_literal(H),tl=make_literal(T)};
+make_literal(T) when tuple(T) ->
+ #c_tuple{es=make_literal_list(tuple_to_list(T))}.
+
+make_literal_list(Vals) -> lists:map(fun make_literal/1, Vals).
+
+%% make_values([CoreExpr] | CoreExpr) -> #c_values{} | CoreExpr.
+%% Make a suitable values structure, expr or values, depending on
+%% Expr.
+
+make_values([E]) -> E;
+make_values([H|_]=Es) -> #c_values{anno=get_anno(H),es=Es};
+make_values([]) -> #c_values{es=[]};
+make_values(E) -> E.
+
+%% map(MapFun, CoreExpr) -> CoreExpr.
+%% This function traverses the core parse format, at each level
+%% applying the submited argument function, assumed to do the real
+%% work.
+%%
+%% The "eager" style, where each component of a construct are
+%% descended to before the construct itself, admits that some
+%% companion functions (the F:s) may be made simpler, since it may be
+%% safely assumed that no lower illegal instanced will be
+%% created/uncovered by actions on the current level.
+
+map(F, #c_tuple{es=Es}=R) ->
+ F(R#c_tuple{es=map_list(F, Es)});
+map(F, #c_cons{hd=Hd, tl=Tl}=R) ->
+ F(R#c_cons{hd=map(F, Hd),
+ tl=map(F, Tl)});
+map(F, #c_values{es=Es}=R) ->
+ F(R#c_values{es=map_list(F, Es)});
+
+map(F, #c_alias{var=Var, pat=Pat}=R) ->
+ F(R#c_alias{var=map(F, Var),
+ pat=map(F, Pat)});
+
+map(F, #c_module{defs=Defs}=R) ->
+ F(R#c_module{defs=map_list(F, Defs)});
+map(F, #c_def{val=Val}=R) ->
+ F(R#c_def{val=map(F, Val)});
+
+map(F, #c_fun{vars=Vars, body=Body}=R) ->
+ F(R#c_fun{vars=map_list(F, Vars),
+ body=map(F, Body)});
+map(F, #c_let{vars=Vs, arg=Arg, body=Body}=R) ->
+ F(R#c_let{vars=map_list(F, Vs),
+ arg=map(F, Arg),
+ body=map(F, Body)});
+map(F, #c_letrec{defs=Fs,body=Body}=R) ->
+ F(R#c_letrec{defs=map_list(F, Fs),
+ body=map(F, Body)});
+map(F, #c_seq{arg=Arg, body=Body}=R) ->
+ F(R#c_seq{arg=map(F, Arg),
+ body=map(F, Body)});
+map(F, #c_case{arg=Arg, clauses=Clauses}=R) ->
+ F(R#c_case{arg=map(F, Arg),
+ clauses=map_list(F, Clauses)});
+map(F, #c_clause{pats=Ps, guard=Guard, body=Body}=R) ->
+ F(R#c_clause{pats=map_list(F, Ps),
+ guard=map(F, Guard),
+ body=map(F, Body)});
+map(F, #c_receive{clauses=Cls, timeout=Tout, action=Act}=R) ->
+ F(R#c_receive{clauses=map_list(F, Cls),
+ timeout=map(F, Tout),
+ action=map(F, Act)});
+map(F, #c_apply{op=Op,args=Args}=R) ->
+ F(R#c_apply{op=map(F, Op),
+ args=map_list(F, Args)});
+map(F, #c_call{module=M,name=N,args=Args}=R) ->
+ F(R#c_call{module=map(F, M),
+ name=map(F, N),
+ args=map_list(F, Args)});
+map(F, #c_primop{name=N,args=Args}=R) ->
+ F(R#c_primop{name=map(F, N),
+ args=map_list(F, Args)});
+map(F, #c_try{arg=Expr,vars=Vars,body=Body,evars=Evars,handler=Handler}=R) ->
+ F(R#c_try{arg=map(F, Expr),
+ vars=map(F, Vars),
+ body=map(F, Body),
+ evars=map(F, Evars),
+ handler=map(F, Handler)});
+map(F, #c_catch{body=Body}=R) ->
+ F(R#c_catch{body=map(F, Body)});
+map(F, T) -> F(T). %Atomic nodes.
+
+map_list(F, L) -> lists:map(fun (E) -> map(F, E) end, L).
+
+%% fold(FoldFun, Accumulator, CoreExpr) -> Accumulator.
+%% This function traverses the core parse format, at each level
+%% applying the submited argument function, assumed to do the real
+%% work, and keeping the accumulated result in the A (accumulator)
+%% argument.
+
+fold(F, Acc, #c_tuple{es=Es}=R) ->
+ F(R, fold_list(F, Acc, Es));
+fold(F, Acc, #c_cons{hd=Hd, tl=Tl}=R) ->
+ F(R, fold(F, fold(F, Acc, Hd), Tl));
+fold(F, Acc, #c_values{es=Es}=R) ->
+ F(R, fold_list(F, Acc, Es));
+
+fold(F, Acc, #c_alias{pat=P,var=V}=R) ->
+ F(R, fold(F, fold(F, Acc, P), V));
+
+fold(F, Acc, #c_module{defs=Defs}=R) ->
+ F(R, fold_list(F, Acc, Defs));
+fold(F, Acc, #c_def{val=Val}=R) ->
+ F(R, fold(F, Acc, Val));
+
+fold(F, Acc, #c_fun{vars=Vars, body=Body}=R) ->
+ F(R, fold(F, fold_list(F, Acc, Vars), Body));
+fold(F, Acc, #c_let{vars=Vs, arg=Arg, body=Body}=R) ->
+ F(R, fold(F, fold(F, fold_list(F, Acc, Vs), Arg), Body));
+fold(F, Acc, #c_letrec{defs=Fs,body=Body}=R) ->
+ F(R, fold(F, fold_list(F, Acc, Fs), Body));
+fold(F, Acc, #c_seq{arg=Arg, body=Body}=R) ->
+ F(R, fold(F, fold(F, Acc, Arg), Body));
+fold(F, Acc, #c_case{arg=Arg, clauses=Clauses}=R) ->
+ F(R, fold_list(F, fold(F, Acc, Arg), Clauses));
+fold(F, Acc, #c_clause{pats=Ps,guard=G,body=B}=R) ->
+ F(R, fold(F, fold(F, fold_list(F, Acc, Ps), G), B));
+fold(F, Acc, #c_receive{clauses=Cl, timeout=Ti, action=Ac}=R) ->
+ F(R, fold_list(F, fold(F, fold(F, Acc, Ac), Ti), Cl));
+fold(F, Acc, #c_apply{op=Op, args=Args}=R) ->
+ F(R, fold_list(F, fold(F, Acc, Op), Args));
+fold(F, Acc, #c_call{module=Mod,name=Name,args=Args}=R) ->
+ F(R, fold_list(F, fold(F, fold(F, Acc, Mod), Name), Args));
+fold(F, Acc, #c_primop{name=Name,args=Args}=R) ->
+ F(R, fold_list(F, fold(F, Acc, Name), Args));
+fold(F, Acc, #c_try{arg=E,vars=Vs,body=Body,evars=Evs,handler=H}=R) ->
+ NewB = fold(F, fold_list(F, fold(F, Acc, E), Vs), Body),
+ F(R, fold(F, fold_list(F, NewB, Evs), H));
+fold(F, Acc, #c_catch{body=Body}=R) ->
+ F(R, fold(F, Acc, Body));
+fold(F, Acc, T) -> %Atomic nodes
+ F(T, Acc).
+
+fold_list(F, Acc, L) ->
+ lists:foldl(fun (E, A) -> fold(F, A, E) end, Acc, L).
+
+%% mapfold(MapfoldFun, Accumulator, CoreExpr) -> {CoreExpr,Accumulator}.
+%% This function traverses the core parse format, at each level
+%% applying the submited argument function, assumed to do the real
+%% work, and keeping the accumulated result in the A (accumulator)
+%% argument.
+
+mapfold(F, Acc0, #c_tuple{es=Es0}=R) ->
+ {Es1,Acc1} = mapfold_list(F, Acc0, Es0),
+ F(R#c_tuple{es=Es1}, Acc1);
+mapfold(F, Acc0, #c_cons{hd=H0,tl=T0}=R) ->
+ {H1,Acc1} = mapfold(F, Acc0, H0),
+ {T1,Acc2} = mapfold(F, Acc1, T0),
+ F(R#c_cons{hd=H1,tl=T1}, Acc2);
+mapfold(F, Acc0, #c_values{es=Es0}=R) ->
+ {Es1,Acc1} = mapfold_list(F, Acc0, Es0),
+ F(R#c_values{es=Es1}, Acc1);
+
+mapfold(F, Acc0, #c_alias{pat=P0,var=V0}=R) ->
+ {P1,Acc1} = mapfold(F, Acc0, P0),
+ {V1,Acc2} = mapfold(F, Acc1, V0),
+ F(R#c_alias{pat=P1,var=V1}, Acc2);
+
+mapfold(F, Acc0, #c_module{defs=D0}=R) ->
+ {D1,Acc1} = mapfold_list(F, Acc0, D0),
+ F(R#c_module{defs=D1}, Acc1);
+mapfold(F, Acc0, #c_def{val=V0}=R) ->
+ {V1,Acc1} = mapfold(F, Acc0, V0),
+ F(R#c_def{val=V1}, Acc1);
+
+mapfold(F, Acc0, #c_fun{vars=Vs0, body=B0}=R) ->
+ {Vs1,Acc1} = mapfold_list(F, Acc0, Vs0),
+ {B1,Acc2} = mapfold(F, Acc1, B0),
+ F(R#c_fun{vars=Vs1,body=B1}, Acc2);
+mapfold(F, Acc0, #c_let{vars=Vs0, arg=A0, body=B0}=R) ->
+ {Vs1,Acc1} = mapfold_list(F, Acc0, Vs0),
+ {A1,Acc2} = mapfold(F, Acc1, A0),
+ {B1,Acc3} = mapfold(F, Acc2, B0),
+ F(R#c_let{vars=Vs1,arg=A1,body=B1}, Acc3);
+mapfold(F, Acc0, #c_letrec{defs=Fs0,body=B0}=R) ->
+ {Fs1,Acc1} = mapfold_list(F, Acc0, Fs0),
+ {B1,Acc2} = mapfold(F, Acc1, B0),
+ F(R#c_letrec{defs=Fs1,body=B1}, Acc2);
+mapfold(F, Acc0, #c_seq{arg=A0, body=B0}=R) ->
+ {A1,Acc1} = mapfold(F, Acc0, A0),
+ {B1,Acc2} = mapfold(F, Acc1, B0),
+ F(R#c_seq{arg=A1,body=B1}, Acc2);
+mapfold(F, Acc0, #c_case{arg=A0,clauses=Cs0}=R) ->
+ {A1,Acc1} = mapfold(F, Acc0, A0),
+ {Cs1,Acc2} = mapfold_list(F, Acc1, Cs0),
+ F(R#c_case{arg=A1,clauses=Cs1}, Acc2);
+mapfold(F, Acc0, #c_clause{pats=Ps0,guard=G0,body=B0}=R) ->
+ {Ps1,Acc1} = mapfold_list(F, Acc0, Ps0),
+ {G1,Acc2} = mapfold(F, Acc1, G0),
+ {B1,Acc3} = mapfold(F, Acc2, B0),
+ F(R#c_clause{pats=Ps1,guard=G1,body=B1}, Acc3);
+mapfold(F, Acc0, #c_receive{clauses=Cs0,timeout=T0,action=A0}=R) ->
+ {T1,Acc1} = mapfold(F, Acc0, T0),
+ {Cs1,Acc2} = mapfold_list(F, Acc1, Cs0),
+ {A1,Acc3} = mapfold(F, Acc2, A0),
+ F(R#c_receive{clauses=Cs1,timeout=T1,action=A1}, Acc3);
+mapfold(F, Acc0, #c_apply{op=Op0, args=As0}=R) ->
+ {Op1,Acc1} = mapfold(F, Acc0, Op0),
+ {As1,Acc2} = mapfold_list(F, Acc1, As0),
+ F(R#c_apply{op=Op1,args=As1}, Acc2);
+mapfold(F, Acc0, #c_call{module=M0,name=N0,args=As0}=R) ->
+ {M1,Acc1} = mapfold(F, Acc0, M0),
+ {N1,Acc2} = mapfold(F, Acc1, N0),
+ {As1,Acc3} = mapfold_list(F, Acc2, As0),
+ F(R#c_call{module=M1,name=N1,args=As1}, Acc3);
+mapfold(F, Acc0, #c_primop{name=N0, args=As0}=R) ->
+ {N1,Acc1} = mapfold(F, Acc0, N0),
+ {As1,Acc2} = mapfold_list(F, Acc1, As0),
+ F(R#c_primop{name=N1,args=As1}, Acc2);
+mapfold(F, Acc0, #c_try{arg=E0,vars=Vs0,body=B0,evars=Evs0,handler=H0}=R) ->
+ {E1,Acc1} = mapfold(F, Acc0, E0),
+ {Vs1,Acc2} = mapfold_list(F, Acc1, Vs0),
+ {B1,Acc3} = mapfold(F, Acc2, B0),
+ {Evs1,Acc4} = mapfold_list(F, Acc3, Evs0),
+ {H1,Acc5} = mapfold(F, Acc4, H0),
+ F(R#c_try{arg=E1,vars=Vs1,body=B1,evars=Evs1,handler=H1}, Acc5);
+mapfold(F, Acc0, #c_catch{body=B0}=R) ->
+ {B1,Acc1} = mapfold(F, Acc0, B0),
+ F(R#c_catch{body=B1}, Acc1);
+mapfold(F, Acc, T) -> %Atomic nodes
+ F(T, Acc).
+
+mapfold_list(F, Acc, L) ->
+ lists:mapfoldl(fun (E, A) -> mapfold(F, A, E) end, Acc, L).
+
+%% is_var_used(VarName, Expr) -> true | false.
+%% Test if the variable VarName is used in Expr.
+
+is_var_used(V, B) -> vu_body(V, B).
+
+vu_body(V, #c_values{es=Es}) ->
+ vu_expr_list(V, Es);
+vu_body(V, Body) ->
+ vu_expr(V, Body).
+
+vu_expr(V, #c_var{name=V2}) -> V =:= V2;
+vu_expr(V, #c_cons{hd=H,tl=T}) ->
+ case vu_expr(V, H) of
+ true -> true;
+ false -> vu_expr(V, T)
+ end;
+vu_expr(V, #c_tuple{es=Es}) ->
+ vu_expr_list(V, Es);
+vu_expr(V, #c_binary{segments=Ss}) ->
+ vu_seg_list(V, Ss);
+vu_expr(V, #c_fun{vars=Vs,body=B}) ->
+ %% Variables in fun shadow previous variables
+ case vu_var_list(V, Vs) of
+ true -> false;
+ false -> vu_body(V, B)
+ end;
+vu_expr(V, #c_let{vars=Vs,arg=Arg,body=B}) ->
+ case vu_body(V, Arg) of
+ true -> true;
+ false ->
+ %% Variables in let shadow previous variables.
+ case vu_var_list(V, Vs) of
+ true -> false;
+ false -> vu_body(V, B)
+ end
+ end;
+vu_expr(V, #c_letrec{defs=Fs,body=B}) ->
+ case lists:any(fun (#c_def{val=Fb}) -> vu_body(V, Fb) end, Fs) of
+ true -> true;
+ false -> vu_body(V, B)
+ end;
+vu_expr(V, #c_seq{arg=Arg,body=B}) ->
+ case vu_expr(V, Arg) of
+ true -> true;
+ false -> vu_body(V, B)
+ end;
+vu_expr(V, #c_case{arg=Arg,clauses=Cs}) ->
+ case vu_expr(V, Arg) of
+ true -> true;
+ false -> vu_clauses(V, Cs)
+ end;
+vu_expr(V, #c_receive{clauses=Cs,timeout=T,action=A}) ->
+ case vu_clauses(V, Cs) of
+ true -> true;
+ false ->
+ case vu_expr(V, T) of
+ true -> true;
+ false -> vu_body(V, A)
+ end
+ end;
+vu_expr(V, #c_apply{op=Op,args=As}) ->
+ vu_expr_list(V, [Op|As]);
+vu_expr(V, #c_call{module=M,name=N,args=As}) ->
+ vu_expr_list(V, [M,N|As]);
+vu_expr(V, #c_primop{args=As}) -> %Name is an atom
+ vu_expr_list(V, As);
+vu_expr(V, #c_catch{body=B}) ->
+ vu_body(V, B);
+vu_expr(V, #c_try{arg=E,vars=Vs,body=B,evars=Evs,handler=H}) ->
+ case vu_body(V, E) of
+ true -> true;
+ false ->
+ %% Variables shadow previous ones.
+ case case vu_var_list(V, Vs) of
+ true -> false;
+ false -> vu_body(V, B)
+ end of
+ true -> true;
+ false ->
+ case vu_var_list(V, Evs) of
+ true -> false;
+ false -> vu_body(V, H)
+ end
+ end
+ end;
+vu_expr(_, _) -> false. %Everything else
+
+vu_expr_list(V, Es) ->
+ lists:any(fun(E) -> vu_expr(V, E) end, Es).
+
+vu_seg_list(V, Ss) ->
+ lists:any(fun (#c_bitstr{val=Val,size=Size}) ->
+ case vu_expr(V, Val) of
+ true -> true;
+ false -> vu_expr(V, Size)
+ end
+ end, Ss).
+
+%% vu_clause(VarName, Clause) -> true | false.
+%% vu_clauses(VarName, [Clause]) -> true | false.
+%% Have to get the pattern results right.
+
+vu_clause(V, #c_clause{pats=Ps,guard=G,body=B}) ->
+ case vu_pattern_list(V, Ps) of
+ {true,_Shad} -> true; %It is used
+ {false,true} -> false; %Shadowed
+ {false,false} -> %Not affected
+ case vu_expr(V, G) of
+ true -> true;
+ false ->vu_body(V, B)
+ end
+ end.
+
+vu_clauses(V, Cs) ->
+ lists:any(fun(C) -> vu_clause(V, C) end, Cs).
+
+%% vu_pattern(VarName, Pattern) -> {Used,Shadow}.
+%% vu_pattern_list(VarName, [Pattern]) -> {Used,Shadow}.
+%% Binaries complicate patterns as a variable can both be properly
+%% used, in a bit segment size, and shadow. They can also do both.
+
+%%vu_pattern(V, Pat) -> vu_pattern(V, Pat, {false,false}).
+
+vu_pattern(V, #c_var{name=V2}, St) ->
+ setelement(2, St, V =:= V2);
+vu_pattern(V, #c_cons{hd=H,tl=T}, St0) ->
+ case vu_pattern(V, H, St0) of
+ {true,true}=St1 -> St1; %Nothing more to know
+ St1 -> vu_pattern(V, T, St1)
+ end;
+vu_pattern(V, #c_tuple{es=Es}, St) ->
+ vu_pattern_list(V, Es, St);
+vu_pattern(V, #c_binary{segments=Ss}, St) ->
+ vu_pat_seg_list(V, Ss, St);
+vu_pattern(V, #c_alias{var=Var,pat=P}, St0) ->
+ case vu_pattern(V, Var, St0) of
+ {true,true}=St1 -> St1;
+ St1 -> vu_pattern(V, P, St1)
+ end;
+vu_pattern(_, _, St) -> St.
+
+vu_pattern_list(V, Ps) -> vu_pattern_list(V, Ps, {false,false}).
+
+vu_pattern_list(V, Ps, St0) ->
+ lists:foldl(fun(P, St) -> vu_pattern(V, P, St) end, St0, Ps).
+
+vu_pat_seg_list(V, Ss, St) ->
+ lists:foldl(fun (#c_bitstr{val=Val,size=Size}, St0) ->
+ case vu_pattern(V, Val, St0) of
+ {true,true}=St1 -> St1;
+ {_Used,Shad} -> {vu_expr(V, Size),Shad}
+ end
+ end, St, Ss).
+
+%% vu_var_list(VarName, [Var]) -> true | false.
+
+vu_var_list(V, Vs) ->
+ lists:any(fun (#c_var{name=V2}) -> V =:= V2 end, Vs).
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_lint.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_lint.erl
new file mode 100644
index 0000000000..773d1e53c8
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_lint.erl
@@ -0,0 +1,515 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: core_lint.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose : Do necessary checking of Core Erlang code.
+
+%% Check Core module for errors. Seeing this module is used in the
+%% compiler after optimisations wedone more checking than would be
+%% necessary after just parsing. Don't check all constructs.
+%%
+%% We check the following:
+%%
+%% All referred functions, called and exported, are defined.
+%% Format of export list.
+%% Format of attributes
+%% Used variables are defined.
+%% Variables in let and funs.
+%% Patterns case clauses.
+%% Values only as multiple values/variables/patterns.
+%% Return same number of values as requested
+%% Correct number of arguments
+%%
+%% Checks to add:
+%%
+%% Consistency of values/variables
+%% Consistency of function return values/calls.
+%%
+%% We keep the names defined variables and functions in a ordered list
+%% of variable names and function name/arity pairs.
+
+-module(core_lint).
+
+
+-export([module/1,module/2,format_error/1]).
+
+-import(lists, [reverse/1,all/2,foldl/3]).
+-import(ordsets, [add_element/2,is_element/2,union/2]).
+%-import(ordsets, [subtract/2]).
+
+-include("core_parse.hrl").
+
+%% Define the lint state record.
+
+-record(lint, {module=[], %Current module
+ func=[], %Current function
+ errors=[], %Errors
+ warnings=[]}). %Warnings
+
+%% Keep track of defined
+-record(def, {vars=[],
+ funs=[]}).
+
+%%-deftype retcount() -> any | unknown | int().
+
+%% format_error(Error)
+%% Return a string describing the error.
+
+format_error(invalid_exports) -> "invalid exports";
+format_error(invalid_attributes) -> "invalid attributes";
+format_error({undefined_function,{F,A}}) ->
+ io_lib:format("function ~w/~w undefined", [F,A]);
+format_error({undefined_function,{F1,A1},{F2,A2}}) ->
+ io_lib:format("undefined function ~w/~w in ~w/~w", [F1,A1,F2,A2]);
+format_error({illegal_expr,{F,A}}) ->
+ io_lib:format("illegal expression in ~w/~w", [F,A]);
+format_error({illegal_guard,{F,A}}) ->
+ io_lib:format("illegal guard expression in ~w/~w", [F,A]);
+format_error({illegal_pattern,{F,A}}) ->
+ io_lib:format("illegal pattern in ~w/~w", [F,A]);
+format_error({illegal_try,{F,A}}) ->
+ io_lib:format("illegal try expression in ~w/~w", [F,A]);
+format_error({pattern_mismatch,{F,A}}) ->
+ io_lib:format("pattern count mismatch in ~w/~w", [F,A]);
+format_error({return_mismatch,{F,A}}) ->
+ io_lib:format("return count mismatch in ~w/~w", [F,A]);
+format_error({arg_mismatch,{F,A}}) ->
+ io_lib:format("argument count mismatch in ~w/~w", [F,A]);
+format_error({unbound_var,N,{F,A}}) ->
+ io_lib:format("unbound variable ~s in ~w/~w", [N,F,A]);
+format_error({duplicate_var,N,{F,A}}) ->
+ io_lib:format("duplicate variable ~s in ~w/~w", [N,F,A]);
+format_error({not_var,{F,A}}) ->
+ io_lib:format("expecting variable in ~w/~w", [F,A]);
+format_error({not_pattern,{F,A}}) ->
+ io_lib:format("expecting pattern in ~w/~w", [F,A]);
+format_error({not_bs_pattern,{F,A}}) ->
+ io_lib:format("expecting bit syntax pattern in ~w/~w", [F,A]).
+
+%% module(CoreMod) ->
+%% module(CoreMod, [CompileOption]) ->
+%% {ok,[Warning]} | {error,[Error],[Warning]}
+
+module(M) -> module(M, []).
+
+module(#c_module{name=M,exports=Es,attrs=As,defs=Ds}, _Opts) ->
+ Defined = defined_funcs(Ds),
+ St0 = #lint{module=M#c_atom.val},
+ St1 = check_exports(Es, St0),
+ St2 = check_attrs(As, St1),
+ St3 = module_defs(Ds, Defined, St2),
+ St4 = check_state(Es, Defined, St3),
+ return_status(St4).
+
+%% defined_funcs([FuncDef]) -> [Fname].
+
+defined_funcs(Fs) ->
+ foldl(fun (#c_def{name=#c_fname{id=I,arity=A}}, Def) ->
+ add_element({I,A}, Def)
+ end, [], Fs).
+
+%% return_status(State) ->
+%% {ok,[Warning]} | {error,[Error],[Warning]}
+%% Pack errors and warnings properly and return ok | error.
+
+return_status(St) ->
+ Ws = reverse(St#lint.warnings),
+ case reverse(St#lint.errors) of
+ [] -> {ok,[{St#lint.module,Ws}]};
+ Es -> {error,[{St#lint.module,Es}],[{St#lint.module,Ws}]}
+ end.
+
+%% add_error(ErrorDescriptor, State) -> State'
+%% add_warning(ErrorDescriptor, State) -> State'
+%% Note that we don't use line numbers here.
+
+add_error(E, St) -> St#lint{errors=[{none,core_lint,E}|St#lint.errors]}.
+
+%%add_warning(W, St) -> St#lint{warnings=[{none,core_lint,W}|St#lint.warnings]}.
+
+check_exports(Es, St) ->
+ case all(fun (#c_fname{id=Name,arity=Arity}) when
+ atom(Name), integer(Arity) -> true;
+ (_) -> false
+ end, Es) of
+ true -> St;
+ false -> add_error(invalid_exports, St)
+ end.
+
+check_attrs(As, St) ->
+ case all(fun (#c_def{name=#c_atom{},val=V}) -> core_lib:is_literal(V);
+ (_) -> false
+ end, As) of
+ true -> St;
+ false -> add_error(invalid_attributes, St)
+ end.
+
+check_state(Es, Defined, St) ->
+ foldl(fun (#c_fname{id=N,arity=A}, St1) ->
+ F = {N,A},
+ case is_element(F, Defined) of
+ true -> St1;
+ false -> add_error({undefined_function,F}, St)
+ end
+ end, St, Es).
+% Undef = subtract(Es, Defined),
+% St1 = foldl(fun (F, St) -> add_error({undefined_function,F}, St) end,
+% St0, Undef),
+% St1.
+
+%% module_defs(CoreBody, Defined, State) -> State.
+
+module_defs(B, Def, St) ->
+ %% Set top level function name.
+ foldl(fun (Func, St0) ->
+ #c_fname{id=F,arity=A} = Func#c_def.name,
+ St1 = St0#lint{func={F,A}},
+ function(Func, Def, St1)
+ end, St, B).
+
+%% functions([Fdef], Defined, State) -> State.
+
+functions(Fs, Def, St0) ->
+ foldl(fun (F, St) -> function(F, Def, St) end, St0, Fs).
+
+%% function(CoreFunc, Defined, State) -> State.
+
+function(#c_def{name=#c_fname{},val=B}, Def, St) ->
+ %% Body must be a fun!
+ case B of
+ #c_fun{} -> expr(B, Def, any, St);
+ _ -> add_error({illegal_expr,St#lint.func}, St)
+ end.
+
+%% body(Expr, Defined, RetCount, State) -> State.
+
+body(#c_values{es=Es}, Def, Rt, St) ->
+ return_match(Rt, length(Es), expr_list(Es, Def, St));
+body(E, Def, Rt, St0) ->
+ St1 = expr(E, Def, Rt, St0),
+ case core_lib:is_simple_top(E) of
+ true -> return_match(Rt, 1, St1);
+ false -> St1
+ end.
+
+%% guard(Expr, Defined, State) -> State.
+%% Guards are boolean expressions with test wrapped in a protected.
+
+guard(Expr, Def, St) -> gexpr(Expr, Def, 1, St).
+
+%% guard_list([Expr], Defined, State) -> State.
+
+%% guard_list(Es, Def, St0) ->
+%% foldl(fun (E, St) -> guard(E, Def, St) end, St0, Es).
+
+%% gbody(Expr, Defined, RetCount, State) -> State.
+
+gbody(#c_values{es=Es}, Def, Rt, St) ->
+ return_match(Rt, length(Es), gexpr_list(Es, Def, St));
+gbody(E, Def, Rt, St0) ->
+ St1 = gexpr(E, Def, Rt, St0),
+ case core_lib:is_simple_top(E) of
+ true -> return_match(Rt, 1, St1);
+ false -> St1
+ end.
+
+gexpr(#c_var{name=N}, Def, _Rt, St) -> expr_var(N, Def, St);
+gexpr(#c_int{}, _Def, _Rt, St) -> St;
+gexpr(#c_float{}, _Def, _Rt, St) -> St;
+gexpr(#c_atom{}, _Def, _Rt, St) -> St;
+gexpr(#c_char{}, _Def, _Rt, St) -> St;
+gexpr(#c_string{}, _Def, _Rt, St) -> St;
+gexpr(#c_nil{}, _Def, _Rt, St) -> St;
+gexpr(#c_cons{hd=H,tl=T}, Def, _Rt, St) ->
+ gexpr_list([H,T], Def, St);
+gexpr(#c_tuple{es=Es}, Def, _Rt, St) ->
+ gexpr_list(Es, Def, St);
+gexpr(#c_binary{segments=Ss}, Def, _Rt, St) ->
+ gbitstr_list(Ss, Def, St);
+gexpr(#c_seq{arg=Arg,body=B}, Def, Rt, St0) ->
+ St1 = gexpr(Arg, Def, any, St0), %Ignore values
+ gbody(B, Def, Rt, St1);
+gexpr(#c_let{vars=Vs,arg=Arg,body=B}, Def, Rt, St0) ->
+ St1 = gbody(Arg, Def, let_varcount(Vs), St0), %This is a guard body
+ {Lvs,St2} = variable_list(Vs, St1),
+ gbody(B, union(Lvs, Def), Rt, St2);
+gexpr(#c_call{module=#c_atom{val=erlang},
+ name=#c_atom{},
+ args=As}, Def, 1, St) ->
+ gexpr_list(As, Def, St);
+gexpr(#c_primop{name=N,args=As}, Def, _Rt, St0) when record(N, c_atom) ->
+ gexpr_list(As, Def, St0);
+gexpr(#c_try{arg=E,vars=[#c_var{name=X}],body=#c_var{name=X},
+ evars=[#c_var{},#c_var{},#c_var{}],handler=#c_atom{val=false}},
+ Def, Rt, St) ->
+ gbody(E, Def, Rt, St);
+gexpr(_, _, _, St) ->
+ add_error({illegal_guard,St#lint.func}, St).
+
+%% gexpr_list([Expr], Defined, State) -> State.
+
+gexpr_list(Es, Def, St0) ->
+ foldl(fun (E, St) -> gexpr(E, Def, 1, St) end, St0, Es).
+
+%% gbitstr_list([Elem], Defined, State) -> State.
+
+gbitstr_list(Es, Def, St0) ->
+ foldl(fun (E, St) -> gbitstr(E, Def, St) end, St0, Es).
+
+gbitstr(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Def, St0) ->
+ St1 = bit_type(U, T, Fs, St0),
+ gexpr_list([V,S], Def, St1).
+
+%% expr(Expr, Defined, RetCount, State) -> State.
+
+expr(#c_var{name=N}, Def, _Rt, St) -> expr_var(N, Def, St);
+expr(#c_int{}, _Def, _Rt, St) -> St;
+expr(#c_float{}, _Def, _Rt, St) -> St;
+expr(#c_atom{}, _Def, _Rt, St) -> St;
+expr(#c_char{}, _Def, _Rt, St) -> St;
+expr(#c_string{}, _Def, _Rt, St) -> St;
+expr(#c_nil{}, _Def, _Rt, St) -> St;
+expr(#c_cons{hd=H,tl=T}, Def, _Rt, St) ->
+ expr_list([H,T], Def, St);
+expr(#c_tuple{es=Es}, Def, _Rt, St) ->
+ expr_list(Es, Def, St);
+expr(#c_binary{segments=Ss}, Def, _Rt, St) ->
+ bitstr_list(Ss, Def, St);
+expr(#c_fname{id=I,arity=A}, Def, _Rt, St) ->
+ expr_fname({I,A}, Def, St);
+expr(#c_fun{vars=Vs,body=B}, Def, Rt, St0) ->
+ {Vvs,St1} = variable_list(Vs, St0),
+ return_match(Rt, 1, body(B, union(Vvs, Def), any, St1));
+expr(#c_seq{arg=Arg,body=B}, Def, Rt, St0) ->
+ St1 = expr(Arg, Def, any, St0), %Ignore values
+ body(B, Def, Rt, St1);
+expr(#c_let{vars=Vs,arg=Arg,body=B}, Def, Rt, St0) ->
+ St1 = body(Arg, Def, let_varcount(Vs), St0), %This is a body
+ {Lvs,St2} = variable_list(Vs, St1),
+ body(B, union(Lvs, Def), Rt, St2);
+expr(#c_letrec{defs=Fs,body=B}, Def0, Rt, St0) ->
+ Def1 = union(defined_funcs(Fs), Def0), %All defined stuff
+ St1 = functions(Fs, Def1, St0),
+ body(B, Def1, Rt, St1#lint{func=St0#lint.func});
+expr(#c_case{arg=Arg,clauses=Cs}, Def, Rt, St0) ->
+ Pc = case_patcount(Cs),
+ St1 = body(Arg, Def, Pc, St0),
+ clauses(Cs, Def, Pc, Rt, St1);
+expr(#c_receive{clauses=Cs,timeout=T,action=A}, Def, Rt, St0) ->
+ St1 = expr(T, Def, 1, St0),
+ St2 = body(A, Def, Rt, St1),
+ clauses(Cs, Def, 1, Rt, St2);
+expr(#c_apply{op=Op,args=As}, Def, _Rt, St0) ->
+ St1 = apply_op(Op, Def, length(As), St0),
+ expr_list(As, Def, St1);
+expr(#c_call{module=M,name=N,args=As}, Def, _Rt, St0) ->
+ St1 = expr(M, Def, 1, St0),
+ St2 = expr(N, Def, 1, St1),
+ expr_list(As, Def, St2);
+expr(#c_primop{name=N,args=As}, Def, _Rt, St0) when record(N, c_atom) ->
+ expr_list(As, Def, St0);
+expr(#c_catch{body=B}, Def, Rt, St) ->
+ return_match(Rt, 1, body(B, Def, 1, St));
+expr(#c_try{arg=A,vars=Vs,body=B,evars=Evs,handler=H}, Def, Rt, St0) ->
+ St1 = case length(Evs) of
+ 2 -> St0;
+ _ -> add_error({illegal_try,St0#lint.func}, St0)
+ end,
+ St2 = body(A, Def, let_varcount(Vs), St1),
+ {Ns,St3} = variable_list(Vs, St2),
+ St4 = body(B, union(Ns, Def), Rt, St3),
+ {Ens,St5} = variable_list(Evs, St4),
+ body(H, union(Ens, Def), Rt, St5);
+expr(_, _, _, St) ->
+ %%io:fwrite("clint: ~p~n", [Other]),
+ add_error({illegal_expr,St#lint.func}, St).
+
+%% expr_list([Expr], Defined, State) -> State.
+
+expr_list(Es, Def, St0) ->
+ foldl(fun (E, St) -> expr(E, Def, 1, St) end, St0, Es).
+
+%% bitstr_list([Elem], Defined, State) -> State.
+
+bitstr_list(Es, Def, St0) ->
+ foldl(fun (E, St) -> bitstr(E, Def, St) end, St0, Es).
+
+bitstr(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Def, St0) ->
+ St1 = bit_type(U, T, Fs, St0),
+ expr_list([V,S], Def, St1).
+
+%% apply_op(Op, Defined, ArgCount, State) -> State.
+%% A apply op is either an fname or an expression.
+
+apply_op(#c_fname{id=I,arity=A}, Def, Ac, St0) ->
+ St1 = expr_fname({I,A}, Def, St0),
+ arg_match(Ac, A, St1);
+apply_op(E, Def, _, St) -> expr(E, Def, 1, St). %Hard to check
+
+%% expr_var(VarName, Defined, State) -> State.
+
+expr_var(N, Def, St) ->
+ case is_element(N, Def) of
+ true -> St;
+ false -> add_error({unbound_var,N,St#lint.func}, St)
+ end.
+
+%% expr_fname(Fname, Defined, State) -> State.
+
+expr_fname(Fname, Def, St) ->
+ case is_element(Fname, Def) of
+ true -> St;
+ false -> add_error({undefined_function,Fname,St#lint.func}, St)
+ end.
+
+%% let_varcount([Var]) -> int().
+
+let_varcount([]) -> any; %Ignore values
+let_varcount(Es) -> length(Es).
+
+%% case_patcount([Clause]) -> int().
+
+case_patcount([#c_clause{pats=Ps}|_]) -> length(Ps).
+
+%% clauses([Clause], Defined, PatCount, RetCount, State) -> State.
+
+clauses(Cs, Def, Pc, Rt, St0) ->
+ foldl(fun (C, St) -> clause(C, Def, Pc, Rt, St) end, St0, Cs).
+
+%% clause(Clause, Defined, PatCount, RetCount, State) -> State.
+
+clause(#c_clause{pats=Ps,guard=G,body=B}, Def0, Pc, Rt, St0) ->
+ St1 = pattern_match(Pc, length(Ps), St0),
+ {Pvs,St2} = pattern_list(Ps, Def0, St1),
+ Def1 = union(Pvs, Def0),
+ St3 = guard(G, Def1, St2),
+ body(B, Def1, Rt, St3).
+
+%% variable(Var, [PatVar], State) -> {[VarName],State}.
+
+variable(#c_var{name=N}, Ps, St) ->
+ case is_element(N, Ps) of
+ true -> {[],add_error({duplicate_var,N,St#lint.func}, St)};
+ false -> {[N],St}
+ end;
+variable(_, Def, St) -> {Def,add_error({not_var,St#lint.func}, St)}.
+
+%% variable_list([Var], State) -> {[Var],State}.
+%% variable_list([Var], [PatVar], State) -> {[Var],State}.
+
+variable_list(Vs, St) -> variable_list(Vs, [], St).
+
+variable_list(Vs, Ps, St) ->
+ foldl(fun (V, {Ps0,St0}) ->
+ {Vvs,St1} = variable(V, Ps0, St0),
+ {union(Vvs, Ps0),St1}
+ end, {Ps,St}, Vs).
+
+%% pattern(Pattern, Defined, State) -> {[PatVar],State}.
+%% pattern(Pattern, Defined, [PatVar], State) -> {[PatVar],State}.
+%% Patterns are complicated by sizes in binaries. These are pure
+%% input variables which create no bindings. We, therefor, need to
+%% carry around the original defined variables to get the correct
+%% handling.
+
+%% pattern(P, Def, St) -> pattern(P, Def, [], St).
+
+pattern(#c_var{name=N}, Def, Ps, St) ->
+ pat_var(N, Def, Ps, St);
+pattern(#c_int{}, _Def, Ps, St) -> {Ps,St};
+pattern(#c_float{}, _Def, Ps, St) -> {Ps,St};
+pattern(#c_atom{}, _Def, Ps, St) -> {Ps,St};
+pattern(#c_char{}, _Def, Ps, St) -> {Ps,St};
+pattern(#c_string{}, _Def, Ps, St) -> {Ps,St};
+pattern(#c_nil{}, _Def, Ps, St) -> {Ps,St};
+pattern(#c_cons{hd=H,tl=T}, Def, Ps, St) ->
+ pattern_list([H,T], Def, Ps, St);
+pattern(#c_tuple{es=Es}, Def, Ps, St) ->
+ pattern_list(Es, Def, Ps, St);
+pattern(#c_binary{segments=Ss}, Def, Ps, St) ->
+ pat_bin(Ss, Def, Ps, St);
+pattern(#c_alias{var=V,pat=P}, Def, Ps, St0) ->
+ {Vvs,St1} = variable(V, Ps, St0),
+ pattern(P, Def, union(Vvs, Ps), St1);
+pattern(_, _, Ps, St) -> {Ps,add_error({not_pattern,St#lint.func}, St)}.
+
+pat_var(N, _Def, Ps, St) ->
+ case is_element(N, Ps) of
+ true -> {Ps,add_error({duplicate_var,N,St#lint.func}, St)};
+ false -> {add_element(N, Ps),St}
+ end.
+
+%% pat_bin_list([Elem], Defined, [PatVar], State) -> {[PatVar],State}.
+
+pat_bin(Es, Def, Ps0, St0) ->
+ foldl(fun (E, {Ps,St}) -> pat_segment(E, Def, Ps, St) end, {Ps0,St0}, Es).
+
+pat_segment(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Def, Ps, St0) ->
+ St1 = bit_type(U, T, Fs, St0),
+ St2 = pat_bit_expr(S, T, Def, St1),
+ pattern(V, Def, Ps, St2);
+pat_segment(_, _, Ps, St) ->
+ {Ps,add_error({not_bs_pattern,St#lint.func}, St)}.
+
+%% pat_bit_expr(SizePat, Type, Defined, State) -> State.
+%% Check the Size pattern, this is an input! Be a bit tough here.
+
+pat_bit_expr(#c_int{val=I}, _, _, St) when I >= 0 -> St;
+pat_bit_expr(#c_var{name=N}, _, Def, St) ->
+ expr_var(N, Def, St);
+pat_bit_expr(#c_atom{val=all}, binary, _Def, St) -> St;
+pat_bit_expr(_, _, _, St) ->
+ add_error({illegal_expr,St#lint.func}, St).
+
+bit_type(Unit, Type, Flags, St) ->
+ U = core_lib:literal_value(Unit),
+ T = core_lib:literal_value(Type),
+ Fs = core_lib:literal_value(Flags),
+ case erl_bits:set_bit_type(default, [T,{unit,U}|Fs]) of
+ {ok,_,_} -> St;
+ {error,E} -> add_error({E,St#lint.func}, St)
+ end.
+
+%% pattern_list([Var], Defined, State) -> {[PatVar],State}.
+%% pattern_list([Var], Defined, [PatVar], State) -> {[PatVar],State}.
+
+pattern_list(Pats, Def, St) -> pattern_list(Pats, Def, [], St).
+
+pattern_list(Pats, Def, Ps0, St0) ->
+ foldl(fun (P, {Ps,St}) -> pattern(P, Def, Ps, St) end, {Ps0,St0}, Pats).
+
+%% pattern_match(Required, Supplied, State) -> State.
+%% Check that the required number of patterns match the supplied.
+
+pattern_match(N, N, St) -> St;
+pattern_match(_Req, _Sup, St) ->
+ add_error({pattern_mismatch,St#lint.func}, St).
+
+%% return_match(Required, Supplied, State) -> State.
+%% Check that the required number of return values match the supplied.
+
+return_match(any, _Sup, St) -> St;
+return_match(_Req, unknown, St) -> St;
+return_match(N, N, St) -> St;
+return_match(_Req, _Sup, St) ->
+ add_error({return_mismatch,St#lint.func}, St).
+
+%% arg_match(Required, Supplied, State) -> State.
+
+arg_match(_Req, unknown, St) -> St;
+arg_match(N, N, St) -> St;
+arg_match(_Req, _Sup, St) ->
+ add_error({arg_mismatch,St#lint.func}, St).
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_parse.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_parse.erl
new file mode 100644
index 0000000000..77c33c561b
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_parse.erl
@@ -0,0 +1,4909 @@
+-module(core_parse).
+-define(THIS_MODULE, core_parse).
+-export([parse/1, parse_and_scan/1, format_error/1]).
+
+-export([abstract/1,abstract/2,normalise/1]).
+
+%% The following directive is needed for (significantly) faster compilation
+%% of the generated .erl file by the HiPE compiler. Please do not remove.
+-compile([{hipe,[{regalloc,linear_scan}]}]).
+
+-include("core_parse.hrl").
+
+tok_val(T) -> element(3, T).
+tok_line(T) -> element(2, T).
+
+abstract(T, _N) -> abstract(T).
+
+abstract(Term) -> core_lib:make_literal(Term).
+
+normalise(Core) -> core_lib:literal_value(Core).
+
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: core_parse.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% The parser generator will insert appropriate declarations before this line.%
+
+parse(Tokens) ->
+ case catch yeccpars1(Tokens, false, 0, [], []) of
+ error ->
+ Errorline =
+ if Tokens == [] -> 0; true -> element(2, hd(Tokens)) end,
+ {error,
+ {Errorline, ?THIS_MODULE, "syntax error at or after this line."}};
+ Other ->
+ Other
+ end.
+
+parse_and_scan({Mod, Fun, Args}) ->
+ case apply(Mod, Fun, Args) of
+ {eof, _} ->
+ {ok, eof};
+ {error, Descriptor, _} ->
+ {error, Descriptor};
+ {ok, Tokens, _} ->
+ yeccpars1(Tokens, {Mod, Fun, Args}, 0, [], [])
+ end.
+
+format_error(Message) ->
+ case io_lib:deep_char_list(Message) of
+ true ->
+ Message;
+ _ ->
+ io_lib:write(Message)
+ end.
+
+% To be used in grammar files to throw an error message to the parser toplevel.
+% Doesn't have to be exported!
+return_error(Line, Message) ->
+ throw({error, {Line, ?THIS_MODULE, Message}}).
+
+
+% Don't change yeccpars1/6 too much, it is called recursively by yeccpars2/8!
+yeccpars1([Token | Tokens], Tokenizer, State, States, Vstack) ->
+ yeccpars2(State, element(1, Token), States, Vstack, Token, Tokens,
+ Tokenizer);
+yeccpars1([], {M, F, A}, State, States, Vstack) ->
+ case catch apply(M, F, A) of
+ {eof, Endline} ->
+ {error, {Endline, ?THIS_MODULE, "end_of_file"}};
+ {error, Descriptor, _Endline} ->
+ {error, Descriptor};
+ {'EXIT', Reason} ->
+ {error, {0, ?THIS_MODULE, Reason}};
+ {ok, Tokens, _Endline} ->
+ case catch yeccpars1(Tokens, {M, F, A}, State, States, Vstack) of
+ error ->
+ Errorline = element(2, hd(Tokens)),
+ {error, {Errorline, ?THIS_MODULE,
+ "syntax error at or after this line."}};
+ Other ->
+ Other
+ end
+ end;
+yeccpars1([], false, State, States, Vstack) ->
+ yeccpars2(State, '$end', States, Vstack, {'$end', 999999}, [], false).
+
+% For internal use only.
+yeccerror(Token) ->
+ {error,
+ {element(2, Token), ?THIS_MODULE,
+ ["syntax error before: ", yecctoken2string(Token)]}}.
+
+yecctoken2string({atom, _, A}) -> io_lib:write(A);
+yecctoken2string({integer,_,N}) -> io_lib:write(N);
+yecctoken2string({float,_,F}) -> io_lib:write(F);
+yecctoken2string({char,_,C}) -> io_lib:write_char(C);
+yecctoken2string({var,_,V}) -> io_lib:format('~s', [V]);
+yecctoken2string({string,_,S}) -> io_lib:write_string(S);
+yecctoken2string({reserved_symbol, _, A}) -> io_lib:format('~w', [A]);
+yecctoken2string({_Cat, _, Val}) -> io_lib:format('~w', [Val]);
+
+yecctoken2string({'dot', _}) -> io_lib:format('~w', ['.']);
+yecctoken2string({'$end', _}) ->
+ [];
+yecctoken2string({Other, _}) when atom(Other) ->
+ io_lib:format('~w', [Other]);
+yecctoken2string(Other) ->
+ io_lib:write(Other).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+yeccpars2(0, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 1, [0 | __Ss], [__T | __Stack]);
+yeccpars2(0, 'module', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 2, [0 | __Ss], [__T | __Stack]);
+yeccpars2(0, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(1, 'module', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 313, [1 | __Ss], [__T | __Stack]);
+yeccpars2(1, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(2, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 4, [2 | __Ss], [__T | __Stack]);
+yeccpars2(2, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(3, '$end', _, __Stack, _, _, _) ->
+ {ok, hd(__Stack)};
+yeccpars2(3, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(4, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 5, [4 | __Ss], [__T | __Stack]);
+yeccpars2(4, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(5, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 11, [5 | __Ss], [__T | __Stack]);
+yeccpars2(5, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 306, [5 | __Ss], [__T | __Stack]);
+yeccpars2(5, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(6, 'attributes', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 7, [6 | __Ss], [__T | __Stack]);
+yeccpars2(6, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(7, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 276, [7 | __Ss], [__T | __Stack]);
+yeccpars2(7, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(8, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 9, [8 | __Ss], [__T | __Stack]);
+yeccpars2(8, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 11, [8 | __Ss], [__T | __Stack]);
+yeccpars2(8, __Cat, __Ss, __Stack, __T, __Ts, __Tzr) ->
+ __Val = [],
+ yeccpars2(13, __Cat, [8 | __Ss], [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(9, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 11, [9 | __Ss], [__T | __Stack]);
+yeccpars2(9, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(10, '=', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 20, [10 | __Ss], [__T | __Stack]);
+yeccpars2(10, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(11, '/', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 18, [11 | __Ss], [__T | __Stack]);
+yeccpars2(11, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(12, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 9, [12 | __Ss], [__T | __Stack]);
+yeccpars2(12, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 11, [12 | __Ss], [__T | __Stack]);
+yeccpars2(12, __Cat, __Ss, __Stack, __T, __Ts, __Tzr) ->
+ __Val = [],
+ yeccpars2(17, __Cat, [12 | __Ss], [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(13, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(module_defs, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(14, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(anno_function_name, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(15, 'end', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 16, [15 | __Ss], [__T | __Stack]);
+yeccpars2(15, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(16, __Cat, __Ss, [__6,__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_module{name = #c_atom{val = tok_val(__2)}, exports = __3, attrs = __4, defs = __5},
+ __Nss = lists:nthtail(5, __Ss),
+ yeccpars2(yeccgoto(module_definition, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(17, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__2],
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(function_definitions, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(18, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 19, [18 | __Ss], [__T | __Stack]);
+yeccpars2(18, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(19, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_fname{id = tok_val(__1), arity = tok_val(__3)},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(function_name, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(20, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [20 | __Ss], [__T | __Stack]);
+yeccpars2(20, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 21, [20 | __Ss], [__T | __Stack]);
+yeccpars2(20, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(21, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [21 | __Ss], [__T | __Stack]);
+yeccpars2(21, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(22, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_def{name = __1, val = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(function_definition, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(23, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 25, [23 | __Ss], [__T | __Stack]);
+yeccpars2(23, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(24, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(anno_fun, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(25, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 27, [25 | __Ss], [__T | __Stack]);
+yeccpars2(25, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 26, [25 | __Ss], [__T | __Stack]);
+yeccpars2(25, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [25 | __Ss], [__T | __Stack]);
+yeccpars2(25, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(26, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [26 | __Ss], [__T | __Stack]);
+yeccpars2(26, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(27, '->', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 265, [27 | __Ss], [__T | __Stack]);
+yeccpars2(27, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(28, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 263, [28 | __Ss], [__T | __Stack]);
+yeccpars2(28, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(anno_variables, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(29, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 32, [29 | __Ss], [__T | __Stack]);
+yeccpars2(29, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(30, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_var{name = tok_val(__1)},
+ yeccpars2(yeccgoto(variable, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(31, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(anno_variable, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(32, '->', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 33, [32 | __Ss], [__T | __Stack]);
+yeccpars2(32, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(33, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(34, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 247, [34 | __Ss], [__T | __Stack]);
+yeccpars2(34, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(35, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(36, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 240, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(37, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 149, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(38, __Cat, __Ss, [__6,__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_fun{vars = __3, body = __6},
+ __Nss = lists:nthtail(5, __Ss),
+ yeccpars2(yeccgoto(fun_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(39, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(40, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(41, '/', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 18, [41 | __Ss], [__T | __Stack]);
+yeccpars2(41, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_atom{val = tok_val(__1)},
+ yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(42, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(43, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(44, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(45, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(46, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(47, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(48, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(49, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(50, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_char{val = tok_val(__1)},
+ yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(51, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(52, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(53, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(anno_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(54, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_float{val = tok_val(__1)},
+ yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(55, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(56, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(57, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_int{val = tok_val(__1)},
+ yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(58, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 83, [58 | __Ss], [__T | __Stack]);
+yeccpars2(58, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 26, [58 | __Ss], [__T | __Stack]);
+yeccpars2(58, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [58 | __Ss], [__T | __Stack]);
+yeccpars2(58, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(59, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(60, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 9, [60 | __Ss], [__T | __Stack]);
+yeccpars2(60, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 11, [60 | __Ss], [__T | __Stack]);
+yeccpars2(60, __Cat, __Ss, __Stack, __T, __Ts, __Tzr) ->
+ __Val = [],
+ yeccpars2(210, __Cat, [60 | __Ss], [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(61, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(62, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_nil{},
+ yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(63, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 208, [63 | __Ss], [__T | __Stack]);
+yeccpars2(63, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(64, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(65, 'after', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 99, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 97, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 96, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(66, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(67, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(68, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(69, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_string{val = tok_val(__1)},
+ yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(70, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(71, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(72, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(73, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(74, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 77, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(75, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 79, [75 | __Ss], [__T | __Stack]);
+yeccpars2(75, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(anno_expressions, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(76, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 78, [76 | __Ss], [__T | __Stack]);
+yeccpars2(76, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(77, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_tuple{es = []},
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(tuple, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(78, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_tuple{es = __2},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tuple, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(79, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(80, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(anno_expressions, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(81, 'of', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 82, [81 | __Ss], [__T | __Stack]);
+yeccpars2(81, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(82, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 83, [82 | __Ss], [__T | __Stack]);
+yeccpars2(82, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 26, [82 | __Ss], [__T | __Stack]);
+yeccpars2(82, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [82 | __Ss], [__T | __Stack]);
+yeccpars2(82, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(83, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 92, [83 | __Ss], [__T | __Stack]);
+yeccpars2(83, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 26, [83 | __Ss], [__T | __Stack]);
+yeccpars2(83, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [83 | __Ss], [__T | __Stack]);
+yeccpars2(83, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(84, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(let_vars, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(85, '->', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 86, [85 | __Ss], [__T | __Stack]);
+yeccpars2(85, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(86, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(87, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 88, [87 | __Ss], [__T | __Stack]);
+yeccpars2(87, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(88, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 83, [88 | __Ss], [__T | __Stack]);
+yeccpars2(88, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 26, [88 | __Ss], [__T | __Stack]);
+yeccpars2(88, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [88 | __Ss], [__T | __Stack]);
+yeccpars2(88, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(89, '->', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 90, [89 | __Ss], [__T | __Stack]);
+yeccpars2(89, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(90, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(91, __Cat, __Ss, [__10,__9,__8,__7,__6,__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = if length(__8) == 2 -> #c_try{arg = __2, vars = __4, body = __6, evars = __8, handler = __10}; true -> return_error(tok_line(__7),"expected 2 exception variables in 'try'") end,
+ __Nss = lists:nthtail(9, __Ss),
+ yeccpars2(yeccgoto(try_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(92, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [],
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(let_vars, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(93, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 94, [93 | __Ss], [__T | __Stack]);
+yeccpars2(93, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(94, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __2,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(let_vars, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(95, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 190, [95 | __Ss], [__T | __Stack]);
+yeccpars2(95, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(96, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 97, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 115, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(97, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 182, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 115, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(98, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 115, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 149, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(99, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(100, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 97, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 96, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(anno_clauses, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(101, 'after', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 99, [101 | __Ss], [__T | __Stack]);
+yeccpars2(101, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(102, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(clause_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(103, '=', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 162, [103 | __Ss], [__T | __Stack]);
+yeccpars2(103, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(anno_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(104, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_atom{val = tok_val(__1)},
+ yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(105, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(atomic_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(106, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(other_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(107, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(other_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(108, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(anno_clause, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(109, 'when', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 164, [109 | __Ss], [__T | __Stack]);
+yeccpars2(109, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(110, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(other_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(111, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(anno_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(112, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = begin
+ {T,A} = __2, #c_receive{clauses = [], timeout = T, action = A}
+ end,
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(receive_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(113, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(other_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(114, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 115, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 118, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(115, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 26, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(116, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 120, [116 | __Ss], [__T | __Stack]);
+yeccpars2(116, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(anno_patterns, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(117, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 119, [117 | __Ss], [__T | __Stack]);
+yeccpars2(117, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(118, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_tuple{es = []},
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(tuple_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(119, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_tuple{es = __2},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tuple_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(120, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 115, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(121, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(anno_patterns, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(122, '=', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 162, [122 | __Ss], [__T | __Stack]);
+yeccpars2(122, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(123, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 159, [123 | __Ss], [__T | __Stack]);
+yeccpars2(123, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(124, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 125, [124 | __Ss], [__T | __Stack]);
+yeccpars2(124, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(anno_variable, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(125, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 126, [125 | __Ss], [__T | __Stack]);
+yeccpars2(125, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(126, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 129, [126 | __Ss], [__T | __Stack]);
+yeccpars2(126, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 142, [126 | __Ss], [__T | __Stack]);
+yeccpars2(126, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 140, [126 | __Ss], [__T | __Stack]);
+yeccpars2(126, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 131, [126 | __Ss], [__T | __Stack]);
+yeccpars2(126, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 137, [126 | __Ss], [__T | __Stack]);
+yeccpars2(126, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 138, [126 | __Ss], [__T | __Stack]);
+yeccpars2(126, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 133, [126 | __Ss], [__T | __Stack]);
+yeccpars2(126, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 130, [126 | __Ss], [__T | __Stack]);
+yeccpars2(126, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(127, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 128, [127 | __Ss], [__T | __Stack]);
+yeccpars2(127, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(128, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = core_lib:set_anno(__2,__4),
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(anno_variable, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(129, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 129, [129 | __Ss], [__T | __Stack]);
+yeccpars2(129, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 142, [129 | __Ss], [__T | __Stack]);
+yeccpars2(129, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 140, [129 | __Ss], [__T | __Stack]);
+yeccpars2(129, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 131, [129 | __Ss], [__T | __Stack]);
+yeccpars2(129, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 137, [129 | __Ss], [__T | __Stack]);
+yeccpars2(129, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 138, [129 | __Ss], [__T | __Stack]);
+yeccpars2(129, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 133, [129 | __Ss], [__T | __Stack]);
+yeccpars2(129, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 149, [129 | __Ss], [__T | __Stack]);
+yeccpars2(129, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(130, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [],
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(annotation, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(131, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = tok_val(__1),
+ yeccpars2(yeccgoto(atomic_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(132, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(133, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = tok_val(__1),
+ yeccpars2(yeccgoto(atomic_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(134, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(135, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 147, [135 | __Ss], [__T | __Stack]);
+yeccpars2(135, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(constants, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(136, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 146, [136 | __Ss], [__T | __Stack]);
+yeccpars2(136, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(137, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = tok_val(__1),
+ yeccpars2(yeccgoto(atomic_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(138, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = tok_val(__1),
+ yeccpars2(yeccgoto(atomic_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(139, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [],
+ yeccpars2(yeccgoto(atomic_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(140, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = tok_val(__1),
+ yeccpars2(yeccgoto(atomic_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(141, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(142, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 129, [142 | __Ss], [__T | __Stack]);
+yeccpars2(142, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 142, [142 | __Ss], [__T | __Stack]);
+yeccpars2(142, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 144, [142 | __Ss], [__T | __Stack]);
+yeccpars2(142, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 140, [142 | __Ss], [__T | __Stack]);
+yeccpars2(142, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 131, [142 | __Ss], [__T | __Stack]);
+yeccpars2(142, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 137, [142 | __Ss], [__T | __Stack]);
+yeccpars2(142, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 138, [142 | __Ss], [__T | __Stack]);
+yeccpars2(142, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 133, [142 | __Ss], [__T | __Stack]);
+yeccpars2(142, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(143, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 145, [143 | __Ss], [__T | __Stack]);
+yeccpars2(143, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(144, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = {},
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(tuple_constant, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(145, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = list_to_tuple(__2),
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tuple_constant, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(146, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __2,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(annotation, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(147, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 129, [147 | __Ss], [__T | __Stack]);
+yeccpars2(147, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 142, [147 | __Ss], [__T | __Stack]);
+yeccpars2(147, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 140, [147 | __Ss], [__T | __Stack]);
+yeccpars2(147, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 131, [147 | __Ss], [__T | __Stack]);
+yeccpars2(147, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 137, [147 | __Ss], [__T | __Stack]);
+yeccpars2(147, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 138, [147 | __Ss], [__T | __Stack]);
+yeccpars2(147, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 133, [147 | __Ss], [__T | __Stack]);
+yeccpars2(147, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(148, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(constants, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(149, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = {nil,tok_line(__1)},
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(nil, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(150, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 151, [150 | __Ss], [__T | __Stack]);
+yeccpars2(150, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 154, [150 | __Ss], [__T | __Stack]);
+yeccpars2(150, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 152, [150 | __Ss], [__T | __Stack]);
+yeccpars2(150, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(151, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 129, [151 | __Ss], [__T | __Stack]);
+yeccpars2(151, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 142, [151 | __Ss], [__T | __Stack]);
+yeccpars2(151, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 140, [151 | __Ss], [__T | __Stack]);
+yeccpars2(151, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 131, [151 | __Ss], [__T | __Stack]);
+yeccpars2(151, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 137, [151 | __Ss], [__T | __Stack]);
+yeccpars2(151, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 138, [151 | __Ss], [__T | __Stack]);
+yeccpars2(151, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 133, [151 | __Ss], [__T | __Stack]);
+yeccpars2(151, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(152, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [],
+ yeccpars2(yeccgoto(tail_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(153, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__2|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(cons_constant, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(154, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 129, [154 | __Ss], [__T | __Stack]);
+yeccpars2(154, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 142, [154 | __Ss], [__T | __Stack]);
+yeccpars2(154, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 140, [154 | __Ss], [__T | __Stack]);
+yeccpars2(154, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 131, [154 | __Ss], [__T | __Stack]);
+yeccpars2(154, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 137, [154 | __Ss], [__T | __Stack]);
+yeccpars2(154, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 138, [154 | __Ss], [__T | __Stack]);
+yeccpars2(154, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 133, [154 | __Ss], [__T | __Stack]);
+yeccpars2(154, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(155, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 156, [155 | __Ss], [__T | __Stack]);
+yeccpars2(155, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(156, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __2,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tail_constant, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(157, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 151, [157 | __Ss], [__T | __Stack]);
+yeccpars2(157, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 154, [157 | __Ss], [__T | __Stack]);
+yeccpars2(157, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 152, [157 | __Ss], [__T | __Stack]);
+yeccpars2(157, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(158, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__2|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tail_constant, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(159, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 126, [159 | __Ss], [__T | __Stack]);
+yeccpars2(159, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(160, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 161, [160 | __Ss], [__T | __Stack]);
+yeccpars2(160, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(161, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = core_lib:set_anno(__2,__4),
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(anno_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(162, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 115, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(163, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_alias{var = __1, pat = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(other_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(164, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(165, '->', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 166, [165 | __Ss], [__T | __Stack]);
+yeccpars2(165, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(166, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(167, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_clause{pats = __1, guard = __3, body = __5},
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(clause, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(168, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = begin
+ {T,A} = __3, #c_receive{clauses = __2, timeout = T, action = A}
+ end,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(receive_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(169, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__2],
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(anno_clauses, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(170, '->', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 171, [170 | __Ss], [__T | __Stack]);
+yeccpars2(170, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(171, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(172, __Cat, __Ss, [__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = {__2,__4},
+ __Nss = lists:nthtail(3, __Ss),
+ yeccpars2(yeccgoto(timeout, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(173, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 174, [173 | __Ss], [__T | __Stack]);
+yeccpars2(173, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 177, [173 | __Ss], [__T | __Stack]);
+yeccpars2(173, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 175, [173 | __Ss], [__T | __Stack]);
+yeccpars2(173, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(174, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 115, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(175, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_nil{},
+ yeccpars2(yeccgoto(tail_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(176, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_cons{hd = __2, tl = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(cons_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(177, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 115, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(178, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 179, [178 | __Ss], [__T | __Stack]);
+yeccpars2(178, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(179, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __2,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tail_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(180, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 174, [180 | __Ss], [__T | __Stack]);
+yeccpars2(180, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 177, [180 | __Ss], [__T | __Stack]);
+yeccpars2(180, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 175, [180 | __Ss], [__T | __Stack]);
+yeccpars2(180, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(181, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_cons{hd = __2, tl = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tail_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(182, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [],
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(clause_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(183, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 184, [183 | __Ss], [__T | __Stack]);
+yeccpars2(183, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(184, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __2,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(clause_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(185, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 187, [185 | __Ss], [__T | __Stack]);
+yeccpars2(185, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(186, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 159, [186 | __Ss], [__T | __Stack]);
+yeccpars2(186, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(anno_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(187, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 126, [187 | __Ss], [__T | __Stack]);
+yeccpars2(187, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(188, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 189, [188 | __Ss], [__T | __Stack]);
+yeccpars2(188, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(189, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = core_lib:set_anno(__2,__4),
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(anno_clause, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(190, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 191, [190 | __Ss], [__T | __Stack]);
+yeccpars2(190, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 194, [190 | __Ss], [__T | __Stack]);
+yeccpars2(190, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(191, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 200, [191 | __Ss], [__T | __Stack]);
+yeccpars2(191, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(192, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 198, [192 | __Ss], [__T | __Stack]);
+yeccpars2(192, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(segment_patterns, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(193, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 196, [193 | __Ss], [__T | __Stack]);
+yeccpars2(193, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(194, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 195, [194 | __Ss], [__T | __Stack]);
+yeccpars2(194, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(195, __Cat, __Ss, [__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_binary{segments = []},
+ __Nss = lists:nthtail(3, __Ss),
+ yeccpars2(yeccgoto(binary_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(196, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 197, [196 | __Ss], [__T | __Stack]);
+yeccpars2(196, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(197, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_binary{segments = __3},
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(binary_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(198, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 191, [198 | __Ss], [__T | __Stack]);
+yeccpars2(198, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(199, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(segment_patterns, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(200, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 115, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(201, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 202, [201 | __Ss], [__T | __Stack]);
+yeccpars2(201, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(202, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 203, [202 | __Ss], [__T | __Stack]);
+yeccpars2(202, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(203, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 205, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(204, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = case __5 of [S,U,T,Fs] -> #c_bitstr{val = __3, size = S, unit = U, type = T, flags = Fs}; true -> return_error(tok_line(__1),"expected 4 arguments in binary segment") end,
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(segment_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(205, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [],
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(arg_list, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(206, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 207, [206 | __Ss], [__T | __Stack]);
+yeccpars2(206, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(207, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __2,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(arg_list, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(208, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 203, [208 | __Ss], [__T | __Stack]);
+yeccpars2(208, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(209, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = begin
+ Name = #c_atom{val = tok_val(__2)}, #c_primop{name = Name, args = __3}
+ end,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(primop_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(210, 'in', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 211, [210 | __Ss], [__T | __Stack]);
+yeccpars2(210, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(211, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(212, __Cat, __Ss, [__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_letrec{defs = __2, body = __4},
+ __Nss = lists:nthtail(3, __Ss),
+ yeccpars2(yeccgoto(letrec_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(213, '=', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 214, [213 | __Ss], [__T | __Stack]);
+yeccpars2(213, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(214, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(215, 'in', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 216, [215 | __Ss], [__T | __Stack]);
+yeccpars2(215, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(216, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(217, __Cat, __Ss, [__6,__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_let{vars = __2, arg = __4, body = __6},
+ __Nss = lists:nthtail(5, __Ss),
+ yeccpars2(yeccgoto(let_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(218, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(219, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_seq{arg = __2, body = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(sequence, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(220, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_catch{body = __2},
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(catch_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(221, 'of', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 222, [221 | __Ss], [__T | __Stack]);
+yeccpars2(221, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(222, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 97, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 96, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(223, 'end', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 224, [223 | __Ss], [__T | __Stack]);
+yeccpars2(223, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(224, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_case{arg = __2, clauses = __4},
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(case_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(225, ':', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 226, [225 | __Ss], [__T | __Stack]);
+yeccpars2(225, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(226, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(227, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 203, [227 | __Ss], [__T | __Stack]);
+yeccpars2(227, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(228, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_call{module = __2, name = __4, args = __5},
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(call_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(229, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 203, [229 | __Ss], [__T | __Stack]);
+yeccpars2(229, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(230, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_apply{op = __2, args = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(application_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(231, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 232, [231 | __Ss], [__T | __Stack]);
+yeccpars2(231, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 235, [231 | __Ss], [__T | __Stack]);
+yeccpars2(231, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 233, [231 | __Ss], [__T | __Stack]);
+yeccpars2(231, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(232, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(233, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_nil{},
+ yeccpars2(yeccgoto(tail, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(234, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_cons{hd = __2, tl = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(cons, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(235, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(236, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 237, [236 | __Ss], [__T | __Stack]);
+yeccpars2(236, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(237, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __2,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tail, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(238, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 232, [238 | __Ss], [__T | __Stack]);
+yeccpars2(238, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 235, [238 | __Ss], [__T | __Stack]);
+yeccpars2(238, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 233, [238 | __Ss], [__T | __Stack]);
+yeccpars2(238, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(239, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_cons{hd = __2, tl = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tail, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(240, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_values{es = []},
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(expression, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(241, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 242, [241 | __Ss], [__T | __Stack]);
+yeccpars2(241, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(242, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_values{es = __2},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(expression, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(243, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 244, [243 | __Ss], [__T | __Stack]);
+yeccpars2(243, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(244, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 126, [244 | __Ss], [__T | __Stack]);
+yeccpars2(244, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(245, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 246, [245 | __Ss], [__T | __Stack]);
+yeccpars2(245, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(246, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = core_lib:set_anno(__2,__4),
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(anno_expression, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(247, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 248, [247 | __Ss], [__T | __Stack]);
+yeccpars2(247, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 251, [247 | __Ss], [__T | __Stack]);
+yeccpars2(247, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(248, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 257, [248 | __Ss], [__T | __Stack]);
+yeccpars2(248, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(249, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 255, [249 | __Ss], [__T | __Stack]);
+yeccpars2(249, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(segments, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(250, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 253, [250 | __Ss], [__T | __Stack]);
+yeccpars2(250, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(251, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 252, [251 | __Ss], [__T | __Stack]);
+yeccpars2(251, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(252, __Cat, __Ss, [__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_binary{segments = []},
+ __Nss = lists:nthtail(3, __Ss),
+ yeccpars2(yeccgoto(binary, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(253, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 254, [253 | __Ss], [__T | __Stack]);
+yeccpars2(253, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(254, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_binary{segments = __3},
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(binary, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(255, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 248, [255 | __Ss], [__T | __Stack]);
+yeccpars2(255, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(256, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(segments, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(257, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(258, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 259, [258 | __Ss], [__T | __Stack]);
+yeccpars2(258, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(259, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 260, [259 | __Ss], [__T | __Stack]);
+yeccpars2(259, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(260, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(261, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 262, [261 | __Ss], [__T | __Stack]);
+yeccpars2(261, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(262, __Cat, __Ss, [__7,__6,__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = case __6 of [S,U,T,Fs] -> #c_bitstr{val = __3, size = S, unit = U, type = T, flags = Fs}; true -> return_error(tok_line(__1),"expected 4 arguments in binary segment") end,
+ __Nss = lists:nthtail(6, __Ss),
+ yeccpars2(yeccgoto(segment, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(263, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 26, [263 | __Ss], [__T | __Stack]);
+yeccpars2(263, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [263 | __Ss], [__T | __Stack]);
+yeccpars2(263, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(264, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(anno_variables, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(265, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(266, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_fun{vars = [], body = __5},
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(fun_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(267, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 125, [267 | __Ss], [__T | __Stack]);
+yeccpars2(267, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(268, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 269, [268 | __Ss], [__T | __Stack]);
+yeccpars2(268, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(269, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 126, [269 | __Ss], [__T | __Stack]);
+yeccpars2(269, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(270, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 271, [270 | __Ss], [__T | __Stack]);
+yeccpars2(270, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(271, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = core_lib:set_anno(__2,__4),
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(anno_fun, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(272, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 273, [272 | __Ss], [__T | __Stack]);
+yeccpars2(272, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(273, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 126, [273 | __Ss], [__T | __Stack]);
+yeccpars2(273, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(274, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 275, [274 | __Ss], [__T | __Stack]);
+yeccpars2(274, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(275, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = core_lib:set_anno(__2,__4),
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(anno_function_name, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(276, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 278, [276 | __Ss], [__T | __Stack]);
+yeccpars2(276, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 277, [276 | __Ss], [__T | __Stack]);
+yeccpars2(276, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(277, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(module_attribute, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(278, '=', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 284, [278 | __Ss], [__T | __Stack]);
+yeccpars2(278, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(279, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 282, [279 | __Ss], [__T | __Stack]);
+yeccpars2(279, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(attribute_list, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(280, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 281, [280 | __Ss], [__T | __Stack]);
+yeccpars2(280, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(281, __Cat, __Ss, [__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __3,
+ __Nss = lists:nthtail(3, __Ss),
+ yeccpars2(yeccgoto(module_attribute, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(282, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 278, [282 | __Ss], [__T | __Stack]);
+yeccpars2(282, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(283, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(attribute_list, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(284, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 285, [284 | __Ss], [__T | __Stack]);
+yeccpars2(284, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 290, [284 | __Ss], [__T | __Stack]);
+yeccpars2(284, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [284 | __Ss], [__T | __Stack]);
+yeccpars2(284, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [284 | __Ss], [__T | __Stack]);
+yeccpars2(284, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [284 | __Ss], [__T | __Stack]);
+yeccpars2(284, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [284 | __Ss], [__T | __Stack]);
+yeccpars2(284, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [284 | __Ss], [__T | __Stack]);
+yeccpars2(284, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(285, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 285, [285 | __Ss], [__T | __Stack]);
+yeccpars2(285, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 290, [285 | __Ss], [__T | __Stack]);
+yeccpars2(285, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [285 | __Ss], [__T | __Stack]);
+yeccpars2(285, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [285 | __Ss], [__T | __Stack]);
+yeccpars2(285, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [285 | __Ss], [__T | __Stack]);
+yeccpars2(285, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [285 | __Ss], [__T | __Stack]);
+yeccpars2(285, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [285 | __Ss], [__T | __Stack]);
+yeccpars2(285, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 149, [285 | __Ss], [__T | __Stack]);
+yeccpars2(285, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(286, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(287, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(288, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_def{name = #c_atom{val = tok_val(__1)}, val = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(attribute, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(289, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(290, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 285, [290 | __Ss], [__T | __Stack]);
+yeccpars2(290, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 290, [290 | __Ss], [__T | __Stack]);
+yeccpars2(290, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 293, [290 | __Ss], [__T | __Stack]);
+yeccpars2(290, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [290 | __Ss], [__T | __Stack]);
+yeccpars2(290, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [290 | __Ss], [__T | __Stack]);
+yeccpars2(290, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [290 | __Ss], [__T | __Stack]);
+yeccpars2(290, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [290 | __Ss], [__T | __Stack]);
+yeccpars2(290, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [290 | __Ss], [__T | __Stack]);
+yeccpars2(290, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(291, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 295, [291 | __Ss], [__T | __Stack]);
+yeccpars2(291, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(literals, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(292, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 294, [292 | __Ss], [__T | __Stack]);
+yeccpars2(292, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(293, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_tuple{es = []},
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(tuple_literal, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(294, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_tuple{es = __2},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tuple_literal, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(295, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 285, [295 | __Ss], [__T | __Stack]);
+yeccpars2(295, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 290, [295 | __Ss], [__T | __Stack]);
+yeccpars2(295, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [295 | __Ss], [__T | __Stack]);
+yeccpars2(295, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [295 | __Ss], [__T | __Stack]);
+yeccpars2(295, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [295 | __Ss], [__T | __Stack]);
+yeccpars2(295, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [295 | __Ss], [__T | __Stack]);
+yeccpars2(295, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [295 | __Ss], [__T | __Stack]);
+yeccpars2(295, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(296, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(literals, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(297, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 298, [297 | __Ss], [__T | __Stack]);
+yeccpars2(297, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 301, [297 | __Ss], [__T | __Stack]);
+yeccpars2(297, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 299, [297 | __Ss], [__T | __Stack]);
+yeccpars2(297, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(298, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 285, [298 | __Ss], [__T | __Stack]);
+yeccpars2(298, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 290, [298 | __Ss], [__T | __Stack]);
+yeccpars2(298, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [298 | __Ss], [__T | __Stack]);
+yeccpars2(298, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [298 | __Ss], [__T | __Stack]);
+yeccpars2(298, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [298 | __Ss], [__T | __Stack]);
+yeccpars2(298, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [298 | __Ss], [__T | __Stack]);
+yeccpars2(298, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [298 | __Ss], [__T | __Stack]);
+yeccpars2(298, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(299, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_nil{},
+ yeccpars2(yeccgoto(tail_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(300, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_cons{hd = __2, tl = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(cons_literal, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(301, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 285, [301 | __Ss], [__T | __Stack]);
+yeccpars2(301, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 290, [301 | __Ss], [__T | __Stack]);
+yeccpars2(301, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [301 | __Ss], [__T | __Stack]);
+yeccpars2(301, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [301 | __Ss], [__T | __Stack]);
+yeccpars2(301, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [301 | __Ss], [__T | __Stack]);
+yeccpars2(301, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [301 | __Ss], [__T | __Stack]);
+yeccpars2(301, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [301 | __Ss], [__T | __Stack]);
+yeccpars2(301, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(302, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 303, [302 | __Ss], [__T | __Stack]);
+yeccpars2(302, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(303, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __2,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tail_literal, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(304, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 298, [304 | __Ss], [__T | __Stack]);
+yeccpars2(304, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 301, [304 | __Ss], [__T | __Stack]);
+yeccpars2(304, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 299, [304 | __Ss], [__T | __Stack]);
+yeccpars2(304, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(305, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_cons{hd = __2, tl = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tail_literal, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(306, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [],
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(module_export, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(307, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 311, [307 | __Ss], [__T | __Stack]);
+yeccpars2(307, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(exported_names, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(308, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 310, [308 | __Ss], [__T | __Stack]);
+yeccpars2(308, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(309, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(exported_name, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(310, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __2,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(module_export, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(311, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 11, [311 | __Ss], [__T | __Stack]);
+yeccpars2(311, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(312, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(exported_names, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(313, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 314, [313 | __Ss], [__T | __Stack]);
+yeccpars2(313, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(314, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 5, [314 | __Ss], [__T | __Stack]);
+yeccpars2(314, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(315, 'attributes', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 7, [315 | __Ss], [__T | __Stack]);
+yeccpars2(315, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(316, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 9, [316 | __Ss], [__T | __Stack]);
+yeccpars2(316, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 11, [316 | __Ss], [__T | __Stack]);
+yeccpars2(316, __Cat, __Ss, __Stack, __T, __Ts, __Tzr) ->
+ __Val = [],
+ yeccpars2(13, __Cat, [316 | __Ss], [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(317, 'end', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 318, [317 | __Ss], [__T | __Stack]);
+yeccpars2(317, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(318, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 319, [318 | __Ss], [__T | __Stack]);
+yeccpars2(318, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(319, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 126, [319 | __Ss], [__T | __Stack]);
+yeccpars2(319, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(320, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 321, [320 | __Ss], [__T | __Stack]);
+yeccpars2(320, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(321, __Cat, __Ss, [__10,__9,__8,__7,__6,__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_module{anno = __9, name = tok_val(__3), exports = __4, attrs = __5, defs = __6},
+ __Nss = lists:nthtail(9, __Ss),
+ yeccpars2(yeccgoto(module_definition, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(__Other, _, _, _, _, _, _) ->
+ exit({parser, __Other, missing_state_in_action_table}).
+
+yeccgoto(anno_clause, 65) ->
+ 100;
+yeccgoto(anno_clause, 100) ->
+ 100;
+yeccgoto(anno_clause, 222) ->
+ 100;
+yeccgoto(anno_clauses, 65) ->
+ 101;
+yeccgoto(anno_clauses, 100) ->
+ 169;
+yeccgoto(anno_clauses, 222) ->
+ 223;
+yeccgoto(anno_expression, 33) ->
+ 38;
+yeccgoto(anno_expression, 36) ->
+ 75;
+yeccgoto(anno_expression, 37) ->
+ 231;
+yeccgoto(anno_expression, 40) ->
+ 229;
+yeccgoto(anno_expression, 44) ->
+ 225;
+yeccgoto(anno_expression, 46) ->
+ 221;
+yeccgoto(anno_expression, 48) ->
+ 220;
+yeccgoto(anno_expression, 52) ->
+ 218;
+yeccgoto(anno_expression, 70) ->
+ 81;
+yeccgoto(anno_expression, 74) ->
+ 75;
+yeccgoto(anno_expression, 79) ->
+ 75;
+yeccgoto(anno_expression, 86) ->
+ 87;
+yeccgoto(anno_expression, 90) ->
+ 91;
+yeccgoto(anno_expression, 99) ->
+ 170;
+yeccgoto(anno_expression, 164) ->
+ 165;
+yeccgoto(anno_expression, 166) ->
+ 167;
+yeccgoto(anno_expression, 171) ->
+ 172;
+yeccgoto(anno_expression, 203) ->
+ 75;
+yeccgoto(anno_expression, 211) ->
+ 212;
+yeccgoto(anno_expression, 214) ->
+ 215;
+yeccgoto(anno_expression, 216) ->
+ 217;
+yeccgoto(anno_expression, 218) ->
+ 219;
+yeccgoto(anno_expression, 226) ->
+ 227;
+yeccgoto(anno_expression, 232) ->
+ 238;
+yeccgoto(anno_expression, 235) ->
+ 236;
+yeccgoto(anno_expression, 257) ->
+ 258;
+yeccgoto(anno_expression, 260) ->
+ 75;
+yeccgoto(anno_expression, 265) ->
+ 266;
+yeccgoto(anno_expressions, 36) ->
+ 241;
+yeccgoto(anno_expressions, 74) ->
+ 76;
+yeccgoto(anno_expressions, 79) ->
+ 80;
+yeccgoto(anno_expressions, 203) ->
+ 206;
+yeccgoto(anno_expressions, 260) ->
+ 261;
+yeccgoto(anno_fun, 20) ->
+ 22;
+yeccgoto(anno_function_name, 8) ->
+ 10;
+yeccgoto(anno_function_name, 12) ->
+ 10;
+yeccgoto(anno_function_name, 60) ->
+ 10;
+yeccgoto(anno_function_name, 316) ->
+ 10;
+yeccgoto(anno_pattern, 65) ->
+ 102;
+yeccgoto(anno_pattern, 96) ->
+ 102;
+yeccgoto(anno_pattern, 97) ->
+ 116;
+yeccgoto(anno_pattern, 98) ->
+ 173;
+yeccgoto(anno_pattern, 100) ->
+ 102;
+yeccgoto(anno_pattern, 114) ->
+ 116;
+yeccgoto(anno_pattern, 120) ->
+ 116;
+yeccgoto(anno_pattern, 162) ->
+ 163;
+yeccgoto(anno_pattern, 174) ->
+ 180;
+yeccgoto(anno_pattern, 177) ->
+ 178;
+yeccgoto(anno_pattern, 200) ->
+ 201;
+yeccgoto(anno_pattern, 222) ->
+ 102;
+yeccgoto(anno_patterns, 97) ->
+ 183;
+yeccgoto(anno_patterns, 114) ->
+ 117;
+yeccgoto(anno_patterns, 120) ->
+ 121;
+yeccgoto(anno_variable, 25) ->
+ 28;
+yeccgoto(anno_variable, 58) ->
+ 84;
+yeccgoto(anno_variable, 65) ->
+ 103;
+yeccgoto(anno_variable, 82) ->
+ 84;
+yeccgoto(anno_variable, 83) ->
+ 28;
+yeccgoto(anno_variable, 88) ->
+ 84;
+yeccgoto(anno_variable, 96) ->
+ 103;
+yeccgoto(anno_variable, 97) ->
+ 103;
+yeccgoto(anno_variable, 98) ->
+ 103;
+yeccgoto(anno_variable, 100) ->
+ 103;
+yeccgoto(anno_variable, 114) ->
+ 103;
+yeccgoto(anno_variable, 115) ->
+ 122;
+yeccgoto(anno_variable, 120) ->
+ 103;
+yeccgoto(anno_variable, 162) ->
+ 103;
+yeccgoto(anno_variable, 174) ->
+ 103;
+yeccgoto(anno_variable, 177) ->
+ 103;
+yeccgoto(anno_variable, 200) ->
+ 103;
+yeccgoto(anno_variable, 222) ->
+ 103;
+yeccgoto(anno_variable, 263) ->
+ 28;
+yeccgoto(anno_variables, 25) ->
+ 29;
+yeccgoto(anno_variables, 83) ->
+ 93;
+yeccgoto(anno_variables, 263) ->
+ 264;
+yeccgoto(annotation, 125) ->
+ 127;
+yeccgoto(annotation, 159) ->
+ 160;
+yeccgoto(annotation, 187) ->
+ 188;
+yeccgoto(annotation, 244) ->
+ 245;
+yeccgoto(annotation, 269) ->
+ 270;
+yeccgoto(annotation, 273) ->
+ 274;
+yeccgoto(annotation, 319) ->
+ 320;
+yeccgoto(application_expr, 33) ->
+ 39;
+yeccgoto(application_expr, 35) ->
+ 39;
+yeccgoto(application_expr, 36) ->
+ 39;
+yeccgoto(application_expr, 37) ->
+ 39;
+yeccgoto(application_expr, 40) ->
+ 39;
+yeccgoto(application_expr, 44) ->
+ 39;
+yeccgoto(application_expr, 46) ->
+ 39;
+yeccgoto(application_expr, 48) ->
+ 39;
+yeccgoto(application_expr, 52) ->
+ 39;
+yeccgoto(application_expr, 70) ->
+ 39;
+yeccgoto(application_expr, 74) ->
+ 39;
+yeccgoto(application_expr, 79) ->
+ 39;
+yeccgoto(application_expr, 86) ->
+ 39;
+yeccgoto(application_expr, 90) ->
+ 39;
+yeccgoto(application_expr, 99) ->
+ 39;
+yeccgoto(application_expr, 164) ->
+ 39;
+yeccgoto(application_expr, 166) ->
+ 39;
+yeccgoto(application_expr, 171) ->
+ 39;
+yeccgoto(application_expr, 203) ->
+ 39;
+yeccgoto(application_expr, 211) ->
+ 39;
+yeccgoto(application_expr, 214) ->
+ 39;
+yeccgoto(application_expr, 216) ->
+ 39;
+yeccgoto(application_expr, 218) ->
+ 39;
+yeccgoto(application_expr, 226) ->
+ 39;
+yeccgoto(application_expr, 232) ->
+ 39;
+yeccgoto(application_expr, 235) ->
+ 39;
+yeccgoto(application_expr, 257) ->
+ 39;
+yeccgoto(application_expr, 260) ->
+ 39;
+yeccgoto(application_expr, 265) ->
+ 39;
+yeccgoto(arg_list, 202) ->
+ 204;
+yeccgoto(arg_list, 208) ->
+ 209;
+yeccgoto(arg_list, 227) ->
+ 228;
+yeccgoto(arg_list, 229) ->
+ 230;
+yeccgoto(atomic_constant, 126) ->
+ 132;
+yeccgoto(atomic_constant, 129) ->
+ 132;
+yeccgoto(atomic_constant, 142) ->
+ 132;
+yeccgoto(atomic_constant, 147) ->
+ 132;
+yeccgoto(atomic_constant, 151) ->
+ 132;
+yeccgoto(atomic_constant, 154) ->
+ 132;
+yeccgoto(atomic_literal, 33) ->
+ 42;
+yeccgoto(atomic_literal, 35) ->
+ 42;
+yeccgoto(atomic_literal, 36) ->
+ 42;
+yeccgoto(atomic_literal, 37) ->
+ 42;
+yeccgoto(atomic_literal, 40) ->
+ 42;
+yeccgoto(atomic_literal, 44) ->
+ 42;
+yeccgoto(atomic_literal, 46) ->
+ 42;
+yeccgoto(atomic_literal, 48) ->
+ 42;
+yeccgoto(atomic_literal, 52) ->
+ 42;
+yeccgoto(atomic_literal, 65) ->
+ 105;
+yeccgoto(atomic_literal, 70) ->
+ 42;
+yeccgoto(atomic_literal, 74) ->
+ 42;
+yeccgoto(atomic_literal, 79) ->
+ 42;
+yeccgoto(atomic_literal, 86) ->
+ 42;
+yeccgoto(atomic_literal, 90) ->
+ 42;
+yeccgoto(atomic_literal, 96) ->
+ 105;
+yeccgoto(atomic_literal, 97) ->
+ 105;
+yeccgoto(atomic_literal, 98) ->
+ 105;
+yeccgoto(atomic_literal, 99) ->
+ 42;
+yeccgoto(atomic_literal, 100) ->
+ 105;
+yeccgoto(atomic_literal, 114) ->
+ 105;
+yeccgoto(atomic_literal, 115) ->
+ 105;
+yeccgoto(atomic_literal, 120) ->
+ 105;
+yeccgoto(atomic_literal, 162) ->
+ 105;
+yeccgoto(atomic_literal, 164) ->
+ 42;
+yeccgoto(atomic_literal, 166) ->
+ 42;
+yeccgoto(atomic_literal, 171) ->
+ 42;
+yeccgoto(atomic_literal, 174) ->
+ 105;
+yeccgoto(atomic_literal, 177) ->
+ 105;
+yeccgoto(atomic_literal, 200) ->
+ 105;
+yeccgoto(atomic_literal, 203) ->
+ 42;
+yeccgoto(atomic_literal, 211) ->
+ 42;
+yeccgoto(atomic_literal, 214) ->
+ 42;
+yeccgoto(atomic_literal, 216) ->
+ 42;
+yeccgoto(atomic_literal, 218) ->
+ 42;
+yeccgoto(atomic_literal, 222) ->
+ 105;
+yeccgoto(atomic_literal, 226) ->
+ 42;
+yeccgoto(atomic_literal, 232) ->
+ 42;
+yeccgoto(atomic_literal, 235) ->
+ 42;
+yeccgoto(atomic_literal, 257) ->
+ 42;
+yeccgoto(atomic_literal, 260) ->
+ 42;
+yeccgoto(atomic_literal, 265) ->
+ 42;
+yeccgoto(atomic_literal, 284) ->
+ 286;
+yeccgoto(atomic_literal, 285) ->
+ 286;
+yeccgoto(atomic_literal, 290) ->
+ 286;
+yeccgoto(atomic_literal, 295) ->
+ 286;
+yeccgoto(atomic_literal, 298) ->
+ 286;
+yeccgoto(atomic_literal, 301) ->
+ 286;
+yeccgoto(atomic_pattern, 65) ->
+ 106;
+yeccgoto(atomic_pattern, 96) ->
+ 106;
+yeccgoto(atomic_pattern, 97) ->
+ 106;
+yeccgoto(atomic_pattern, 98) ->
+ 106;
+yeccgoto(atomic_pattern, 100) ->
+ 106;
+yeccgoto(atomic_pattern, 114) ->
+ 106;
+yeccgoto(atomic_pattern, 115) ->
+ 106;
+yeccgoto(atomic_pattern, 120) ->
+ 106;
+yeccgoto(atomic_pattern, 162) ->
+ 106;
+yeccgoto(atomic_pattern, 174) ->
+ 106;
+yeccgoto(atomic_pattern, 177) ->
+ 106;
+yeccgoto(atomic_pattern, 200) ->
+ 106;
+yeccgoto(atomic_pattern, 222) ->
+ 106;
+yeccgoto(attribute, 276) ->
+ 279;
+yeccgoto(attribute, 282) ->
+ 279;
+yeccgoto(attribute_list, 276) ->
+ 280;
+yeccgoto(attribute_list, 282) ->
+ 283;
+yeccgoto(binary, 33) ->
+ 43;
+yeccgoto(binary, 35) ->
+ 43;
+yeccgoto(binary, 36) ->
+ 43;
+yeccgoto(binary, 37) ->
+ 43;
+yeccgoto(binary, 40) ->
+ 43;
+yeccgoto(binary, 44) ->
+ 43;
+yeccgoto(binary, 46) ->
+ 43;
+yeccgoto(binary, 48) ->
+ 43;
+yeccgoto(binary, 52) ->
+ 43;
+yeccgoto(binary, 70) ->
+ 43;
+yeccgoto(binary, 74) ->
+ 43;
+yeccgoto(binary, 79) ->
+ 43;
+yeccgoto(binary, 86) ->
+ 43;
+yeccgoto(binary, 90) ->
+ 43;
+yeccgoto(binary, 99) ->
+ 43;
+yeccgoto(binary, 164) ->
+ 43;
+yeccgoto(binary, 166) ->
+ 43;
+yeccgoto(binary, 171) ->
+ 43;
+yeccgoto(binary, 203) ->
+ 43;
+yeccgoto(binary, 211) ->
+ 43;
+yeccgoto(binary, 214) ->
+ 43;
+yeccgoto(binary, 216) ->
+ 43;
+yeccgoto(binary, 218) ->
+ 43;
+yeccgoto(binary, 226) ->
+ 43;
+yeccgoto(binary, 232) ->
+ 43;
+yeccgoto(binary, 235) ->
+ 43;
+yeccgoto(binary, 257) ->
+ 43;
+yeccgoto(binary, 260) ->
+ 43;
+yeccgoto(binary, 265) ->
+ 43;
+yeccgoto(binary_pattern, 65) ->
+ 107;
+yeccgoto(binary_pattern, 96) ->
+ 107;
+yeccgoto(binary_pattern, 97) ->
+ 107;
+yeccgoto(binary_pattern, 98) ->
+ 107;
+yeccgoto(binary_pattern, 100) ->
+ 107;
+yeccgoto(binary_pattern, 114) ->
+ 107;
+yeccgoto(binary_pattern, 115) ->
+ 107;
+yeccgoto(binary_pattern, 120) ->
+ 107;
+yeccgoto(binary_pattern, 162) ->
+ 107;
+yeccgoto(binary_pattern, 174) ->
+ 107;
+yeccgoto(binary_pattern, 177) ->
+ 107;
+yeccgoto(binary_pattern, 200) ->
+ 107;
+yeccgoto(binary_pattern, 222) ->
+ 107;
+yeccgoto(call_expr, 33) ->
+ 45;
+yeccgoto(call_expr, 35) ->
+ 45;
+yeccgoto(call_expr, 36) ->
+ 45;
+yeccgoto(call_expr, 37) ->
+ 45;
+yeccgoto(call_expr, 40) ->
+ 45;
+yeccgoto(call_expr, 44) ->
+ 45;
+yeccgoto(call_expr, 46) ->
+ 45;
+yeccgoto(call_expr, 48) ->
+ 45;
+yeccgoto(call_expr, 52) ->
+ 45;
+yeccgoto(call_expr, 70) ->
+ 45;
+yeccgoto(call_expr, 74) ->
+ 45;
+yeccgoto(call_expr, 79) ->
+ 45;
+yeccgoto(call_expr, 86) ->
+ 45;
+yeccgoto(call_expr, 90) ->
+ 45;
+yeccgoto(call_expr, 99) ->
+ 45;
+yeccgoto(call_expr, 164) ->
+ 45;
+yeccgoto(call_expr, 166) ->
+ 45;
+yeccgoto(call_expr, 171) ->
+ 45;
+yeccgoto(call_expr, 203) ->
+ 45;
+yeccgoto(call_expr, 211) ->
+ 45;
+yeccgoto(call_expr, 214) ->
+ 45;
+yeccgoto(call_expr, 216) ->
+ 45;
+yeccgoto(call_expr, 218) ->
+ 45;
+yeccgoto(call_expr, 226) ->
+ 45;
+yeccgoto(call_expr, 232) ->
+ 45;
+yeccgoto(call_expr, 235) ->
+ 45;
+yeccgoto(call_expr, 257) ->
+ 45;
+yeccgoto(call_expr, 260) ->
+ 45;
+yeccgoto(call_expr, 265) ->
+ 45;
+yeccgoto(case_expr, 33) ->
+ 47;
+yeccgoto(case_expr, 35) ->
+ 47;
+yeccgoto(case_expr, 36) ->
+ 47;
+yeccgoto(case_expr, 37) ->
+ 47;
+yeccgoto(case_expr, 40) ->
+ 47;
+yeccgoto(case_expr, 44) ->
+ 47;
+yeccgoto(case_expr, 46) ->
+ 47;
+yeccgoto(case_expr, 48) ->
+ 47;
+yeccgoto(case_expr, 52) ->
+ 47;
+yeccgoto(case_expr, 70) ->
+ 47;
+yeccgoto(case_expr, 74) ->
+ 47;
+yeccgoto(case_expr, 79) ->
+ 47;
+yeccgoto(case_expr, 86) ->
+ 47;
+yeccgoto(case_expr, 90) ->
+ 47;
+yeccgoto(case_expr, 99) ->
+ 47;
+yeccgoto(case_expr, 164) ->
+ 47;
+yeccgoto(case_expr, 166) ->
+ 47;
+yeccgoto(case_expr, 171) ->
+ 47;
+yeccgoto(case_expr, 203) ->
+ 47;
+yeccgoto(case_expr, 211) ->
+ 47;
+yeccgoto(case_expr, 214) ->
+ 47;
+yeccgoto(case_expr, 216) ->
+ 47;
+yeccgoto(case_expr, 218) ->
+ 47;
+yeccgoto(case_expr, 226) ->
+ 47;
+yeccgoto(case_expr, 232) ->
+ 47;
+yeccgoto(case_expr, 235) ->
+ 47;
+yeccgoto(case_expr, 257) ->
+ 47;
+yeccgoto(case_expr, 260) ->
+ 47;
+yeccgoto(case_expr, 265) ->
+ 47;
+yeccgoto(catch_expr, 33) ->
+ 49;
+yeccgoto(catch_expr, 35) ->
+ 49;
+yeccgoto(catch_expr, 36) ->
+ 49;
+yeccgoto(catch_expr, 37) ->
+ 49;
+yeccgoto(catch_expr, 40) ->
+ 49;
+yeccgoto(catch_expr, 44) ->
+ 49;
+yeccgoto(catch_expr, 46) ->
+ 49;
+yeccgoto(catch_expr, 48) ->
+ 49;
+yeccgoto(catch_expr, 52) ->
+ 49;
+yeccgoto(catch_expr, 70) ->
+ 49;
+yeccgoto(catch_expr, 74) ->
+ 49;
+yeccgoto(catch_expr, 79) ->
+ 49;
+yeccgoto(catch_expr, 86) ->
+ 49;
+yeccgoto(catch_expr, 90) ->
+ 49;
+yeccgoto(catch_expr, 99) ->
+ 49;
+yeccgoto(catch_expr, 164) ->
+ 49;
+yeccgoto(catch_expr, 166) ->
+ 49;
+yeccgoto(catch_expr, 171) ->
+ 49;
+yeccgoto(catch_expr, 203) ->
+ 49;
+yeccgoto(catch_expr, 211) ->
+ 49;
+yeccgoto(catch_expr, 214) ->
+ 49;
+yeccgoto(catch_expr, 216) ->
+ 49;
+yeccgoto(catch_expr, 218) ->
+ 49;
+yeccgoto(catch_expr, 226) ->
+ 49;
+yeccgoto(catch_expr, 232) ->
+ 49;
+yeccgoto(catch_expr, 235) ->
+ 49;
+yeccgoto(catch_expr, 257) ->
+ 49;
+yeccgoto(catch_expr, 260) ->
+ 49;
+yeccgoto(catch_expr, 265) ->
+ 49;
+yeccgoto(clause, 65) ->
+ 108;
+yeccgoto(clause, 96) ->
+ 185;
+yeccgoto(clause, 100) ->
+ 108;
+yeccgoto(clause, 222) ->
+ 108;
+yeccgoto(clause_pattern, 65) ->
+ 109;
+yeccgoto(clause_pattern, 96) ->
+ 109;
+yeccgoto(clause_pattern, 100) ->
+ 109;
+yeccgoto(clause_pattern, 222) ->
+ 109;
+yeccgoto(cons, 33) ->
+ 51;
+yeccgoto(cons, 35) ->
+ 51;
+yeccgoto(cons, 36) ->
+ 51;
+yeccgoto(cons, 37) ->
+ 51;
+yeccgoto(cons, 40) ->
+ 51;
+yeccgoto(cons, 44) ->
+ 51;
+yeccgoto(cons, 46) ->
+ 51;
+yeccgoto(cons, 48) ->
+ 51;
+yeccgoto(cons, 52) ->
+ 51;
+yeccgoto(cons, 70) ->
+ 51;
+yeccgoto(cons, 74) ->
+ 51;
+yeccgoto(cons, 79) ->
+ 51;
+yeccgoto(cons, 86) ->
+ 51;
+yeccgoto(cons, 90) ->
+ 51;
+yeccgoto(cons, 99) ->
+ 51;
+yeccgoto(cons, 164) ->
+ 51;
+yeccgoto(cons, 166) ->
+ 51;
+yeccgoto(cons, 171) ->
+ 51;
+yeccgoto(cons, 203) ->
+ 51;
+yeccgoto(cons, 211) ->
+ 51;
+yeccgoto(cons, 214) ->
+ 51;
+yeccgoto(cons, 216) ->
+ 51;
+yeccgoto(cons, 218) ->
+ 51;
+yeccgoto(cons, 226) ->
+ 51;
+yeccgoto(cons, 232) ->
+ 51;
+yeccgoto(cons, 235) ->
+ 51;
+yeccgoto(cons, 257) ->
+ 51;
+yeccgoto(cons, 260) ->
+ 51;
+yeccgoto(cons, 265) ->
+ 51;
+yeccgoto(cons_constant, 126) ->
+ 134;
+yeccgoto(cons_constant, 129) ->
+ 134;
+yeccgoto(cons_constant, 142) ->
+ 134;
+yeccgoto(cons_constant, 147) ->
+ 134;
+yeccgoto(cons_constant, 151) ->
+ 134;
+yeccgoto(cons_constant, 154) ->
+ 134;
+yeccgoto(cons_literal, 284) ->
+ 287;
+yeccgoto(cons_literal, 285) ->
+ 287;
+yeccgoto(cons_literal, 290) ->
+ 287;
+yeccgoto(cons_literal, 295) ->
+ 287;
+yeccgoto(cons_literal, 298) ->
+ 287;
+yeccgoto(cons_literal, 301) ->
+ 287;
+yeccgoto(cons_pattern, 65) ->
+ 110;
+yeccgoto(cons_pattern, 96) ->
+ 110;
+yeccgoto(cons_pattern, 97) ->
+ 110;
+yeccgoto(cons_pattern, 98) ->
+ 110;
+yeccgoto(cons_pattern, 100) ->
+ 110;
+yeccgoto(cons_pattern, 114) ->
+ 110;
+yeccgoto(cons_pattern, 115) ->
+ 110;
+yeccgoto(cons_pattern, 120) ->
+ 110;
+yeccgoto(cons_pattern, 162) ->
+ 110;
+yeccgoto(cons_pattern, 174) ->
+ 110;
+yeccgoto(cons_pattern, 177) ->
+ 110;
+yeccgoto(cons_pattern, 200) ->
+ 110;
+yeccgoto(cons_pattern, 222) ->
+ 110;
+yeccgoto(constant, 126) ->
+ 135;
+yeccgoto(constant, 129) ->
+ 150;
+yeccgoto(constant, 142) ->
+ 135;
+yeccgoto(constant, 147) ->
+ 135;
+yeccgoto(constant, 151) ->
+ 157;
+yeccgoto(constant, 154) ->
+ 155;
+yeccgoto(constants, 126) ->
+ 136;
+yeccgoto(constants, 142) ->
+ 143;
+yeccgoto(constants, 147) ->
+ 148;
+yeccgoto(exported_name, 5) ->
+ 307;
+yeccgoto(exported_name, 311) ->
+ 307;
+yeccgoto(exported_names, 5) ->
+ 308;
+yeccgoto(exported_names, 311) ->
+ 312;
+yeccgoto(expression, 33) ->
+ 53;
+yeccgoto(expression, 35) ->
+ 243;
+yeccgoto(expression, 36) ->
+ 53;
+yeccgoto(expression, 37) ->
+ 53;
+yeccgoto(expression, 40) ->
+ 53;
+yeccgoto(expression, 44) ->
+ 53;
+yeccgoto(expression, 46) ->
+ 53;
+yeccgoto(expression, 48) ->
+ 53;
+yeccgoto(expression, 52) ->
+ 53;
+yeccgoto(expression, 70) ->
+ 53;
+yeccgoto(expression, 74) ->
+ 53;
+yeccgoto(expression, 79) ->
+ 53;
+yeccgoto(expression, 86) ->
+ 53;
+yeccgoto(expression, 90) ->
+ 53;
+yeccgoto(expression, 99) ->
+ 53;
+yeccgoto(expression, 164) ->
+ 53;
+yeccgoto(expression, 166) ->
+ 53;
+yeccgoto(expression, 171) ->
+ 53;
+yeccgoto(expression, 203) ->
+ 53;
+yeccgoto(expression, 211) ->
+ 53;
+yeccgoto(expression, 214) ->
+ 53;
+yeccgoto(expression, 216) ->
+ 53;
+yeccgoto(expression, 218) ->
+ 53;
+yeccgoto(expression, 226) ->
+ 53;
+yeccgoto(expression, 232) ->
+ 53;
+yeccgoto(expression, 235) ->
+ 53;
+yeccgoto(expression, 257) ->
+ 53;
+yeccgoto(expression, 260) ->
+ 53;
+yeccgoto(expression, 265) ->
+ 53;
+yeccgoto(fun_expr, 20) ->
+ 24;
+yeccgoto(fun_expr, 21) ->
+ 268;
+yeccgoto(fun_expr, 33) ->
+ 55;
+yeccgoto(fun_expr, 35) ->
+ 55;
+yeccgoto(fun_expr, 36) ->
+ 55;
+yeccgoto(fun_expr, 37) ->
+ 55;
+yeccgoto(fun_expr, 40) ->
+ 55;
+yeccgoto(fun_expr, 44) ->
+ 55;
+yeccgoto(fun_expr, 46) ->
+ 55;
+yeccgoto(fun_expr, 48) ->
+ 55;
+yeccgoto(fun_expr, 52) ->
+ 55;
+yeccgoto(fun_expr, 70) ->
+ 55;
+yeccgoto(fun_expr, 74) ->
+ 55;
+yeccgoto(fun_expr, 79) ->
+ 55;
+yeccgoto(fun_expr, 86) ->
+ 55;
+yeccgoto(fun_expr, 90) ->
+ 55;
+yeccgoto(fun_expr, 99) ->
+ 55;
+yeccgoto(fun_expr, 164) ->
+ 55;
+yeccgoto(fun_expr, 166) ->
+ 55;
+yeccgoto(fun_expr, 171) ->
+ 55;
+yeccgoto(fun_expr, 203) ->
+ 55;
+yeccgoto(fun_expr, 211) ->
+ 55;
+yeccgoto(fun_expr, 214) ->
+ 55;
+yeccgoto(fun_expr, 216) ->
+ 55;
+yeccgoto(fun_expr, 218) ->
+ 55;
+yeccgoto(fun_expr, 226) ->
+ 55;
+yeccgoto(fun_expr, 232) ->
+ 55;
+yeccgoto(fun_expr, 235) ->
+ 55;
+yeccgoto(fun_expr, 257) ->
+ 55;
+yeccgoto(fun_expr, 260) ->
+ 55;
+yeccgoto(fun_expr, 265) ->
+ 55;
+yeccgoto(function_definition, 8) ->
+ 12;
+yeccgoto(function_definition, 12) ->
+ 12;
+yeccgoto(function_definition, 60) ->
+ 12;
+yeccgoto(function_definition, 316) ->
+ 12;
+yeccgoto(function_definitions, 8) ->
+ 13;
+yeccgoto(function_definitions, 12) ->
+ 17;
+yeccgoto(function_definitions, 60) ->
+ 210;
+yeccgoto(function_definitions, 316) ->
+ 13;
+yeccgoto(function_name, 5) ->
+ 309;
+yeccgoto(function_name, 8) ->
+ 14;
+yeccgoto(function_name, 9) ->
+ 272;
+yeccgoto(function_name, 12) ->
+ 14;
+yeccgoto(function_name, 33) ->
+ 56;
+yeccgoto(function_name, 35) ->
+ 56;
+yeccgoto(function_name, 36) ->
+ 56;
+yeccgoto(function_name, 37) ->
+ 56;
+yeccgoto(function_name, 40) ->
+ 56;
+yeccgoto(function_name, 44) ->
+ 56;
+yeccgoto(function_name, 46) ->
+ 56;
+yeccgoto(function_name, 48) ->
+ 56;
+yeccgoto(function_name, 52) ->
+ 56;
+yeccgoto(function_name, 60) ->
+ 14;
+yeccgoto(function_name, 70) ->
+ 56;
+yeccgoto(function_name, 74) ->
+ 56;
+yeccgoto(function_name, 79) ->
+ 56;
+yeccgoto(function_name, 86) ->
+ 56;
+yeccgoto(function_name, 90) ->
+ 56;
+yeccgoto(function_name, 99) ->
+ 56;
+yeccgoto(function_name, 164) ->
+ 56;
+yeccgoto(function_name, 166) ->
+ 56;
+yeccgoto(function_name, 171) ->
+ 56;
+yeccgoto(function_name, 203) ->
+ 56;
+yeccgoto(function_name, 211) ->
+ 56;
+yeccgoto(function_name, 214) ->
+ 56;
+yeccgoto(function_name, 216) ->
+ 56;
+yeccgoto(function_name, 218) ->
+ 56;
+yeccgoto(function_name, 226) ->
+ 56;
+yeccgoto(function_name, 232) ->
+ 56;
+yeccgoto(function_name, 235) ->
+ 56;
+yeccgoto(function_name, 257) ->
+ 56;
+yeccgoto(function_name, 260) ->
+ 56;
+yeccgoto(function_name, 265) ->
+ 56;
+yeccgoto(function_name, 311) ->
+ 309;
+yeccgoto(function_name, 316) ->
+ 14;
+yeccgoto(let_expr, 33) ->
+ 59;
+yeccgoto(let_expr, 35) ->
+ 59;
+yeccgoto(let_expr, 36) ->
+ 59;
+yeccgoto(let_expr, 37) ->
+ 59;
+yeccgoto(let_expr, 40) ->
+ 59;
+yeccgoto(let_expr, 44) ->
+ 59;
+yeccgoto(let_expr, 46) ->
+ 59;
+yeccgoto(let_expr, 48) ->
+ 59;
+yeccgoto(let_expr, 52) ->
+ 59;
+yeccgoto(let_expr, 70) ->
+ 59;
+yeccgoto(let_expr, 74) ->
+ 59;
+yeccgoto(let_expr, 79) ->
+ 59;
+yeccgoto(let_expr, 86) ->
+ 59;
+yeccgoto(let_expr, 90) ->
+ 59;
+yeccgoto(let_expr, 99) ->
+ 59;
+yeccgoto(let_expr, 164) ->
+ 59;
+yeccgoto(let_expr, 166) ->
+ 59;
+yeccgoto(let_expr, 171) ->
+ 59;
+yeccgoto(let_expr, 203) ->
+ 59;
+yeccgoto(let_expr, 211) ->
+ 59;
+yeccgoto(let_expr, 214) ->
+ 59;
+yeccgoto(let_expr, 216) ->
+ 59;
+yeccgoto(let_expr, 218) ->
+ 59;
+yeccgoto(let_expr, 226) ->
+ 59;
+yeccgoto(let_expr, 232) ->
+ 59;
+yeccgoto(let_expr, 235) ->
+ 59;
+yeccgoto(let_expr, 257) ->
+ 59;
+yeccgoto(let_expr, 260) ->
+ 59;
+yeccgoto(let_expr, 265) ->
+ 59;
+yeccgoto(let_vars, 58) ->
+ 213;
+yeccgoto(let_vars, 82) ->
+ 85;
+yeccgoto(let_vars, 88) ->
+ 89;
+yeccgoto(letrec_expr, 33) ->
+ 61;
+yeccgoto(letrec_expr, 35) ->
+ 61;
+yeccgoto(letrec_expr, 36) ->
+ 61;
+yeccgoto(letrec_expr, 37) ->
+ 61;
+yeccgoto(letrec_expr, 40) ->
+ 61;
+yeccgoto(letrec_expr, 44) ->
+ 61;
+yeccgoto(letrec_expr, 46) ->
+ 61;
+yeccgoto(letrec_expr, 48) ->
+ 61;
+yeccgoto(letrec_expr, 52) ->
+ 61;
+yeccgoto(letrec_expr, 70) ->
+ 61;
+yeccgoto(letrec_expr, 74) ->
+ 61;
+yeccgoto(letrec_expr, 79) ->
+ 61;
+yeccgoto(letrec_expr, 86) ->
+ 61;
+yeccgoto(letrec_expr, 90) ->
+ 61;
+yeccgoto(letrec_expr, 99) ->
+ 61;
+yeccgoto(letrec_expr, 164) ->
+ 61;
+yeccgoto(letrec_expr, 166) ->
+ 61;
+yeccgoto(letrec_expr, 171) ->
+ 61;
+yeccgoto(letrec_expr, 203) ->
+ 61;
+yeccgoto(letrec_expr, 211) ->
+ 61;
+yeccgoto(letrec_expr, 214) ->
+ 61;
+yeccgoto(letrec_expr, 216) ->
+ 61;
+yeccgoto(letrec_expr, 218) ->
+ 61;
+yeccgoto(letrec_expr, 226) ->
+ 61;
+yeccgoto(letrec_expr, 232) ->
+ 61;
+yeccgoto(letrec_expr, 235) ->
+ 61;
+yeccgoto(letrec_expr, 257) ->
+ 61;
+yeccgoto(letrec_expr, 260) ->
+ 61;
+yeccgoto(letrec_expr, 265) ->
+ 61;
+yeccgoto(literal, 284) ->
+ 288;
+yeccgoto(literal, 285) ->
+ 297;
+yeccgoto(literal, 290) ->
+ 291;
+yeccgoto(literal, 295) ->
+ 291;
+yeccgoto(literal, 298) ->
+ 304;
+yeccgoto(literal, 301) ->
+ 302;
+yeccgoto(literals, 290) ->
+ 292;
+yeccgoto(literals, 295) ->
+ 296;
+yeccgoto(module_attribute, 6) ->
+ 8;
+yeccgoto(module_attribute, 315) ->
+ 316;
+yeccgoto(module_definition, 0) ->
+ 3;
+yeccgoto(module_defs, 8) ->
+ 15;
+yeccgoto(module_defs, 316) ->
+ 317;
+yeccgoto(module_export, 4) ->
+ 6;
+yeccgoto(module_export, 314) ->
+ 315;
+yeccgoto(nil, 33) ->
+ 62;
+yeccgoto(nil, 35) ->
+ 62;
+yeccgoto(nil, 36) ->
+ 62;
+yeccgoto(nil, 37) ->
+ 62;
+yeccgoto(nil, 40) ->
+ 62;
+yeccgoto(nil, 44) ->
+ 62;
+yeccgoto(nil, 46) ->
+ 62;
+yeccgoto(nil, 48) ->
+ 62;
+yeccgoto(nil, 52) ->
+ 62;
+yeccgoto(nil, 65) ->
+ 62;
+yeccgoto(nil, 70) ->
+ 62;
+yeccgoto(nil, 74) ->
+ 62;
+yeccgoto(nil, 79) ->
+ 62;
+yeccgoto(nil, 86) ->
+ 62;
+yeccgoto(nil, 90) ->
+ 62;
+yeccgoto(nil, 96) ->
+ 62;
+yeccgoto(nil, 97) ->
+ 62;
+yeccgoto(nil, 98) ->
+ 62;
+yeccgoto(nil, 99) ->
+ 62;
+yeccgoto(nil, 100) ->
+ 62;
+yeccgoto(nil, 114) ->
+ 62;
+yeccgoto(nil, 115) ->
+ 62;
+yeccgoto(nil, 120) ->
+ 62;
+yeccgoto(nil, 126) ->
+ 139;
+yeccgoto(nil, 129) ->
+ 139;
+yeccgoto(nil, 142) ->
+ 139;
+yeccgoto(nil, 147) ->
+ 139;
+yeccgoto(nil, 151) ->
+ 139;
+yeccgoto(nil, 154) ->
+ 139;
+yeccgoto(nil, 162) ->
+ 62;
+yeccgoto(nil, 164) ->
+ 62;
+yeccgoto(nil, 166) ->
+ 62;
+yeccgoto(nil, 171) ->
+ 62;
+yeccgoto(nil, 174) ->
+ 62;
+yeccgoto(nil, 177) ->
+ 62;
+yeccgoto(nil, 200) ->
+ 62;
+yeccgoto(nil, 203) ->
+ 62;
+yeccgoto(nil, 211) ->
+ 62;
+yeccgoto(nil, 214) ->
+ 62;
+yeccgoto(nil, 216) ->
+ 62;
+yeccgoto(nil, 218) ->
+ 62;
+yeccgoto(nil, 222) ->
+ 62;
+yeccgoto(nil, 226) ->
+ 62;
+yeccgoto(nil, 232) ->
+ 62;
+yeccgoto(nil, 235) ->
+ 62;
+yeccgoto(nil, 257) ->
+ 62;
+yeccgoto(nil, 260) ->
+ 62;
+yeccgoto(nil, 265) ->
+ 62;
+yeccgoto(nil, 284) ->
+ 62;
+yeccgoto(nil, 285) ->
+ 62;
+yeccgoto(nil, 290) ->
+ 62;
+yeccgoto(nil, 295) ->
+ 62;
+yeccgoto(nil, 298) ->
+ 62;
+yeccgoto(nil, 301) ->
+ 62;
+yeccgoto(other_pattern, 65) ->
+ 111;
+yeccgoto(other_pattern, 96) ->
+ 186;
+yeccgoto(other_pattern, 97) ->
+ 111;
+yeccgoto(other_pattern, 98) ->
+ 111;
+yeccgoto(other_pattern, 100) ->
+ 111;
+yeccgoto(other_pattern, 114) ->
+ 111;
+yeccgoto(other_pattern, 115) ->
+ 123;
+yeccgoto(other_pattern, 120) ->
+ 111;
+yeccgoto(other_pattern, 162) ->
+ 111;
+yeccgoto(other_pattern, 174) ->
+ 111;
+yeccgoto(other_pattern, 177) ->
+ 111;
+yeccgoto(other_pattern, 200) ->
+ 111;
+yeccgoto(other_pattern, 222) ->
+ 111;
+yeccgoto(primop_expr, 33) ->
+ 64;
+yeccgoto(primop_expr, 35) ->
+ 64;
+yeccgoto(primop_expr, 36) ->
+ 64;
+yeccgoto(primop_expr, 37) ->
+ 64;
+yeccgoto(primop_expr, 40) ->
+ 64;
+yeccgoto(primop_expr, 44) ->
+ 64;
+yeccgoto(primop_expr, 46) ->
+ 64;
+yeccgoto(primop_expr, 48) ->
+ 64;
+yeccgoto(primop_expr, 52) ->
+ 64;
+yeccgoto(primop_expr, 70) ->
+ 64;
+yeccgoto(primop_expr, 74) ->
+ 64;
+yeccgoto(primop_expr, 79) ->
+ 64;
+yeccgoto(primop_expr, 86) ->
+ 64;
+yeccgoto(primop_expr, 90) ->
+ 64;
+yeccgoto(primop_expr, 99) ->
+ 64;
+yeccgoto(primop_expr, 164) ->
+ 64;
+yeccgoto(primop_expr, 166) ->
+ 64;
+yeccgoto(primop_expr, 171) ->
+ 64;
+yeccgoto(primop_expr, 203) ->
+ 64;
+yeccgoto(primop_expr, 211) ->
+ 64;
+yeccgoto(primop_expr, 214) ->
+ 64;
+yeccgoto(primop_expr, 216) ->
+ 64;
+yeccgoto(primop_expr, 218) ->
+ 64;
+yeccgoto(primop_expr, 226) ->
+ 64;
+yeccgoto(primop_expr, 232) ->
+ 64;
+yeccgoto(primop_expr, 235) ->
+ 64;
+yeccgoto(primop_expr, 257) ->
+ 64;
+yeccgoto(primop_expr, 260) ->
+ 64;
+yeccgoto(primop_expr, 265) ->
+ 64;
+yeccgoto(receive_expr, 33) ->
+ 66;
+yeccgoto(receive_expr, 35) ->
+ 66;
+yeccgoto(receive_expr, 36) ->
+ 66;
+yeccgoto(receive_expr, 37) ->
+ 66;
+yeccgoto(receive_expr, 40) ->
+ 66;
+yeccgoto(receive_expr, 44) ->
+ 66;
+yeccgoto(receive_expr, 46) ->
+ 66;
+yeccgoto(receive_expr, 48) ->
+ 66;
+yeccgoto(receive_expr, 52) ->
+ 66;
+yeccgoto(receive_expr, 70) ->
+ 66;
+yeccgoto(receive_expr, 74) ->
+ 66;
+yeccgoto(receive_expr, 79) ->
+ 66;
+yeccgoto(receive_expr, 86) ->
+ 66;
+yeccgoto(receive_expr, 90) ->
+ 66;
+yeccgoto(receive_expr, 99) ->
+ 66;
+yeccgoto(receive_expr, 164) ->
+ 66;
+yeccgoto(receive_expr, 166) ->
+ 66;
+yeccgoto(receive_expr, 171) ->
+ 66;
+yeccgoto(receive_expr, 203) ->
+ 66;
+yeccgoto(receive_expr, 211) ->
+ 66;
+yeccgoto(receive_expr, 214) ->
+ 66;
+yeccgoto(receive_expr, 216) ->
+ 66;
+yeccgoto(receive_expr, 218) ->
+ 66;
+yeccgoto(receive_expr, 226) ->
+ 66;
+yeccgoto(receive_expr, 232) ->
+ 66;
+yeccgoto(receive_expr, 235) ->
+ 66;
+yeccgoto(receive_expr, 257) ->
+ 66;
+yeccgoto(receive_expr, 260) ->
+ 66;
+yeccgoto(receive_expr, 265) ->
+ 66;
+yeccgoto(segment, 247) ->
+ 249;
+yeccgoto(segment, 255) ->
+ 249;
+yeccgoto(segment_pattern, 190) ->
+ 192;
+yeccgoto(segment_pattern, 198) ->
+ 192;
+yeccgoto(segment_patterns, 190) ->
+ 193;
+yeccgoto(segment_patterns, 198) ->
+ 199;
+yeccgoto(segments, 247) ->
+ 250;
+yeccgoto(segments, 255) ->
+ 256;
+yeccgoto(sequence, 33) ->
+ 67;
+yeccgoto(sequence, 35) ->
+ 67;
+yeccgoto(sequence, 36) ->
+ 67;
+yeccgoto(sequence, 37) ->
+ 67;
+yeccgoto(sequence, 40) ->
+ 67;
+yeccgoto(sequence, 44) ->
+ 67;
+yeccgoto(sequence, 46) ->
+ 67;
+yeccgoto(sequence, 48) ->
+ 67;
+yeccgoto(sequence, 52) ->
+ 67;
+yeccgoto(sequence, 70) ->
+ 67;
+yeccgoto(sequence, 74) ->
+ 67;
+yeccgoto(sequence, 79) ->
+ 67;
+yeccgoto(sequence, 86) ->
+ 67;
+yeccgoto(sequence, 90) ->
+ 67;
+yeccgoto(sequence, 99) ->
+ 67;
+yeccgoto(sequence, 164) ->
+ 67;
+yeccgoto(sequence, 166) ->
+ 67;
+yeccgoto(sequence, 171) ->
+ 67;
+yeccgoto(sequence, 203) ->
+ 67;
+yeccgoto(sequence, 211) ->
+ 67;
+yeccgoto(sequence, 214) ->
+ 67;
+yeccgoto(sequence, 216) ->
+ 67;
+yeccgoto(sequence, 218) ->
+ 67;
+yeccgoto(sequence, 226) ->
+ 67;
+yeccgoto(sequence, 232) ->
+ 67;
+yeccgoto(sequence, 235) ->
+ 67;
+yeccgoto(sequence, 257) ->
+ 67;
+yeccgoto(sequence, 260) ->
+ 67;
+yeccgoto(sequence, 265) ->
+ 67;
+yeccgoto(single_expression, 33) ->
+ 68;
+yeccgoto(single_expression, 35) ->
+ 68;
+yeccgoto(single_expression, 36) ->
+ 68;
+yeccgoto(single_expression, 37) ->
+ 68;
+yeccgoto(single_expression, 40) ->
+ 68;
+yeccgoto(single_expression, 44) ->
+ 68;
+yeccgoto(single_expression, 46) ->
+ 68;
+yeccgoto(single_expression, 48) ->
+ 68;
+yeccgoto(single_expression, 52) ->
+ 68;
+yeccgoto(single_expression, 70) ->
+ 68;
+yeccgoto(single_expression, 74) ->
+ 68;
+yeccgoto(single_expression, 79) ->
+ 68;
+yeccgoto(single_expression, 86) ->
+ 68;
+yeccgoto(single_expression, 90) ->
+ 68;
+yeccgoto(single_expression, 99) ->
+ 68;
+yeccgoto(single_expression, 164) ->
+ 68;
+yeccgoto(single_expression, 166) ->
+ 68;
+yeccgoto(single_expression, 171) ->
+ 68;
+yeccgoto(single_expression, 203) ->
+ 68;
+yeccgoto(single_expression, 211) ->
+ 68;
+yeccgoto(single_expression, 214) ->
+ 68;
+yeccgoto(single_expression, 216) ->
+ 68;
+yeccgoto(single_expression, 218) ->
+ 68;
+yeccgoto(single_expression, 226) ->
+ 68;
+yeccgoto(single_expression, 232) ->
+ 68;
+yeccgoto(single_expression, 235) ->
+ 68;
+yeccgoto(single_expression, 257) ->
+ 68;
+yeccgoto(single_expression, 260) ->
+ 68;
+yeccgoto(single_expression, 265) ->
+ 68;
+yeccgoto(tail, 231) ->
+ 234;
+yeccgoto(tail, 238) ->
+ 239;
+yeccgoto(tail_constant, 150) ->
+ 153;
+yeccgoto(tail_constant, 157) ->
+ 158;
+yeccgoto(tail_literal, 297) ->
+ 300;
+yeccgoto(tail_literal, 304) ->
+ 305;
+yeccgoto(tail_pattern, 173) ->
+ 176;
+yeccgoto(tail_pattern, 180) ->
+ 181;
+yeccgoto(timeout, 65) ->
+ 112;
+yeccgoto(timeout, 101) ->
+ 168;
+yeccgoto(try_expr, 33) ->
+ 71;
+yeccgoto(try_expr, 35) ->
+ 71;
+yeccgoto(try_expr, 36) ->
+ 71;
+yeccgoto(try_expr, 37) ->
+ 71;
+yeccgoto(try_expr, 40) ->
+ 71;
+yeccgoto(try_expr, 44) ->
+ 71;
+yeccgoto(try_expr, 46) ->
+ 71;
+yeccgoto(try_expr, 48) ->
+ 71;
+yeccgoto(try_expr, 52) ->
+ 71;
+yeccgoto(try_expr, 70) ->
+ 71;
+yeccgoto(try_expr, 74) ->
+ 71;
+yeccgoto(try_expr, 79) ->
+ 71;
+yeccgoto(try_expr, 86) ->
+ 71;
+yeccgoto(try_expr, 90) ->
+ 71;
+yeccgoto(try_expr, 99) ->
+ 71;
+yeccgoto(try_expr, 164) ->
+ 71;
+yeccgoto(try_expr, 166) ->
+ 71;
+yeccgoto(try_expr, 171) ->
+ 71;
+yeccgoto(try_expr, 203) ->
+ 71;
+yeccgoto(try_expr, 211) ->
+ 71;
+yeccgoto(try_expr, 214) ->
+ 71;
+yeccgoto(try_expr, 216) ->
+ 71;
+yeccgoto(try_expr, 218) ->
+ 71;
+yeccgoto(try_expr, 226) ->
+ 71;
+yeccgoto(try_expr, 232) ->
+ 71;
+yeccgoto(try_expr, 235) ->
+ 71;
+yeccgoto(try_expr, 257) ->
+ 71;
+yeccgoto(try_expr, 260) ->
+ 71;
+yeccgoto(try_expr, 265) ->
+ 71;
+yeccgoto(tuple, 33) ->
+ 72;
+yeccgoto(tuple, 35) ->
+ 72;
+yeccgoto(tuple, 36) ->
+ 72;
+yeccgoto(tuple, 37) ->
+ 72;
+yeccgoto(tuple, 40) ->
+ 72;
+yeccgoto(tuple, 44) ->
+ 72;
+yeccgoto(tuple, 46) ->
+ 72;
+yeccgoto(tuple, 48) ->
+ 72;
+yeccgoto(tuple, 52) ->
+ 72;
+yeccgoto(tuple, 70) ->
+ 72;
+yeccgoto(tuple, 74) ->
+ 72;
+yeccgoto(tuple, 79) ->
+ 72;
+yeccgoto(tuple, 86) ->
+ 72;
+yeccgoto(tuple, 90) ->
+ 72;
+yeccgoto(tuple, 99) ->
+ 72;
+yeccgoto(tuple, 164) ->
+ 72;
+yeccgoto(tuple, 166) ->
+ 72;
+yeccgoto(tuple, 171) ->
+ 72;
+yeccgoto(tuple, 203) ->
+ 72;
+yeccgoto(tuple, 211) ->
+ 72;
+yeccgoto(tuple, 214) ->
+ 72;
+yeccgoto(tuple, 216) ->
+ 72;
+yeccgoto(tuple, 218) ->
+ 72;
+yeccgoto(tuple, 226) ->
+ 72;
+yeccgoto(tuple, 232) ->
+ 72;
+yeccgoto(tuple, 235) ->
+ 72;
+yeccgoto(tuple, 257) ->
+ 72;
+yeccgoto(tuple, 260) ->
+ 72;
+yeccgoto(tuple, 265) ->
+ 72;
+yeccgoto(tuple_constant, 126) ->
+ 141;
+yeccgoto(tuple_constant, 129) ->
+ 141;
+yeccgoto(tuple_constant, 142) ->
+ 141;
+yeccgoto(tuple_constant, 147) ->
+ 141;
+yeccgoto(tuple_constant, 151) ->
+ 141;
+yeccgoto(tuple_constant, 154) ->
+ 141;
+yeccgoto(tuple_literal, 284) ->
+ 289;
+yeccgoto(tuple_literal, 285) ->
+ 289;
+yeccgoto(tuple_literal, 290) ->
+ 289;
+yeccgoto(tuple_literal, 295) ->
+ 289;
+yeccgoto(tuple_literal, 298) ->
+ 289;
+yeccgoto(tuple_literal, 301) ->
+ 289;
+yeccgoto(tuple_pattern, 65) ->
+ 113;
+yeccgoto(tuple_pattern, 96) ->
+ 113;
+yeccgoto(tuple_pattern, 97) ->
+ 113;
+yeccgoto(tuple_pattern, 98) ->
+ 113;
+yeccgoto(tuple_pattern, 100) ->
+ 113;
+yeccgoto(tuple_pattern, 114) ->
+ 113;
+yeccgoto(tuple_pattern, 115) ->
+ 113;
+yeccgoto(tuple_pattern, 120) ->
+ 113;
+yeccgoto(tuple_pattern, 162) ->
+ 113;
+yeccgoto(tuple_pattern, 174) ->
+ 113;
+yeccgoto(tuple_pattern, 177) ->
+ 113;
+yeccgoto(tuple_pattern, 200) ->
+ 113;
+yeccgoto(tuple_pattern, 222) ->
+ 113;
+yeccgoto(variable, 25) ->
+ 31;
+yeccgoto(variable, 26) ->
+ 267;
+yeccgoto(variable, 33) ->
+ 73;
+yeccgoto(variable, 35) ->
+ 73;
+yeccgoto(variable, 36) ->
+ 73;
+yeccgoto(variable, 37) ->
+ 73;
+yeccgoto(variable, 40) ->
+ 73;
+yeccgoto(variable, 44) ->
+ 73;
+yeccgoto(variable, 46) ->
+ 73;
+yeccgoto(variable, 48) ->
+ 73;
+yeccgoto(variable, 52) ->
+ 73;
+yeccgoto(variable, 58) ->
+ 31;
+yeccgoto(variable, 65) ->
+ 31;
+yeccgoto(variable, 70) ->
+ 73;
+yeccgoto(variable, 74) ->
+ 73;
+yeccgoto(variable, 79) ->
+ 73;
+yeccgoto(variable, 82) ->
+ 31;
+yeccgoto(variable, 83) ->
+ 31;
+yeccgoto(variable, 86) ->
+ 73;
+yeccgoto(variable, 88) ->
+ 31;
+yeccgoto(variable, 90) ->
+ 73;
+yeccgoto(variable, 96) ->
+ 124;
+yeccgoto(variable, 97) ->
+ 31;
+yeccgoto(variable, 98) ->
+ 31;
+yeccgoto(variable, 99) ->
+ 73;
+yeccgoto(variable, 100) ->
+ 31;
+yeccgoto(variable, 114) ->
+ 31;
+yeccgoto(variable, 115) ->
+ 124;
+yeccgoto(variable, 120) ->
+ 31;
+yeccgoto(variable, 162) ->
+ 31;
+yeccgoto(variable, 164) ->
+ 73;
+yeccgoto(variable, 166) ->
+ 73;
+yeccgoto(variable, 171) ->
+ 73;
+yeccgoto(variable, 174) ->
+ 31;
+yeccgoto(variable, 177) ->
+ 31;
+yeccgoto(variable, 200) ->
+ 31;
+yeccgoto(variable, 203) ->
+ 73;
+yeccgoto(variable, 211) ->
+ 73;
+yeccgoto(variable, 214) ->
+ 73;
+yeccgoto(variable, 216) ->
+ 73;
+yeccgoto(variable, 218) ->
+ 73;
+yeccgoto(variable, 222) ->
+ 31;
+yeccgoto(variable, 226) ->
+ 73;
+yeccgoto(variable, 232) ->
+ 73;
+yeccgoto(variable, 235) ->
+ 73;
+yeccgoto(variable, 257) ->
+ 73;
+yeccgoto(variable, 260) ->
+ 73;
+yeccgoto(variable, 263) ->
+ 31;
+yeccgoto(variable, 265) ->
+ 73;
+yeccgoto(__Symbol, __State) ->
+ exit({__Symbol, __State, missing_in_goto_table}).
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_parse.hrl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_parse.hrl
new file mode 100644
index 0000000000..3d60360f47
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_parse.hrl
@@ -0,0 +1,111 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: core_parse.hrl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose : Core Erlang syntax trees as records.
+
+%% It would be nice to incorporate some generic functions as well but
+%% this could make including this file difficult.
+
+%% Note: the annotation list is *always* the first record field.
+%% Thus it is possible to define the macros:
+%% -define(get_ann(X), element(2, X)).
+%% -define(set_ann(X, Y), setelement(2, X, Y)).
+
+-record(c_int, {anno=[], val}). % val :: integer()
+
+-record(c_float, {anno=[], val}). % val :: float()
+
+-record(c_atom, {anno=[], val}). % val :: atom()
+
+-record(c_char, {anno=[], val}). % val :: char()
+
+-record(c_string, {anno=[], val}). % val :: string()
+
+-record(c_nil, {anno=[]}).
+
+-record(c_binary, {anno=[], segments}). % segments :: [#ce_bitstr{}]
+
+-record(c_bitstr, {anno=[],val, % val :: Tree,
+ size, % size :: Tree,
+ unit, % unit :: integer(),
+ type, % type :: atom(),
+ flags}). % flags :: [atom()],
+
+-record(c_cons, {anno=[], hd, % hd :: Tree,
+ tl}). % tl :: Tree
+
+-record(c_tuple, {anno=[], es}). % es :: [Tree]
+
+-record(c_var, {anno=[], name}). % name :: integer() | atom()
+
+-record(c_fname, {anno=[], id, % id :: atom(),
+ arity}). % arity :: integer()
+
+-record(c_values, {anno=[], es}). % es :: [Tree]
+
+-record(c_fun, {anno=[], vars, % vars :: [Tree],
+ body}). % body :: Tree
+
+-record(c_seq, {anno=[], arg, % arg :: Tree,
+ body}). % body :: Tree
+
+-record(c_let, {anno=[], vars, % vars :: [Tree],
+ arg, % arg :: Tree,
+ body}). % body :: Tree
+
+-record(c_letrec, {anno=[], defs, % defs :: [#ce_def{}],
+ body}). % body :: Tree
+
+-record(c_def, {anno=[], name, % name :: Tree,
+ val}). % val :: Tree,
+
+-record(c_case, {anno=[], arg, % arg :: Tree,
+ clauses}). % clauses :: [Tree]
+
+-record(c_clause, {anno=[], pats, % pats :: [Tree],
+ guard, % guard :: Tree,
+ body}). % body :: Tree
+
+-record(c_alias, {anno=[], var, % var :: Tree,
+ pat}). % pat :: Tree
+
+-record(c_receive, {anno=[], clauses, % clauses :: [Tree],
+ timeout, % timeout :: Tree,
+ action}). % action :: Tree
+
+-record(c_apply, {anno=[], op, % op :: Tree,
+ args}). % args :: [Tree]
+
+-record(c_call, {anno=[], module, % module :: Tree,
+ name, % name :: Tree,
+ args}). % args :: [Tree]
+
+-record(c_primop, {anno=[], name, % name :: Tree,
+ args}). % args :: [Tree]
+
+-record(c_try, {anno=[], arg, % arg :: Tree,
+ vars, % vars :: [Tree],
+ body, % body :: Tree
+ evars, % evars :: [Tree],
+ handler}). % handler :: Tree
+
+-record(c_catch, {anno=[], body}). % body :: Tree
+
+-record(c_module, {anno=[], name, % name :: Tree,
+ exports, % exports :: [Tree],
+ attrs, % attrs :: [#ce_def{}],
+ defs}). % defs :: [#ce_def{}]
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_pp.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_pp.erl
new file mode 100644
index 0000000000..2bfbcb85e2
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_pp.erl
@@ -0,0 +1,430 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: core_pp.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose : Core Erlang (naive) prettyprinter
+
+-module(core_pp).
+
+-export([format/1]).
+
+-include("core_parse.hrl").
+
+%% ====================================================================== %%
+%% format(Node) -> Text
+%% Node = coreErlang()
+%% Text = string() | [Text]
+%%
+%% Prettyprint-formats (naively) an abstract Core Erlang syntax
+%% tree.
+
+-record(ctxt, {class = term,
+ indent = 0,
+ item_indent = 2,
+ body_indent = 4,
+ tab_width = 8,
+ line = 0}).
+
+format(Node) -> case catch format(Node, #ctxt{}) of
+ {'EXIT',_} -> io_lib:format("~p",[Node]);
+ Other -> Other
+ end.
+
+maybe_anno(Node, Fun, Ctxt) ->
+ As = core_lib:get_anno(Node),
+ case get_line(As) of
+ none ->
+ maybe_anno(Node, Fun, Ctxt, As);
+ Line ->
+ if Line > Ctxt#ctxt.line ->
+ [io_lib:format("%% Line ~w",[Line]),
+ nl_indent(Ctxt),
+ maybe_anno(Node, Fun, Ctxt#ctxt{line = Line}, As)
+ ];
+ true ->
+ maybe_anno(Node, Fun, Ctxt, As)
+ end
+ end.
+
+maybe_anno(Node, Fun, Ctxt, As) ->
+ case strip_line(As) of
+ [] ->
+ Fun(Node, Ctxt);
+ List ->
+ Ctxt1 = add_indent(Ctxt, 2),
+ Ctxt2 = add_indent(Ctxt1, 3),
+ ["( ",
+ Fun(Node, Ctxt1),
+ nl_indent(Ctxt1),
+ "-| ",format_1(core_lib:make_literal(List), Ctxt2)," )"
+ ]
+ end.
+
+strip_line([A | As]) when integer(A) ->
+ strip_line(As);
+strip_line([A | As]) ->
+ [A | strip_line(As)];
+strip_line([]) ->
+ [].
+
+get_line([L | _As]) when integer(L) ->
+ L;
+get_line([_ | As]) ->
+ get_line(As);
+get_line([]) ->
+ none.
+
+format(Node, Ctxt) ->
+ maybe_anno(Node, fun format_1/2, Ctxt).
+
+format_1(#c_char{val=C}, _) -> io_lib:write_char(C);
+format_1(#c_int{val=I}, _) -> integer_to_list(I);
+format_1(#c_float{val=F}, _) -> float_to_list(F);
+format_1(#c_atom{val=A}, _) -> core_atom(A);
+format_1(#c_nil{}, _) -> "[]";
+format_1(#c_string{val=S}, _) -> io_lib:write_string(S);
+format_1(#c_var{name=V}, _) ->
+ %% Internal variable names may be:
+ %% - atoms representing proper Erlang variable names, or
+ %% any atoms that may be printed without single-quoting
+ %% - nonnegative integers.
+ %% It is important that when printing variables, no two names
+ %% should ever map to the same string.
+ if atom(V) ->
+ S = atom_to_list(V),
+ case S of
+ [C | _] when C >= $A, C =< $Z ->
+ %% Ordinary uppercase-prefixed names are
+ %% printed just as they are.
+ S;
+ [$_ | _] ->
+ %% Already "_"-prefixed names are prefixed
+ %% with "_X", e.g. '_foo' => '_X_foo', to
+ %% avoid generating things like "____foo" upon
+ %% repeated writing and reading of code.
+ %% ("_X_X_X_foo" is better.)
+ [$_, $X | S];
+ _ ->
+ %% Plain atoms are prefixed with a single "_".
+ %% E.g. foo => "_foo".
+ [$_ | S]
+ end;
+ integer(V) ->
+ %% Integers are also simply prefixed with "_".
+ [$_ | integer_to_list(V)]
+ end;
+format_1(#c_binary{segments=Segs}, Ctxt) ->
+ ["#{",
+ format_vseq(Segs, "", ",", add_indent(Ctxt, 2),
+ fun format_bitstr/2),
+ "}#"
+ ];
+format_1(#c_tuple{es=Es}, Ctxt) ->
+ [${,
+ format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2),
+ $}
+ ];
+format_1(#c_cons{hd=H,tl=T}, Ctxt) ->
+ Txt = ["["|format(H, add_indent(Ctxt, 1))],
+ [Txt|format_list_tail(T, add_indent(Ctxt, width(Txt, Ctxt)))];
+format_1(#c_values{es=Es}, Ctxt) ->
+ format_values(Es, Ctxt);
+format_1(#c_alias{var=V,pat=P}, Ctxt) ->
+ Txt = [format(V, Ctxt)|" = "],
+ [Txt|format(P, add_indent(Ctxt, width(Txt, Ctxt)))];
+format_1(#c_let{vars=Vs,arg=A,body=B}, Ctxt) ->
+ Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["let ",
+ format_values(Vs, add_indent(Ctxt, 4)),
+ " =",
+ nl_indent(Ctxt1),
+ format(A, Ctxt1),
+ nl_indent(Ctxt),
+ "in "
+ | format(B, add_indent(Ctxt, 4))
+ ];
+format_1(#c_letrec{defs=Fs,body=B}, Ctxt) ->
+ Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["letrec",
+ nl_indent(Ctxt1),
+ format_funcs(Fs, Ctxt1),
+ nl_indent(Ctxt),
+ "in "
+ | format(B, add_indent(Ctxt, 4))
+ ];
+format_1(#c_seq{arg=A,body=B}, Ctxt) ->
+ Ctxt1 = add_indent(Ctxt, 4),
+ ["do ",
+ format(A, Ctxt1),
+ nl_indent(Ctxt1)
+ | format(B, Ctxt1)
+ ];
+format_1(#c_case{arg=A,clauses=Cs}, Ctxt) ->
+ Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.item_indent),
+ ["case ",
+ format(A, add_indent(Ctxt, 5)),
+ " of",
+ nl_indent(Ctxt1),
+ format_clauses(Cs, Ctxt1),
+ nl_indent(Ctxt)
+ | "end"
+ ];
+format_1(#c_receive{clauses=Cs,timeout=T,action=A}, Ctxt) ->
+ Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.item_indent),
+ ["receive",
+ nl_indent(Ctxt1),
+ format_clauses(Cs, Ctxt1),
+ nl_indent(Ctxt),
+ "after ",
+ format(T, add_indent(Ctxt, 6)),
+ " ->",
+ nl_indent(Ctxt1),
+ format(A, Ctxt1)
+ ];
+format_1(#c_fname{id=I,arity=A}, _) ->
+ [core_atom(I),$/,integer_to_list(A)];
+format_1(#c_fun{vars=Vs,body=B}, Ctxt) ->
+ Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["fun (",
+ format_hseq(Vs, ",", add_indent(Ctxt, 5), fun format/2),
+ ") ->",
+ nl_indent(Ctxt1)
+ | format(B, Ctxt1)
+ ];
+format_1(#c_apply{op=O,args=As}, Ctxt0) ->
+ Ctxt1 = add_indent(Ctxt0, 6), %"apply "
+ Op = format(O, Ctxt1),
+ Ctxt2 = add_indent(Ctxt0, 4),
+ ["apply ",Op,
+ nl_indent(Ctxt2),
+ $(,format_hseq(As, ", ", add_indent(Ctxt2, 1), fun format/2),$)
+ ];
+format_1(#c_call{module=M,name=N,args=As}, Ctxt0) ->
+ Ctxt1 = add_indent(Ctxt0, 5), %"call "
+ Mod = format(M, Ctxt1),
+ Ctxt2 = add_indent(Ctxt1, width(Mod, Ctxt1)+1),
+ Name = format(N, Ctxt2),
+ Ctxt3 = add_indent(Ctxt0, 4),
+ ["call ",Mod,":",Name,
+ nl_indent(Ctxt3),
+ $(,format_hseq(As, ", ", add_indent(Ctxt3, 1), fun format/2),$)
+ ];
+format_1(#c_primop{name=N,args=As}, Ctxt0) ->
+ Ctxt1 = add_indent(Ctxt0, 7), %"primop "
+ Name = format(N, Ctxt1),
+ Ctxt2 = add_indent(Ctxt0, 4),
+ ["primop ",Name,
+ nl_indent(Ctxt2),
+ $(,format_hseq(As, ", ", add_indent(Ctxt2, 1), fun format/2),$)
+ ];
+format_1(#c_catch{body=B}, Ctxt) ->
+ Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["catch",
+ nl_indent(Ctxt1),
+ format(B, Ctxt1)
+ ];
+format_1(#c_try{arg=E,vars=Vs,body=B,evars=Evs,handler=H}, Ctxt) ->
+ Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["try",
+ nl_indent(Ctxt1),
+ format(E, Ctxt1),
+ nl_indent(Ctxt),
+ "of ",
+ format_values(Vs, add_indent(Ctxt, 3)),
+ " ->",
+ nl_indent(Ctxt1),
+ format(B, Ctxt1),
+ nl_indent(Ctxt),
+ "catch ",
+ format_values(Evs, add_indent(Ctxt, 6)),
+ " ->",
+ nl_indent(Ctxt1)
+ | format(H, Ctxt1)
+ ];
+format_1(#c_def{name=N,val=V}, Ctxt) ->
+ Ctxt1 = add_indent(set_class(Ctxt, expr), Ctxt#ctxt.body_indent),
+ [format(N, Ctxt),
+ " =",
+ nl_indent(Ctxt1)
+ | format(V, Ctxt1)
+ ];
+format_1(#c_module{name=N,exports=Es,attrs=As,defs=Ds}, Ctxt) ->
+ Mod = ["module ", format(N, Ctxt)],
+ [Mod," [",
+ format_vseq(Es,
+ "", ",",
+ add_indent(set_class(Ctxt, term), width(Mod, Ctxt)+2),
+ fun format/2),
+ "]",
+ nl_indent(Ctxt),
+ " attributes [",
+ format_vseq(As,
+ "", ",",
+ add_indent(set_class(Ctxt, def), 16),
+ fun format/2),
+ "]",
+ nl_indent(Ctxt),
+ format_funcs(Ds, Ctxt),
+ nl_indent(Ctxt)
+ | "end"
+ ];
+format_1(Type, _) ->
+ ["** Unsupported type: ",
+ io_lib:write(Type)
+ | " **"
+ ].
+
+format_funcs(Fs, Ctxt) ->
+ format_vseq(Fs,
+ "", "",
+ set_class(Ctxt, def),
+ fun format/2).
+
+format_values(Vs, Ctxt) ->
+ [$<,
+ format_hseq(Vs, ",", add_indent(Ctxt, 1), fun format/2),
+ $>].
+
+format_bitstr(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Ctxt0) ->
+ Vs = [S, U, T, Fs],
+ Ctxt1 = add_indent(Ctxt0, 2),
+ Val = format(V, Ctxt1),
+ Ctxt2 = add_indent(Ctxt1, width(Val, Ctxt1) + 2),
+ ["#<", Val, ">(", format_hseq(Vs,",", Ctxt2, fun format/2), $)].
+
+format_clauses(Cs, Ctxt) ->
+ format_vseq(Cs, "", "", set_class(Ctxt, clause),
+ fun format_clause/2).
+
+format_clause(Node, Ctxt) ->
+ maybe_anno(Node, fun format_clause_1/2, Ctxt).
+
+format_clause_1(#c_clause{pats=Ps,guard=G,body=B}, Ctxt) ->
+ Ptxt = format_values(Ps, Ctxt),
+ Ctxt2 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
+ [Ptxt,
+ " when ",
+ format_guard(G, add_indent(set_class(Ctxt, expr),
+ width(Ptxt, Ctxt) + 6)),
+ " ->",
+ nl_indent(Ctxt2)
+ | format(B, set_class(Ctxt2, expr))
+ ].
+
+format_guard(Node, Ctxt) ->
+ maybe_anno(Node, fun format_guard_1/2, Ctxt).
+
+format_guard_1(#c_call{module=M,name=N,args=As}, Ctxt0) ->
+ Ctxt1 = add_indent(Ctxt0, 5), %"call "
+ Mod = format(M, Ctxt1),
+ Ctxt2 = add_indent(Ctxt1, width(Mod, Ctxt1)+1),
+ Name = format(N, Ctxt2),
+ Ctxt3 = add_indent(Ctxt0, 4),
+ ["call ",Mod,":",Name,
+ nl_indent(Ctxt3),
+ $(,format_vseq(As, "",",", add_indent(Ctxt3, 1), fun format_guard/2),$)
+ ];
+format_guard_1(E, Ctxt) -> format_1(E, Ctxt). %Anno already done
+
+%% format_hseq([Thing], Separator, Context, Fun) -> Txt.
+%% Format a sequence horizontally on the same line with Separator between.
+
+format_hseq([H], _, Ctxt, Fun) ->
+ Fun(H, Ctxt);
+format_hseq([H|T], Sep, Ctxt, Fun) ->
+ Txt = [Fun(H, Ctxt)|Sep],
+ Ctxt1 = add_indent(Ctxt, width(Txt, Ctxt)),
+ [Txt|format_hseq(T, Sep, Ctxt1, Fun)];
+format_hseq([], _, _, _) -> "".
+
+%% format_vseq([Thing], LinePrefix, LineSuffix, Context, Fun) -> Txt.
+%% Format a sequence vertically in indented lines adding LinePrefix
+%% to the beginning of each line and LineSuffix to the end of each
+%% line. No prefix on the first line or suffix on the last line.
+
+format_vseq([H], _Pre, _Suf, Ctxt, Fun) ->
+ Fun(H, Ctxt);
+format_vseq([H|T], Pre, Suf, Ctxt, Fun) ->
+ [Fun(H, Ctxt),Suf,nl_indent(Ctxt),Pre|
+ format_vseq(T, Pre, Suf, Ctxt, Fun)];
+format_vseq([], _, _, _, _) -> "".
+
+format_list_tail(#c_nil{anno=[]}, _) -> "]";
+format_list_tail(#c_cons{anno=[],hd=H,tl=T}, Ctxt) ->
+ Txt = [$,|format(H, Ctxt)],
+ Ctxt1 = add_indent(Ctxt, width(Txt, Ctxt)),
+ [Txt|format_list_tail(T, Ctxt1)];
+format_list_tail(Tail, Ctxt) ->
+ ["|",format(Tail, add_indent(Ctxt, 1)),"]"].
+
+indent(Ctxt) -> indent(Ctxt#ctxt.indent, Ctxt).
+
+indent(N, _) when N =< 0 -> "";
+indent(N, Ctxt) ->
+ T = Ctxt#ctxt.tab_width,
+ string:chars($\t, N div T, string:chars($\s, N rem T)).
+
+nl_indent(Ctxt) -> [$\n|indent(Ctxt)].
+
+
+unindent(T, Ctxt) ->
+ unindent(T, Ctxt#ctxt.indent, Ctxt, []).
+
+unindent(T, N, _, C) when N =< 0 ->
+ [T|C];
+unindent([$\s|T], N, Ctxt, C) ->
+ unindent(T, N - 1, Ctxt, C);
+unindent([$\t|T], N, Ctxt, C) ->
+ Tab = Ctxt#ctxt.tab_width,
+ if N >= Tab ->
+ unindent(T, N - Tab, Ctxt, C);
+ true ->
+ unindent([string:chars($\s, Tab - N)|T], 0, Ctxt, C)
+ end;
+unindent([L|T], N, Ctxt, C) when list(L) ->
+ unindent(L, N, Ctxt, [T|C]);
+unindent([H|T], _, _, C) ->
+ [H|[T|C]];
+unindent([], N, Ctxt, [H|T]) ->
+ unindent(H, N, Ctxt, T);
+unindent([], _, _, []) -> [].
+
+
+width(Txt, Ctxt) ->
+ case catch width(Txt, 0, Ctxt, []) of
+ {'EXIT',_} -> exit({bad_text,Txt});
+ Other -> Other
+ end.
+
+width([$\t|T], A, Ctxt, C) ->
+ width(T, A + Ctxt#ctxt.tab_width, Ctxt, C);
+width([$\n|T], _, Ctxt, C) ->
+ width(unindent([T|C], Ctxt), Ctxt);
+width([H|T], A, Ctxt, C) when list(H) ->
+ width(H, A, Ctxt, [T|C]);
+width([_|T], A, Ctxt, C) ->
+ width(T, A + 1, Ctxt, C);
+width([], A, Ctxt, [H|T]) ->
+ width(H, A, Ctxt, T);
+width([], A, _, []) -> A.
+
+add_indent(Ctxt, Dx) ->
+ Ctxt#ctxt{indent = Ctxt#ctxt.indent + Dx}.
+
+set_class(Ctxt, Class) ->
+ Ctxt#ctxt{class = Class}.
+
+core_atom(A) -> io_lib:write_string(atom_to_list(A), $').
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_scan.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_scan.erl
new file mode 100644
index 0000000000..a97270b9f3
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_scan.erl
@@ -0,0 +1,495 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: core_scan.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose: Scanner for Core Erlang.
+
+%% For handling ISO 8859-1 (Latin-1) we use the following type
+%% information:
+%%
+%% 000 - 037 NUL - US control
+%% 040 - 057 SPC - / punctuation
+%% 060 - 071 0 - 9 digit
+%% 072 - 100 : - @ punctuation
+%% 101 - 132 A - Z uppercase
+%% 133 - 140 [ - ` punctuation
+%% 141 - 172 a - z lowercase
+%% 173 - 176 { - ~ punctuation
+%% 177 DEL control
+%% 200 - 237 control
+%% 240 - 277 NBSP - ¿ punctuation
+%% 300 - 326 À - Ö uppercase
+%% 327 × punctuation
+%% 330 - 336 Ø - Þ uppercase
+%% 337 - 366 ß - ö lowercase
+%% 367 ÷ punctuation
+%% 370 - 377 ø - ÿ lowercase
+%%
+%% Many punctuation characters region have special meaning. Must
+%% watch using × \327, bvery close to x \170
+
+-module(core_scan).
+
+-export([string/1,string/2,tokens/3,format_error/1]).
+
+-import(lists, [reverse/1]).
+
+%% tokens(Continuation, CharList, StartPos) ->
+%% {done, {ok, [Tok], EndPos}, Rest} |
+%% {done, {error,{ErrorPos,core_scan,What}, EndPos}, Rest} |
+%% {more, Continuation'}
+%% This is the main function into the re-entrant scanner. It calls the
+%% re-entrant pre-scanner until this says done, then calls scan/1 on
+%% the result.
+%%
+%% The continuation has the form:
+%% {RestChars,CharsSoFar,CurrentPos,StartPos}
+
+tokens([], Chars, Pos) -> %First call
+ tokens({[],[],Pos,Pos}, Chars, Pos);
+tokens({Chars,SoFar0,Cp,Sp}, MoreChars, _) ->
+ In = Chars ++ MoreChars,
+ case pre_scan(In, SoFar0, Cp) of
+ {done,_,[],Ep} -> %Found nothing
+ {done,{eof,Ep},[]};
+ {done,_,SoFar1,Ep} -> %Got complete tokens
+ Res = case scan(reverse(SoFar1), Sp) of
+ {ok,Toks} -> {ok,Toks,Ep};
+ {error,E} -> {error,E,Ep}
+ end,
+ {done,Res,[]};
+ {more,Rest,SoFar1,Cp1} -> %Missing end token
+ {more,{Rest,SoFar1,Cp1,Sp}};
+ Other -> %An error has occurred
+ {done,Other,[]}
+ end.
+
+%% string([Char]) ->
+%% string([Char], StartPos) ->
+%% {ok, [Tok], EndPos} |
+%% {error,{Pos,core_scan,What}, EndPos}
+
+string(Cs) -> string(Cs, 1).
+
+string(Cs, Sp) ->
+ %% Add an 'eof' to always get correct handling.
+ case string_pre_scan(Cs, [], Sp) of
+ {done,_,SoFar,Ep} -> %Got tokens
+ case scan(reverse(SoFar), Sp) of
+ {ok,Toks} -> {ok,Toks,Ep};
+ {error,E} -> {error,E,Ep}
+ end;
+ Other -> Other %An error has occurred
+ end.
+
+%% string_pre_scan(Cs, SoFar0, StartPos) ->
+%% {done,Rest,SoFar,EndPos} | {error,E,EndPos}.
+
+string_pre_scan(Cs, SoFar0, Sp) ->
+ case pre_scan(Cs, SoFar0, Sp) of
+ {done,Rest,SoFar1,Ep} -> %Got complete tokens
+ {done,Rest,SoFar1,Ep};
+ {more,Rest,SoFar1,Ep} -> %Missing end token
+ string_pre_scan(Rest ++ eof, SoFar1, Ep);
+ Other -> Other %An error has occurred
+ end.
+
+%% format_error(Error)
+%% Return a string describing the error.
+
+format_error({string,Quote,Head}) ->
+ ["unterminated " ++ string_thing(Quote) ++
+ " starting with " ++ io_lib:write_string(Head,Quote)];
+format_error({illegal,Type}) -> io_lib:fwrite("illegal ~w", [Type]);
+format_error(char) -> "unterminated character";
+format_error(scan) -> "premature end";
+format_error({base,Base}) -> io_lib:fwrite("illegal base '~w'", [Base]);
+format_error(float) -> "bad float";
+format_error(Other) -> io_lib:write(Other).
+
+string_thing($') -> "atom";
+string_thing($") -> "string".
+
+%% Re-entrant pre-scanner.
+%%
+%% If the input list of characters is insufficient to build a term the
+%% scanner returns a request for more characters and a continuation to be
+%% used when trying to build a term with more characters. To indicate
+%% end-of-file the input character list should be replaced with 'eof'
+%% as an empty list has meaning.
+%%
+%% When more characters are need inside a comment, string or quoted
+%% atom, which can become rather long, instead of pushing the
+%% characters read so far back onto RestChars to be reread, a special
+%% reentry token is returned indicating the middle of a construct.
+%% The token is the start character as an atom, '%', '"' and '\''.
+
+%% pre_scan([Char], SoFar, StartPos) ->
+%% {done,RestChars,ScannedChars,NewPos} |
+%% {more,RestChars,ScannedChars,NewPos} |
+%% {error,{ErrorPos,core_scan,Description},NewPos}.
+%% Main pre-scan function. It has been split into 2 functions because of
+%% efficiency, with a good indexing compiler it would be unnecessary.
+
+pre_scan([C|Cs], SoFar, Pos) ->
+ pre_scan(C, Cs, SoFar, Pos);
+pre_scan([], SoFar, Pos) ->
+ {more,[],SoFar,Pos};
+pre_scan(eof, SoFar, Pos) ->
+ {done,eof,SoFar,Pos}.
+
+%% pre_scan(Char, [Char], SoFar, Pos)
+
+pre_scan($$, Cs0, SoFar0, Pos) ->
+ case pre_char(Cs0, [$$|SoFar0]) of
+ {Cs,SoFar} ->
+ pre_scan(Cs, SoFar, Pos);
+ more ->
+ {more,[$$|Cs0],SoFar0, Pos};
+ error ->
+ pre_error(char, Pos, Pos)
+ end;
+pre_scan($', Cs, SoFar, Pos) ->
+ pre_string(Cs, $', '\'', Pos, [$'|SoFar], Pos);
+pre_scan({'\'',Sp}, Cs, SoFar, Pos) -> %Re-entering quoted atom
+ pre_string(Cs, $', '\'', Sp, SoFar, Pos);
+pre_scan($", Cs, SoFar, Pos) ->
+ pre_string(Cs, $", '"', Pos, [$"|SoFar], Pos);
+pre_scan({'"',Sp}, Cs, SoFar, Pos) -> %Re-entering string
+ pre_string(Cs, $", '"', Sp, SoFar, Pos);
+pre_scan($%, Cs, SoFar, Pos) ->
+ pre_comment(Cs, SoFar, Pos);
+pre_scan('%', Cs, SoFar, Pos) -> %Re-entering comment
+ pre_comment(Cs, SoFar, Pos);
+pre_scan($\n, Cs, SoFar, Pos) ->
+ pre_scan(Cs, [$\n|SoFar], Pos+1);
+pre_scan(C, Cs, SoFar, Pos) ->
+ pre_scan(Cs, [C|SoFar], Pos).
+
+%% pre_string([Char], Quote, Reent, StartPos, SoFar, Pos)
+
+pre_string([Q|Cs], Q, _, _, SoFar, Pos) ->
+ pre_scan(Cs, [Q|SoFar], Pos);
+pre_string([$\n|Cs], Q, Reent, Sp, SoFar, Pos) ->
+ pre_string(Cs, Q, Reent, Sp, [$\n|SoFar], Pos+1);
+pre_string([$\\|Cs0], Q, Reent, Sp, SoFar0, Pos) ->
+ case pre_escape(Cs0, SoFar0) of
+ {Cs,SoFar} ->
+ pre_string(Cs, Q, Reent, Sp, SoFar, Pos);
+ more ->
+ {more,[{Reent,Sp},$\\|Cs0],SoFar0,Pos};
+ error ->
+ pre_string_error(Q, Sp, SoFar0, Pos)
+ end;
+pre_string([C|Cs], Q, Reent, Sp, SoFar, Pos) ->
+ pre_string(Cs, Q, Reent, Sp, [C|SoFar], Pos);
+pre_string([], _, Reent, Sp, SoFar, Pos) ->
+ {more,[{Reent,Sp}],SoFar,Pos};
+pre_string(eof, Q, _, Sp, SoFar, Pos) ->
+ pre_string_error(Q, Sp, SoFar, Pos).
+
+pre_string_error(Q, Sp, SoFar, Pos) ->
+ S = reverse(string:substr(SoFar, 1, string:chr(SoFar, Q)-1)),
+ pre_error({string,Q,string:substr(S, 1, 16)}, Sp, Pos).
+
+pre_char([C|Cs], SoFar) -> pre_char(C, Cs, SoFar);
+pre_char([], _) -> more;
+pre_char(eof, _) -> error.
+
+pre_char($\\, Cs, SoFar) ->
+ pre_escape(Cs, SoFar);
+pre_char(C, Cs, SoFar) ->
+ {Cs,[C|SoFar]}.
+
+pre_escape([$^|Cs0], SoFar) ->
+ case Cs0 of
+ [C3|Cs] ->
+ {Cs,[C3,$^,$\\|SoFar]};
+ [] -> more;
+ eof -> error
+ end;
+pre_escape([C|Cs], SoFar) ->
+ {Cs,[C,$\\|SoFar]};
+pre_escape([], _) -> more;
+pre_escape(eof, _) -> error.
+
+%% pre_comment([Char], SoFar, Pos)
+%% Comments are replaced by one SPACE.
+
+pre_comment([$\n|Cs], SoFar, Pos) ->
+ pre_scan(Cs, [$\n,$\s|SoFar], Pos+1); %Terminate comment
+pre_comment([_|Cs], SoFar, Pos) ->
+ pre_comment(Cs, SoFar, Pos);
+pre_comment([], SoFar, Pos) ->
+ {more,['%'],SoFar,Pos};
+pre_comment(eof, Sofar, Pos) ->
+ pre_scan(eof, [$\s|Sofar], Pos).
+
+pre_error(E, Epos, Pos) ->
+ {error,{Epos,core_scan,E}, Pos}.
+
+%% scan(CharList, StartPos)
+%% This takes a list of characters and tries to tokenise them.
+%%
+%% The token list is built in reverse order (in a stack) to save appending
+%% and then reversed when all the tokens have been collected. Most tokens
+%% are built in the same way.
+%%
+%% Returns:
+%% {ok,[Tok]}
+%% {error,{ErrorPos,core_scan,What}}
+
+scan(Cs, Pos) ->
+ scan1(Cs, [], Pos).
+
+%% scan1(Characters, TokenStack, Position)
+%% Scan a list of characters into tokens.
+
+scan1([$\n|Cs], Toks, Pos) -> %Skip newline
+ scan1(Cs, Toks, Pos+1);
+scan1([C|Cs], Toks, Pos) when C >= $\000, C =< $\s -> %Skip control chars
+ scan1(Cs, Toks, Pos);
+scan1([C|Cs], Toks, Pos) when C >= $\200, C =< $\240 ->
+ scan1(Cs, Toks, Pos);
+scan1([C|Cs], Toks, Pos) when C >= $a, C =< $z -> %Keywords
+ scan_key_word(C, Cs, Toks, Pos);
+scan1([C|Cs], Toks, Pos) when C >= $ß, C =< $ÿ, C /= $÷ ->
+ scan_key_word(C, Cs, Toks, Pos);
+scan1([C|Cs], Toks, Pos) when C >= $A, C =< $Z -> %Variables
+ scan_variable(C, Cs, Toks, Pos);
+scan1([C|Cs], Toks, Pos) when C >= $À, C =< $Þ, C /= $× ->
+ scan_variable(C, Cs, Toks, Pos);
+scan1([C|Cs], Toks, Pos) when C >= $0, C =< $9 -> %Numbers
+ scan_number(C, Cs, Toks, Pos);
+scan1([$-,C|Cs], Toks, Pos) when C >= $0, C =< $9 -> %Signed numbers
+ scan_signed_number($-, C, Cs, Toks, Pos);
+scan1([$+,C|Cs], Toks, Pos) when C >= $0, C =< $9 -> %Signed numbers
+ scan_signed_number($+, C, Cs, Toks, Pos);
+scan1([$_|Cs], Toks, Pos) -> %_ variables
+ scan_variable($_, Cs, Toks, Pos);
+scan1([$$|Cs0], Toks, Pos) -> %Character constant
+ {C,Cs,Pos1} = scan_char(Cs0, Pos),
+ scan1(Cs, [{char,Pos,C}|Toks], Pos1);
+scan1([$'|Cs0], Toks, Pos) -> %Atom (always quoted)
+ {S,Cs1,Pos1} = scan_string(Cs0, $', Pos),
+ case catch list_to_atom(S) of
+ A when atom(A) ->
+ scan1(Cs1, [{atom,Pos,A}|Toks], Pos1);
+ _Error -> scan_error({illegal,atom}, Pos)
+ end;
+scan1([$"|Cs0], Toks, Pos) -> %String
+ {S,Cs1,Pos1} = scan_string(Cs0, $", Pos),
+ scan1(Cs1, [{string,Pos,S}|Toks], Pos1);
+%% Punctuation characters and operators, first recognise multiples.
+scan1("->" ++ Cs, Toks, Pos) ->
+ scan1(Cs, [{'->',Pos}|Toks], Pos);
+scan1("-|" ++ Cs, Toks, Pos) ->
+ scan1(Cs, [{'-|',Pos}|Toks], Pos);
+scan1([C|Cs], Toks, Pos) -> %Punctuation character
+ P = list_to_atom([C]),
+ scan1(Cs, [{P,Pos}|Toks], Pos);
+scan1([], Toks0, _) ->
+ Toks = reverse(Toks0),
+ {ok,Toks}.
+
+%% scan_key_word(FirstChar, CharList, Tokens, Pos)
+%% scan_variable(FirstChar, CharList, Tokens, Pos)
+
+scan_key_word(C, Cs0, Toks, Pos) ->
+ {Wcs,Cs} = scan_name(Cs0, []),
+ case catch list_to_atom([C|reverse(Wcs)]) of
+ Name when atom(Name) ->
+ scan1(Cs, [{Name,Pos}|Toks], Pos);
+ _Error -> scan_error({illegal,atom}, Pos)
+ end.
+
+scan_variable(C, Cs0, Toks, Pos) ->
+ {Wcs,Cs} = scan_name(Cs0, []),
+ case catch list_to_atom([C|reverse(Wcs)]) of
+ Name when atom(Name) ->
+ scan1(Cs, [{var,Pos,Name}|Toks], Pos);
+ _Error -> scan_error({illegal,var}, Pos)
+ end.
+
+%% scan_name(Cs) -> lists:splitwith(fun (C) -> name_char(C) end, Cs).
+
+scan_name([C|Cs], Ncs) ->
+ case name_char(C) of
+ true -> scan_name(Cs, [C|Ncs]);
+ false -> {Ncs,[C|Cs]} %Must rebuild here, sigh!
+ end;
+scan_name([], Ncs) ->
+ {Ncs,[]}.
+
+name_char(C) when C >= $a, C =< $z -> true;
+name_char(C) when C >= $ß, C =< $ÿ, C /= $÷ -> true;
+name_char(C) when C >= $A, C =< $Z -> true;
+name_char(C) when C >= $À, C =< $Þ, C /= $× -> true;
+name_char(C) when C >= $0, C =< $9 -> true;
+name_char($_) -> true;
+name_char($@) -> true;
+name_char(_) -> false.
+
+%% scan_string(CharList, QuoteChar, Pos) -> {StringChars,RestChars,NewPos}.
+
+scan_string(Cs, Q, Pos) ->
+ scan_string(Cs, [], Q, Pos).
+
+scan_string([Q|Cs], Scs, Q, Pos) ->
+ {reverse(Scs),Cs,Pos};
+scan_string([$\n|Cs], Scs, Q, Pos) ->
+ scan_string(Cs, [$\n|Scs], Q, Pos+1);
+scan_string([$\\|Cs0], Scs, Q, Pos) ->
+ {C,Cs,Pos1} = scan_escape(Cs0, Pos),
+ scan_string(Cs, [C|Scs], Q, Pos1);
+scan_string([C|Cs], Scs, Q, Pos) ->
+ scan_string(Cs, [C|Scs], Q, Pos).
+
+%% scan_char(Chars, Pos) -> {Char,RestChars,NewPos}.
+%% Read a single character from a character constant. The pre-scan
+%% phase has checked for errors here.
+
+scan_char([$\\|Cs], Pos) ->
+ scan_escape(Cs, Pos);
+scan_char([$\n|Cs], Pos) -> %Newline
+ {$\n,Cs,Pos+1};
+scan_char([C|Cs], Pos) ->
+ {C,Cs,Pos}.
+
+scan_escape([O1,O2,O3|Cs], Pos) when %\<1-3> octal digits
+ O1 >= $0, O1 =< $7, O2 >= $0, O2 =< $7, O3 >= $0, O3 =< $7 ->
+ Val = (O1*8 + O2)*8 + O3 - 73*$0,
+ {Val,Cs,Pos};
+scan_escape([O1,O2|Cs], Pos) when
+ O1 >= $0, O1 =< $7, O2 >= $0, O2 =< $7 ->
+ Val = (O1*8 + O2) - 9*$0,
+ {Val,Cs,Pos};
+scan_escape([O1|Cs], Pos) when
+ O1 >= $0, O1 =< $7 ->
+ {O1 - $0,Cs,Pos};
+scan_escape([$^,C|Cs], Pos) -> %\^X -> CTL-X
+ Val = C band 31,
+ {Val,Cs,Pos};
+%scan_escape([$\n,C1|Cs],Pos) ->
+% {C1,Cs,Pos+1};
+%scan_escape([C,C1|Cs],Pos) when C >= $\000, C =< $\s ->
+% {C1,Cs,Pos};
+scan_escape([$\n|Cs],Pos) ->
+ {$\n,Cs,Pos+1};
+scan_escape([C0|Cs],Pos) ->
+ C = escape_char(C0),
+ {C,Cs,Pos}.
+
+escape_char($n) -> $\n; %\n = LF
+escape_char($r) -> $\r; %\r = CR
+escape_char($t) -> $\t; %\t = TAB
+escape_char($v) -> $\v; %\v = VT
+escape_char($b) -> $\b; %\b = BS
+escape_char($f) -> $\f; %\f = FF
+escape_char($e) -> $\e; %\e = ESC
+escape_char($s) -> $\s; %\s = SPC
+escape_char($d) -> $\d; %\d = DEL
+escape_char(C) -> C.
+
+%% scan_number(Char, CharList, TokenStack, Pos)
+%% We can handle simple radix notation:
+%% # - the digits read in that base
+%% - the digits in base 10
+%% .
+%% .E+-
+%%
+%% Except for explicitly based integers we build a list of all the
+%% characters and then use list_to_integer/1 or list_to_float/1 to
+%% generate the value.
+
+%% SPos == Start position
+%% CPos == Current position
+
+scan_number(C, Cs0, Toks, Pos) ->
+ {Ncs,Cs,Pos1} = scan_integer(Cs0, [C], Pos),
+ scan_after_int(Cs, Ncs, Toks, Pos, Pos1).
+
+scan_signed_number(S, C, Cs0, Toks, Pos) ->
+ {Ncs,Cs,Pos1} = scan_integer(Cs0, [C,S], Pos),
+ scan_after_int(Cs, Ncs, Toks, Pos, Pos1).
+
+scan_integer([C|Cs], Stack, Pos) when C >= $0, C =< $9 ->
+ scan_integer(Cs, [C|Stack], Pos);
+scan_integer(Cs, Stack, Pos) ->
+ {Stack,Cs,Pos}.
+
+scan_after_int([$.,C|Cs0], Ncs0, Toks, SPos, CPos) when C >= $0, C =< $9 ->
+ {Ncs,Cs,CPos1} = scan_integer(Cs0, [C,$.|Ncs0], CPos),
+ scan_after_fraction(Cs, Ncs, Toks, SPos, CPos1);
+scan_after_int([$#|Cs], Ncs, Toks, SPos, CPos) ->
+ case list_to_integer(reverse(Ncs)) of
+ Base when Base >= 2, Base =< 16 ->
+ scan_based_int(Cs, 0, Base, Toks, SPos, CPos);
+ Base ->
+ scan_error({base,Base}, CPos)
+ end;
+scan_after_int(Cs, Ncs, Toks, SPos, CPos) ->
+ N = list_to_integer(reverse(Ncs)),
+ scan1(Cs, [{integer,SPos,N}|Toks], CPos).
+
+scan_based_int([C|Cs], SoFar, Base, Toks, SPos, CPos) when
+ C >= $0, C =< $9, C < Base + $0 ->
+ Next = SoFar * Base + (C - $0),
+ scan_based_int(Cs, Next, Base, Toks, SPos, CPos);
+scan_based_int([C|Cs], SoFar, Base, Toks, SPos, CPos) when
+ C >= $a, C =< $f, C < Base + $a - 10 ->
+ Next = SoFar * Base + (C - $a + 10),
+ scan_based_int(Cs, Next, Base, Toks, SPos, CPos);
+scan_based_int([C|Cs], SoFar, Base, Toks, SPos, CPos) when
+ C >= $A, C =< $F, C < Base + $A - 10 ->
+ Next = SoFar * Base + (C - $A + 10),
+ scan_based_int(Cs, Next, Base, Toks, SPos, CPos);
+scan_based_int(Cs, SoFar, _, Toks, SPos, CPos) ->
+ scan1(Cs, [{integer,SPos,SoFar}|Toks], CPos).
+
+scan_after_fraction([$E|Cs], Ncs, Toks, SPos, CPos) ->
+ scan_exponent(Cs, [$E|Ncs], Toks, SPos, CPos);
+scan_after_fraction([$e|Cs], Ncs, Toks, SPos, CPos) ->
+ scan_exponent(Cs, [$E|Ncs], Toks, SPos, CPos);
+scan_after_fraction(Cs, Ncs, Toks, SPos, CPos) ->
+ case catch list_to_float(reverse(Ncs)) of
+ N when float(N) ->
+ scan1(Cs, [{float,SPos,N}|Toks], CPos);
+ _Error -> scan_error({illegal,float}, SPos)
+ end.
+
+%% scan_exponent(CharList, NumberCharStack, TokenStack, StartPos, CurPos)
+%% Generate an error here if E{+|-} not followed by any digits.
+
+scan_exponent([$+|Cs], Ncs, Toks, SPos, CPos) ->
+ scan_exponent1(Cs, [$+|Ncs], Toks, SPos, CPos);
+scan_exponent([$-|Cs], Ncs, Toks, SPos, CPos) ->
+ scan_exponent1(Cs, [$-|Ncs], Toks, SPos, CPos);
+scan_exponent(Cs, Ncs, Toks, SPos, CPos) ->
+ scan_exponent1(Cs, Ncs, Toks, SPos, CPos).
+
+scan_exponent1([C|Cs0], Ncs0, Toks, SPos, CPos) when C >= $0, C =< $9 ->
+ {Ncs,Cs,CPos1} = scan_integer(Cs0, [C|Ncs0], CPos),
+ case catch list_to_float(reverse(Ncs)) of
+ N when float(N) ->
+ scan1(Cs, [{float,SPos,N}|Toks], CPos1);
+ _Error -> scan_error({illegal,float}, SPos)
+ end;
+scan_exponent1(_, _, _, _, CPos) ->
+ scan_error(float, CPos).
+
+scan_error(In, Pos) ->
+ {error,{Pos,core_scan,In}}.
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/erl_bifs.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/erl_bifs.erl
new file mode 100644
index 0000000000..1dbeefb5ac
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/erl_bifs.erl
@@ -0,0 +1,486 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: erl_bifs.erl,v 1.2 2009/09/17 09:46:19 kostis Exp $
+%%
+%% Purpose: Information about the Erlang built-in functions.
+
+-module(erl_bifs).
+
+-export([is_bif/3, is_guard_bif/3, is_pure/3, is_safe/3]).
+
+
+%% =====================================================================
+%% is_bif(Module, Name, Arity) -> boolean()
+%%
+%% Module = Name = atom()
+%% Arity = integer()
+%%
+%% Returns `true' if the function `Module:Name/Arity' is a Built-In
+%% Function (BIF) of Erlang. BIFs "come with the implementation",
+%% and can be assumed to exist and have the same behaviour in any
+%% later versions of the same implementation of the language. Being
+%% a BIF does *not* imply that the function belongs to the module
+%% `erlang', nor that it is implemented in C or assembler (cf.
+%% `erlang:is_builtin/3'), or that it is auto-imported by the
+%% compiler (cf. `erl_internal:bif/3').
+
+is_bif(erlang, '!', 2) -> true;
+is_bif(erlang, '*', 2) -> true;
+is_bif(erlang, '+', 1) -> true;
+is_bif(erlang, '+', 2) -> true;
+is_bif(erlang, '++', 2) -> true;
+is_bif(erlang, '-', 1) -> true;
+is_bif(erlang, '-', 2) -> true;
+is_bif(erlang, '--', 2) -> true;
+is_bif(erlang, '/', 2) -> true;
+is_bif(erlang, '/=', 2) -> true;
+is_bif(erlang, '<', 2) -> true;
+is_bif(erlang, '=/=', 2) -> true;
+is_bif(erlang, '=:=', 2) -> true;
+is_bif(erlang, '=<', 2) -> true;
+is_bif(erlang, '==', 2) -> true;
+is_bif(erlang, '>', 2) -> true;
+is_bif(erlang, '>=', 2) -> true;
+is_bif(erlang, 'and', 2) -> true;
+is_bif(erlang, 'band', 2) -> true;
+is_bif(erlang, 'bnot', 1) -> true;
+is_bif(erlang, 'bor', 2) -> true;
+is_bif(erlang, 'bsl', 2) -> true;
+is_bif(erlang, 'bsr', 2) -> true;
+is_bif(erlang, 'bxor', 2) -> true;
+is_bif(erlang, 'div', 2) -> true;
+is_bif(erlang, 'not', 1) -> true;
+is_bif(erlang, 'or', 2) -> true;
+is_bif(erlang, 'rem', 2) -> true;
+is_bif(erlang, 'xor', 2) -> true;
+is_bif(erlang, abs, 1) -> true;
+is_bif(erlang, append_element, 2) -> true;
+is_bif(erlang, apply, 2) -> true;
+is_bif(erlang, apply, 3) -> true;
+is_bif(erlang, atom_to_list, 1) -> true;
+is_bif(erlang, binary_to_list, 1) -> true;
+is_bif(erlang, binary_to_list, 3) -> true;
+is_bif(erlang, binary_to_term, 1) -> true;
+is_bif(erlang, cancel_timer, 1) -> true;
+is_bif(erlang, concat_binary, 1) -> true;
+is_bif(erlang, date, 0) -> true;
+is_bif(erlang, demonitor, 1) -> true;
+is_bif(erlang, disconnect_node, 1) -> true;
+is_bif(erlang, display, 1) -> true;
+is_bif(erlang, element, 2) -> true;
+is_bif(erlang, erase, 0) -> true;
+is_bif(erlang, erase, 1) -> true;
+is_bif(erlang, error, 1) -> true;
+is_bif(erlang, error, 2) -> true;
+is_bif(erlang, exit, 1) -> true;
+is_bif(erlang, exit, 2) -> true;
+is_bif(erlang, fault, 1) -> true;
+is_bif(erlang, fault, 2) -> true;
+is_bif(erlang, float, 1) -> true;
+is_bif(erlang, float_to_list, 1) -> true;
+is_bif(erlang, fun_info, 1) -> true;
+is_bif(erlang, fun_info, 2) -> true;
+is_bif(erlang, fun_to_list, 1) -> true;
+is_bif(erlang, get, 0) -> true;
+is_bif(erlang, get, 1) -> true;
+is_bif(erlang, get_cookie, 0) -> true;
+is_bif(erlang, get_keys, 1) -> true;
+is_bif(erlang, group_leader, 0) -> true;
+is_bif(erlang, group_leader, 2) -> true;
+is_bif(erlang, halt, 0) -> false;
+is_bif(erlang, halt, 1) -> false;
+is_bif(erlang, hash, 2) -> false;
+is_bif(erlang, hd, 1) -> true;
+is_bif(erlang, info, 1) -> true;
+is_bif(erlang, integer_to_list, 1) -> true;
+is_bif(erlang, is_alive, 0) -> true;
+is_bif(erlang, is_atom, 1) -> true;
+is_bif(erlang, is_binary, 1) -> true;
+is_bif(erlang, is_boolean, 1) -> true;
+is_bif(erlang, is_builtin, 3) -> true;
+is_bif(erlang, is_constant, 1) -> true;
+is_bif(erlang, is_float, 1) -> true;
+is_bif(erlang, is_function, 1) -> true;
+is_bif(erlang, is_integer, 1) -> true;
+is_bif(erlang, is_list, 1) -> true;
+is_bif(erlang, is_number, 1) -> true;
+is_bif(erlang, is_pid, 1) -> true;
+is_bif(erlang, is_port, 1) -> true;
+is_bif(erlang, is_process_alive, 1) -> true;
+is_bif(erlang, is_record, 3) -> true;
+is_bif(erlang, is_reference, 1) -> true;
+is_bif(erlang, is_tuple, 1) -> true;
+is_bif(erlang, length, 1) -> true;
+is_bif(erlang, link, 1) -> true;
+is_bif(erlang, list_to_atom, 1) -> true;
+is_bif(erlang, list_to_binary, 1) -> true;
+is_bif(erlang, list_to_float, 1) -> true;
+is_bif(erlang, list_to_integer, 1) -> true;
+is_bif(erlang, list_to_pid, 1) -> true;
+is_bif(erlang, list_to_tuple, 1) -> true;
+is_bif(erlang, loaded, 0) -> true;
+is_bif(erlang, localtime, 0) -> true;
+is_bif(erlang, localtime_to_universaltime, 1) -> true;
+is_bif(erlang, make_ref, 0) -> true;
+is_bif(erlang, make_tuple, 2) -> true;
+is_bif(erlang, md5, 1) -> true;
+is_bif(erlang, md5_final, 1) -> true;
+is_bif(erlang, md5_init, 0) -> true;
+is_bif(erlang, md5_update, 2) -> true;
+is_bif(erlang, monitor, 2) -> true;
+is_bif(erlang, monitor_node, 2) -> true;
+is_bif(erlang, node, 0) -> true;
+is_bif(erlang, node, 1) -> true;
+is_bif(erlang, nodes, 0) -> true;
+is_bif(erlang, now, 0) -> true;
+is_bif(erlang, open_port, 2) -> true;
+is_bif(erlang, phash, 2) -> true;
+is_bif(erlang, pid_to_list, 1) -> true;
+is_bif(erlang, port_close, 2) -> true;
+is_bif(erlang, port_command, 2) -> true;
+is_bif(erlang, port_connect, 2) -> true;
+is_bif(erlang, port_control, 3) -> true;
+is_bif(erlang, port_info, 2) -> true;
+is_bif(erlang, port_to_list, 1) -> true;
+is_bif(erlang, ports, 0) -> true;
+is_bif(erlang, pre_loaded, 0) -> true;
+is_bif(erlang, process_display, 2) -> true;
+is_bif(erlang, process_flag, 2) -> true;
+is_bif(erlang, process_flag, 3) -> true;
+is_bif(erlang, process_info, 1) -> true;
+is_bif(erlang, process_info, 2) -> true;
+is_bif(erlang, processes, 0) -> true;
+is_bif(erlang, put, 2) -> true;
+is_bif(erlang, read_timer, 1) -> true;
+is_bif(erlang, ref_to_list, 1) -> true;
+is_bif(erlang, register, 2) -> true;
+is_bif(erlang, registered, 0) -> true;
+is_bif(erlang, resume_process, 1) -> true;
+is_bif(erlang, round, 1) -> true;
+is_bif(erlang, self, 0) -> true;
+is_bif(erlang, send_after, 3) -> true;
+is_bif(erlang, set_cookie, 2) -> true;
+is_bif(erlang, setelement, 3) -> true;
+is_bif(erlang, size, 1) -> true;
+is_bif(erlang, spawn, 1) -> true;
+is_bif(erlang, spawn, 2) -> true;
+is_bif(erlang, spawn, 3) -> true;
+is_bif(erlang, spawn, 4) -> true;
+is_bif(erlang, spawn_link, 1) -> true;
+is_bif(erlang, spawn_link, 2) -> true;
+is_bif(erlang, spawn_link, 3) -> true;
+is_bif(erlang, spawn_link, 4) -> true;
+is_bif(erlang, spawn_opt, 4) -> true;
+is_bif(erlang, split_binary, 2) -> true;
+is_bif(erlang, start_timer, 3) -> true;
+is_bif(erlang, statistics, 1) -> true;
+is_bif(erlang, suspend_process, 1) -> true;
+is_bif(erlang, system_flag, 2) -> true;
+is_bif(erlang, system_info, 1) -> true;
+is_bif(erlang, term_to_binary, 1) -> true;
+is_bif(erlang, term_to_binary, 2) -> true;
+is_bif(erlang, throw, 1) -> true;
+is_bif(erlang, time, 0) -> true;
+is_bif(erlang, tl, 1) -> true;
+is_bif(erlang, trace, 3) -> true;
+is_bif(erlang, trace_info, 2) -> true;
+is_bif(erlang, trace_pattern, 2) -> true;
+is_bif(erlang, trace_pattern, 3) -> true;
+is_bif(erlang, trunc, 1) -> true;
+is_bif(erlang, tuple_to_list, 1) -> true;
+is_bif(erlang, universaltime, 0) -> true;
+is_bif(erlang, universaltime_to_localtime, 1) -> true;
+is_bif(erlang, unlink, 1) -> true;
+is_bif(erlang, unregister, 1) -> true;
+is_bif(erlang, whereis, 1) -> true;
+is_bif(erlang, yield, 0) -> true;
+is_bif(lists, append, 2) -> true;
+is_bif(lists, reverse, 1) -> true;
+is_bif(lists, reverse, 2) -> true;
+is_bif(lists, subtract, 2) -> true;
+is_bif(math, acos, 1) -> true;
+is_bif(math, acosh, 1) -> true;
+is_bif(math, asin, 1) -> true;
+is_bif(math, asinh, 1) -> true;
+is_bif(math, atan, 1) -> true;
+is_bif(math, atan2, 2) -> true;
+is_bif(math, atanh, 1) -> true;
+is_bif(math, cos, 1) -> true;
+is_bif(math, cosh, 1) -> true;
+is_bif(math, erf, 1) -> true;
+is_bif(math, erfc, 1) -> true;
+is_bif(math, exp, 1) -> true;
+is_bif(math, log, 1) -> true;
+is_bif(math, log10, 1) -> true;
+is_bif(math, pow, 2) -> true;
+is_bif(math, sin, 1) -> true;
+is_bif(math, sinh, 1) -> true;
+is_bif(math, sqrt, 1) -> true;
+is_bif(math, tan, 1) -> true;
+is_bif(math, tanh, 1) -> true;
+is_bif(_, _, _) -> false.
+
+
+%% =====================================================================
+%% is_guard_bif(Module, Name, Arity) -> boolean()
+%%
+%% Module = Name = atom()
+%% Arity = integer()
+%%
+%% Returns `true' if the built-in function `Module:Name/Arity' may
+%% be called from a clause guard. Note that such "guard BIFs" are
+%% not necessarily "pure", since some (notably `erlang:self/0') may
+%% depend on the current state, nor "safe", since many guard BIFs
+%% can fail. Also note that even a "pure" function could be
+%% unsuitable for calling from a guard because of its time or space
+%% complexity.
+
+is_guard_bif(erlang, '*', 2) -> true;
+is_guard_bif(erlang, '+', 1) -> true;
+is_guard_bif(erlang, '+', 2) -> true;
+is_guard_bif(erlang, '-', 1) -> true;
+is_guard_bif(erlang, '-', 2) -> true;
+is_guard_bif(erlang, '/', 2) -> true;
+is_guard_bif(erlang, '/=', 2) -> true;
+is_guard_bif(erlang, '<', 2) -> true;
+is_guard_bif(erlang, '=/=', 2) -> true;
+is_guard_bif(erlang, '=:=', 2) -> true;
+is_guard_bif(erlang, '=<', 2) -> true;
+is_guard_bif(erlang, '==', 2) -> true;
+is_guard_bif(erlang, '>', 2) -> true;
+is_guard_bif(erlang, '>=', 2) -> true;
+is_guard_bif(erlang, 'and', 2) -> true;
+is_guard_bif(erlang, 'band', 2) -> true;
+is_guard_bif(erlang, 'bnot', 1) -> true;
+is_guard_bif(erlang, 'bor', 2) -> true;
+is_guard_bif(erlang, 'bsl', 2) -> true;
+is_guard_bif(erlang, 'bsr', 2) -> true;
+is_guard_bif(erlang, 'bxor', 2) -> true;
+is_guard_bif(erlang, 'div', 2) -> true;
+is_guard_bif(erlang, 'not', 1) -> true;
+is_guard_bif(erlang, 'or', 2) -> true;
+is_guard_bif(erlang, 'rem', 2) -> true;
+is_guard_bif(erlang, 'xor', 2) -> true;
+is_guard_bif(erlang, abs, 1) -> true;
+is_guard_bif(erlang, element, 2) -> true;
+is_guard_bif(erlang, error, 1) -> true; % unorthodox
+is_guard_bif(erlang, exit, 1) -> true; % unorthodox
+is_guard_bif(erlang, fault, 1) -> true; % unorthodox
+is_guard_bif(erlang, float, 1) -> true; % (the type coercion function)
+is_guard_bif(erlang, hd, 1) -> true;
+is_guard_bif(erlang, is_atom, 1) -> true;
+is_guard_bif(erlang, is_boolean, 1) -> true;
+is_guard_bif(erlang, is_binary, 1) -> true;
+is_guard_bif(erlang, is_constant, 1) -> true;
+is_guard_bif(erlang, is_float, 1) -> true;
+is_guard_bif(erlang, is_function, 1) -> true;
+is_guard_bif(erlang, is_integer, 1) -> true;
+is_guard_bif(erlang, is_list, 1) -> true;
+is_guard_bif(erlang, is_number, 1) -> true;
+is_guard_bif(erlang, is_pid, 1) -> true;
+is_guard_bif(erlang, is_port, 1) -> true;
+is_guard_bif(erlang, is_reference, 1) -> true;
+is_guard_bif(erlang, is_tuple, 1) -> true;
+is_guard_bif(erlang, length, 1) -> true;
+is_guard_bif(erlang, list_to_atom, 1) -> true; % unorthodox
+is_guard_bif(erlang, node, 0) -> true; % (not pure)
+is_guard_bif(erlang, node, 1) -> true; % (not pure)
+is_guard_bif(erlang, round, 1) -> true;
+is_guard_bif(erlang, self, 0) -> true; % (not pure)
+is_guard_bif(erlang, size, 1) -> true;
+is_guard_bif(erlang, throw, 1) -> true; % unorthodox
+is_guard_bif(erlang, tl, 1) -> true;
+is_guard_bif(erlang, trunc, 1) -> true;
+is_guard_bif(math, acos, 1) -> true; % unorthodox
+is_guard_bif(math, acosh, 1) -> true; % unorthodox
+is_guard_bif(math, asin, 1) -> true; % unorthodox
+is_guard_bif(math, asinh, 1) -> true; % unorthodox
+is_guard_bif(math, atan, 1) -> true; % unorthodox
+is_guard_bif(math, atan2, 2) -> true; % unorthodox
+is_guard_bif(math, atanh, 1) -> true; % unorthodox
+is_guard_bif(math, cos, 1) -> true; % unorthodox
+is_guard_bif(math, cosh, 1) -> true; % unorthodox
+is_guard_bif(math, erf, 1) -> true; % unorthodox
+is_guard_bif(math, erfc, 1) -> true; % unorthodox
+is_guard_bif(math, exp, 1) -> true; % unorthodox
+is_guard_bif(math, log, 1) -> true; % unorthodox
+is_guard_bif(math, log10, 1) -> true; % unorthodox
+is_guard_bif(math, pow, 2) -> true; % unorthodox
+is_guard_bif(math, sin, 1) -> true; % unorthodox
+is_guard_bif(math, sinh, 1) -> true; % unorthodox
+is_guard_bif(math, sqrt, 1) -> true; % unorthodox
+is_guard_bif(math, tan, 1) -> true; % unorthodox
+is_guard_bif(math, tanh, 1) -> true; % unorthodox
+is_guard_bif(_, _, _) -> false.
+
+
+%% =====================================================================
+%% is_pure(Module, Name, Arity) -> boolean()
+%%
+%% Module = Name = atom()
+%% Arity = integer()
+%%
+%% Returns `true' if the function `Module:Name/Arity' does not
+%% affect the state, nor depend on the state, although its
+%% evaluation is not guaranteed to complete normally for all input.
+
+is_pure(erlang, '*', 2) -> true;
+is_pure(erlang, '+', 1) -> true; % (even for non-numbers)
+is_pure(erlang, '+', 2) -> true;
+is_pure(erlang, '++', 2) -> true;
+is_pure(erlang, '-', 1) -> true;
+is_pure(erlang, '-', 2) -> true;
+is_pure(erlang, '--', 2) -> true;
+is_pure(erlang, '/', 2) -> true;
+is_pure(erlang, '/=', 2) -> true;
+is_pure(erlang, '<', 2) -> true;
+is_pure(erlang, '=/=', 2) -> true;
+is_pure(erlang, '=:=', 2) -> true;
+is_pure(erlang, '=<', 2) -> true;
+is_pure(erlang, '==', 2) -> true;
+is_pure(erlang, '>', 2) -> true;
+is_pure(erlang, '>=', 2) -> true;
+is_pure(erlang, 'and', 2) -> true;
+is_pure(erlang, 'band', 2) -> true;
+is_pure(erlang, 'bnot', 1) -> true;
+is_pure(erlang, 'bor', 2) -> true;
+is_pure(erlang, 'bsl', 2) -> true;
+is_pure(erlang, 'bsr', 2) -> true;
+is_pure(erlang, 'bxor', 2) -> true;
+is_pure(erlang, 'div', 2) -> true;
+is_pure(erlang, 'not', 1) -> true;
+is_pure(erlang, 'or', 2) -> true;
+is_pure(erlang, 'rem', 2) -> true;
+is_pure(erlang, 'xor', 2) -> true;
+is_pure(erlang, abs, 1) -> true;
+is_pure(erlang, atom_to_list, 1) -> true;
+is_pure(erlang, binary_to_list, 1) -> true;
+is_pure(erlang, binary_to_list, 3) -> true;
+is_pure(erlang, concat_binary, 1) -> true;
+is_pure(erlang, element, 2) -> true;
+is_pure(erlang, float, 1) -> true;
+is_pure(erlang, float_to_list, 1) -> true;
+is_pure(erlang, hash, 2) -> false;
+is_pure(erlang, hd, 1) -> true;
+is_pure(erlang, integer_to_list, 1) -> true;
+is_pure(erlang, is_atom, 1) -> true;
+is_pure(erlang, is_boolean, 1) -> true;
+is_pure(erlang, is_binary, 1) -> true;
+is_pure(erlang, is_builtin, 3) -> true;
+is_pure(erlang, is_constant, 1) -> true;
+is_pure(erlang, is_float, 1) -> true;
+is_pure(erlang, is_function, 1) -> true;
+is_pure(erlang, is_integer, 1) -> true;
+is_pure(erlang, is_list, 1) -> true;
+is_pure(erlang, is_number, 1) -> true;
+is_pure(erlang, is_pid, 1) -> true;
+is_pure(erlang, is_port, 1) -> true;
+is_pure(erlang, is_record, 3) -> true;
+is_pure(erlang, is_reference, 1) -> true;
+is_pure(erlang, is_tuple, 1) -> true;
+is_pure(erlang, length, 1) -> true;
+is_pure(erlang, list_to_atom, 1) -> true;
+is_pure(erlang, list_to_binary, 1) -> true;
+is_pure(erlang, list_to_float, 1) -> true;
+is_pure(erlang, list_to_integer, 1) -> true;
+is_pure(erlang, list_to_pid, 1) -> true;
+is_pure(erlang, list_to_tuple, 1) -> true;
+is_pure(erlang, phash, 2) -> false;
+is_pure(erlang, pid_to_list, 1) -> true;
+is_pure(erlang, round, 1) -> true;
+is_pure(erlang, setelement, 3) -> true;
+is_pure(erlang, size, 1) -> true;
+is_pure(erlang, split_binary, 2) -> true;
+is_pure(erlang, term_to_binary, 1) -> true;
+is_pure(erlang, tl, 1) -> true;
+is_pure(erlang, trunc, 1) -> true;
+is_pure(erlang, tuple_to_list, 1) -> true;
+is_pure(lists, append, 2) -> true;
+is_pure(lists, subtract, 2) -> true;
+is_pure(math, acos, 1) -> true;
+is_pure(math, acosh, 1) -> true;
+is_pure(math, asin, 1) -> true;
+is_pure(math, asinh, 1) -> true;
+is_pure(math, atan, 1) -> true;
+is_pure(math, atan2, 2) -> true;
+is_pure(math, atanh, 1) -> true;
+is_pure(math, cos, 1) -> true;
+is_pure(math, cosh, 1) -> true;
+is_pure(math, erf, 1) -> true;
+is_pure(math, erfc, 1) -> true;
+is_pure(math, exp, 1) -> true;
+is_pure(math, log, 1) -> true;
+is_pure(math, log10, 1) -> true;
+is_pure(math, pow, 2) -> true;
+is_pure(math, sin, 1) -> true;
+is_pure(math, sinh, 1) -> true;
+is_pure(math, sqrt, 1) -> true;
+is_pure(math, tan, 1) -> true;
+is_pure(math, tanh, 1) -> true;
+is_pure(_, _, _) -> false.
+
+
+%% =====================================================================
+%% is_safe(Module, Name, Arity) -> boolean()
+%%
+%% Module = Name = atom()
+%% Arity = integer()
+%%
+%% Returns `true' if the function `Module:Name/Arity' is completely
+%% effect free, i.e., if its evaluation always completes normally
+%% and does not affect the state (although the value it returns
+%% might depend on the state).
+
+is_safe(erlang, '/=', 2) -> true;
+is_safe(erlang, '<', 2) -> true;
+is_safe(erlang, '=/=', 2) -> true;
+is_safe(erlang, '=:=', 2) -> true;
+is_safe(erlang, '=<', 2) -> true;
+is_safe(erlang, '==', 2) -> true;
+is_safe(erlang, '>', 2) -> true;
+is_safe(erlang, '>=', 2) -> true;
+is_safe(erlang, date, 0) -> true;
+is_safe(erlang, get, 0) -> true;
+is_safe(erlang, get, 1) -> true;
+is_safe(erlang, get_cookie, 0) -> true;
+is_safe(erlang, get_keys, 1) -> true;
+is_safe(erlang, group_leader, 0) -> true;
+is_safe(erlang, is_alive, 0) -> true;
+is_safe(erlang, is_atom, 1) -> true;
+is_safe(erlang, is_boolean, 1) -> true;
+is_safe(erlang, is_binary, 1) -> true;
+is_safe(erlang, is_constant, 1) -> true;
+is_safe(erlang, is_float, 1) -> true;
+is_safe(erlang, is_function, 1) -> true;
+is_safe(erlang, is_integer, 1) -> true;
+is_safe(erlang, is_list, 1) -> true;
+is_safe(erlang, is_number, 1) -> true;
+is_safe(erlang, is_pid, 1) -> true;
+is_safe(erlang, is_port, 1) -> true;
+is_safe(erlang, is_record, 3) -> true;
+is_safe(erlang, is_reference, 1) -> true;
+is_safe(erlang, is_tuple, 1) -> true;
+is_safe(erlang, make_ref, 0) -> true;
+is_safe(erlang, node, 0) -> true;
+is_safe(erlang, nodes, 0) -> true;
+is_safe(erlang, ports, 0) -> true;
+is_safe(erlang, pre_loaded, 0) -> true;
+is_safe(erlang, processes, 0) -> true;
+is_safe(erlang, registered, 0) -> true;
+is_safe(erlang, self, 0) -> true;
+is_safe(erlang, term_to_binary, 1) -> true;
+is_safe(erlang, time, 0) -> true;
+is_safe(_, _, _) -> false.
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/rec_env.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/rec_env.erl
new file mode 100644
index 0000000000..01c2512397
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/rec_env.erl
@@ -0,0 +1,611 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id: rec_env.erl,v 1.2 2009/09/17 09:46:19 kostis Exp $
+%%
+%% @author Richard Carlsson
+%% @copyright 1999-2004 Richard Carlsson
+%% @doc Abstract environments, supporting self-referential bindings and
+%% automatic new-key generation.
+
+%% The current implementation is based on Erlang standard library
+%% dictionaries.
+
+%%% -define(DEBUG, true).
+
+-module(rec_env).
+
+-export([bind/3, bind_list/3, bind_recursive/4, delete/2, empty/0,
+ get/2, is_defined/2, is_empty/1, keys/1, lookup/2, new_key/1,
+ new_key/2, new_keys/2, new_keys/3, size/1, to_list/1]).
+
+-ifdef(DEBUG).
+-export([test/1, test_custom/1, test_custom/2]).
+-endif.
+
+-ifdef(DEBUG).
+%% Code for testing:
+%%@hidden
+test(N) ->
+ test_0(integer, N).
+
+%%@hidden
+test_custom(N) ->
+ F = fun (X) -> list_to_atom("X"++integer_to_list(X)) end,
+ test_custom(F, N).
+
+%%@hidden
+test_custom(F, N) ->
+ test_0({custom, F}, N).
+
+test_0(Type, N) ->
+ put(new_key_calls, 0),
+ put(new_key_retries, 0),
+ put(new_key_max, 0),
+ Env = test_1(Type, N, empty()),
+ io:fwrite("\ncalls: ~w.\n", [get(new_key_calls)]),
+ io:fwrite("\nretries: ~w.\n", [get(new_key_retries)]),
+ io:fwrite("\nmax: ~w.\n", [get(new_key_max)]),
+ dict:to_list(element(1,Env)).
+
+test_1(integer = Type, N, Env) when integer(N), N > 0 ->
+ Key = new_key(Env),
+ test_1(Type, N - 1, bind(Key, value, Env));
+test_1({custom, F} = Type, N, Env) when integer(N), N > 0 ->
+ Key = new_key(F, Env),
+ test_1(Type, N - 1, bind(Key, value, Env));
+test_1(_,0, Env) ->
+ Env.
+-endif.
+
+
+%% Representation:
+%%
+%% environment() = [Mapping]
+%%
+%% Mapping = {map, Dict} | {rec, Dict, Dict}
+%% Dict = dict:dictionary()
+%%
+%% An empty environment is a list containing a single `{map, Dict}'
+%% element - empty lists are not valid environments. To find a key in an
+%% environment, it is searched for in each mapping in the list, in
+%% order, until it the key is found in some mapping, or the end of the
+%% list is reached. In a 'rec' mapping, we keep the original dictionary
+%% together with a version where entries may have been deleted - this
+%% makes it possible to garbage collect the entire 'rec' mapping when
+%% all its entries are unused (for example, by being shadowed by later
+%% definitions).
+
+
+
+%% =====================================================================
+%% @type environment(). An abstract environment.
+
+
+%% =====================================================================
+%% @spec empty() -> environment()
+%%
+%% @doc Returns an empty environment.
+
+empty() ->
+ [{map, dict:new()}].
+
+
+%% =====================================================================
+%% @spec is_empty(Env::environment()) -> boolean()
+%%
+%% @doc Returns true
if the environment is empty, otherwise
+%% false
.
+
+is_empty([{map, Dict} | Es]) ->
+ N = dict:size(Dict),
+ if N /= 0 -> false;
+ Es == [] -> true;
+ true -> is_empty(Es)
+ end;
+is_empty([{rec, Dict, _} | Es]) ->
+ N = dict:size(Dict),
+ if N /= 0 -> false;
+ Es == [] -> true;
+ true -> is_empty(Es)
+ end.
+
+
+%% =====================================================================
+%% @spec size(Env::environment()) -> integer()
+%%
+%% @doc Returns the number of entries in an environment.
+
+%% (The name 'size' cannot be used in local calls, since there exists a
+%% built-in function with the same name.)
+
+size(Env) ->
+ env_size(Env).
+
+env_size([{map, Dict}]) ->
+ dict:size(Dict);
+env_size([{map, Dict} | Env]) ->
+ dict:size(Dict) + env_size(Env);
+env_size([{rec, Dict, _Dict0} | Env]) ->
+ dict:size(Dict) + env_size(Env).
+
+
+%% =====================================================================
+%% @spec is_defined(Key, Env) -> boolean()
+%%
+%% Key = term()
+%% Env = environment()
+%%
+%% @doc Returns true
if Key
is bound in the
+%% environment, otherwise false
.
+
+is_defined(Key, [{map, Dict} | Env]) ->
+ case dict:is_key(Key, Dict) of
+ true ->
+ true;
+ false when Env == [] ->
+ false;
+ false ->
+ is_defined(Key, Env)
+ end;
+is_defined(Key, [{rec, Dict, _Dict0} | Env]) ->
+ case dict:is_key(Key, Dict) of
+ true ->
+ true;
+ false ->
+ is_defined(Key, Env)
+ end.
+
+
+%% =====================================================================
+%% @spec keys(Env::environment()) -> [term()]
+%%
+%% @doc Returns the ordered list of all keys in the environment.
+
+keys(Env) ->
+ lists:sort(keys(Env, [])).
+
+keys([{map, Dict}], S) ->
+ dict:fetch_keys(Dict) ++ S;
+keys([{map, Dict} | Env], S) ->
+ keys(Env, dict:fetch_keys(Dict) ++ S);
+keys([{rec, Dict, _Dict0} | Env], S) ->
+ keys(Env, dict:fetch_keys(Dict) ++ S).
+
+
+%% =====================================================================
+%% @spec to_list(Env) -> [{Key, Value}]
+%%
+%% Env = environment()
+%% Key = term()
+%% Value = term()
+%%
+%% @doc Returns an ordered list of {Key, Value}
pairs for
+%% all keys in Env
. Value
is the same as that
+%% returned by {@link get/2}.
+
+to_list(Env) ->
+ lists:sort(to_list(Env, [])).
+
+to_list([{map, Dict}], S) ->
+ dict:to_list(Dict) ++ S;
+to_list([{map, Dict} | Env], S) ->
+ to_list(Env, dict:to_list(Dict) ++ S);
+to_list([{rec, Dict, _Dict0} | Env], S) ->
+ to_list(Env, dict:to_list(Dict) ++ S).
+
+
+%% =====================================================================
+%% @spec bind(Key, Value, Env) -> environment()
+%%
+%% Key = term()
+%% Value = term()
+%% Env = environment()
+%%
+%% @doc Make a nonrecursive entry. This binds Key
to
+%% Value
. If the key already existed in the environment,
+%% the old entry is replaced.
+
+%% Note that deletion is done to free old bindings so they can be
+%% garbage collected.
+
+bind(Key, Value, [{map, Dict}]) ->
+ [{map, dict:store(Key, Value, Dict)}];
+bind(Key, Value, [{map, Dict} | Env]) ->
+ [{map, dict:store(Key, Value, Dict)} | delete_any(Key, Env)];
+bind(Key, Value, Env) ->
+ [{map, dict:store(Key, Value, dict:new())} | delete_any(Key, Env)].
+
+
+%% =====================================================================
+%% @spec bind_list(Keys, Values, Env) -> environment()
+%%
+%% Keys = [term()]
+%% Values = [term()]
+%% Env = environment()
+%%
+%% @doc Make N nonrecursive entries. This binds each key in
+%% Keys
to the corresponding value in
+%% Values
. If some key already existed in the environment,
+%% the previous entry is replaced. If Keys
does not have
+%% the same length as Values
, an exception is generated.
+
+bind_list(Ks, Vs, [{map, Dict}]) ->
+ [{map, store_list(Ks, Vs, Dict)}];
+bind_list(Ks, Vs, [{map, Dict} | Env]) ->
+ [{map, store_list(Ks, Vs, Dict)} | delete_list(Ks, Env)];
+bind_list(Ks, Vs, Env) ->
+ [{map, store_list(Ks, Vs, dict:new())} | delete_list(Ks, Env)].
+
+store_list([K | Ks], [V | Vs], Dict) ->
+ store_list(Ks, Vs, dict:store(K, V, Dict));
+store_list([], _, Dict) ->
+ Dict.
+
+delete_list([K | Ks], Env) ->
+ delete_list(Ks, delete_any(K, Env));
+delete_list([], Env) ->
+ Env.
+
+%% By not calling `delete' unless we have to, we avoid unnecessary
+%% rewriting of the data.
+
+delete_any(Key, Env) ->
+ case is_defined(Key, Env) of
+ true ->
+ delete(Key, Env);
+ false ->
+ Env
+ end.
+
+%% =====================================================================
+%% @spec delete(Key, Env) -> environment()
+%%
+%% Key = term()
+%% Env = environment()
+%%
+%% @doc Delete an entry. This removes Key
from the
+%% environment.
+
+delete(Key, [{map, Dict} = E | Env]) ->
+ case dict:is_key(Key, Dict) of
+ true ->
+ [{map, dict:erase(Key, Dict)} | Env];
+ false ->
+ delete_1(Key, Env, E)
+ end;
+delete(Key, [{rec, Dict, Dict0} = E | Env]) ->
+ case dict:is_key(Key, Dict) of
+ true ->
+ %% The Dict0 component must be preserved as it is until all
+ %% keys in Dict have been deleted.
+ Dict1 = dict:erase(Key, Dict),
+ case dict:size(Dict1) of
+ 0 ->
+ Env; % the whole {rec,...} is now garbage
+ _ ->
+ [{rec, Dict1, Dict0} | Env]
+ end;
+ false ->
+ [E | delete(Key, Env)]
+ end.
+
+%% This is just like above, except we pass on the preceding 'map'
+%% mapping in the list to enable merging when removing 'rec' mappings.
+
+delete_1(Key, [{rec, Dict, Dict0} = E | Env], E1) ->
+ case dict:is_key(Key, Dict) of
+ true ->
+ Dict1 = dict:erase(Key, Dict),
+ case dict:size(Dict1) of
+ 0 ->
+ concat(E1, Env);
+ _ ->
+ [E1, {rec, Dict1, Dict0} | Env]
+ end;
+ false ->
+ [E1, E | delete(Key, Env)]
+ end.
+
+concat({map, D1}, [{map, D2} | Env]) ->
+ [dict:merge(fun (_K, V1, _V2) -> V1 end, D1, D2) | Env];
+concat(E1, Env) ->
+ [E1 | Env].
+
+
+%% =====================================================================
+%% @spec bind_recursive(Keys, Values, Fun, Env) -> NewEnv
+%%
+%% Keys = [term()]
+%% Values = [term()]
+%% Fun = (Value, Env) -> term()
+%% Env = environment()
+%% NewEnv = environment()
+%%
+%% @doc Make N recursive entries. This binds each key in
+%% Keys
to the value of Fun(Value, NewEnv)
for
+%% the corresponding Value
. If Keys
does not
+%% have the same length as Values
, an exception is
+%% generated. If some key already existed in the environment, the old
+%% entry is replaced.
+%%
+%% Note: the function Fun
is evaluated each time one of
+%% the stored keys is looked up, but only then.
+%%
+%% Examples:
+%%
+%% NewEnv = bind_recursive([foo, bar], [1, 2],
+%% fun (V, E) -> V end,
+%% Env)
+%%
+%% This does nothing interesting; get(foo, NewEnv)
yields
+%% 1
and get(bar, NewEnv)
yields
+%% 2
, but there is more overhead than if the {@link
+%% bind_list/3} function had been used.
+%%
+%%
+%% NewEnv = bind_recursive([foo, bar], [1, 2],
+%% fun (V, E) -> {V, E} end,
+%% Env)
+%%
+%% Here, however, get(foo, NewEnv)
will yield {1,
+%% NewEnv}
and get(bar, NewEnv)
will yield {2,
+%% NewEnv}
, i.e., the environment NewEnv
contains
+%% recursive bindings.
+
+bind_recursive([], [], _, Env) ->
+ Env;
+bind_recursive(Ks, Vs, F, Env) ->
+ F1 = fun (V) ->
+ fun (Dict) -> F(V, [{rec, Dict, Dict} | Env]) end
+ end,
+ Dict = bind_recursive_1(Ks, Vs, F1, dict:new()),
+ [{rec, Dict, Dict} | Env].
+
+bind_recursive_1([K | Ks], [V | Vs], F, Dict) ->
+ bind_recursive_1(Ks, Vs, F, dict:store(K, F(V), Dict));
+bind_recursive_1([], [], _, Dict) ->
+ Dict.
+
+
+%% =====================================================================
+%% @spec lookup(Key, Env) -> error | {ok, Value}
+%%
+%% Key = term()
+%% Env = environment()
+%% Value = term()
+%%
+%% @doc Returns {ok, Value}
if Key
is bound to
+%% Value
in Env
, and error
+%% otherwise.
+
+lookup(Key, [{map, Dict} | Env]) ->
+ case dict:find(Key, Dict) of
+ {ok, _}=Value ->
+ Value;
+ error when Env == [] ->
+ error;
+ error ->
+ lookup(Key, Env)
+ end;
+lookup(Key, [{rec, Dict, Dict0} | Env]) ->
+ case dict:find(Key, Dict) of
+ {ok, F} ->
+ {ok, F(Dict0)};
+ error ->
+ lookup(Key, Env)
+ end.
+
+
+%% =====================================================================
+%% @spec get(Key, Env) -> Value
+%%
+%% Key = term()
+%% Env = environment()
+%% Value = term()
+%%
+%% @doc Returns the value that Key
is bound to in
+%% Env
. Throws {undefined, Key}
if the key
+%% does not exist in Env
.
+
+get(Key, Env) ->
+ case lookup(Key, Env) of
+ {ok, Value} -> Value;
+ error -> throw({undefined, Key})
+ end.
+
+
+%% =====================================================================
+%% The key-generating algorithm could possibly be further improved. The
+%% important thing to keep in mind is, that when we need a new key, we
+%% are generally in mid-traversal of a syntax tree, and existing names
+%% in the tree may be closely grouped and evenly distributed or even
+%% forming a compact range (often having been generated by a "gensym",
+%% or by this very algorithm itself). This means that if we generate an
+%% identifier whose value is too close to those already seen (i.e.,
+%% which are in the environment), it is very probable that we will
+%% shadow a not-yet-seen identifier further down in the tree, the result
+%% being that we induce another later renaming, and end up renaming most
+%% of the identifiers, completely contrary to our intention. We need to
+%% generate new identifiers in a way that avoids such systematic
+%% collisions.
+%%
+%% One way of getting a new key to try when the previous attempt failed
+%% is of course to e.g. add one to the last tried value. However, in
+%% general it's a bad idea to try adjacent identifiers: the percentage
+%% of retries will typically increase a lot, so you may lose big on the
+%% extra lookups while gaining only a little from the quicker
+%% computation.
+%%
+%% We want an initial range that is large enough for most typical cases.
+%% If we start with, say, a range of 10, we might quickly use up most of
+%% the values in the range 1-10 (or 1-100) for new top-level variables -
+%% but as we start traversing the syntax tree, it is quite likely that
+%% exactly those variables will be encountered again (this depends on
+%% how the names in the tree were created), and will then need to be
+%% renamed. If we instead begin with a larger range, it is less likely
+%% that any top-level names that we introduce will shadow names that we
+%% will find in the tree. Of course we cannot know how large is large
+%% enough: for any initial range, there is some syntax tree that uses
+%% all the values in that range, and thus any top-level names introduced
+%% will shadow names in the tree. The point is to avoid this happening
+%% all the time - a range of about 1000 seems enough for most programs.
+%%
+%% The following values have been shown to work well:
+
+-define(MINIMUM_RANGE, 1000).
+-define(START_RANGE_FACTOR, 50).
+-define(MAX_RETRIES, 2). % retries before enlarging range
+-define(ENLARGE_FACTOR, 10). % range enlargment factor
+
+-ifdef(DEBUG).
+%% If you want to use these process dictionary counters, make sure to
+%% initialise them to zero before you call any of the key-generating
+%% functions.
+%%
+%% new_key_calls total number of calls
+%% new_key_retries failed key generation attempts
+%% new_key_max maximum generated integer value
+%%
+-define(measure_calls(),
+ put(new_key_calls, 1 + get(new_key_calls))).
+-define(measure_max_key(N),
+ case N > get(new_key_max) of
+ true ->
+ put(new_key_max, N);
+ false ->
+ ok
+ end).
+-define(measure_retries(N),
+ put(new_key_retries, get(new_key_retries) + N)).
+-else.
+-define(measure_calls(), ok).
+-define(measure_max_key(N), ok).
+-define(measure_retries(N), ok).
+-endif.
+
+
+%% =====================================================================
+%% @spec new_key(Env::environment()) -> integer()
+%%
+%% @doc Returns an integer which is not already used as key in the
+%% environment. New integers are generated using an algorithm which
+%% tries to keep the values randomly distributed within a reasonably
+%% small range relative to the number of entries in the environment.
+%%
+%% This function uses the Erlang standard library module
+%% random
to generate new keys.
+%%
+%% Note that only the new key is returned; the environment itself is
+%% not updated by this function.
+
+new_key(Env) ->
+ new_key(fun (X) -> X end, Env).
+
+
+%% =====================================================================
+%% @spec new_key(Function, Env) -> term()
+%%
+%% Function = (integer()) -> term()
+%% Env = environment()
+%%
+%% @doc Returns a term which is not already used as key in the
+%% environment. The term is generated by applying Function
+%% to an integer generated as in {@link new_key/1}.
+%%
+%% Note that only the generated term is returned; the environment
+%% itself is not updated by this function.
+
+new_key(F, Env) ->
+ ?measure_calls(),
+ R = start_range(Env),
+%%% io:fwrite("Start range: ~w.\n", [R]),
+ new_key(R, F, Env).
+
+new_key(R, F, Env) ->
+ new_key(generate(R, R), R, 0, F, Env).
+
+new_key(N, R, T, F, Env) when T < ?MAX_RETRIES ->
+ A = F(N),
+ case is_defined(A, Env) of
+ true ->
+%%% io:fwrite("CLASH: ~w.\n", [A]),
+ new_key(generate(N, R), R, T + 1, F, Env);
+ false ->
+ ?measure_max_key(N),
+ ?measure_retries(T),
+%%% io:fwrite("New: ~w.\n", [N]),
+ A
+ end;
+new_key(N, R, _T, F, Env) ->
+ %% Too many retries - enlarge the range and start over.
+ ?measure_retries((_T + 1)),
+ R1 = trunc(R * ?ENLARGE_FACTOR),
+%%% io:fwrite("**NEW RANGE**: ~w.\n", [R1]),
+ new_key(generate(N, R1), R1, 0, F, Env).
+
+start_range(Env) ->
+ max(env_size(Env) * ?START_RANGE_FACTOR, ?MINIMUM_RANGE).
+
+max(X, Y) when X > Y -> X;
+max(_, Y) -> Y.
+
+%% The previous key might or might not be used to compute the next key
+%% to be tried. It is currently not used.
+%%
+%% In order to avoid causing cascading renamings, it is important that
+%% this function does not generate values in order, but
+%% (pseudo-)randomly distributed over the range.
+
+generate(_N, Range) ->
+ random:uniform(Range). % works well
+
+
+%% =====================================================================
+%% @spec new_keys(N, Env) -> [integer()]
+%%
+%% N = integer()
+%% Env = environment()
+%%
+%% @doc Returns a list of N
distinct integers that are not
+%% already used as keys in the environment. See {@link new_key/1} for
+%% details.
+
+new_keys(N, Env) when integer(N) ->
+ new_keys(N, fun (X) -> X end, Env).
+
+
+%% =====================================================================
+%% @spec new_keys(N, Function, Env) -> [term()]
+%%
+%% N = integer()
+%% Function = (integer()) -> term()
+%% Env = environment()
+%%
+%% @doc Returns a list of N
distinct terms that are not
+%% already used as keys in the environment. See {@link new_key/3} for
+%% details.
+
+new_keys(N, F, Env) when integer(N) ->
+ R = start_range(Env),
+ new_keys(N, [], R, F, Env).
+
+new_keys(N, Ks, R, F, Env) when N > 0 ->
+ Key = new_key(R, F, Env),
+ Env1 = bind(Key, true, Env), % dummy binding
+ new_keys(N - 1, [Key | Ks], R, F, Env1);
+new_keys(0, Ks, _, _, _) ->
+ Ks.
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/sys_expand_pmod.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/sys_expand_pmod.erl
new file mode 100644
index 0000000000..f48cc05b9c
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/sys_expand_pmod.erl
@@ -0,0 +1,425 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: sys_expand_pmod.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+-module(sys_expand_pmod).
+
+%% Expand function definition forms of parameterized module. We assume
+%% all record definitions, imports, queries, etc., have been expanded
+%% away. Any calls on the form 'foo(...)' must be calls to local
+%% functions. Auto-generated functions (module_info,...) have not yet
+%% been added to the function definitions, but are listed in 'defined'
+%% and 'exports'. The 'new/N' function is neither added to the
+%% definitions nor to the 'exports'/'defines' lists yet.
+
+-export([forms/4]).
+
+-record(pmod, {parameters, exports, defined, predef}).
+
+%% TODO: more abstract handling of predefined/static functions.
+
+forms(Fs0, Ps, Es0, Ds0) ->
+ PreDef = [{module_info,0},{module_info,1}],
+ forms(Fs0, Ps, Es0, Ds0, PreDef).
+
+forms(Fs0, Ps, Es0, Ds0, PreDef) ->
+ St0 = #pmod{parameters=Ps,exports=Es0,defined=Ds0, predef=PreDef},
+ {Fs1, St1} = forms(Fs0, St0),
+ Es1 = update_function_names(Es0, St1),
+ Ds1 = update_function_names(Ds0, St1),
+ Fs2 = update_forms(Fs1, St1),
+ {Fs2,Es1,Ds1}.
+
+%% This is extremely simplistic for now; all functions get an extra
+%% parameter, whether they need it or not, except for static functions.
+
+update_function_names(Es, St) ->
+ [update_function_name(E, St) || E <- Es].
+
+update_function_name(E={F,A}, St) ->
+ case ordsets:is_element(E, St#pmod.predef) of
+ true -> E;
+ false -> {F, A + 1}
+ end.
+
+update_forms([{function,L,N,A,Cs}|Fs],St) ->
+ [{function,L,N,A+1,Cs}|update_forms(Fs,St)];
+update_forms([F|Fs],St) ->
+ [F|update_forms(Fs,St)];
+update_forms([],_St) ->
+ [].
+
+%% Process the program forms.
+
+forms([F0|Fs0],St0) ->
+ {F1,St1} = form(F0,St0),
+ {Fs1,St2} = forms(Fs0,St1),
+ {[F1|Fs1],St2};
+forms([], St0) ->
+ {[], St0}.
+
+%% Only function definitions are of interest here. State is not updated.
+form({function,Line,Name0,Arity0,Clauses0},St) ->
+ {Name,Arity,Clauses} = function(Name0, Arity0, Clauses0, St),
+ {{function,Line,Name,Arity,Clauses},St};
+%% Pass anything else through
+form(F,St) -> {F,St}.
+
+function(Name, Arity, Clauses0, St) ->
+ Clauses1 = clauses(Clauses0,St),
+ {Name,Arity,Clauses1}.
+
+clauses([C|Cs],St) ->
+ {clause,L,H,G,B} = clause(C,St),
+ T = {tuple,L,[{var,L,V} || V <- ['_'|St#pmod.parameters]]},
+ [{clause,L,H++[{match,L,T,{var,L,'THIS'}}],G,B}|clauses(Cs,St)];
+clauses([],_St) -> [].
+
+clause({clause,Line,H0,G0,B0},St) ->
+ H1 = head(H0,St),
+ G1 = guard(G0,St),
+ B1 = exprs(B0,St),
+ {clause,Line,H1,G1,B1}.
+
+head(Ps,St) -> patterns(Ps,St).
+
+patterns([P0|Ps],St) ->
+ P1 = pattern(P0,St),
+ [P1|patterns(Ps,St)];
+patterns([],_St) -> [].
+
+string_to_conses([], _Line, Tail) ->
+ Tail;
+string_to_conses([E|Rest], Line, Tail) ->
+ {cons, Line, {integer, Line, E}, string_to_conses(Rest, Line, Tail)}.
+
+pattern({var,Line,V},_St) -> {var,Line,V};
+pattern({match,Line,L0,R0},St) ->
+ L1 = pattern(L0,St),
+ R1 = pattern(R0,St),
+ {match,Line,L1,R1};
+pattern({integer,Line,I},_St) -> {integer,Line,I};
+pattern({char,Line,C},_St) -> {char,Line,C};
+pattern({float,Line,F},_St) -> {float,Line,F};
+pattern({atom,Line,A},_St) -> {atom,Line,A};
+pattern({string,Line,S},_St) -> {string,Line,S};
+pattern({nil,Line},_St) -> {nil,Line};
+pattern({cons,Line,H0,T0},St) ->
+ H1 = pattern(H0,St),
+ T1 = pattern(T0,St),
+ {cons,Line,H1,T1};
+pattern({tuple,Line,Ps0},St) ->
+ Ps1 = pattern_list(Ps0,St),
+ {tuple,Line,Ps1};
+pattern({bin,Line,Fs},St) ->
+ Fs2 = pattern_grp(Fs,St),
+ {bin,Line,Fs2};
+pattern({op,_Line,'++',{nil,_},R},St) ->
+ pattern(R,St);
+pattern({op,_Line,'++',{cons,Li,{char,C2,I},T},R},St) ->
+ pattern({cons,Li,{char,C2,I},{op,Li,'++',T,R}},St);
+pattern({op,_Line,'++',{cons,Li,{integer,L2,I},T},R},St) ->
+ pattern({cons,Li,{integer,L2,I},{op,Li,'++',T,R}},St);
+pattern({op,_Line,'++',{string,Li,L},R},St) ->
+ pattern(string_to_conses(L, Li, R),St);
+pattern({op,Line,Op,A},_St) ->
+ {op,Line,Op,A};
+pattern({op,Line,Op,L,R},_St) ->
+ {op,Line,Op,L,R}.
+
+pattern_grp([{bin_element,L1,E1,S1,T1} | Fs],St) ->
+ S2 = case S1 of
+ default ->
+ default;
+ _ ->
+ expr(S1,St)
+ end,
+ T2 = case T1 of
+ default ->
+ default;
+ _ ->
+ bit_types(T1)
+ end,
+ [{bin_element,L1,expr(E1,St),S2,T2} | pattern_grp(Fs,St)];
+pattern_grp([],_St) ->
+ [].
+
+bit_types([]) ->
+ [];
+bit_types([Atom | Rest]) when atom(Atom) ->
+ [Atom | bit_types(Rest)];
+bit_types([{Atom, Integer} | Rest]) when atom(Atom), integer(Integer) ->
+ [{Atom, Integer} | bit_types(Rest)].
+
+pattern_list([P0|Ps],St) ->
+ P1 = pattern(P0,St),
+ [P1|pattern_list(Ps,St)];
+pattern_list([],_St) -> [].
+
+guard([G0|Gs],St) when list(G0) ->
+ [guard0(G0,St) | guard(Gs,St)];
+guard(L,St) ->
+ guard0(L,St).
+
+guard0([G0|Gs],St) ->
+ G1 = guard_test(G0,St),
+ [G1|guard0(Gs,St)];
+guard0([],_St) -> [].
+
+guard_test(Expr={call,Line,{atom,La,F},As0},St) ->
+ case erl_internal:type_test(F, length(As0)) of
+ true ->
+ As1 = gexpr_list(As0,St),
+ {call,Line,{atom,La,F},As1};
+ _ ->
+ gexpr(Expr,St)
+ end;
+guard_test(Any,St) ->
+ gexpr(Any,St).
+
+gexpr({var,L,V},_St) ->
+ {var,L,V};
+% %% alternative implementation of accessing module parameters
+% case index(V,St#pmod.parameters) of
+% N when N > 0 ->
+% {call,L,{remote,L,{atom,L,erlang},{atom,L,element}},
+% [{integer,L,N+1},{var,L,'THIS'}]};
+% _ ->
+% {var,L,V}
+% end;
+gexpr({integer,Line,I},_St) -> {integer,Line,I};
+gexpr({char,Line,C},_St) -> {char,Line,C};
+gexpr({float,Line,F},_St) -> {float,Line,F};
+gexpr({atom,Line,A},_St) -> {atom,Line,A};
+gexpr({string,Line,S},_St) -> {string,Line,S};
+gexpr({nil,Line},_St) -> {nil,Line};
+gexpr({cons,Line,H0,T0},St) ->
+ H1 = gexpr(H0,St),
+ T1 = gexpr(T0,St),
+ {cons,Line,H1,T1};
+gexpr({tuple,Line,Es0},St) ->
+ Es1 = gexpr_list(Es0,St),
+ {tuple,Line,Es1};
+gexpr({call,Line,{atom,La,F},As0},St) ->
+ case erl_internal:guard_bif(F, length(As0)) of
+ true -> As1 = gexpr_list(As0,St),
+ {call,Line,{atom,La,F},As1}
+ end;
+% Pre-expansion generated calls to erlang:is_record/3 must also be handled
+gexpr({call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,is_record}},As0},St)
+ when length(As0) == 3 ->
+ As1 = gexpr_list(As0,St),
+ {call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,is_record}},As1};
+% Guard bif's can be remote, but only in the module erlang...
+gexpr({call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,F}},As0},St) ->
+ case erl_internal:guard_bif(F, length(As0)) or
+ erl_internal:arith_op(F, length(As0)) or
+ erl_internal:comp_op(F, length(As0)) or
+ erl_internal:bool_op(F, length(As0)) of
+ true -> As1 = gexpr_list(As0,St),
+ {call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,F}},As1}
+ end;
+% Unfortunately, writing calls as {M,F}(...) is also allowed.
+gexpr({call,Line,{tuple,La,[{atom,Lb,erlang},{atom,Lc,F}]},As0},St) ->
+ case erl_internal:guard_bif(F, length(As0)) or
+ erl_internal:arith_op(F, length(As0)) or
+ erl_internal:comp_op(F, length(As0)) or
+ erl_internal:bool_op(F, length(As0)) of
+ true -> As1 = gexpr_list(As0,St),
+ {call,Line,{tuple,La,[{atom,Lb,erlang},{atom,Lc,F}]},As1}
+ end;
+gexpr({bin,Line,Fs},St) ->
+ Fs2 = pattern_grp(Fs,St),
+ {bin,Line,Fs2};
+gexpr({op,Line,Op,A0},St) ->
+ case erl_internal:arith_op(Op, 1) or
+ erl_internal:bool_op(Op, 1) of
+ true -> A1 = gexpr(A0,St),
+ {op,Line,Op,A1}
+ end;
+gexpr({op,Line,Op,L0,R0},St) ->
+ case erl_internal:arith_op(Op, 2) or
+ erl_internal:bool_op(Op, 2) or
+ erl_internal:comp_op(Op, 2) of
+ true ->
+ L1 = gexpr(L0,St),
+ R1 = gexpr(R0,St),
+ {op,Line,Op,L1,R1}
+ end.
+
+gexpr_list([E0|Es],St) ->
+ E1 = gexpr(E0,St),
+ [E1|gexpr_list(Es,St)];
+gexpr_list([],_St) -> [].
+
+exprs([E0|Es],St) ->
+ E1 = expr(E0,St),
+ [E1|exprs(Es,St)];
+exprs([],_St) -> [].
+
+expr({var,L,V},_St) ->
+ {var,L,V};
+% case index(V,St#pmod.parameters) of
+% N when N > 0 ->
+% {call,L,{remote,L,{atom,L,erlang},{atom,L,element}},
+% [{integer,L,N+1},{var,L,'THIS'}]};
+% _ ->
+% {var,L,V}
+% end;
+expr({integer,Line,I},_St) -> {integer,Line,I};
+expr({float,Line,F},_St) -> {float,Line,F};
+expr({atom,Line,A},_St) -> {atom,Line,A};
+expr({string,Line,S},_St) -> {string,Line,S};
+expr({char,Line,C},_St) -> {char,Line,C};
+expr({nil,Line},_St) -> {nil,Line};
+expr({cons,Line,H0,T0},St) ->
+ H1 = expr(H0,St),
+ T1 = expr(T0,St),
+ {cons,Line,H1,T1};
+expr({lc,Line,E0,Qs0},St) ->
+ Qs1 = lc_quals(Qs0,St),
+ E1 = expr(E0,St),
+ {lc,Line,E1,Qs1};
+expr({tuple,Line,Es0},St) ->
+ Es1 = expr_list(Es0,St),
+ {tuple,Line,Es1};
+expr({block,Line,Es0},St) ->
+ Es1 = exprs(Es0,St),
+ {block,Line,Es1};
+expr({'if',Line,Cs0},St) ->
+ Cs1 = icr_clauses(Cs0,St),
+ {'if',Line,Cs1};
+expr({'case',Line,E0,Cs0},St) ->
+ E1 = expr(E0,St),
+ Cs1 = icr_clauses(Cs0,St),
+ {'case',Line,E1,Cs1};
+expr({'receive',Line,Cs0},St) ->
+ Cs1 = icr_clauses(Cs0,St),
+ {'receive',Line,Cs1};
+expr({'receive',Line,Cs0,To0,ToEs0},St) ->
+ To1 = expr(To0,St),
+ ToEs1 = exprs(ToEs0,St),
+ Cs1 = icr_clauses(Cs0,St),
+ {'receive',Line,Cs1,To1,ToEs1};
+expr({'try',Line,Es0,Scs0,Ccs0,As0},St) ->
+ Es1 = exprs(Es0,St),
+ Scs1 = icr_clauses(Scs0,St),
+ Ccs1 = icr_clauses(Ccs0,St),
+ As1 = exprs(As0,St),
+ {'try',Line,Es1,Scs1,Ccs1,As1};
+expr({'fun',Line,Body,Info},St) ->
+ case Body of
+ {clauses,Cs0} ->
+ Cs1 = fun_clauses(Cs0,St),
+ {'fun',Line,{clauses,Cs1},Info};
+ {function,F,A} ->
+ {F1,A1} = update_function_name({F,A},St),
+ if A1 == A ->
+ {'fun',Line,{function,F,A},Info};
+ true ->
+ %% Must rewrite local fun-name to a fun that does a
+ %% call with the extra THIS parameter.
+ As = make_vars(A, Line),
+ As1 = As ++ [{var,Line,'THIS'}],
+ Call = {call,Line,{atom,Line,F1},As1},
+ Cs = [{clause,Line,As,[],[Call]}],
+ {'fun',Line,{clauses,Cs},Info}
+ end;
+ {function,M,F,A} -> %This is an error in lint!
+ {'fun',Line,{function,M,F,A},Info}
+ end;
+expr({call,Lc,{atom,_,new}=Name,As0},#pmod{parameters=Ps}=St)
+ when length(As0) =:= length(Ps) ->
+ %% The new() function does not take a 'THIS' argument (it's static).
+ As1 = expr_list(As0,St),
+ {call,Lc,Name,As1};
+expr({call,Lc,{atom,_,module_info}=Name,As0},St)
+ when length(As0) == 0; length(As0) == 1 ->
+ %% The module_info/0 and module_info/1 functions are also static.
+ As1 = expr_list(As0,St),
+ {call,Lc,Name,As1};
+expr({call,Lc,{atom,Lf,F},As0},St) ->
+ %% Local function call - needs THIS parameter.
+ As1 = expr_list(As0,St),
+ {call,Lc,{atom,Lf,F},As1 ++ [{var,0,'THIS'}]};
+expr({call,Line,F0,As0},St) ->
+ %% Other function call
+ F1 = expr(F0,St),
+ As1 = expr_list(As0,St),
+ {call,Line,F1,As1};
+expr({'catch',Line,E0},St) ->
+ E1 = expr(E0,St),
+ {'catch',Line,E1};
+expr({match,Line,P0,E0},St) ->
+ E1 = expr(E0,St),
+ P1 = pattern(P0,St),
+ {match,Line,P1,E1};
+expr({bin,Line,Fs},St) ->
+ Fs2 = pattern_grp(Fs,St),
+ {bin,Line,Fs2};
+expr({op,Line,Op,A0},St) ->
+ A1 = expr(A0,St),
+ {op,Line,Op,A1};
+expr({op,Line,Op,L0,R0},St) ->
+ L1 = expr(L0,St),
+ R1 = expr(R0,St),
+ {op,Line,Op,L1,R1};
+%% The following are not allowed to occur anywhere!
+expr({remote,Line,M0,F0},St) ->
+ M1 = expr(M0,St),
+ F1 = expr(F0,St),
+ {remote,Line,M1,F1}.
+
+expr_list([E0|Es],St) ->
+ E1 = expr(E0,St),
+ [E1|expr_list(Es,St)];
+expr_list([],_St) -> [].
+
+icr_clauses([C0|Cs],St) ->
+ C1 = clause(C0,St),
+ [C1|icr_clauses(Cs,St)];
+icr_clauses([],_St) -> [].
+
+lc_quals([{generate,Line,P0,E0}|Qs],St) ->
+ E1 = expr(E0,St),
+ P1 = pattern(P0,St),
+ [{generate,Line,P1,E1}|lc_quals(Qs,St)];
+lc_quals([E0|Qs],St) ->
+ E1 = expr(E0,St),
+ [E1|lc_quals(Qs,St)];
+lc_quals([],_St) -> [].
+
+fun_clauses([C0|Cs],St) ->
+ C1 = clause(C0,St),
+ [C1|fun_clauses(Cs,St)];
+fun_clauses([],_St) -> [].
+
+% %% Return index from 1 upwards, or 0 if not in the list.
+%
+% index(X,Ys) -> index(X,Ys,1).
+%
+% index(X,[X|Ys],A) -> A;
+% index(X,[Y|Ys],A) -> index(X,Ys,A+1);
+% index(X,[],A) -> 0.
+
+make_vars(N, L) ->
+ make_vars(1, N, L).
+
+make_vars(N, M, L) when N =< M ->
+ V = list_to_atom("X"++integer_to_list(N)),
+ [{var,L,V} | make_vars(N + 1, M, L)];
+make_vars(_, _, _) ->
+ [].
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/sys_pre_attributes.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/sys_pre_attributes.erl
new file mode 100644
index 0000000000..21d28868f0
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/sys_pre_attributes.erl
@@ -0,0 +1,212 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: sys_pre_attributes.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose : Transform Erlang compiler attributes
+
+-module(sys_pre_attributes).
+
+-export([parse_transform/2]).
+
+-define(OPTION_TAG, attributes).
+
+-record(state, {forms,
+ pre_ops = [],
+ post_ops = [],
+ options}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Inserts, deletes and replaces Erlang compiler attributes.
+%%
+%% Valid options are:
+%%
+%% {attribute, insert, AttrName, NewAttrVal}
+%% {attribute, replace, AttrName, NewAttrVal} % replace first occurrence
+%% {attribute, delete, AttrName}
+%%
+%% The transformation is performed in two passes:
+%%
+%% pre_transform
+%% -------------
+%% Searches for attributes in the list of Forms in order to
+%% delete or replace them. 'delete' will delete all occurrences
+%% of attributes with the given name. 'replace' will replace the
+%% first occurrence of the attribute. This pass is will only be
+%% performed if there are replace or delete operations stated
+%% as options.
+%%
+%% post_transform
+%% -------------
+%% Looks up the module attribute and inserts the new attributes
+%% directly after. This pass will only be performed if there are
+%% any attributes left to be inserted after pre_transform. The left
+%% overs will be those replace operations that not has been performed
+%% due to that the pre_transform pass did not find the attribute plus
+%% all insert operations.
+
+parse_transform(Forms, Options) ->
+ S = #state{forms = Forms, options = Options},
+ S2 = init_transform(S),
+ report_verbose("Pre options: ~p~n", [S2#state.pre_ops], S2),
+ report_verbose("Post options: ~p~n", [S2#state.post_ops], S2),
+ S3 = pre_transform(S2),
+ S4 = post_transform(S3),
+ S4#state.forms.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Computes the lists of pre_ops and post_ops that are
+%% used in the real transformation.
+init_transform(S) ->
+ case S#state.options of
+ Options when list(Options) ->
+ init_transform(Options, S);
+ Option ->
+ init_transform([Option], S)
+ end.
+
+init_transform([{attribute, insert, Name, Val} | Tail], S) ->
+ Op = {insert, Name, Val},
+ PostOps = [Op | S#state.post_ops],
+ init_transform(Tail, S#state{post_ops = PostOps});
+init_transform([{attribute, replace, Name, Val} | Tail], S) ->
+ Op = {replace, Name, Val},
+ PreOps = [Op | S#state.pre_ops],
+ PostOps = [Op | S#state.post_ops],
+ init_transform(Tail, S#state{pre_ops = PreOps, post_ops = PostOps});
+init_transform([{attribute, delete, Name} | Tail], S) ->
+ Op = {delete, Name},
+ PreOps = [Op | S#state.pre_ops],
+ init_transform(Tail, S#state{pre_ops = PreOps});
+init_transform([], S) ->
+ S;
+init_transform([_ | T], S) ->
+ init_transform(T, S);
+init_transform(BadOpt, S) ->
+ report_error("Illegal option (ignored): ~p~n", [BadOpt], S),
+ S.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Handle delete and perhaps replace
+
+pre_transform(S) when S#state.pre_ops == [] ->
+ S;
+pre_transform(S) ->
+ pre_transform(S#state.forms, [], S).
+
+pre_transform([H | T], Acc, S) ->
+ case H of
+ {attribute, Line, Name, Val} ->
+ case lists:keysearch(Name, 2, S#state.pre_ops) of
+ false ->
+ pre_transform(T, [H | Acc], S);
+
+ {value, {replace, Name, NewVal}} ->
+ report_warning("Replace attribute ~p: ~p -> ~p~n",
+ [Name, Val, NewVal],
+ S),
+ New = {attribute, Line, Name, NewVal},
+ Pre = lists:keydelete(Name, 2, S#state.pre_ops),
+ Post = lists:keydelete(Name, 2, S#state.post_ops),
+ S2 = S#state{pre_ops = Pre, post_ops = Post},
+ if
+ Pre == [] ->
+ %% No need to search the rest of the Forms
+ Forms = lists:reverse(Acc, [New | T]),
+ S2#state{forms = Forms};
+ true ->
+ pre_transform(T, [New | Acc], S2)
+ end;
+
+ {value, {delete, Name}} ->
+ report_warning("Delete attribute ~p: ~p~n",
+ [Name, Val],
+ S),
+ pre_transform(T, Acc, S)
+ end;
+ _Any ->
+ pre_transform(T, [H | Acc], S)
+ end;
+pre_transform([], Acc, S) ->
+ S#state{forms = lists:reverse(Acc)}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Handle insert and perhaps replace
+
+post_transform(S) when S#state.post_ops == [] ->
+ S;
+post_transform(S) ->
+ post_transform(S#state.forms, [], S).
+
+post_transform([H | T], Acc, S) ->
+ case H of
+ {attribute, Line, module, Val} ->
+ Acc2 = lists:reverse([{attribute, Line, module, Val} | Acc]),
+ Forms = Acc2 ++ attrs(S#state.post_ops, Line, S) ++ T,
+ S#state{forms = Forms, post_ops = []};
+ _Any ->
+ post_transform(T, [H | Acc], S)
+ end;
+post_transform([], Acc, S) ->
+ S#state{forms = lists:reverse(Acc)}.
+
+attrs([{replace, Name, NewVal} | T], Line, S) ->
+ report_verbose("Insert attribute ~p: ~p~n", [Name, NewVal], S),
+ [{attribute, Line, Name, NewVal} | attrs(T, Line, S)];
+attrs([{insert, Name, NewVal} | T], Line, S) ->
+ report_verbose("Insert attribute ~p: ~p~n", [Name, NewVal], S),
+ [{attribute, Line, Name, NewVal} | attrs(T, Line, S)];
+attrs([], _, _) ->
+ [].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Report functions.
+%%
+%% Errors messages are controlled with the 'report_errors' compiler option
+%% Warning messages are controlled with the 'report_warnings' compiler option
+%% Verbose messages are controlled with the 'verbose' compiler option
+
+report_error(Format, Args, S) ->
+ case is_error(S) of
+ true ->
+ io:format("~p: * ERROR * " ++ Format, [?MODULE | Args]);
+ false ->
+ ok
+ end.
+
+report_warning(Format, Args, S) ->
+ case is_warning(S) of
+ true ->
+ io:format("~p: * WARNING * " ++ Format, [?MODULE | Args]);
+ false ->
+ ok
+ end.
+
+report_verbose(Format, Args, S) ->
+ case is_verbose(S) of
+ true ->
+ io:format("~p: " ++ Format, [?MODULE | Args]);
+ false ->
+ ok
+ end.
+
+is_error(S) ->
+ lists:member(report_errors, S#state.options) or is_verbose(S).
+
+is_warning(S) ->
+ lists:member(report_warnings, S#state.options) or is_verbose(S).
+
+is_verbose(S) ->
+ lists:member(verbose, S#state.options).
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/sys_pre_expand.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/sys_pre_expand.erl
new file mode 100644
index 0000000000..08bc6cb147
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/sys_pre_expand.erl
@@ -0,0 +1,1026 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: sys_pre_expand.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose : Expand some source Erlang constructions. This is part of the
+%% pre-processing phase.
+
+%% N.B. Although structs (tagged tuples) are not yet allowed in the
+%% language there is code included in pattern/2 and expr/3 (commented out)
+%% that handles them by transforming them to tuples.
+
+-module(sys_pre_expand).
+
+%% Main entry point.
+-export([module/2]).
+
+-import(ordsets, [from_list/1,add_element/2,
+ union/1,union/2,intersection/1,intersection/2,subtract/2]).
+-import(lists, [member/2,map/2,foldl/3,foldr/3,sort/1,reverse/1,duplicate/2]).
+
+-include("../my_include/erl_bits.hrl").
+
+-record(expand, {module=[], %Module name
+ parameters=undefined, %Module parameters
+ package="", %Module package
+ exports=[], %Exports
+ imports=[], %Imports
+ mod_imports, %Module Imports
+ compile=[], %Compile flags
+ records=dict:new(), %Record definitions
+ attributes=[], %Attributes
+ defined=[], %Defined functions
+ vcount=0, %Variable counter
+ func=[], %Current function
+ arity=[], %Arity for current function
+ fcount=0, %Local fun count
+ fun_index=0, %Global index for funs
+ bitdefault,
+ bittypes
+ }).
+
+%% module(Forms, CompileOptions)
+%% {ModuleName,Exports,TransformedForms}
+%% Expand the forms in one module. N.B.: the lists of predefined
+%% exports and imports are really ordsets!
+
+module(Fs, Opts) ->
+ %% Set pre-defined exported functions.
+ PreExp = [{module_info,0},{module_info,1}],
+
+ %% Set pre-defined module imports.
+ PreModImp = [{erlang,erlang},{packages,packages}],
+
+ %% Build initial expand record.
+ St0 = #expand{exports=PreExp,
+ mod_imports=dict:from_list(PreModImp),
+ compile=Opts,
+ defined=PreExp,
+ bitdefault = erl_bits:system_bitdefault(),
+ bittypes = erl_bits:system_bittypes()
+ },
+ %% Expand the functions.
+ {Tfs,St1} = forms(Fs, foldl(fun define_function/2, St0, Fs)),
+ {Efs,St2} = expand_pmod(Tfs, St1),
+ %% Get the correct list of exported functions.
+ Exports = case member(export_all, St2#expand.compile) of
+ true -> St2#expand.defined;
+ false -> St2#expand.exports
+ end,
+ %% Generate all functions from stored info.
+ {Ats,St3} = module_attrs(St2#expand{exports = Exports}),
+ {Mfs,St4} = module_predef_funcs(St3),
+ {St4#expand.module, St4#expand.exports, Ats ++ Efs ++ Mfs,
+ St4#expand.compile}.
+
+expand_pmod(Fs0, St) ->
+ case St#expand.parameters of
+ undefined ->
+ {Fs0,St};
+ Ps ->
+ {Fs1,Xs,Ds} = sys_expand_pmod:forms(Fs0, Ps,
+ St#expand.exports,
+ St#expand.defined),
+ A = length(Ps),
+ Vs = [{var,0,V} || V <- Ps],
+ N = {atom,0,St#expand.module},
+ B = [{tuple,0,[N|Vs]}],
+ F = {function,0,new,A,[{clause,0,Vs,[],B}]},
+ As = St#expand.attributes,
+ {[F|Fs1],St#expand{exports=add_element({new,A}, Xs),
+ defined=add_element({new,A}, Ds),
+ attributes = [{abstract, true} | As]}}
+ end.
+
+%% -type define_function(Form, State) -> State.
+%% Add function to defined if form a function.
+
+define_function({function,_,N,A,_Cs}, St) ->
+ St#expand{defined=add_element({N,A}, St#expand.defined)};
+define_function(_, St) -> St.
+
+module_attrs(St) ->
+ {[{attribute,0,Name,Val} || {Name,Val} <- St#expand.attributes],St}.
+
+module_predef_funcs(St) ->
+ PreDef = [{module_info,0},{module_info,1}],
+ PreExp = PreDef,
+ {[{function,0,module_info,0,
+ [{clause,0,[],[],
+ [{call,0,{remote,0,{atom,0,erlang},{atom,0,get_module_info}},
+ [{atom,0,St#expand.module}]}]}]},
+ {function,0,module_info,1,
+ [{clause,0,[{var,0,'X'}],[],
+ [{call,0,{remote,0,{atom,0,erlang},{atom,0,get_module_info}},
+ [{atom,0,St#expand.module},{var,0,'X'}]}]}]}],
+ St#expand{defined=union(from_list(PreDef), St#expand.defined),
+ exports=union(from_list(PreExp), St#expand.exports)}}.
+
+%% forms(Forms, State) ->
+%% {TransformedForms,State'}
+%% Process the forms. Attributes are lost and just affect the state.
+%% Ignore uninteresting forms like eof and type.
+
+forms([{attribute,_,Name,Val}|Fs0], St0) ->
+ St1 = attribute(Name, Val, St0),
+ forms(Fs0, St1);
+forms([{function,L,N,A,Cs}|Fs0], St0) ->
+ {Ff,St1} = function(L, N, A, Cs, St0),
+ {Fs,St2} = forms(Fs0, St1),
+ {[Ff|Fs],St2};
+forms([_|Fs], St) -> forms(Fs, St);
+forms([], St) -> {[],St}.
+
+%% -type attribute(Attribute, Value, State) ->
+%% State.
+%% Process an attribute, this just affects the state.
+
+attribute(module, {Module, As}, St) ->
+ M = package_to_string(Module),
+ St#expand{module=list_to_atom(M),
+ package = packages:strip_last(M),
+ parameters=As};
+attribute(module, Module, St) ->
+ M = package_to_string(Module),
+ St#expand{module=list_to_atom(M),
+ package = packages:strip_last(M)};
+attribute(export, Es, St) ->
+ St#expand{exports=union(from_list(Es), St#expand.exports)};
+attribute(import, Is, St) ->
+ import(Is, St);
+attribute(compile, C, St) when list(C) ->
+ St#expand{compile=St#expand.compile ++ C};
+attribute(compile, C, St) ->
+ St#expand{compile=St#expand.compile ++ [C]};
+attribute(record, {Name,Defs}, St) ->
+ St#expand{records=dict:store(Name, normalise_fields(Defs),
+ St#expand.records)};
+attribute(file, _File, St) -> St; %This is ignored
+attribute(Name, Val, St) when list(Val) ->
+ St#expand{attributes=St#expand.attributes ++ [{Name,Val}]};
+attribute(Name, Val, St) ->
+ St#expand{attributes=St#expand.attributes ++ [{Name,[Val]}]}.
+
+function(L, N, A, Cs0, St0) ->
+ {Cs,St} = clauses(Cs0, St0#expand{func=N,arity=A,fcount=0}),
+ {{function,L,N,A,Cs},St}.
+
+%% -type clauses([Clause], State) ->
+%% {[TransformedClause],State}.
+%% Expand function clauses.
+
+clauses([{clause,Line,H0,G0,B0}|Cs0], St0) ->
+ {H,Hvs,_Hus,St1} = head(H0, St0),
+ {G,Gvs,_Gus,St2} = guard(G0, Hvs, St1),
+ {B,_Bvs,_Bus,St3} = exprs(B0, union(Hvs, Gvs), St2),
+ {Cs,St4} = clauses(Cs0, St3),
+ {[{clause,Line,H,G,B}|Cs],St4};
+clauses([], St) -> {[],St}.
+
+%% head(HeadPatterns, State) ->
+%% {TransformedPatterns,Variables,UsedVariables,State'}
+
+head(As, St) -> pattern_list(As, St).
+
+%% pattern(Pattern, State) ->
+%% {TransformedPattern,Variables,UsedVariables,State'}
+%% BITS: added used variables for bit patterns with varaible length
+%%
+
+pattern({var,_,'_'}=Var, St) -> %Ignore anonymous variable.
+ {Var,[],[],St};
+pattern({var,_,V}=Var, St) ->
+ {Var,[V],[],St};
+pattern({char,_,_}=Char, St) ->
+ {Char,[],[],St};
+pattern({integer,_,_}=Int, St) ->
+ {Int,[],[],St};
+pattern({float,_,_}=Float, St) ->
+ {Float,[],[],St};
+pattern({atom,_,_}=Atom, St) ->
+ {Atom,[],[],St};
+pattern({string,_,_}=String, St) ->
+ {String,[],[],St};
+pattern({nil,_}=Nil, St) ->
+ {Nil,[],[],St};
+pattern({cons,Line,H,T}, St0) ->
+ {TH,THvs,Hus,St1} = pattern(H, St0),
+ {TT,TTvs,Tus,St2} = pattern(T, St1),
+ {{cons,Line,TH,TT},union(THvs, TTvs),union(Hus,Tus),St2};
+pattern({tuple,Line,Ps}, St0) ->
+ {TPs,TPsvs,Tus,St1} = pattern_list(Ps, St0),
+ {{tuple,Line,TPs},TPsvs,Tus,St1};
+%%pattern({struct,Line,Tag,Ps}, St0) ->
+%% {TPs,TPsvs,St1} = pattern_list(Ps, St0),
+%% {{tuple,Line,[{atom,Line,Tag}|TPs]},TPsvs,St1};
+pattern({record_field,_,_,_}=M, St) ->
+ {expand_package(M, St), [], [], St}; % must be a package name
+pattern({record_index,Line,Name,Field}, St) ->
+ {index_expr(Line, Field, Name, record_fields(Name, St)),[],[],St};
+pattern({record,Line,Name,Pfs}, St0) ->
+ Fs = record_fields(Name, St0),
+ {TMs,TMsvs,Us,St1} = pattern_list(pattern_fields(Fs, Pfs), St0),
+ {{tuple,Line,[{atom,Line,Name}|TMs]},TMsvs,Us,St1};
+pattern({bin,Line,Es0}, St0) ->
+ {Es1,Esvs,Esus,St1} = pattern_bin(Es0, St0),
+ {{bin,Line,Es1},Esvs,Esus,St1};
+pattern({op,_,'++',{nil,_},R}, St) ->
+ pattern(R, St);
+pattern({op,_,'++',{cons,Li,H,T},R}, St) ->
+ pattern({cons,Li,H,{op,Li,'++',T,R}}, St);
+pattern({op,_,'++',{string,Li,L},R}, St) ->
+ pattern(string_to_conses(Li, L, R), St);
+pattern({match,Line,Pat1, Pat2}, St0) ->
+ {TH,Hvt,Hus,St1} = pattern(Pat2, St0),
+ {TT,Tvt,Tus,St2} = pattern(Pat1, St1),
+ {{match,Line,TT,TH}, union(Hvt,Tvt), union(Hus,Tus), St2};
+%% Compile-time pattern expressions, including unary operators.
+pattern({op,Line,Op,A}, St) ->
+ { erl_eval:partial_eval({op,Line,Op,A}), [], [], St};
+pattern({op,Line,Op,L,R}, St) ->
+ { erl_eval:partial_eval({op,Line,Op,L,R}), [], [], St}.
+
+pattern_list([P0|Ps0], St0) ->
+ {P,Pvs,Pus,St1} = pattern(P0, St0),
+ {Ps,Psvs,Psus,St2} = pattern_list(Ps0, St1),
+ {[P|Ps],union(Pvs, Psvs),union(Pus, Psus),St2};
+pattern_list([], St) -> {[],[],[],St}.
+
+%% guard(Guard, VisibleVariables, State) ->
+%% {TransformedGuard,NewVariables,UsedVariables,State'}
+%% Transform a list of guard tests. We KNOW that this has been checked
+%% and what the guards test are. Use expr for transforming the guard
+%% expressions.
+
+guard([G0|Gs0], Vs, St0) ->
+ {G,Hvs,Hus,St1} = guard_tests(G0, Vs, St0),
+ {Gs,Tvs,Tus,St2} = guard(Gs0, Vs, St1),
+ {[G|Gs],union(Hvs, Tvs),union(Hus, Tus),St2};
+guard([], _, St) -> {[],[],[],St}.
+
+guard_tests([Gt0|Gts0], Vs, St0) ->
+ {Gt1,Gvs,Gus,St1} = guard_test(Gt0, Vs, St0),
+ {Gts1,Gsvs,Gsus,St2} = guard_tests(Gts0, union(Gvs, Vs), St1),
+ {[Gt1|Gts1],union(Gvs, Gsvs),union(Gus, Gsus),St2};
+guard_tests([], _, St) -> {[],[],[],St}.
+
+guard_test({call,Line,{atom,_,record},[A,{atom,_,Name}]}, Vs, St) ->
+ record_test_in_guard(Line, A, Name, Vs, St);
+guard_test({call,Line,{atom,Lt,Tname},As}, Vs, St) ->
+ %% XXX This is ugly. We can remove this workaround if/when
+ %% we'll allow 'andalso' in guards. For now, we must have
+ %% different code in guards and in bodies.
+ Test = {remote,Lt,
+ {atom,Lt,erlang},
+ {atom,Lt,normalise_test(Tname, length(As))}},
+ put(sys_pre_expand_in_guard, yes),
+ R = expr({call,Line,Test,As}, Vs, St),
+ erase(sys_pre_expand_in_guard),
+ R;
+guard_test(Test, Vs, St) ->
+ %% XXX See the previous clause.
+ put(sys_pre_expand_in_guard, yes),
+ R = expr(Test, Vs, St),
+ erase(sys_pre_expand_in_guard),
+ R.
+
+%% record_test(Line, Term, Name, Vs, St) -> TransformedExpr
+%% Generate code for is_record/1.
+
+record_test(Line, Term, Name, Vs, St) ->
+ case get(sys_pre_expand_in_guard) of
+ undefined ->
+ record_test_in_body(Line, Term, Name, Vs, St);
+ yes ->
+ record_test_in_guard(Line, Term, Name, Vs, St)
+ end.
+
+record_test_in_guard(Line, Term, Name, Vs, St) ->
+ %% Notes: (1) To keep is_record/3 properly atomic (e.g. when inverted
+ %% using 'not'), we cannot convert it to an instruction
+ %% sequence here. It must remain a single call.
+ %% (2) Later passes assume that the last argument (the size)
+ %% is a literal.
+ %% (3) We don't want calls to erlang:is_record/3 (in the source code)
+ %% confused we the internal instruction. (Reason: (2) above +
+ %% code bloat.)
+ %% (4) Xref may be run on the abstract code, so the name in the
+ %% abstract code must be erlang:is_record/3.
+ %% (5) To achive both (3) and (4) at the same time, set the name
+ %% here to erlang:is_record/3, but mark it as compiler-generated.
+ %% The v3_core pass will change the name to erlang:internal_is_record/3.
+ Fs = record_fields(Name, St),
+ expr({call,-Line,{remote,-Line,{atom,-Line,erlang},{atom,-Line,is_record}},
+ [Term,{atom,Line,Name},{integer,Line,length(Fs)+1}]},
+ Vs, St).
+
+record_test_in_body(Line, Expr, Name, Vs, St0) ->
+ %% As Expr may have side effects, we must evaluate it
+ %% first and bind the value to a new variable.
+ %% We must use also handle the case that Expr does not
+ %% evaluate to a tuple properly.
+ Fs = record_fields(Name, St0),
+ {Var,St} = new_var(Line, St0),
+
+ expr({block,Line,
+ [{match,Line,Var,Expr},
+ {op,Line,
+ 'andalso',
+ {call,Line,{atom,Line,is_tuple},[Var]},
+ {op,Line,'andalso',
+ {op,Line,'=:=',
+ {call,Line,{atom,Line,size},[Var]},
+ {integer,Line,length(Fs)+1}},
+ {op,Line,'=:=',
+ {call,Line,{atom,Line,element},[{integer,Line,1},Var]},
+ {atom,Line,Name}}}}]}, Vs, St).
+
+normalise_test(atom, 1) -> is_atom;
+normalise_test(binary, 1) -> is_binary;
+normalise_test(constant, 1) -> is_constant;
+normalise_test(float, 1) -> is_float;
+normalise_test(function, 1) -> is_function;
+normalise_test(integer, 1) -> is_integer;
+normalise_test(list, 1) -> is_list;
+normalise_test(number, 1) -> is_number;
+normalise_test(pid, 1) -> is_pid;
+normalise_test(port, 1) -> is_port;
+normalise_test(reference, 1) -> is_reference;
+normalise_test(tuple, 1) -> is_tuple;
+normalise_test(Name, _) -> Name.
+
+%% exprs(Expressions, VisibleVariables, State) ->
+%% {TransformedExprs,NewVariables,UsedVariables,State'}
+
+exprs([E0|Es0], Vs, St0) ->
+ {E,Evs,Eus,St1} = expr(E0, Vs, St0),
+ {Es,Esvs,Esus,St2} = exprs(Es0, union(Evs, Vs), St1),
+ {[E|Es],union(Evs, Esvs),union(Eus, Esus),St2};
+exprs([], _, St) -> {[],[],[],St}.
+
+%% expr(Expression, VisibleVariables, State) ->
+%% {TransformedExpression,NewVariables,UsedVariables,State'}
+
+expr({var,_,V}=Var, _Vs, St) ->
+ {Var,[],[V],St};
+expr({char,_,_}=Char, _Vs, St) ->
+ {Char,[],[],St};
+expr({integer,_,_}=Int, _Vs, St) ->
+ {Int,[],[],St};
+expr({float,_,_}=Float, _Vs, St) ->
+ {Float,[],[],St};
+expr({atom,_,_}=Atom, _Vs, St) ->
+ {Atom,[],[],St};
+expr({string,_,_}=String, _Vs, St) ->
+ {String,[],[],St};
+expr({nil,_}=Nil, _Vs, St) ->
+ {Nil,[],[],St};
+expr({cons,Line,H0,T0}, Vs, St0) ->
+ {H,Hvs,Hus,St1} = expr(H0, Vs, St0),
+ {T,Tvs,Tus,St2} = expr(T0, Vs, St1),
+ {{cons,Line,H,T},union(Hvs, Tvs),union(Hus, Tus),St2};
+expr({lc,Line,E0,Qs0}, Vs, St0) ->
+ {E1,Qs1,_,Lvs,Lus,St1} = lc_tq(Line, E0, Qs0, {nil,Line}, Vs, St0),
+ {{lc,Line,E1,Qs1},Lvs,Lus,St1};
+expr({tuple,Line,Es0}, Vs, St0) ->
+ {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0),
+ {{tuple,Line,Es1},Esvs,Esus,St1};
+%%expr({struct,Line,Tag,Es0}, Vs, St0) ->
+%% {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0),
+%% {{tuple,Line,[{atom,Line,Tag}|Es1]},Esvs,Esus,St1};
+expr({record_field,_,_,_}=M, _Vs, St) ->
+ {expand_package(M, St), [], [], St}; % must be a package name
+expr({record_index,Line,Name,F}, Vs, St) ->
+ I = index_expr(Line, F, Name, record_fields(Name, St)),
+ expr(I, Vs, St);
+expr({record,Line,Name,Is}, Vs, St) ->
+ expr({tuple,Line,[{atom,Line,Name}|
+ record_inits(record_fields(Name, St), Is)]},
+ Vs, St);
+expr({record_field,Line,R,Name,F}, Vs, St) ->
+ I = index_expr(Line, F, Name, record_fields(Name, St)),
+ expr({call,Line,{atom,Line,element},[I,R]}, Vs, St);
+expr({record,_,R,Name,Us}, Vs, St0) ->
+ {Ue,St1} = record_update(R, Name, record_fields(Name, St0), Us, St0),
+ expr(Ue, Vs, St1);
+expr({bin,Line,Es0}, Vs, St0) ->
+ {Es1,Esvs,Esus,St1} = expr_bin(Es0, Vs, St0),
+ {{bin,Line,Es1},Esvs,Esus,St1};
+expr({block,Line,Es0}, Vs, St0) ->
+ {Es,Esvs,Esus,St1} = exprs(Es0, Vs, St0),
+ {{block,Line,Es},Esvs,Esus,St1};
+expr({'if',Line,Cs0}, Vs, St0) ->
+ {Cs,Csvss,Csuss,St1} = icr_clauses(Cs0, Vs, St0),
+ All = new_in_all(Vs, Csvss),
+ {{'if',Line,Cs},All,union(Csuss),St1};
+expr({'case',Line,E0,Cs0}, Vs, St0) ->
+ {E,Evs,Eus,St1} = expr(E0, Vs, St0),
+ {Cs,Csvss,Csuss,St2} = icr_clauses(Cs0, union(Evs, Vs), St1),
+ All = new_in_all(Vs, Csvss),
+ {{'case',Line,E,Cs},union(Evs, All),union([Eus|Csuss]),St2};
+expr({'cond',Line,Cs}, Vs, St0) ->
+ {V,St1} = new_var(Line,St0),
+ expr(cond_clauses(Cs,V), Vs, St1);
+expr({'receive',Line,Cs0}, Vs, St0) ->
+ {Cs,Csvss,Csuss,St1} = icr_clauses(Cs0, Vs, St0),
+ All = new_in_all(Vs, Csvss),
+ {{'receive',Line,Cs},All,union(Csuss),St1};
+expr({'receive',Line,Cs0,To0,ToEs0}, Vs, St0) ->
+ {To,Tovs,Tous,St1} = expr(To0, Vs, St0),
+ {ToEs,ToEsvs,_ToEsus,St2} = exprs(ToEs0, Vs, St1),
+ {Cs,Csvss,Csuss,St3} = icr_clauses(Cs0, Vs, St2),
+ All = new_in_all(Vs, [ToEsvs|Csvss]),
+ {{'receive',Line,Cs,To,ToEs},union(Tovs, All),union([Tous|Csuss]),St3};
+expr({'fun',Line,Body}, Vs, St) ->
+ fun_tq(Line, Body, Vs, St);
+%%% expr({call,_,{atom,La,this_module},[]}, _Vs, St) ->
+%%% {{atom,La,St#expand.module}, [], [], St};
+%%% expr({call,_,{atom,La,this_package},[]}, _Vs, St) ->
+%%% {{atom,La,list_to_atom(St#expand.package)}, [], [], St};
+%%% expr({call,_,{atom,La,this_package},[{atom,_,Name}]}, _Vs, St) ->
+%%% M = packages:concat(St#expand.package,Name),
+%%% {{atom,La,list_to_atom(M)}, [], [], St};
+%%% expr({call,Line,{atom,La,this_package},[A]}, Vs, St) ->
+%%% M = {call,Line,{remote,La,{atom,La,packages},{atom,La,concat}},
+%%% [{string,La,St#expand.package}, A]},
+%%% expr({call,Line,{atom,Line,list_to_atom},[M]}, Vs, St);
+expr({call,Line,{atom,_,is_record},[A,{atom,_,Name}]}, Vs, St) ->
+ record_test(Line, A, Name, Vs, St);
+expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}},
+ [A,{atom,_,Name}]}, Vs, St) ->
+ record_test(Line, A, Name, Vs, St);
+expr({call,Line,{atom,La,N},As0}, Vs, St0) ->
+ {As,Asvs,Asus,St1} = expr_list(As0, Vs, St0),
+ Ar = length(As),
+ case erl_internal:bif(N, Ar) of
+ true ->
+ {{call,Line,{remote,La,{atom,La,erlang},{atom,La,N}},As},
+ Asvs,Asus,St1};
+ false ->
+ case imported(N, Ar, St1) of
+ {yes,Mod} ->
+ {{call,Line,{remote,La,{atom,La,Mod},{atom,La,N}},As},
+ Asvs,Asus,St1};
+ no ->
+ case {N,Ar} of
+ {record_info,2} ->
+ record_info_call(Line, As, St1);
+ _ ->
+ {{call,Line,{atom,La,N},As},Asvs,Asus,St1}
+ end
+ end
+ end;
+expr({call,Line,{record_field,_,_,_}=M,As0}, Vs, St0) ->
+ expr({call,Line,expand_package(M, St0),As0}, Vs, St0);
+expr({call,Line,{remote,Lr,M,F},As0}, Vs, St0) ->
+ M1 = expand_package(M, St0),
+ {[M2,F1|As1],Asvs,Asus,St1} = expr_list([M1,F|As0], Vs, St0),
+ {{call,Line,{remote,Lr,M2,F1},As1},Asvs,Asus,St1};
+expr({call,Line,{tuple,_,[{atom,_,_}=M,{atom,_,_}=F]},As}, Vs, St) ->
+ %% Rewrite {Mod,Function}(Args...) to Mod:Function(Args...).
+ expr({call,Line,{remote,Line,M,F},As}, Vs, St);
+expr({call,Line,F,As0}, Vs, St0) ->
+ {[Fun1|As1],Asvs,Asus,St1} = expr_list([F|As0], Vs, St0),
+ {{call,Line,Fun1,As1},Asvs,Asus,St1};
+expr({'try',Line,Es0,Scs0,Ccs0,As0}, Vs, St0) ->
+ {Es1,Esvs,Esus,St1} = exprs(Es0, Vs, St0),
+ Cvs = union(Esvs, Vs),
+ {Scs1,Scsvss,Scsuss,St2} = icr_clauses(Scs0, Cvs, St1),
+ {Ccs1,Ccsvss,Ccsuss,St3} = icr_clauses(Ccs0, Cvs, St2),
+ Csvss = Scsvss ++ Ccsvss,
+ Csuss = Scsuss ++ Ccsuss,
+ All = new_in_all(Vs, Csvss),
+ {As1,Asvs,Asus,St4} = exprs(As0, Cvs, St3),
+ {{'try',Line,Es1,Scs1,Ccs1,As1}, union([Asvs,Esvs,All]),
+ union([Esus,Asus|Csuss]), St4};
+expr({'catch',Line,E0}, Vs, St0) ->
+ %% Catch exports no new variables.
+ {E,_Evs,Eus,St1} = expr(E0, Vs, St0),
+ {{'catch',Line,E},[],Eus,St1};
+expr({match,Line,P0,E0}, Vs, St0) ->
+ {E,Evs,Eus,St1} = expr(E0, Vs, St0),
+ {P,Pvs,Pus,St2} = pattern(P0, St1),
+ {{match,Line,P,E},
+ union(subtract(Pvs, Vs), Evs),
+ union(intersection(Pvs, Vs), union(Eus,Pus)),St2};
+expr({op,L,'andalso',E1,E2}, Vs, St0) ->
+ {V,St1} = new_var(L,St0),
+ E = make_bool_switch(L,E1,V,
+ make_bool_switch(L,E2,V,{atom,L,true},
+ {atom,L,false}),
+ {atom,L,false}),
+ expr(E, Vs, St1);
+expr({op,L,'orelse',E1,E2}, Vs, St0) ->
+ {V,St1} = new_var(L,St0),
+ E = make_bool_switch(L,E1,V,{atom,L,true},
+ make_bool_switch(L,E2,V,{atom,L,true},
+ {atom,L,false})),
+ expr(E, Vs, St1);
+expr({op,Line,'++',{lc,Ll,E0,Qs0},M0}, Vs, St0) ->
+ {E1,Qs1,M1,Lvs,Lus,St1} = lc_tq(Ll, E0, Qs0, M0, Vs, St0),
+ {{op,Line,'++',{lc,Ll,E1,Qs1},M1},Lvs,Lus,St1};
+expr({op,_,'++',{string,L1,S1},{string,_,S2}}, _Vs, St) ->
+ {{string,L1,S1 ++ S2},[],[],St};
+expr({op,Ll,'++',{string,L1,S1}=Str,R0}, Vs, St0) ->
+ {R1,Rvs,Rus,St1} = expr(R0, Vs, St0),
+ E = case R1 of
+ {string,_,S2} -> {string,L1,S1 ++ S2};
+ _Other when length(S1) < 8 -> string_to_conses(L1, S1, R1);
+ _Other -> {op,Ll,'++',Str,R1}
+ end,
+ {E,Rvs,Rus,St1};
+expr({op,Ll,'++',{cons,Lc,H,T},L2}, Vs, St) ->
+ expr({cons,Ll,H,{op,Lc,'++',T,L2}}, Vs, St);
+expr({op,_,'++',{nil,_},L2}, Vs, St) ->
+ expr(L2, Vs, St);
+expr({op,Line,Op,A0}, Vs, St0) ->
+ {A,Avs,Aus,St1} = expr(A0, Vs, St0),
+ {{op,Line,Op,A},Avs,Aus,St1};
+expr({op,Line,Op,L0,R0}, Vs, St0) ->
+ {L,Lvs,Lus,St1} = expr(L0, Vs, St0),
+ {R,Rvs,Rus,St2} = expr(R0, Vs, St1),
+ {{op,Line,Op,L,R},union(Lvs, Rvs),union(Lus, Rus),St2}.
+
+expr_list([E0|Es0], Vs, St0) ->
+ {E,Evs,Eus,St1} = expr(E0, Vs, St0),
+ {Es,Esvs,Esus,St2} = expr_list(Es0, Vs, St1),
+ {[E|Es],union(Evs, Esvs),union(Eus, Esus),St2};
+expr_list([], _, St) ->
+ {[],[],[],St}.
+
+%% icr_clauses([Clause], [VisibleVariable], State) ->
+%% {[TransformedClause],[[NewVariable]],[[UsedVariable]],State'}
+%% Be very careful here to return the variables that are really used
+%% and really new.
+
+icr_clauses([], _, St) ->
+ {[],[[]],[],St};
+icr_clauses(Clauses, Vs, St) ->
+ icr_clauses2(Clauses, Vs, St).
+
+icr_clauses2([{clause,Line,H0,G0,B0}|Cs0], Vs, St0) ->
+ {H,Hvs,Hus,St1} = head(H0, St0), %Hvs is really used!
+ {G,Gvs,Gus,St2} = guard(G0, union(Hvs, Vs), St1),
+ {B,Bvs,Bus,St3} = exprs(B0, union([Vs,Hvs,Gvs]), St2),
+ New = subtract(union([Hvs,Gvs,Bvs]), Vs), %Really new
+ Used = intersection(union([Hvs,Hus,Gus,Bus]), Vs), %Really used
+ {Cs,Csvs,Csus,St4} = icr_clauses2(Cs0, Vs, St3),
+ {[{clause,Line,H,G,B}|Cs],[New|Csvs],[Used|Csus],St4};
+icr_clauses2([], _, St) ->
+ {[],[],[],St}.
+
+%% lc_tq(Line, Expr, Qualifiers, More, [VisibleVar], State) ->
+%% {TransExpr,[TransQual],TransMore,[NewVar],[UsedVar],State'}
+
+lc_tq(Line, E0, [{generate,Lg,P0,G0}|Qs0], M0, Vs, St0) ->
+ {G1,Gvs,Gus,St1} = expr(G0, Vs, St0),
+ {P1,Pvs,Pus,St2} = pattern(P0, St1),
+ {E1,Qs1,M1,Lvs,Lus,St3} = lc_tq(Line, E0, Qs0, M0, union(Pvs, Vs), St2),
+ {E1,[{generate,Lg,P1,G1}|Qs1],M1,
+ union(Gvs, Lvs),union([Gus,Pus,Lus]),St3};
+lc_tq(Line, E0, [F0|Qs0], M0, Vs, St0) ->
+ %% Allow record/2 and expand out as guard test.
+ case erl_lint:is_guard_test(F0) of
+ true ->
+ {F1,Fvs,_Fus,St1} = guard_tests([F0], Vs, St0),
+ {E1,Qs1,M1,Lvs,Lus,St2} = lc_tq(Line, E0, Qs0, M0, union(Fvs, Vs), St1),
+ {E1,F1++Qs1,M1,Lvs,Lus,St2};
+ false ->
+ {F1,Fvs,_Fus,St1} = expr(F0, Vs, St0),
+ {E1,Qs1,M1,Lvs,Lus,St2} = lc_tq(Line, E0, Qs0, M0, union(Fvs, Vs), St1),
+ {E1,[F1|Qs1],M1,Lvs,Lus,St2}
+ end;
+lc_tq(_Line, E0, [], M0, Vs, St0) ->
+ {E1,Evs,Eus,St1} = expr(E0, Vs, St0),
+ {M1,Mvs,Mus,St2} = expr(M0, Vs, St1),
+ {E1,[],M1,union(Evs, Mvs),union(Eus, Mus),St2}.
+
+%% fun_tq(Line, Body, VisibleVariables, State) ->
+%% {Fun,NewVariables,UsedVariables,State'}
+%% Transform an "explicit" fun {'fun', Line, {clauses, Cs}} into an
+%% extended form {'fun', Line, {clauses, Cs}, Info}, unless it is the
+%% name of a BIF (erl_lint has checked that it is not an import).
+%% Process the body sequence directly to get the new and used variables.
+%% "Implicit" funs {'fun', Line, {function, F, A}} are not changed.
+
+fun_tq(Lf, {function,F,A}, Vs, St0) ->
+ {As,St1} = new_vars(A, Lf, St0),
+ Cs = [{clause,Lf,As,[],[{call,Lf,{atom,Lf,F},As}]}],
+ case erl_internal:bif(F, A) of
+ true ->
+ fun_tq(Lf, {clauses,Cs}, Vs, St1);
+ false ->
+ Index = St0#expand.fun_index,
+ Uniq = erlang:hash(Cs, (1 bsl 27)-1),
+ {Fname,St2} = new_fun_name(St1),
+ {{'fun',Lf,{function,F,A},{Index,Uniq,Fname}},[],[],
+ St2#expand{fun_index=Index+1}}
+ end;
+fun_tq(Lf, {clauses,Cs0}, Vs, St0) ->
+ Uniq = erlang:hash(Cs0, (1 bsl 27)-1),
+ {Cs1,_Hvss,Frees,St1} = fun_clauses(Cs0, Vs, St0),
+ Ufrees = union(Frees),
+ Index = St1#expand.fun_index,
+ {Fname,St2} = new_fun_name(St1),
+ {{'fun',Lf,{clauses,Cs1},{Index,Uniq,Fname}},[],Ufrees,
+ St2#expand{fun_index=Index+1}}.
+
+fun_clauses([{clause,L,H0,G0,B0}|Cs0], Vs, St0) ->
+ {H,Hvs,Hus,St1} = head(H0, St0),
+ {G,Gvs,Gus,St2} = guard(G0, union(Hvs, Vs), St1),
+ {B,Bvs,Bus,St3} = exprs(B0, union([Vs,Hvs,Gvs]), St2),
+ %% Free variables cannot be new anywhere in the clause.
+ Free = subtract(union([Gus,Hus,Bus]), union([Hvs,Gvs,Bvs])),
+ %%io:format(" Gus :~p~n Bvs :~p~n Bus :~p~n Free:~p~n" ,[Gus,Bvs,Bus,Free]),
+ {Cs,Hvss,Frees,St4} = fun_clauses(Cs0, Vs, St3),
+ {[{clause,L,H,G,B}|Cs],[Hvs|Hvss],[Free|Frees],St4};
+fun_clauses([], _, St) -> {[],[],[],St}.
+
+%% new_fun_name(State) -> {FunName,State}.
+
+new_fun_name(#expand{func=F,arity=A,fcount=I}=St) ->
+ Name = "-" ++ atom_to_list(F) ++ "/" ++ integer_to_list(A)
+ ++ "-fun-" ++ integer_to_list(I) ++ "-",
+ {list_to_atom(Name),St#expand{fcount=I+1}}.
+
+
+%% normalise_fields([RecDef]) -> [Field].
+%% Normalise the field definitions to always have a default value. If
+%% none has been given then use 'undefined'.
+
+normalise_fields(Fs) ->
+ map(fun ({record_field,Lf,Field}) ->
+ {record_field,Lf,Field,{atom,Lf,undefined}};
+ (F) -> F end, Fs).
+
+%% record_fields(RecordName, State)
+%% find_field(FieldName, Fields)
+
+record_fields(R, St) -> dict:fetch(R, St#expand.records).
+
+find_field(F, [{record_field,_,{atom,_,F},Val}|_]) -> {ok,Val};
+find_field(F, [_|Fs]) -> find_field(F, Fs);
+find_field(_, []) -> error.
+
+%% field_names(RecFields) -> [Name].
+%% Return a list of the field names structures.
+
+field_names(Fs) ->
+ map(fun ({record_field,_,Field,_Val}) -> Field end, Fs).
+
+%% index_expr(Line, FieldExpr, Name, Fields) -> IndexExpr.
+%% Return an expression which evaluates to the index of a
+%% field. Currently only handle the case where the field is an
+%% atom. This expansion must be passed through expr again.
+
+index_expr(Line, {atom,_,F}, _Name, Fs) ->
+ {integer,Line,index_expr(F, Fs, 2)}.
+
+index_expr(F, [{record_field,_,{atom,_,F},_}|_], I) -> I;
+index_expr(F, [_|Fs], I) ->
+ index_expr(F, Fs, I+1).
+
+%% pattern_fields([RecDefField], [Match]) -> [Pattern].
+%% Build a list of match patterns for the record tuple elements.
+%% This expansion must be passed through pattern again. N.B. We are
+%% scanning the record definition field list!
+
+pattern_fields(Fs, Ms) ->
+ Wildcard = record_wildcard_init(Ms),
+ map(fun ({record_field,L,{atom,_,F},_}) ->
+ case find_field(F, Ms) of
+ {ok,Match} -> Match;
+ error when Wildcard =:= none -> {var,L,'_'};
+ error -> Wildcard
+ end end,
+ Fs).
+
+%% record_inits([RecDefField], [Init]) -> [InitExpr].
+%% Build a list of initialisation expressions for the record tuple
+%% elements. This expansion must be passed through expr
+%% again. N.B. We are scanning the record definition field list!
+
+record_inits(Fs, Is) ->
+ WildcardInit = record_wildcard_init(Is),
+ map(fun ({record_field,_,{atom,_,F},D}) ->
+ case find_field(F, Is) of
+ {ok,Init} -> Init;
+ error when WildcardInit =:= none -> D;
+ error -> WildcardInit
+ end end,
+ Fs).
+
+record_wildcard_init([{record_field,_,{var,_,'_'},D}|_]) -> D;
+record_wildcard_init([_|Is]) -> record_wildcard_init(Is);
+record_wildcard_init([]) -> none.
+
+%% record_update(Record, RecordName, [RecDefField], [Update], State) ->
+%% {Expr,State'}
+%% Build an expression to update fields in a record returning a new
+%% record. Try to be smart and optimise this. This expansion must be
+%% passed through expr again.
+
+record_update(R, Name, Fs, Us0, St0) ->
+ Line = element(2, R),
+ {Pre,Us,St1} = record_exprs(Us0, St0),
+ Nf = length(Fs), %# of record fields
+ Nu = length(Us), %# of update fields
+ Nc = Nf - Nu, %# of copy fields
+
+ %% We need a new variable for the record expression
+ %% to guarantee that it is only evaluated once.
+ {Var,St2} = new_var(Line, St1),
+
+ %% Try to be intelligent about which method of updating record to use.
+ {Update,St} =
+ if
+ Nu == 0 -> {R,St2}; %No fields updated
+ Nu =< Nc -> %Few fields updated
+ {record_setel(Var, Name, Fs, Us), St2};
+ true -> %The wide area inbetween
+ record_match(Var, Name, Fs, Us, St2)
+ end,
+ {{block,element(2, R),Pre ++ [{match,Line,Var,R},Update]},St}.
+
+%% record_match(Record, RecordName, [RecDefField], [Update], State)
+%% Build a 'case' expression to modify record fields.
+
+record_match(R, Name, Fs, Us, St0) ->
+ {Ps,News,St1} = record_upd_fs(Fs, Us, St0),
+ Lr = element(2, hd(Us)),
+ {{'case',Lr,R,
+ [{clause,Lr,[{tuple,Lr,[{atom,Lr,Name}|Ps]}],[],
+ [{tuple,Lr,[{atom,Lr,Name}|News]}]},
+ {clause,Lr,[{var,Lr,'_'}],[],
+ [call_error(Lr, {tuple,Lr,[{atom,Lr,badrecord},{atom,Lr,Name}]})]}
+ ]},
+ St1}.
+
+record_upd_fs([{record_field,Lf,{atom,_La,F},_Val}|Fs], Us, St0) ->
+ {P,St1} = new_var(Lf, St0),
+ {Ps,News,St2} = record_upd_fs(Fs, Us, St1),
+ case find_field(F, Us) of
+ {ok,New} -> {[P|Ps],[New|News],St2};
+ error -> {[P|Ps],[P|News],St2}
+ end;
+record_upd_fs([], _, St) -> {[],[],St}.
+
+%% record_setel(Record, RecordName, [RecDefField], [Update])
+%% Build a nested chain of setelement calls to build the
+%% updated record tuple.
+
+record_setel(R, Name, Fs, Us0) ->
+ Us1 = foldl(fun ({record_field,Lf,Field,Val}, Acc) ->
+ I = index_expr(Lf, Field, Name, Fs),
+ [{I,Lf,Val}|Acc]
+ end, [], Us0),
+ Us = sort(Us1),
+ Lr = element(2, hd(Us)),
+ Wildcards = duplicate(length(Fs), {var,Lr,'_'}),
+ {'case',Lr,R,
+ [{clause,Lr,[{tuple,Lr,[{atom,Lr,Name}|Wildcards]}],[],
+ [foldr(fun ({I,Lf,Val}, Acc) ->
+ {call,Lf,{atom,Lf,setelement},[I,Acc,Val]} end,
+ R, Us)]},
+ {clause,Lr,[{var,Lr,'_'}],[],
+ [call_error(Lr, {tuple,Lr,[{atom,Lr,badrecord},{atom,Lr,Name}]})]}]}.
+
+%% Expand a call to record_info/2. We have checked that it is not
+%% shadowed by an import.
+
+record_info_call(Line, [{atom,_Li,Info},{atom,_Ln,Name}], St) ->
+ case Info of
+ size ->
+ {{integer,Line,1+length(record_fields(Name, St))},[],[],St};
+ fields ->
+ {make_list(field_names(record_fields(Name, St)), Line),
+ [],[],St}
+ end.
+
+%% Break out expressions from an record update list and bind to new
+%% variables. The idea is that we will evaluate all update expressions
+%% before starting to update the record.
+
+record_exprs(Us, St) ->
+ record_exprs(Us, St, [], []).
+
+record_exprs([{record_field,Lf,{atom,_La,_F}=Name,Val}=Field0|Us], St0, Pre, Fs) ->
+ case is_simple_val(Val) of
+ true ->
+ record_exprs(Us, St0, Pre, [Field0|Fs]);
+ false ->
+ {Var,St} = new_var(Lf, St0),
+ Bind = {match,Lf,Var,Val},
+ Field = {record_field,Lf,Name,Var},
+ record_exprs(Us, St, [Bind|Pre], [Field|Fs])
+ end;
+record_exprs([], St, Pre, Fs) ->
+ {reverse(Pre),Fs,St}.
+
+is_simple_val({var,_,_}) -> true;
+is_simple_val({atom,_,_}) -> true;
+is_simple_val({integer,_,_}) -> true;
+is_simple_val({float,_,_}) -> true;
+is_simple_val({nil,_}) -> true;
+is_simple_val(_) -> false.
+
+%% pattern_bin([Element], State) -> {[Element],[Variable],[UsedVar],State}.
+
+pattern_bin(Es0, St) ->
+ Es1 = bin_expand_strings(Es0),
+ foldr(fun (E, Acc) -> pattern_element(E, Acc) end, {[],[],[],St}, Es1).
+
+pattern_element({bin_element,Line,Expr,Size,Type}, {Es,Esvs,Esus,St0}) ->
+ {Expr1,Vs1,Us1,St1} = pattern(Expr, St0),
+ {Size1,Vs2,Us2,St2} = pat_bit_size(Size, St1),
+ {Size2,Type1} = make_bit_type(Line, Size1,Type),
+ {[{bin_element,Line,Expr1,Size2,Type1}|Es],
+ union([Vs1,Vs2,Esvs]),union([Us1,Us2,Esus]),St2}.
+
+pat_bit_size(default, St) -> {default,[],[],St};
+pat_bit_size({atom,_La,all}=All, St) -> {All,[],[],St};
+pat_bit_size({var,_Lv,V}=Var, St) -> {Var,[],[V],St};
+pat_bit_size(Size, St) ->
+ Line = element(2, Size),
+ {value,Sz,_} = erl_eval:expr(Size, erl_eval:new_bindings()),
+ {{integer,Line,Sz},[],[],St}.
+
+make_bit_type(Line, default, Type0) ->
+ case erl_bits:set_bit_type(default, Type0) of
+ {ok,all,Bt} -> {{atom,Line,all},erl_bits:as_list(Bt)};
+ {ok,Size,Bt} -> {{integer,Line,Size},erl_bits:as_list(Bt)}
+ end;
+make_bit_type(_Line, Size, Type0) -> %Integer or 'all'
+ {ok,Size,Bt} = erl_bits:set_bit_type(Size, Type0),
+ {Size,erl_bits:as_list(Bt)}.
+
+%% expr_bin([Element], [VisibleVar], State) ->
+%% {[Element],[NewVar],[UsedVar],State}.
+
+expr_bin(Es0, Vs, St) ->
+ Es1 = bin_expand_strings(Es0),
+ foldr(fun (E, Acc) -> bin_element(E, Vs, Acc) end, {[],[],[],St}, Es1).
+
+bin_element({bin_element,Line,Expr,Size,Type}, Vs, {Es,Esvs,Esus,St0}) ->
+ {Expr1,Vs1,Us1,St1} = expr(Expr, Vs, St0),
+ {Size1,Vs2,Us2,St2} = if Size == default -> {default,[],[],St1};
+ true -> expr(Size, Vs, St1)
+ end,
+ {Size2,Type1} = make_bit_type(Line, Size1, Type),
+ {[{bin_element,Line,Expr1,Size2,Type1}|Es],
+ union([Vs1,Vs2,Esvs]),union([Us1,Us2,Esus]),St2}.
+
+bin_expand_strings(Es) ->
+ foldr(fun ({bin_element,Line,{string,_,S},default,default}, Es1) ->
+ foldr(fun (C, Es2) ->
+ [{bin_element,Line,{char,Line,C},default,default}|Es2]
+ end, Es1, S);
+ (E, Es1) -> [E|Es1]
+ end, [], Es).
+
+%% new_var_name(State) -> {VarName,State}.
+
+new_var_name(St) ->
+ C = St#expand.vcount,
+ {list_to_atom("pre" ++ integer_to_list(C)),St#expand{vcount=C+1}}.
+
+%% new_var(Line, State) -> {Var,State}.
+
+new_var(L, St0) ->
+ {New,St1} = new_var_name(St0),
+ {{var,L,New},St1}.
+
+%% new_vars(Count, Line, State) -> {[Var],State}.
+%% Make Count new variables.
+
+new_vars(N, L, St) -> new_vars(N, L, St, []).
+
+new_vars(N, L, St0, Vs) when N > 0 ->
+ {V,St1} = new_var(L, St0),
+ new_vars(N-1, L, St1, [V|Vs]);
+new_vars(0, _L, St, Vs) -> {Vs,St}.
+
+%% make_list(TermList, Line) -> ConsTerm.
+
+make_list(Ts, Line) ->
+ foldr(fun (H, T) -> {cons,Line,H,T} end, {nil,Line}, Ts).
+
+string_to_conses(Line, Cs, Tail) ->
+ foldr(fun (C, T) -> {cons,Line,{char,Line,C},T} end, Tail, Cs).
+
+
+%% In syntax trees, module/package names are atoms or lists of atoms.
+
+package_to_string(A) when atom(A) -> atom_to_list(A);
+package_to_string(L) when list(L) -> packages:concat(L).
+
+expand_package({atom,L,A} = M, St) ->
+ case dict:find(A, St#expand.mod_imports) of
+ {ok, A1} ->
+ {atom,L,A1};
+ error ->
+ case packages:is_segmented(A) of
+ true ->
+ M;
+ false ->
+ M1 = packages:concat(St#expand.package, A),
+ {atom,L,list_to_atom(M1)}
+ end
+ end;
+expand_package(M, _St) ->
+ case erl_parse:package_segments(M) of
+ error ->
+ M;
+ M1 ->
+ {atom,element(2,M),list_to_atom(package_to_string(M1))}
+ end.
+
+%% Create a case-switch on true/false, generating badarg for all other
+%% values.
+
+make_bool_switch(L, E, V, T, F) ->
+ make_bool_switch_1(L, E, V, [T], [F]).
+
+make_bool_switch_1(L, E, V, T, F) ->
+ case get(sys_pre_expand_in_guard) of
+ undefined -> make_bool_switch_body(L, E, V, T, F);
+ yes -> make_bool_switch_guard(L, E, V, T, F)
+ end.
+
+make_bool_switch_guard(_, E, _, [{atom,_,true}], [{atom,_,false}]) -> E;
+make_bool_switch_guard(L, E, V, T, F) ->
+ NegL = -abs(L),
+ {'case',NegL,E,
+ [{clause,NegL,[{atom,NegL,true}],[],T},
+ {clause,NegL,[{atom,NegL,false}],[],F},
+ {clause,NegL,[V],[],[V]}
+ ]}.
+
+make_bool_switch_body(L, E, V, T, F) ->
+ NegL = -abs(L),
+ {'case',NegL,E,
+ [{clause,NegL,[{atom,NegL,true}],[],T},
+ {clause,NegL,[{atom,NegL,false}],[],F},
+ {clause,NegL,[V],[],
+ [call_error(NegL,{tuple,NegL,[{atom,NegL,badarg},V]})]}
+ ]}.
+
+%% Expand a list of cond-clauses to a sequence of case-switches.
+
+cond_clauses([{clause,L,[],[[E]],B}],V) ->
+ make_bool_switch_1(L,E,V,B,[call_error(L,{atom,L,cond_clause})]);
+cond_clauses([{clause,L,[],[[E]],B} | Cs],V) ->
+ make_bool_switch_1(L,E,V,B,[cond_clauses(Cs,V)]).
+
+%% call_error(Line, Reason) -> Expr.
+%% Build a call to erlang:error/1 with reason Reason.
+
+call_error(L, R) ->
+ {call,L,{remote,L,{atom,L,erlang},{atom,L,error}},[R]}.
+
+%% new_in_all(Before, RegionList) -> NewInAll
+%% Return the variables new in all clauses.
+
+new_in_all(Before, Region) ->
+ InAll = intersection(Region),
+ subtract(InAll, Before).
+
+%% import(Line, Imports, State) ->
+%% State'
+%% imported(Name, Arity, State) ->
+%% {yes,Module} | no
+%% Handle import declarations and est for imported functions. No need to
+%% check when building imports as code is correct.
+
+import({Mod0,Fs}, St) ->
+ Mod = list_to_atom(package_to_string(Mod0)),
+ Mfs = from_list(Fs),
+ St#expand{imports=add_imports(Mod, Mfs, St#expand.imports)};
+import(Mod0, St) ->
+ Mod = package_to_string(Mod0),
+ Key = list_to_atom(packages:last(Mod)),
+ St#expand{mod_imports=dict:store(Key, list_to_atom(Mod),
+ St#expand.mod_imports)}.
+
+add_imports(Mod, [F|Fs], Is) ->
+ add_imports(Mod, Fs, orddict:store(F, Mod, Is));
+add_imports(_, [], Is) -> Is.
+
+imported(F, A, St) ->
+ case orddict:find({F,A}, St#expand.imports) of
+ {ok,Mod} -> {yes,Mod};
+ error -> no
+ end.
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_codegen.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_codegen.erl
new file mode 100644
index 0000000000..6b787e8c95
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_codegen.erl
@@ -0,0 +1,1755 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: v3_codegen.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose : Code generator for Beam.
+
+%% The following assumptions have been made:
+%%
+%% 1. Matches, i.e. things with {match,M,Ret} wrappers, only return
+%% values; no variables are exported. If the match would have returned
+%% extra variables then these have been transformed to multiple return
+%% values.
+%%
+%% 2. All BIF's called in guards are gc-safe so there is no need to
+%% put thing on the stack in the guard. While this would in principle
+%% work it would be difficult to keep track of the stack depth when
+%% trimming.
+%%
+%% The code generation uses variable lifetime information added by
+%% the v3_life module to save variables, allocate registers and
+%% move registers to the stack when necessary.
+%%
+%% We try to use a consistent variable name scheme throughout. The
+%% StackReg record is always called Bef,Int,Aft.
+
+-module(v3_codegen).
+
+%% The main interface.
+-export([module/2]).
+
+-import(lists, [member/2,keymember/3,keysort/2,keysearch/3,append/1,
+ map/2,flatmap/2,foldl/3,foldr/3,mapfoldl/3,
+ sort/1,reverse/1,reverse/2]).
+-import(v3_life, [vdb_find/2]).
+
+%%-compile([export_all]).
+
+-include("v3_life.hrl").
+
+%% Main codegen structure.
+-record(cg, {lcount=1, %Label counter
+ mod, %Current module
+ func, %Current function
+ finfo, %Function info label
+ fcode, %Function code label
+ btype, %Type of bif used.
+ bfail, %Fail label of bif
+ break, %Break label
+ recv, %Receive label
+ is_top_block, %Boolean: top block or not
+ functable = [], %Table of local functions:
+ %[{{Name, Arity}, Label}...]
+ in_catch=false, %Inside a catch or not.
+ need_frame, %Need a stack frame.
+ new_funs=true}). %Generate new fun instructions.
+
+%% Stack/register state record.
+-record(sr, {reg=[], %Register table
+ stk=[], %Stack table
+ res=[]}). %Reserved regs: [{reserved,I,V}]
+
+module({Mod,Exp,Attr,Forms}, Options) ->
+ NewFunsFlag = not member(no_new_funs, Options),
+ {Fs,St} = functions(Forms, #cg{mod=Mod,new_funs=NewFunsFlag}),
+ {ok,{Mod,Exp,Attr,Fs,St#cg.lcount}}.
+
+functions(Forms, St0) ->
+ mapfoldl(fun (F, St) -> function(F, St) end, St0#cg{lcount=1}, Forms).
+
+function({function,Name,Arity,As0,Vb,Vdb}, St0) ->
+ %%ok = io:fwrite("cg ~w:~p~n", [?LINE,{Name,Arity}]),
+ St1 = St0#cg{func={Name,Arity}},
+ {Fun,St2} = cg_fun(Vb, As0, Vdb, St1),
+ Func0 = {function,Name,Arity,St2#cg.fcode,Fun},
+ Func = bs_function(Func0),
+ {Func,St2}.
+
+%% cg_fun([Lkexpr], [HeadVar], Vdb, State) -> {[Ainstr],State}
+
+cg_fun(Les, Hvs, Vdb, St0) ->
+ {Name,Arity} = St0#cg.func,
+ {Fi,St1} = new_label(St0), %FuncInfo label
+ {Fl,St2} = local_func_label(Name, Arity, St1),
+ %% Create initial stack/register state, clear unused arguments.
+ Bef = clear_dead(#sr{reg=foldl(fun ({var,V}, Reg) ->
+ put_reg(V, Reg)
+ end, [], Hvs),
+ stk=[]}, 0, Vdb),
+ {B2,_Aft,St3} = cg_list(Les, 0, Vdb, Bef, St2#cg{btype=exit,
+ bfail=Fi,
+ finfo=Fi,
+ fcode=Fl,
+ is_top_block=true}),
+ A = [{label,Fi},{func_info,{atom,St3#cg.mod},{atom,Name},Arity},
+ {label,Fl}|B2],
+ {A,St3}.
+
+%% cg(Lkexpr, Vdb, StackReg, State) -> {[Ainstr],StackReg,State}.
+%% Generate code for a kexpr.
+%% Split function into two steps for clarity, not efficiency.
+
+cg(Le, Vdb, Bef, St) ->
+ cg(Le#l.ke, Le, Vdb, Bef, St).
+
+cg({block,Es}, Le, Vdb, Bef, St) ->
+ block_cg(Es, Le, Vdb, Bef, St);
+cg({match,M,Rs}, Le, Vdb, Bef, St) ->
+ match_cg(M, Rs, Le, Vdb, Bef, St);
+cg({match_fail,F}, Le, Vdb, Bef, St) ->
+ match_fail_cg(F, Le, Vdb, Bef, St);
+cg({call,Func,As,Rs}, Le, Vdb, Bef, St) ->
+ call_cg(Func, As, Rs, Le, Vdb, Bef, St);
+cg({enter,Func,As}, Le, Vdb, Bef, St) ->
+ enter_cg(Func, As, Le, Vdb, Bef, St);
+cg({bif,Bif,As,Rs}, Le, Vdb, Bef, St) ->
+ bif_cg(Bif, As, Rs, Le, Vdb, Bef, St);
+cg({receive_loop,Te,Rvar,Rm,Tes,Rs}, Le, Vdb, Bef, St) ->
+ recv_loop_cg(Te, Rvar, Rm, Tes, Rs, Le, Vdb, Bef, St);
+cg(receive_next, Le, Vdb, Bef, St) ->
+ recv_next_cg(Le, Vdb, Bef, St);
+cg(receive_accept, _Le, _Vdb, Bef, St) -> {[remove_message],Bef,St};
+cg({'try',Ta,Vs,Tb,Evs,Th,Rs}, Le, Vdb, Bef, St) ->
+ try_cg(Ta, Vs, Tb, Evs, Th, Rs, Le, Vdb, Bef, St);
+cg({'catch',Cb,R}, Le, Vdb, Bef, St) ->
+ catch_cg(Cb, R, Le, Vdb, Bef, St);
+cg({set,Var,Con}, Le, Vdb, Bef, St) -> set_cg(Var, Con, Le, Vdb, Bef, St);
+cg({return,Rs}, Le, Vdb, Bef, St) -> return_cg(Rs, Le, Vdb, Bef, St);
+cg({break,Bs}, Le, Vdb, Bef, St) -> break_cg(Bs, Le, Vdb, Bef, St);
+cg({need_heap,0}, _Le, _Vdb, Bef, St) ->
+ {[],Bef,St};
+cg({need_heap,H}, _Le, _Vdb, Bef, St) ->
+ {[{test_heap,H,max_reg(Bef#sr.reg)}],Bef,St}.
+
+%% cg_list([Kexpr], FirstI, Vdb, StackReg, St) -> {[Ainstr],StackReg,St}.
+
+cg_list(Kes, I, Vdb, Bef, St0) ->
+ {Keis,{Aft,St1}} =
+ flatmapfoldl(fun (Ke, {Inta,Sta}) ->
+% ok = io:fwrite(" %% ~p\n", [Inta]),
+% ok = io:fwrite("cgl:~p\n", [Ke]),
+ {Keis,Intb,Stb} = cg(Ke, Vdb, Inta, Sta),
+% ok = io:fwrite(" ~p\n", [Keis]),
+% ok = io:fwrite(" %% ~p\n", [Intb]),
+ {comment(Inta) ++ Keis,{Intb,Stb}}
+ end, {Bef,St0}, need_heap(Kes, I)),
+ {Keis,Aft,St1}.
+
+%% need_heap([Lkexpr], I, BifType) -> [Lkexpr].
+%% Insert need_heap instructions in Kexpr list. Try to be smart and
+%% collect them together as much as possible.
+
+need_heap(Kes0, I) ->
+ {Kes1,{H,F}} = flatmapfoldr(fun (Ke, {H0,F0}) ->
+ {Ns,H1,F1} = need_heap_1(Ke, H0, F0),
+ {[Ke|Ns],{H1,F1}}
+ end, {0,false}, Kes0),
+ %% Prepend need_heap if necessary.
+ Kes2 = need_heap_need(I, H, F) ++ Kes1,
+% ok = io:fwrite("need_heap: ~p~n",
+% [{{H,F},
+% map(fun (#l{ke={match,M,Rs}}) -> match;
+% (Lke) -> Lke#l.ke end, Kes2)}]),
+ Kes2.
+
+need_heap_1(#l{ke={set,_,{binary,_}},i=I}, H, F) ->
+ {need_heap_need(I, H, F),0,false};
+need_heap_1(#l{ke={set,_,Val}}, H, F) ->
+ %% Just pass through adding to needed heap.
+ {[],H + case Val of
+ {cons,_} -> 2;
+ {tuple,Es} -> 1 + length(Es);
+ {string,S} -> 2 * length(S);
+ _Other -> 0
+ end,F};
+need_heap_1(#l{ke={call,_Func,_As,_Rs},i=I}, H, F) ->
+ %% Calls generate a need if necessary and also force one.
+ {need_heap_need(I, H, F),0,true};
+need_heap_1(#l{ke={bif,dsetelement,_As,_Rs},i=I}, H, F) ->
+ {need_heap_need(I, H, F),0,true};
+need_heap_1(#l{ke={bif,{make_fun,_,_,_,_},_As,_Rs},i=I}, H, F) ->
+ {need_heap_need(I, H, F),0,true};
+need_heap_1(#l{ke={bif,_Bif,_As,_Rs}}, H, F) ->
+ {[],H,F};
+need_heap_1(#l{i=I}, H, F) ->
+ %% Others kexprs generate a need if necessary but don't force.
+ {need_heap_need(I, H, F),0,false}.
+
+need_heap_need(_I, 0, false) -> [];
+need_heap_need(I, H, _F) -> [#l{ke={need_heap,H},i=I}].
+
+
+%% match_cg(Match, [Ret], Le, Vdb, StackReg, State) ->
+%% {[Ainstr],StackReg,State}.
+%% Generate code for a match. First save all variables on the stack
+%% that are to survive after the match. We leave saved variables in
+%% their registers as they might actually be in the right place.
+%% Should test this.
+
+match_cg(M, Rs, Le, Vdb, Bef, St0) ->
+ I = Le#l.i,
+ {Sis,Int0} = adjust_stack(Bef, I, I+1, Vdb),
+ {B,St1} = new_label(St0),
+ {Mis,Int1,St2} = match_cg(M, none, Int0, St1#cg{break=B}),
+ %% Put return values in registers.
+ Reg = load_vars(Rs, Int1#sr.reg),
+ {Sis ++ Mis ++ [{label,B}],
+ clear_dead(Int1#sr{reg=Reg}, I, Vdb),
+ St2#cg{break=St1#cg.break}}.
+
+%% match_cg(Match, Fail, StackReg, State) -> {[Ainstr],StackReg,State}.
+%% Generate code for a match tree. N.B. there is no need pass Vdb
+%% down as each level which uses this takes its own internal Vdb not
+%% the outer one.
+
+match_cg(Le, Fail, Bef, St) ->
+ match_cg(Le#l.ke, Le, Fail, Bef, St).
+
+match_cg({alt,F,S}, _Le, Fail, Bef, St0) ->
+ {Tf,St1} = new_label(St0),
+ {Fis,Faft,St2} = match_cg(F, Tf, Bef, St1),
+ {Sis,Saft,St3} = match_cg(S, Fail, Bef, St2),
+ Aft = sr_merge(Faft, Saft),
+ {Fis ++ [{label,Tf}] ++ Sis,Aft,St3};
+match_cg({select,V,Scs}, _Va, Fail, Bef, St) ->
+ match_fmf(fun (S, F, Sta) ->
+ select_cg(S, V, F, Fail, Bef, Sta) end,
+ Fail, St, Scs);
+match_cg({guard,Gcs}, _Le, Fail, Bef, St) ->
+ match_fmf(fun (G, F, Sta) -> guard_clause_cg(G, F, Bef, Sta) end,
+ Fail, St, Gcs);
+match_cg({block,Es}, Le, _Fail, Bef, St) ->
+ %% Must clear registers and stack of dead variables.
+ Int = clear_dead(Bef, Le#l.i, Le#l.vdb),
+ block_cg(Es, Le, Int, St).
+
+%% match_fail_cg(FailReason, Le, Vdb, StackReg, State) ->
+%% {[Ainstr],StackReg,State}.
+%% Generate code for the match_fail "call". N.B. there is no generic
+%% case for when the fail value has been created elsewhere.
+
+match_fail_cg({function_clause,As}, Le, Vdb, Bef, St) ->
+ %% Must have the args in {x,0}, {x,1},...
+ {Sis,Int} = cg_setup_call(As, Bef, Le#l.i, Vdb),
+ {Sis ++ [{jump,{f,St#cg.finfo}}],
+ Int#sr{reg=clear_regs(Int#sr.reg)},St};
+match_fail_cg({badmatch,Term}, Le, Vdb, Bef, St) ->
+ R = cg_reg_arg(Term, Bef),
+ Int0 = clear_dead(Bef, Le#l.i, Vdb),
+ {Sis,Int} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb),
+ {Sis ++ [{badmatch,R}],
+ Int#sr{reg=clear_regs(Int0#sr.reg)},St};
+match_fail_cg({case_clause,Reason}, Le, Vdb, Bef, St) ->
+ R = cg_reg_arg(Reason, Bef),
+ Int0 = clear_dead(Bef, Le#l.i, Vdb),
+ {Sis,Int} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb),
+ {Sis++[{case_end,R}],
+ Int#sr{reg=clear_regs(Bef#sr.reg)},St};
+match_fail_cg(if_clause, Le, Vdb, Bef, St) ->
+ Int0 = clear_dead(Bef, Le#l.i, Vdb),
+ {Sis,Int1} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb),
+ {Sis++[if_end],Int1#sr{reg=clear_regs(Int1#sr.reg)},St};
+match_fail_cg({try_clause,Reason}, Le, Vdb, Bef, St) ->
+ R = cg_reg_arg(Reason, Bef),
+ Int0 = clear_dead(Bef, Le#l.i, Vdb),
+ {Sis,Int} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb),
+ {Sis ++ [{try_case_end,R}],
+ Int#sr{reg=clear_regs(Int0#sr.reg)},St}.
+
+
+%% block_cg([Kexpr], Le, Vdb, StackReg, St) -> {[Ainstr],StackReg,St}.
+%% block_cg([Kexpr], Le, StackReg, St) -> {[Ainstr],StackReg,St}.
+
+block_cg(Es, Le, _Vdb, Bef, St) ->
+ block_cg(Es, Le, Bef, St).
+
+block_cg(Es, Le, Bef, St0) ->
+ case St0#cg.is_top_block of
+ false ->
+ cg_block(Es, Le#l.i, Le#l.vdb, Bef, St0);
+ true ->
+ {Keis,Aft,St1} = cg_block(Es, Le#l.i, Le#l.vdb, Bef,
+ St0#cg{is_top_block=false,
+ need_frame=false}),
+ top_level_block(Keis, Aft, max_reg(Bef#sr.reg), St1)
+ end.
+
+cg_block([], _I, _Vdb, Bef, St0) ->
+ {[],Bef,St0};
+cg_block(Kes0, I, Vdb, Bef, St0) ->
+ {Kes2,Int1,St1} =
+ case basic_block(Kes0) of
+ {Kes1,LastI,Args,Rest} ->
+ Ke = hd(Kes1),
+ Fb = Ke#l.i,
+ cg_basic_block(Kes1, Fb, LastI, Args, Vdb, Bef, St0);
+ {Kes1,Rest} ->
+ cg_list(Kes1, I, Vdb, Bef, St0)
+ end,
+ {Kes3,Int2,St2} = cg_block(Rest, I, Vdb, Int1, St1),
+ {Kes2 ++ Kes3,Int2,St2}.
+
+basic_block(Kes) -> basic_block(Kes, []).
+
+basic_block([], Acc) -> {reverse(Acc),[]};
+basic_block([Le|Les], Acc) ->
+ case collect_block(Le#l.ke) of
+ include -> basic_block(Les, [Le|Acc]);
+ {block_end,As} -> {reverse(Acc, [Le]),Le#l.i,As,Les};
+ no_block -> {reverse(Acc, [Le]),Les}
+ end.
+
+collect_block({set,_,{binary,_}}) -> no_block;
+collect_block({set,_,_}) -> include;
+collect_block({call,{var,_}=Var,As,_Rs}) -> {block_end,As++[Var]};
+collect_block({call,Func,As,_Rs}) -> {block_end,As++func_vars(Func)};
+collect_block({enter,{var,_}=Var,As})-> {block_end,As++[Var]};
+collect_block({enter,Func,As}) -> {block_end,As++func_vars(Func)};
+collect_block({return,Rs}) -> {block_end,Rs};
+collect_block({break,Bs}) -> {block_end,Bs};
+collect_block({bif,_Bif,_As,_Rs}) -> include;
+collect_block(_) -> no_block.
+
+func_vars({remote,M,F}) when element(1, M) == var;
+ element(1, F) == var ->
+ [M,F];
+func_vars(_) -> [].
+
+%% cg_basic_block([Kexpr], FirstI, LastI, As, Vdb, StackReg, State) ->
+%% {[Ainstr],StackReg,State}.
+
+cg_basic_block(Kes, Fb, Lf, As, Vdb, Bef, St0) ->
+ Res = make_reservation(As, 0),
+ Regs0 = reserve(Res, Bef#sr.reg, Bef#sr.stk),
+ Stk = extend_stack(Bef, Lf, Lf+1, Vdb),
+ Int0 = Bef#sr{reg=Regs0,stk=Stk,res=Res},
+ X0_v0 = x0_vars(As, Fb, Lf, Vdb),
+ {Keis,{Aft,_,St1}} =
+ flatmapfoldl(fun(Ke, St) -> cg_basic_block(Ke, St, Lf, Vdb) end,
+ {Int0,X0_v0,St0}, need_heap(Kes, Fb)),
+ {Keis,Aft,St1}.
+
+cg_basic_block(Ke, {Inta,X0v,Sta}, _Lf, Vdb) when element(1, Ke#l.ke) =:= need_heap ->
+ {Keis,Intb,Stb} = cg(Ke, Vdb, Inta, Sta),
+ {comment(Inta) ++ Keis, {Intb,X0v,Stb}};
+cg_basic_block(Ke, {Inta,X0_v1,Sta}, Lf, Vdb) ->
+ {Sis,Intb} = save_carefully(Inta, Ke#l.i, Lf+1, Vdb),
+ {X0_v2,Intc} = allocate_x0(X0_v1, Ke#l.i, Intb),
+ Intd = reserve(Intc),
+ {Keis,Inte,Stb} = cg(Ke, Vdb, Intd, Sta),
+ {comment(Inta) ++ Sis ++ Keis, {Inte,X0_v2,Stb}}.
+
+make_reservation([], _) -> [];
+make_reservation([{var,V}|As], I) -> [{I,V}|make_reservation(As, I+1)];
+make_reservation([A|As], I) -> [{I,A}|make_reservation(As, I+1)].
+
+reserve(Sr) -> Sr#sr{reg=reserve(Sr#sr.res, Sr#sr.reg, Sr#sr.stk)}.
+
+reserve([{I,V}|Rs], [free|Regs], Stk) -> [{reserved,I,V}|reserve(Rs, Regs, Stk)];
+reserve([{I,V}|Rs], [{I,V}|Regs], Stk) -> [{I,V}|reserve(Rs, Regs, Stk)];
+reserve([{I,V}|Rs], [{I,Var}|Regs], Stk) ->
+ case on_stack(Var, Stk) of
+ true -> [{reserved,I,V}|reserve(Rs, Regs, Stk)];
+ false -> [{I,Var}|reserve(Rs, Regs, Stk)]
+ end;
+reserve([{I,V}|Rs], [{reserved,I,_}|Regs], Stk) ->
+ [{reserved,I,V}|reserve(Rs, Regs, Stk)];
+%reserve([{I,V}|Rs], [Other|Regs], Stk) -> [Other|reserve(Rs, Regs, Stk)];
+reserve([{I,V}|Rs], [], Stk) -> [{reserved,I,V}|reserve(Rs, [], Stk)];
+reserve([], Regs, _) -> Regs.
+
+extend_stack(Bef, Fb, Lf, Vdb) ->
+ Stk0 = clear_dead_stk(Bef#sr.stk, Fb, Vdb),
+ Saves = [V || {V,F,L} <- Vdb,
+ F < Fb,
+ L >= Lf,
+ not on_stack(V, Stk0)],
+ Stk1 = foldl(fun (V, Stk) -> put_stack(V, Stk) end, Stk0, Saves),
+ Bef#sr.stk ++ lists:duplicate(length(Stk1) - length(Bef#sr.stk), free).
+
+save_carefully(Bef, Fb, Lf, Vdb) ->
+ Stk = Bef#sr.stk,
+ %% New variables that are in use but not on stack.
+ New = [ {V,F,L} || {V,F,L} <- Vdb,
+ F < Fb,
+ L >= Lf,
+ not on_stack(V, Stk) ],
+ Saves = [ V || {V,_,_} <- keysort(2, New) ],
+ save_carefully(Saves, Bef, []).
+
+save_carefully([], Bef, Acc) -> {reverse(Acc),Bef};
+save_carefully([V|Vs], Bef, Acc) ->
+ case put_stack_carefully(V, Bef#sr.stk) of
+ error -> {reverse(Acc),Bef};
+ Stk1 ->
+ SrcReg = fetch_reg(V, Bef#sr.reg),
+ Move = {move,SrcReg,fetch_stack(V, Stk1)},
+ {x,_} = SrcReg, %Assertion - must be X register.
+ save_carefully(Vs, Bef#sr{stk=Stk1}, [Move|Acc])
+ end.
+
+x0_vars([], _Fb, _Lf, _Vdb) -> [];
+x0_vars([{var,V}|_], Fb, _Lf, Vdb) ->
+ {V,F,_L} = VFL = vdb_find(V, Vdb),
+ x0_vars1([VFL], Fb, F, Vdb);
+x0_vars([X0|_], Fb, Lf, Vdb) ->
+ x0_vars1([{X0,Lf,Lf}], Fb, Lf, Vdb).
+
+x0_vars1(X0, Fb, Xf, Vdb) ->
+ Vs0 = [VFL || {_V,F,L}=VFL <- Vdb,
+ F >= Fb,
+ L < Xf],
+ Vs1 = keysort(3, Vs0),
+ keysort(2, X0++Vs1).
+
+allocate_x0([], _, Bef) -> {[],Bef#sr{res=[]}};
+allocate_x0([{_,_,L}|Vs], I, Bef) when L =< I ->
+ allocate_x0(Vs, I, Bef);
+allocate_x0([{V,_F,_L}=VFL|Vs], _, Bef) ->
+ {[VFL|Vs],Bef#sr{res=reserve_x0(V, Bef#sr.res)}}.
+
+reserve_x0(V, [_|Res]) -> [{0,V}|Res];
+reserve_x0(V, []) -> [{0,V}].
+
+top_level_block(Keis, Bef, _MaxRegs, St0) when St0#cg.need_frame =:= false,
+ length(Bef#sr.stk) =:= 0 ->
+ %% This block need no stack frame. However, we still need to turn the
+ %% stack frame upside down.
+ MaxY = length(Bef#sr.stk)-1,
+ Keis1 = flatmap(fun (Tuple) when tuple(Tuple) ->
+ [turn_yregs(size(Tuple), Tuple, MaxY)];
+ (Other) ->
+ [Other]
+ end, Keis),
+ {Keis1, Bef, St0#cg{is_top_block=true}};
+top_level_block(Keis, Bef, MaxRegs, St0) ->
+ %% This top block needs an allocate instruction before it, and a
+ %% deallocate instruction before each return.
+ FrameSz = length(Bef#sr.stk),
+ MaxY = FrameSz-1,
+ Keis1 = flatmap(fun ({call_only,Arity,Func}) ->
+ [{call_last,Arity,Func,FrameSz}];
+ ({call_ext_only,Arity,Func}) ->
+ [{call_ext_last,Arity,Func,FrameSz}];
+ ({apply_only,Arity}) ->
+ [{apply_last,Arity,FrameSz}];
+ (return) ->
+ [{deallocate,FrameSz}, return];
+ (Tuple) when tuple(Tuple) ->
+ [turn_yregs(size(Tuple), Tuple, MaxY)];
+ (Other) ->
+ [Other]
+ end, Keis),
+ {[{allocate_zero,FrameSz,MaxRegs}|Keis1], Bef, St0#cg{is_top_block=true}}.
+
+%% turn_yregs(Size, Tuple, MaxY) -> Tuple'
+%% Renumber y register so that {y, 0} becomes {y, FrameSize-1},
+%% {y, FrameSize-1} becomes {y, 0} and so on. This is to make nested
+%% catches work. The code generation algorithm gives a lower register
+%% number to the outer catch, which is wrong.
+
+turn_yregs(0, Tp, _) -> Tp;
+turn_yregs(El, Tp, MaxY) when element(1, element(El, Tp)) == yy ->
+ turn_yregs(El-1, setelement(El, Tp, {y,MaxY-element(2, element(El, Tp))}), MaxY);
+turn_yregs(El, Tp, MaxY) when list(element(El, Tp)) ->
+ New = map(fun ({yy,YY}) -> {y,MaxY-YY};
+ (Other) -> Other end, element(El, Tp)),
+ turn_yregs(El-1, setelement(El, Tp, New), MaxY);
+turn_yregs(El, Tp, MaxY) ->
+ turn_yregs(El-1, Tp, MaxY).
+
+%% select_cg(Sclause, V, TypeFail, ValueFail, StackReg, State) ->
+%% {Is,StackReg,State}.
+%% Selecting type and value needs two failure labels, TypeFail is the
+%% label to jump to of the next type test when this type fails, and
+%% ValueFail is the label when this type is correct but the value is
+%% wrong. These are different as in the second case there is no need
+%% to try the next type, it will always fail.
+
+select_cg(#l{ke={type_clause,cons,[S]}}, {var,V}, Tf, Vf, Bef, St) ->
+ select_cons(S, V, Tf, Vf, Bef, St);
+select_cg(#l{ke={type_clause,nil,[S]}}, {var,V}, Tf, Vf, Bef, St) ->
+ select_nil(S, V, Tf, Vf, Bef, St);
+select_cg(#l{ke={type_clause,binary,[S]}}, {var,V}, Tf, Vf, Bef, St) ->
+ select_binary(S, V, Tf, Vf, Bef, St);
+select_cg(#l{ke={type_clause,bin_seg,S}}, {var,V}, Tf, Vf, Bef, St) ->
+ select_bin_segs(S, V, Tf, Vf, Bef, St);
+select_cg(#l{ke={type_clause,bin_end,[S]}}, {var,V}, Tf, Vf, Bef, St) ->
+ select_bin_end(S, V, Tf, Vf, Bef, St);
+select_cg(#l{ke={type_clause,Type,Scs}}, {var,V}, Tf, Vf, Bef, St0) ->
+ {Vis,{Aft,St1}} =
+ mapfoldl(fun (S, {Int,Sta}) ->
+ {Val,Is,Inta,Stb} = select_val(S, V, Vf, Bef, Sta),
+ {{Is,[Val]},{sr_merge(Int, Inta),Stb}}
+ end, {void,St0}, Scs),
+ OptVls = combine(lists:sort(combine(Vis))),
+ {Vls,Sis,St2} = select_labels(OptVls, St1, [], []),
+ {select_val_cg(Type, fetch_var(V, Bef), Vls, Tf, Vf, Sis), Aft, St2}.
+
+select_val_cg(tuple, R, [Arity,{f,Lbl}], Tf, Vf, [{label,Lbl}|Sis]) ->
+ [{test,is_tuple,{f,Tf},[R]},{test,test_arity,{f,Vf},[R,Arity]}|Sis];
+select_val_cg(tuple, R, Vls, Tf, Vf, Sis) ->
+ [{test,is_tuple,{f,Tf},[R]},{select_tuple_arity,R,{f,Vf},{list,Vls}}|Sis];
+select_val_cg(Type, R, [Val, {f,Lbl}], Fail, Fail, [{label,Lbl}|Sis]) ->
+ [{test,is_eq_exact,{f,Fail},[R,{Type,Val}]}|Sis];
+select_val_cg(Type, R, [Val, {f,Lbl}], Tf, Vf, [{label,Lbl}|Sis]) ->
+ [{test,select_type_test(Type),{f,Tf},[R]},
+ {test,is_eq_exact,{f,Vf},[R,{Type,Val}]}|Sis];
+select_val_cg(Type, R, Vls0, Tf, Vf, Sis) ->
+ Vls1 = map(fun ({f,Lbl}) -> {f,Lbl};
+ (Value) -> {Type,Value}
+ end, Vls0),
+ [{test,select_type_test(Type),{f,Tf},[R]}, {select_val,R,{f,Vf},{list,Vls1}}|Sis].
+
+select_type_test(tuple) -> is_tuple;
+select_type_test(integer) -> is_integer;
+select_type_test(atom) -> is_atom;
+select_type_test(float) -> is_float.
+
+combine([{Is,Vs1}, {Is,Vs2}|Vis]) -> combine([{Is,Vs1 ++ Vs2}|Vis]);
+combine([V|Vis]) -> [V|combine(Vis)];
+combine([]) -> [].
+
+select_labels([{Is,Vs}|Vis], St0, Vls, Sis) ->
+ {Lbl,St1} = new_label(St0),
+ select_labels(Vis, St1, add_vls(Vs, Lbl, Vls), [[{label,Lbl}|Is]|Sis]);
+select_labels([], St, Vls, Sis) ->
+ {Vls,append(Sis),St}.
+
+add_vls([V|Vs], Lbl, Acc) ->
+ add_vls(Vs, Lbl, [V, {f,Lbl}|Acc]);
+add_vls([], _, Acc) -> Acc.
+
+select_cons(#l{ke={val_clause,{cons,Es},B},i=I,vdb=Vdb}, V, Tf, Vf, Bef, St0) ->
+ {Eis,Int,St1} = select_extract_cons(V, Es, I, Vdb, Bef, St0),
+ {Bis,Aft,St2} = match_cg(B, Vf, Int, St1),
+ {[{test,is_nonempty_list,{f,Tf},[fetch_var(V, Bef)]}] ++ Eis ++ Bis,Aft,St2}.
+
+select_nil(#l{ke={val_clause,nil,B}}, V, Tf, Vf, Bef, St0) ->
+ {Bis,Aft,St1} = match_cg(B, Vf, Bef, St0),
+ {[{test,is_nil,{f,Tf},[fetch_var(V, Bef)]}] ++ Bis,Aft,St1}.
+
+select_binary(#l{ke={val_clause,{old_binary,Var},B}}=L,
+ V, Tf, Vf, Bef, St) ->
+ %% Currently handled in the same way as new binaries.
+ select_binary(L#l{ke={val_clause,{binary,Var},B}}, V, Tf, Vf, Bef, St);
+select_binary(#l{ke={val_clause,{binary,{var,Ivar}},B},i=I,vdb=Vdb},
+ V, Tf, Vf, Bef, St0) ->
+ Int0 = clear_dead(Bef, I, Vdb),
+ {Bis,Aft,St1} = match_cg(B, Vf, Int0, St0),
+ {[{test,bs_start_match,{f,Tf},[fetch_var(V, Bef)]},{bs_save,Ivar}|Bis],
+ Aft,St1}.
+
+select_bin_segs(Scs, Ivar, Tf, _Vf, Bef, St) ->
+ match_fmf(fun(S, Fail, Sta) ->
+ select_bin_seg(S, Ivar, Fail, Bef, Sta) end,
+ Tf, St, Scs).
+
+select_bin_seg(#l{ke={val_clause,{bin_seg,Size,U,T,Fs,Es},B},i=I,vdb=Vdb},
+ Ivar, Fail, Bef, St0) ->
+ {Mis,Int,St1} = select_extract_bin(Es, Size, U, T, Fs, Fail,
+ I, Vdb, Bef, St0),
+ {Bis,Aft,St2} = match_cg(B, Fail, Int, St1),
+ {[{bs_restore,Ivar}|Mis] ++ Bis,Aft,St2}.
+
+select_extract_bin([{var,Hd},{var,Tl}], Size0, Unit, Type, Flags, Vf,
+ I, Vdb, Bef, St) ->
+ SizeReg = get_bin_size_reg(Size0, Bef),
+ {Es,Aft} =
+ case vdb_find(Hd, Vdb) of
+ {_,_,Lhd} when Lhd =< I ->
+ {[{test,bs_skip_bits,{f,Vf},[SizeReg,Unit,{field_flags,Flags}]},
+ {bs_save,Tl}],Bef};
+ {_,_,_} ->
+ Reg0 = put_reg(Hd, Bef#sr.reg),
+ Int1 = Bef#sr{reg=Reg0},
+ Rhd = fetch_reg(Hd, Reg0),
+ Name = get_bits_instr(Type),
+ {[{test,Name,{f,Vf},[SizeReg,Unit,{field_flags,Flags},Rhd]},
+ {bs_save,Tl}],Int1}
+ end,
+ {Es,clear_dead(Aft, I, Vdb),St}.
+
+get_bin_size_reg({var,V}, Bef) ->
+ fetch_var(V, Bef);
+get_bin_size_reg(Literal, _Bef) ->
+ Literal.
+
+select_bin_end(#l{ke={val_clause,bin_end,B}},
+ Ivar, Tf, Vf, Bef, St0) ->
+ {Bis,Aft,St2} = match_cg(B, Vf, Bef, St0),
+ {[{bs_restore,Ivar},{test,bs_test_tail,{f,Tf},[0]}|Bis],Aft,St2}.
+
+get_bits_instr(integer) -> bs_get_integer;
+get_bits_instr(float) -> bs_get_float;
+get_bits_instr(binary) -> bs_get_binary.
+
+select_val(#l{ke={val_clause,{tuple,Es},B},i=I,vdb=Vdb}, V, Vf, Bef, St0) ->
+ {Eis,Int,St1} = select_extract_tuple(V, Es, I, Vdb, Bef, St0),
+ {Bis,Aft,St2} = match_cg(B, Vf, Int, St1),
+ {length(Es),Eis ++ Bis,Aft,St2};
+select_val(#l{ke={val_clause,{_,Val},B}}, _V, Vf, Bef, St0) ->
+ {Bis,Aft,St1} = match_cg(B, Vf, Bef, St0),
+ {Val,Bis,Aft,St1}.
+
+%% select_extract_tuple(Src, [V], I, Vdb, StackReg, State) ->
+%% {[E],StackReg,State}.
+%% Extract tuple elements, but only if they do not immediately die.
+
+select_extract_tuple(Src, Vs, I, Vdb, Bef, St) ->
+ F = fun ({var,V}, {Int0,Elem}) ->
+ case vdb_find(V, Vdb) of
+ {V,_,L} when L =< I -> {[], {Int0,Elem+1}};
+ _Other ->
+ Reg1 = put_reg(V, Int0#sr.reg),
+ Int1 = Int0#sr{reg=Reg1},
+ Rsrc = fetch_var(Src, Int1),
+ {[{get_tuple_element,Rsrc,Elem,fetch_reg(V, Reg1)}],
+ {Int1,Elem+1}}
+ end
+ end,
+ {Es,{Aft,_}} = flatmapfoldl(F, {Bef,0}, Vs),
+ {Es,Aft,St}.
+
+select_extract_cons(Src, [{var,Hd}, {var,Tl}], I, Vdb, Bef, St) ->
+ {Es,Aft} = case {vdb_find(Hd, Vdb), vdb_find(Tl, Vdb)} of
+ {{_,_,Lhd}, {_,_,Ltl}} when Lhd =< I, Ltl =< I ->
+ %% Both head and tail are dead. No need to generate
+ %% any instruction.
+ {[], Bef};
+ _ ->
+ %% At least one of head and tail will be used,
+ %% but we must always fetch both. We will call
+ %% clear_dead/2 to allow reuse of the register
+ %% in case only of them is used.
+
+ Reg0 = put_reg(Tl, put_reg(Hd, Bef#sr.reg)),
+ Int0 = Bef#sr{reg=Reg0},
+ Rsrc = fetch_var(Src, Int0),
+ Rhd = fetch_reg(Hd, Reg0),
+ Rtl = fetch_reg(Tl, Reg0),
+ Int1 = clear_dead(Int0, I, Vdb),
+ {[{get_list,Rsrc,Rhd,Rtl}], Int1}
+ end,
+ {Es,Aft,St}.
+
+
+guard_clause_cg(#l{ke={guard_clause,G,B},vdb=Vdb}, Fail, Bef, St0) ->
+ {Gis,Int,St1} = guard_cg(G, Fail, Vdb, Bef, St0),
+ {Bis,Aft,St2} = match_cg(B, Fail, Int, St1),
+ {Gis ++ Bis,Aft,St2}.
+
+%% guard_cg(Guard, Fail, Vdb, StackReg, State) ->
+%% {[Ainstr],StackReg,State}.
+%% A guard is a boolean expression of tests. Tests return true or
+%% false. A fault in a test causes the test to return false. Tests
+%% never return the boolean, instead we generate jump code to go to
+%% the correct exit point. Primops and tests all go to the next
+%% instruction on success or jump to a failure label.
+
+guard_cg(#l{ke={protected,Ts,Rs},i=I,vdb=Pdb}, Fail, _Vdb, Bef, St) ->
+ protected_cg(Ts, Rs, Fail, I, Pdb, Bef, St);
+guard_cg(#l{ke={block,Ts},i=I,vdb=Bdb}, Fail, _Vdb, Bef, St) ->
+ guard_cg_list(Ts, Fail, I, Bdb, Bef, St);
+guard_cg(#l{ke={test,Test,As},i=I,vdb=_Tdb}, Fail, Vdb, Bef, St) ->
+ test_cg(Test, As, Fail, I, Vdb, Bef, St);
+guard_cg(G, _Fail, Vdb, Bef, St) ->
+ %%ok = io:fwrite("cg ~w: ~p~n", [?LINE,{G,Fail,Vdb,Bef}]),
+ {Gis,Aft,St1} = cg(G, Vdb, Bef, St),
+ %%ok = io:fwrite("cg ~w: ~p~n", [?LINE,{Aft}]),
+ {Gis,Aft,St1}.
+
+%% protected_cg([Kexpr], [Ret], Fail, I, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
+%% Do a protected. Protecteds without return values are just done
+%% for effect, the return value is not checked, success passes on to
+%% the next instruction and failure jumps to Fail. If there are
+%% return values then these must be set to 'false' on failure,
+%% control always passes to the next instruction.
+
+protected_cg(Ts, [], Fail, I, Vdb, Bef, St0) ->
+ %% Protect these calls, revert when done.
+ {Tis,Aft,St1} = guard_cg_list(Ts, Fail, I, Vdb, Bef,
+ St0#cg{btype=fail,bfail=Fail}),
+ {Tis,Aft,St1#cg{btype=St0#cg.btype,bfail=St0#cg.bfail}};
+protected_cg(Ts, Rs, _Fail, I, Vdb, Bef, St0) ->
+ {Pfail,St1} = new_label(St0),
+ {Psucc,St2} = new_label(St1),
+ {Tis,Aft,St3} = guard_cg_list(Ts, Pfail, I, Vdb, Bef,
+ St2#cg{btype=fail,bfail=Pfail}),
+ %%ok = io:fwrite("cg ~w: ~p~n", [?LINE,{Rs,I,Vdb,Aft}]),
+ %% Set return values to false.
+ Mis = map(fun ({var,V}) -> {move,{atom,false},fetch_var(V, Aft)} end, Rs),
+ Live = {'%live',max_reg(Aft#sr.reg)},
+ {Tis ++ [Live,{jump,{f,Psucc}},
+ {label,Pfail}] ++ Mis ++ [Live,{label,Psucc}],
+ Aft,St3#cg{btype=St0#cg.btype,bfail=St0#cg.bfail}}.
+
+%% test_cg(TestName, Args, Fail, I, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
+%% Generate test instruction. Use explicit fail label here.
+
+test_cg(Test, As, Fail, I, Vdb, Bef, St) ->
+ case test_type(Test, length(As)) of
+ {cond_op,Op} ->
+ Ars = cg_reg_args(As, Bef),
+ Int = clear_dead(Bef, I, Vdb),
+ {[{test,Op,{f,Fail},Ars}],
+ clear_dead(Int, I, Vdb),
+ St};
+ {rev_cond_op,Op} ->
+ [S1,S2] = cg_reg_args(As, Bef),
+ Int = clear_dead(Bef, I, Vdb),
+ {[{test,Op,{f,Fail},[S2,S1]}],
+ clear_dead(Int, I, Vdb),
+ St}
+ end.
+
+test_type(is_atom, 1) -> {cond_op,is_atom};
+test_type(is_boolean, 1) -> {cond_op,is_boolean};
+test_type(is_binary, 1) -> {cond_op,is_binary};
+test_type(is_constant, 1) -> {cond_op,is_constant};
+test_type(is_float, 1) -> {cond_op,is_float};
+test_type(is_function, 1) -> {cond_op,is_function};
+test_type(is_integer, 1) -> {cond_op,is_integer};
+test_type(is_list, 1) -> {cond_op,is_list};
+test_type(is_number, 1) -> {cond_op,is_number};
+test_type(is_pid, 1) -> {cond_op,is_pid};
+test_type(is_port, 1) -> {cond_op,is_port};
+test_type(is_reference, 1) -> {cond_op,is_reference};
+test_type(is_tuple, 1) -> {cond_op,is_tuple};
+test_type('=<', 2) -> {rev_cond_op,is_ge};
+test_type('>', 2) -> {rev_cond_op,is_lt};
+test_type('<', 2) -> {cond_op,is_lt};
+test_type('>=', 2) -> {cond_op,is_ge};
+test_type('==', 2) -> {cond_op,is_eq};
+test_type('/=', 2) -> {cond_op,is_ne};
+test_type('=:=', 2) -> {cond_op,is_eq_exact};
+test_type('=/=', 2) -> {cond_op,is_ne_exact};
+test_type(internal_is_record, 3) -> {cond_op,internal_is_record}.
+
+%% guard_cg_list([Kexpr], Fail, I, Vdb, StackReg, St) ->
+%% {[Ainstr],StackReg,St}.
+
+guard_cg_list(Kes, Fail, I, Vdb, Bef, St0) ->
+ {Keis,{Aft,St1}} =
+ flatmapfoldl(fun (Ke, {Inta,Sta}) ->
+ {Keis,Intb,Stb} =
+ guard_cg(Ke, Fail, Vdb, Inta, Sta),
+ {comment(Inta) ++ Keis,{Intb,Stb}}
+ end, {Bef,St0}, need_heap(Kes, I)),
+ {Keis,Aft,St1}.
+
+%% match_fmf(Fun, LastFail, State, [Clause]) -> {Is,Aft,State}.
+%% This is a special flatmapfoldl for match code gen where we
+%% generate a "failure" label for each clause. The last clause uses
+%% an externally generated failure label, LastFail. N.B. We do not
+%% know or care how the failure labels are used.
+
+match_fmf(F, LastFail, St, [H]) ->
+ F(H, LastFail, St);
+match_fmf(F, LastFail, St0, [H|T]) ->
+ {Fail,St1} = new_label(St0),
+ {R,Aft1,St2} = F(H, Fail, St1),
+ {Rs,Aft2,St3} = match_fmf(F, LastFail, St2, T),
+ {R ++ [{label,Fail}] ++ Rs,sr_merge(Aft1, Aft2),St3};
+match_fmf(_, _, St, []) -> {[],void,St}.
+
+%% call_cg(Func, [Arg], [Ret], Le, Vdb, StackReg, State) ->
+%% {[Ainstr],StackReg,State}.
+%% enter_cg(Func, [Arg], Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
+%% Call and enter first put the arguments into registers and save any
+%% other registers, then clean up and compress the stack and set the
+%% frame size. Finally the actual call is made. Call then needs the
+%% return values filled in.
+
+call_cg({var,V}, As, Rs, Le, Vdb, Bef, St0) ->
+ {Sis,Int} = cg_setup_call(As++[{var,V}], Bef, Le#l.i, Vdb),
+ %% Put return values in registers.
+ Reg = load_vars(Rs, clear_regs(Int#sr.reg)),
+ %% Build complete code and final stack/register state.
+ Arity = length(As),
+ {Frees,Aft} = free_dead(clear_dead(Int#sr{reg=Reg}, Le#l.i, Vdb)),
+ {comment({call_fun,{var,V},As}) ++ Sis ++ Frees ++ [{call_fun,Arity}],
+ Aft,need_stack_frame(St0)};
+call_cg({remote,Mod,Name}, As, Rs, Le, Vdb, Bef, St0)
+ when element(1, Mod) == var;
+ element(1, Name) == var ->
+ {Sis,Int} = cg_setup_call(As++[Mod,Name], Bef, Le#l.i, Vdb),
+ %% Put return values in registers.
+ Reg = load_vars(Rs, clear_regs(Int#sr.reg)),
+ %% Build complete code and final stack/register state.
+ Arity = length(As),
+ Call = {apply,Arity},
+ St = need_stack_frame(St0),
+ %%{Call,St1} = build_call(Func, Arity, St0),
+ {Frees,Aft} = free_dead(clear_dead(Int#sr{reg=Reg}, Le#l.i, Vdb)),
+ {Sis ++ Frees ++ [Call],Aft,St};
+call_cg(Func, As, Rs, Le, Vdb, Bef, St0) ->
+ {Sis,Int} = cg_setup_call(As, Bef, Le#l.i, Vdb),
+ %% Put return values in registers.
+ Reg = load_vars(Rs, clear_regs(Int#sr.reg)),
+ %% Build complete code and final stack/register state.
+ Arity = length(As),
+ {Call,St1} = build_call(Func, Arity, St0),
+ {Frees,Aft} = free_dead(clear_dead(Int#sr{reg=Reg}, Le#l.i, Vdb)),
+ {comment({call,Func,As}) ++ Sis ++ Frees ++ Call,Aft,St1}.
+
+build_call({remote,{atom,erlang},{atom,'!'}}, 2, St0) ->
+ {[send],need_stack_frame(St0)};
+build_call({remote,{atom,Mod},{atom,Name}}, Arity, St0) ->
+ {[{call_ext,Arity,{extfunc,Mod,Name,Arity}}],need_stack_frame(St0)};
+build_call(Name, Arity, St0) when atom(Name) ->
+ {Lbl,St1} = local_func_label(Name, Arity, need_stack_frame(St0)),
+ {[{call,Arity,{f,Lbl}}],St1}.
+
+free_dead(#sr{stk=Stk0}=Aft) ->
+ {Instr,Stk} = free_dead(Stk0, 0, [], []),
+ {Instr,Aft#sr{stk=Stk}}.
+
+free_dead([dead|Stk], Y, Instr, StkAcc) ->
+ %% Note: kill/1 is equivalent to init/1 (translated by beam_asm).
+ %% We use kill/1 to help further optimisation passes.
+ free_dead(Stk, Y+1, [{kill,{yy,Y}}|Instr], [free|StkAcc]);
+free_dead([Any|Stk], Y, Instr, StkAcc) ->
+ free_dead(Stk, Y+1, Instr, [Any|StkAcc]);
+free_dead([], _, Instr, StkAcc) -> {Instr,reverse(StkAcc)}.
+
+enter_cg({var,V}, As, Le, Vdb, Bef, St0) ->
+ {Sis,Int} = cg_setup_call(As++[{var,V}], Bef, Le#l.i, Vdb),
+ %% Build complete code and final stack/register state.
+ Arity = length(As),
+ {comment({call_fun,{var,V},As}) ++ Sis ++ [{call_fun,Arity},return],
+ clear_dead(Int#sr{reg=clear_regs(Int#sr.reg)}, Le#l.i, Vdb),
+ need_stack_frame(St0)};
+enter_cg({remote,Mod,Name}=Func, As, Le, Vdb, Bef, St0)
+ when element(1, Mod) == var;
+ element(1, Name) == var ->
+ {Sis,Int} = cg_setup_call(As++[Mod,Name], Bef, Le#l.i, Vdb),
+ %% Build complete code and final stack/register state.
+ Arity = length(As),
+ Call = {apply_only,Arity},
+ St = need_stack_frame(St0),
+ {comment({enter,Func,As}) ++ Sis ++ [Call],
+ clear_dead(Int#sr{reg=clear_regs(Int#sr.reg)}, Le#l.i, Vdb),
+ St};
+enter_cg(Func, As, Le, Vdb, Bef, St0) ->
+ {Sis,Int} = cg_setup_call(As, Bef, Le#l.i, Vdb),
+ %% Build complete code and final stack/register state.
+ Arity = length(As),
+ {Call,St1} = build_enter(Func, Arity, St0),
+ {comment({enter,Func,As}) ++ Sis ++ Call,
+ clear_dead(Int#sr{reg=clear_regs(Int#sr.reg)}, Le#l.i, Vdb),
+ St1}.
+
+build_enter({remote,{atom,erlang},{atom,'!'}}, 2, St0) ->
+ {[send,return],need_stack_frame(St0)};
+build_enter({remote,{atom,Mod},{atom,Name}}, Arity, St0) ->
+ St1 = case trap_bif(Mod, Name, Arity) of
+ true -> need_stack_frame(St0);
+ false -> St0
+ end,
+ {[{call_ext_only,Arity,{extfunc,Mod,Name,Arity}}],St1};
+build_enter(Name, Arity, St0) when is_atom(Name) ->
+ {Lbl,St1} = local_func_label(Name, Arity, St0),
+ {[{call_only,Arity,{f,Lbl}}],St1}.
+
+%% local_func_label(Name, Arity, State) -> {Label,State'}
+%% Get the function entry label for a local function.
+
+local_func_label(Name, Arity, St0) ->
+ Key = {Name,Arity},
+ case keysearch(Key, 1, St0#cg.functable) of
+ {value,{Key,Label}} ->
+ {Label,St0};
+ false ->
+ {Label,St1} = new_label(St0),
+ {Label,St1#cg{functable=[{Key,Label}|St1#cg.functable]}}
+ end.
+
+%% need_stack_frame(State) -> State'
+%% Make a note in the state that this function will need a stack frame.
+
+need_stack_frame(#cg{need_frame=true}=St) -> St;
+need_stack_frame(St) -> St#cg{need_frame=true}.
+
+%% trap_bif(Mod, Name, Arity) -> true|false
+%% Trap bifs that need a stack frame.
+
+trap_bif(erlang, '!', 2) -> true;
+trap_bif(erlang, link, 1) -> true;
+trap_bif(erlang, unlink, 1) -> true;
+trap_bif(erlang, monitor_node, 2) -> true;
+trap_bif(erlang, group_leader, 2) -> true;
+trap_bif(erlang, exit, 2) -> true;
+trap_bif(_, _, _) -> false.
+
+%% bif_cg(Bif, [Arg], [Ret], Le, Vdb, StackReg, State) ->
+%% {[Ainstr],StackReg,State}.
+
+bif_cg(dsetelement, [Index0,Tuple0,New0], _Rs, Le, Vdb, Bef, St0) ->
+ [New,Tuple,{integer,Index1}] = cg_reg_args([New0,Tuple0,Index0], Bef),
+ Index = Index1-1,
+ {[{set_tuple_element,New,Tuple,Index}],
+ clear_dead(Bef, Le#l.i, Vdb), St0};
+bif_cg({make_fun,Func,Arity,Index,Uniq}, As, Rs, Le, Vdb, Bef, St0) ->
+ %% This behaves more like a function call.
+ {Sis,Int} = cg_setup_call(As, Bef, Le#l.i, Vdb),
+ Reg = load_vars(Rs, clear_regs(Int#sr.reg)),
+ {FuncLbl,St1} = local_func_label(Func, Arity, St0),
+ MakeFun = case St0#cg.new_funs of
+ true -> {make_fun2,{f,FuncLbl},Index,Uniq,length(As)};
+ false -> {make_fun,{f,FuncLbl},Uniq,length(As)}
+ end,
+ {comment({make_fun,{Func,Arity,Uniq},As}) ++ Sis ++
+ [MakeFun],
+ clear_dead(Int#sr{reg=Reg}, Le#l.i, Vdb),
+ St1};
+bif_cg(Bif, As, [{var,V}], Le, Vdb, Bef, St0) ->
+ Ars = cg_reg_args(As, Bef),
+
+ %% If we are inside a catch, we must save everything that will
+ %% be alive after the catch (because the BIF might fail and there
+ %% will be a jump to the code after the catch).
+ %% Currently, we are somewhat pessimistic in
+ %% that we save any variable that will be live after this BIF call.
+
+ {Sis,Int0} =
+ case St0#cg.in_catch of
+ true -> adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb);
+ false -> {[],Bef}
+ end,
+
+ Int1 = clear_dead(Int0, Le#l.i, Vdb),
+ Reg = put_reg(V, Int1#sr.reg),
+ Int = Int1#sr{reg=Reg},
+ Dst = fetch_reg(V, Reg),
+ {Sis ++ [{bif,Bif,bif_fail(St0#cg.btype, St0#cg.bfail, length(Ars)),Ars,Dst}],
+ clear_dead(Int, Le#l.i, Vdb), St0}.
+
+bif_fail(_, _, 0) -> nofail;
+bif_fail(exit, _, _) -> {f,0};
+bif_fail(fail, Fail, _) -> {f,Fail}.
+
+%% recv_loop_cg(TimeOut, ReceiveVar, ReceiveMatch, TimeOutExprs,
+%% [Ret], Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
+
+recv_loop_cg(Te, Rvar, Rm, Tes, Rs, Le, Vdb, Bef, St0) ->
+ {Sis,Int0} = adjust_stack(Bef, Le#l.i, Le#l.i, Vdb),
+ Int1 = Int0#sr{reg=clear_regs(Int0#sr.reg)},
+ %% Get labels.
+ {Rl,St1} = new_label(St0),
+ {Tl,St2} = new_label(St1),
+ {Bl,St3} = new_label(St2),
+ St4 = St3#cg{break=Bl,recv=Rl}, %Set correct receive labels
+ {Ris,Raft,St5} = cg_recv_mesg(Rvar, Rm, Tl, Int1, St4),
+ {Wis,Taft,St6} = cg_recv_wait(Te, Tes, Le#l.i, Int1, St5),
+ Int2 = sr_merge(Raft, Taft), %Merge stack/registers
+ Reg = load_vars(Rs, Int2#sr.reg),
+ {Sis ++ Ris ++ [{label,Tl}] ++ Wis ++ [{label,Bl}],
+ clear_dead(Int2#sr{reg=Reg}, Le#l.i, Vdb),
+ St6#cg{break=St0#cg.break,recv=St0#cg.recv}}.
+
+%% cg_recv_mesg( ) -> {[Ainstr],Aft,St}.
+
+cg_recv_mesg({var,R}, Rm, Tl, Bef, St0) ->
+ Int0 = Bef#sr{reg=put_reg(R, Bef#sr.reg)},
+ Ret = fetch_reg(R, Int0#sr.reg),
+ %% Int1 = clear_dead(Int0, I, Rm#l.vdb),
+ Int1 = Int0,
+ {Mis,Int2,St1} = match_cg(Rm, none, Int1, St0),
+ {[{'%live',0},{label,St1#cg.recv},{loop_rec,{f,Tl},Ret}|Mis],Int2,St1}.
+
+%% cg_recv_wait(Te, Tes, I, Vdb, Int2, St3) -> {[Ainstr],Aft,St}.
+
+cg_recv_wait({atom,infinity}, Tes, I, Bef, St0) ->
+ %% We know that the 'after' body will never be executed.
+ %% But to keep the stack and register information up to date,
+ %% we will generate the code for the 'after' body, and then discard it.
+ Int1 = clear_dead(Bef, I, Tes#l.vdb),
+ {_,Int2,St1} = cg_block(Tes#l.ke, Tes#l.i, Tes#l.vdb,
+ Int1#sr{reg=clear_regs(Int1#sr.reg)}, St0),
+ {[{wait,{f,St1#cg.recv}}],Int2,St1};
+cg_recv_wait({integer,0}, Tes, _I, Bef, St0) ->
+ {Tis,Int,St1} = cg_block(Tes#l.ke, Tes#l.i, Tes#l.vdb, Bef, St0),
+ {[timeout|Tis],Int,St1};
+cg_recv_wait(Te, Tes, I, Bef, St0) ->
+ Reg = cg_reg_arg(Te, Bef),
+ %% Must have empty registers here! Bug if anything in registers.
+ Int0 = clear_dead(Bef, I, Tes#l.vdb),
+ {Tis,Int,St1} = cg_block(Tes#l.ke, Tes#l.i, Tes#l.vdb,
+ Int0#sr{reg=clear_regs(Int0#sr.reg)}, St0),
+ {[{wait_timeout,{f,St1#cg.recv},Reg},timeout] ++ Tis,Int,St1}.
+
+%% recv_next_cg(Le, Vdb, StackReg, St) -> {[Ainstr],StackReg,St}.
+%% Use adjust stack to clear stack, but only need it for Aft.
+
+recv_next_cg(Le, Vdb, Bef, St) ->
+ {Sis,Aft} = adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb),
+ {[{loop_rec_end,{f,St#cg.recv}}] ++ Sis,Aft,St}. %Joke
+
+%% try_cg(TryBlock, [BodyVar], TryBody, [ExcpVar], TryHandler, [Ret],
+%% Le, Vdb, StackReg, St) -> {[Ainstr],StackReg,St}.
+
+try_cg(Ta, Vs, Tb, Evs, Th, Rs, Le, Vdb, Bef, St0) ->
+ {B,St1} = new_label(St0), %Body label
+ {H,St2} = new_label(St1), %Handler label
+ {E,St3} = new_label(St2), %End label
+ TryTag = Ta#l.i,
+ Int1 = Bef#sr{stk=put_catch(TryTag, Bef#sr.stk)},
+ TryReg = fetch_stack({catch_tag,TryTag}, Int1#sr.stk),
+ {Ais,Int2,St4} = cg(Ta, Vdb, Int1, St3#cg{break=B,in_catch=true}),
+ Int3 = Int2#sr{stk=drop_catch(TryTag, Int2#sr.stk)},
+ St5 = St4#cg{break=E,in_catch=St3#cg.in_catch},
+ {Bis,Baft,St6} = cg(Tb, Vdb, Int3#sr{reg=load_vars(Vs, Int3#sr.reg)}, St5),
+ {His,Haft,St7} = cg(Th, Vdb, Int3#sr{reg=load_vars(Evs, Int3#sr.reg)}, St6),
+ Int4 = sr_merge(Baft, Haft), %Merge stack/registers
+ Aft = Int4#sr{reg=load_vars(Rs, Int4#sr.reg)},
+ {[{'try',TryReg,{f,H}}] ++ Ais ++
+ [{label,B},{try_end,TryReg}] ++ Bis ++
+ [{label,H},{try_case,TryReg}] ++ His ++
+ [{label,E}],
+ clear_dead(Aft, Le#l.i, Vdb),
+ St7#cg{break=St0#cg.break}}.
+
+%% catch_cg(CatchBlock, Ret, Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
+
+catch_cg(C, {var,R}, Le, Vdb, Bef, St0) ->
+ {B,St1} = new_label(St0),
+ CatchTag = Le#l.i,
+ Int1 = Bef#sr{stk=put_catch(CatchTag, Bef#sr.stk)},
+ CatchReg = fetch_stack({catch_tag,CatchTag}, Int1#sr.stk),
+ {Cis,Int2,St2} = cg_block(C, Le#l.i, Le#l.vdb, Int1,
+ St1#cg{break=B,in_catch=true}),
+ Aft = Int2#sr{reg=load_reg(R, 0, Int2#sr.reg),
+ stk=drop_catch(CatchTag, Int2#sr.stk)},
+ {[{'catch',CatchReg,{f,B}}] ++ Cis ++
+ [{label,B},{catch_end,CatchReg}],
+ clear_dead(Aft, Le#l.i, Vdb),
+ St2#cg{break=St1#cg.break,in_catch=St1#cg.in_catch}}.
+
+%% set_cg([Var], Constr, Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
+%% We have to be careful how a 'set' works. First the structure is
+%% built, then it is filled and finally things can be cleared. The
+%% annotation must reflect this and make sure that the return
+%% variable is allocated first.
+%%
+%% put_list for constructing a cons is an atomic instruction
+%% which can safely resuse one of the source registers as target.
+%% Also binaries can reuse a source register as target.
+
+set_cg([{var,R}], {cons,Es}, Le, Vdb, Bef, St) ->
+ [S1,S2] = map(fun ({var,V}) -> fetch_var(V, Bef);
+ (Other) -> Other
+ end, Es),
+ Int0 = clear_dead(Bef, Le#l.i, Vdb),
+ Int1 = Int0#sr{reg=put_reg(R, Int0#sr.reg)},
+ Ret = fetch_reg(R, Int1#sr.reg),
+ {[{put_list,S1,S2,Ret}], Int1, St};
+set_cg([{var,R}], {old_binary,Segs}, Le, Vdb, Bef, St) ->
+ Fail = bif_fail(St#cg.btype, St#cg.bfail, 42),
+ PutCode = cg_bin_put(Segs, Fail, Bef),
+ Code = cg_binary_old(PutCode),
+ Int0 = clear_dead(Bef, Le#l.i, Vdb),
+ Aft = Int0#sr{reg=put_reg(R, Int0#sr.reg)},
+ Ret = fetch_reg(R, Aft#sr.reg),
+ {Code ++ [{bs_final,Fail,Ret}],Aft,St};
+set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef, #cg{in_catch=InCatch}=St) ->
+ Int0 = Bef#sr{reg=put_reg(R, Bef#sr.reg)},
+ Target = fetch_reg(R, Int0#sr.reg),
+ Fail = bif_fail(St#cg.btype, St#cg.bfail, 42),
+ Temp = find_scratch_reg(Int0#sr.reg),
+ PutCode = cg_bin_put(Segs, Fail, Bef),
+ {Sis,Int1} =
+ case InCatch of
+ true -> adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb);
+ false -> {[],Int0}
+ end,
+ Aft = clear_dead(Int1, Le#l.i, Vdb),
+ Code = cg_binary(PutCode, Target, Temp, Fail, Aft),
+ {Sis++Code,Aft,St};
+set_cg([{var,R}], Con, Le, Vdb, Bef, St) ->
+ %% Find a place for the return register first.
+ Int = Bef#sr{reg=put_reg(R, Bef#sr.reg)},
+ Ret = fetch_reg(R, Int#sr.reg),
+ Ais = case Con of
+ {tuple,Es} ->
+ [{put_tuple,length(Es),Ret}] ++ cg_build_args(Es, Bef);
+ {var,V} -> % Normally removed by kernel optimizer.
+ [{move,fetch_var(V, Int),Ret}];
+ {string,Str} ->
+ [{put_string,length(Str),{string,Str},Ret}];
+ Other ->
+ [{move,Other,Ret}]
+ end,
+ {Ais,clear_dead(Int, Le#l.i, Vdb),St};
+set_cg([], {binary,Segs}, Le, Vdb, Bef, St) ->
+ Fail = bif_fail(St#cg.btype, St#cg.bfail, 42),
+ Target = find_scratch_reg(Bef#sr.reg),
+ Temp = find_scratch_reg(put_reg(Target, Bef#sr.reg)),
+ PutCode = cg_bin_put(Segs, Fail, Bef),
+ Code = cg_binary(PutCode, Target, Temp, Fail, Bef),
+ Aft = clear_dead(Bef, Le#l.i, Vdb),
+ {Code,Aft,St};
+set_cg([], {old_binary,Segs}, Le, Vdb, Bef, St) ->
+ Fail = bif_fail(St#cg.btype, St#cg.bfail, 42),
+ PutCode = cg_bin_put(Segs, Fail, Bef),
+ Ais0 = cg_binary_old(PutCode),
+ Ret = find_scratch_reg(Bef#sr.reg),
+ Ais = Ais0 ++ [{bs_final,Fail,Ret}],
+ {Ais,clear_dead(Bef, Le#l.i, Vdb),St};
+set_cg([], _, Le, Vdb, Bef, St) ->
+ %% This should have been stripped by compiler, just cleanup.
+ {[],clear_dead(Bef, Le#l.i, Vdb), St}.
+
+
+%%%
+%%% Code generation for constructing binaries.
+%%%
+
+cg_binary(PutCode, Target, Temp, Fail, Bef) ->
+ SzCode = cg_binary_size(PutCode, Target, Temp, Fail),
+ MaxRegs = max_reg(Bef#sr.reg),
+ Code = SzCode ++ [{bs_init2,Fail,Target,MaxRegs,{field_flags,[]},Target}|PutCode],
+ cg_bin_opt(Code).
+
+cg_binary_size(PutCode, Target, Temp, Fail) ->
+ Szs = cg_binary_size_1(PutCode, 0, []),
+ cg_binary_size_expr(Szs, Target, Temp, Fail).
+
+cg_binary_size_1([{_Put,_Fail,S,U,_Flags,Src}|T], Bits, Acc) ->
+ cg_binary_size_2(S, U, Src, T, Bits, Acc);
+cg_binary_size_1([], Bits, Acc) ->
+ Bytes = Bits div 8,
+ RemBits = Bits rem 8,
+ Res = sort([{1,{integer,RemBits}},{8,{integer,Bytes}}|Acc]),
+ cg_binary_size_3(Res).
+
+cg_binary_size_2({integer,N}, U, _, Next, Bits, Acc) ->
+ cg_binary_size_1(Next, Bits+N*U, Acc);
+cg_binary_size_2({atom,all}, 8, E, Next, Bits, Acc) ->
+ cg_binary_size_1(Next, Bits, [{8,{size,E}}|Acc]);
+cg_binary_size_2(Reg, 1, _, Next, Bits, Acc) ->
+ cg_binary_size_1(Next, Bits, [{1,Reg}|Acc]);
+cg_binary_size_2(Reg, 8, _, Next, Bits, Acc) ->
+ cg_binary_size_1(Next, Bits, [{8,Reg}|Acc]);
+cg_binary_size_2(Reg, U, _, Next, Bits, Acc) ->
+ cg_binary_size_1(Next, Bits, [{1,{'*',Reg,U}}|Acc]).
+
+cg_binary_size_3([{_,{integer,0}}|T]) ->
+ cg_binary_size_3(T);
+cg_binary_size_3([{U,S1},{U,S2}|T]) ->
+ {L0,Rest} = cg_binary_size_4(T, U, []),
+ L = [S1,S2|L0],
+ [{U,L}|cg_binary_size_3(Rest)];
+cg_binary_size_3([{U,S}|T]) ->
+ [{U,[S]}|cg_binary_size_3(T)];
+cg_binary_size_3([]) -> [].
+
+cg_binary_size_4([{U,S}|T], U, Acc) ->
+ cg_binary_size_4(T, U, [S|Acc]);
+cg_binary_size_4(T, _, Acc) ->
+ {Acc,T}.
+
+%% cg_binary_size_expr/4
+%% Generate code for calculating the resulting size of a binary.
+cg_binary_size_expr(Sizes, Target, Temp, Fail) ->
+ cg_binary_size_expr_1(Sizes, Target, Temp, Fail,
+ [{move,{integer,0},Target}]).
+
+cg_binary_size_expr_1([{1,E0}|T], Target, Temp, Fail, Acc) ->
+ E1 = cg_gen_binsize(E0, Target, Temp, Fail, Acc),
+ E = [{bs_bits_to_bytes,Fail,Target,Target}|E1],
+ cg_binary_size_expr_1(T, Target, Temp, Fail, E);
+cg_binary_size_expr_1([{8,E0}], Target, Temp, Fail, Acc) ->
+ E = cg_gen_binsize(E0, Target, Temp, Fail, Acc),
+ reverse(E);
+cg_binary_size_expr_1([], _, _, _, Acc) -> reverse(Acc).
+
+cg_gen_binsize([{'*',A,B}|T], Target, Temp, Fail, Acc) ->
+ cg_gen_binsize(T, Target, Temp, Fail,
+ [{bs_add,Fail,[Target,A,B],Target}|Acc]);
+cg_gen_binsize([{size,B}|T], Target, Temp, Fail, Acc) ->
+ cg_gen_binsize([Temp|T], Target, Temp, Fail,
+ [{bif,size,Fail,[B],Temp}|Acc]);
+cg_gen_binsize([E0|T], Target, Temp, Fail, Acc) ->
+ cg_gen_binsize(T, Target, Temp, Fail,
+ [{bs_add,Fail,[Target,E0,1],Target}|Acc]);
+cg_gen_binsize([], _, _, _, Acc) -> Acc.
+
+%% cg_bin_opt(Code0) -> Code
+%% Optimize the size calculations for binary construction.
+
+cg_bin_opt([{move,{integer,0},D},{bs_add,_,[D,{integer,_}=S,1],Dst}|Is]) ->
+ cg_bin_opt([{move,S,Dst}|Is]);
+cg_bin_opt([{move,{integer,0},D},{bs_add,Fail,[D,S,U],Dst}|Is]) ->
+ cg_bin_opt([{bs_add,Fail,[{integer,0},S,U],Dst}|Is]);
+cg_bin_opt([{move,{integer,Bytes},D},{bs_init2,Fail,D,Regs0,Flags,D}|Is]) ->
+ Regs = cg_bo_newregs(Regs0, D),
+ cg_bin_opt([{bs_init2,Fail,Bytes,Regs,Flags,D}|Is]);
+cg_bin_opt([{move,Src,D},{bs_init2,Fail,D,Regs0,Flags,D}|Is]) ->
+ Regs = cg_bo_newregs(Regs0, D),
+ cg_bin_opt([{bs_init2,Fail,Src,Regs,Flags,D}|Is]);
+cg_bin_opt([{move,Src,Dst},{bs_bits_to_bytes,Fail,Dst,Dst}|Is]) ->
+ cg_bin_opt([{bs_bits_to_bytes,Fail,Src,Dst}|Is]);
+cg_bin_opt([{move,Src1,Dst},{bs_add,Fail,[Dst,Src2,U],Dst}|Is]) ->
+ cg_bin_opt([{bs_add,Fail,[Src1,Src2,U],Dst}|Is]);
+cg_bin_opt([{bs_bits_to_bytes,Fail,{integer,N},_}|Is0]) when N rem 8 =/= 0 ->
+ case Fail of
+ {f,0} ->
+ Is = [{move,{atom,badarg},{x,0}},
+ {call_ext_only,1,{extfunc,erlang,error,1}}|Is0],
+ cg_bin_opt(Is);
+ _ ->
+ cg_bin_opt([{jump,Fail}|Is0])
+ end;
+cg_bin_opt([I|Is]) ->
+ [I|cg_bin_opt(Is)];
+cg_bin_opt([]) -> [].
+
+cg_bo_newregs(R, {x,X}) when R-1 =:= X -> R-1;
+cg_bo_newregs(R, _) -> R.
+
+%% Common for new and old binary code generation.
+
+cg_bin_put({bin_seg,S0,U,T,Fs,[E0,Next]}, Fail, Bef) ->
+ S1 = case S0 of
+ {var,Sv} -> fetch_var(Sv, Bef);
+ _ -> S0
+ end,
+ E1 = case E0 of
+ {var,V} -> fetch_var(V, Bef);
+ Other -> Other
+ end,
+ Op = case T of
+ integer -> bs_put_integer;
+ binary -> bs_put_binary;
+ float -> bs_put_float
+ end,
+ [{Op,Fail,S1,U,{field_flags,Fs},E1}|cg_bin_put(Next, Fail, Bef)];
+cg_bin_put(bin_end, _, _) -> [].
+
+%% Old style.
+
+cg_binary_old(PutCode) ->
+ [cg_bs_init(PutCode)] ++ need_bin_buf(PutCode).
+
+cg_bs_init(Code) ->
+ {Size,Fs} = foldl(fun ({_,_,{integer,N},U,_,_}, {S,Fs}) ->
+ {S + N*U,Fs};
+ (_, {S,_}) ->
+ {S,[]}
+ end, {0,[exact]}, Code),
+ {bs_init,(Size+7) div 8,{field_flags,Fs}}.
+
+need_bin_buf(Code0) ->
+ {Code1,F,H} = foldr(fun ({_,_,{integer,N},U,_,_}=Bs, {Code,F,H}) ->
+ {[Bs|Code],F,H + N*U};
+ ({_,_,_,_,_,_}=Bs, {Code,F,H}) ->
+ {[Bs|need_bin_buf_need(H, F, Code)],true,0}
+ end, {[],false,0}, Code0),
+ need_bin_buf_need(H, F, Code1).
+
+need_bin_buf_need(0, false, Rest) -> Rest;
+need_bin_buf_need(H, _, Rest) -> [{bs_need_buf,H}|Rest].
+
+cg_build_args(As, Bef) ->
+ map(fun ({var,V}) -> {put,fetch_var(V, Bef)};
+ (Other) -> {put,Other}
+ end, As).
+
+%% return_cg([Val], Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
+%% break_cg([Val], Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
+%% These are very simple, just put return/break values in registers
+%% from 0, then return/break. Use the call setup to clean up stack,
+%% but must clear registers to ensure sr_merge works correctly.
+
+return_cg(Rs, Le, Vdb, Bef, St) ->
+ {Ms,Int} = cg_setup_call(Rs, Bef, Le#l.i, Vdb),
+ {comment({return,Rs}) ++ Ms ++ [return],
+ Int#sr{reg=clear_regs(Int#sr.reg)},St}.
+
+break_cg(Bs, Le, Vdb, Bef, St) ->
+ {Ms,Int} = cg_setup_call(Bs, Bef, Le#l.i, Vdb),
+ {comment({break,Bs}) ++ Ms ++ [{jump,{f,St#cg.break}}],
+ Int#sr{reg=clear_regs(Int#sr.reg)},St}.
+
+%% cg_reg_arg(Arg0, Info) -> Arg
+%% cg_reg_args([Arg0], Info) -> [Arg]
+%% Convert argument[s] into registers. Literal values are returned unchanged.
+
+cg_reg_args(As, Bef) -> [cg_reg_arg(A, Bef) || A <- As].
+
+cg_reg_arg({var,V}, Bef) -> fetch_var(V, Bef);
+cg_reg_arg(Literal, _) -> Literal.
+
+%% cg_setup_call([Arg], Bef, Cur, Vdb) -> {[Instr],Aft}.
+%% Do the complete setup for a call/enter.
+
+cg_setup_call(As, Bef, I, Vdb) ->
+ {Ms,Int0} = cg_call_args(As, Bef, I, Vdb),
+ %% Have set up arguments, can now clean up, compress and save to stack.
+ Int1 = Int0#sr{stk=clear_dead_stk(Int0#sr.stk, I, Vdb),res=[]},
+ {Sis,Int2} = adjust_stack(Int1, I, I+1, Vdb),
+ {Ms ++ Sis ++ [{'%live',length(As)}],Int2}.
+
+%% cg_call_args([Arg], SrState) -> {[Instr],SrState}.
+%% Setup the arguments to a call/enter/bif. Put the arguments into
+%% consecutive registers starting at {x,0} moving any data which
+%% needs to be saved. Return a modified SrState structure with the
+%% new register contents. N.B. the resultant register info will
+%% contain non-variable values when there are non-variable values.
+%%
+%% This routine is complicated by unsaved values in x registers.
+%% We'll move away any unsaved values that are in the registers
+%% to be overwritten by the arguments.
+
+cg_call_args(As, Bef, I, Vdb) ->
+ Regs0 = load_arg_regs(Bef#sr.reg, As),
+ Unsaved = unsaved_registers(Regs0, Bef#sr.stk, I, I+1, Vdb),
+ {UnsavedMoves,Regs} = move_unsaved(Unsaved, Bef#sr.reg, Regs0),
+ Moves0 = gen_moves(As, Bef),
+ Moves = order_moves(Moves0, find_scratch_reg(Regs)),
+ {UnsavedMoves ++ Moves,Bef#sr{reg=Regs}}.
+
+%% load_arg_regs([Reg], Arguments) -> [Reg]
+%% Update the register descriptor to include the arguments (from {x,0}
+%% and upwards). Values in argument register are overwritten.
+%% Values in x registers above the arguments are preserved.
+
+load_arg_regs(Regs, As) -> load_arg_regs(Regs, As, 0).
+
+load_arg_regs([_|Rs], [{var,V}|As], I) -> [{I,V}|load_arg_regs(Rs, As, I+1)];
+load_arg_regs([_|Rs], [A|As], I) -> [{I,A}|load_arg_regs(Rs, As, I+1)];
+load_arg_regs([], [{var,V}|As], I) -> [{I,V}|load_arg_regs([], As, I+1)];
+load_arg_regs([], [A|As], I) -> [{I,A}|load_arg_regs([], As, I+1)];
+load_arg_regs(Rs, [], _) -> Rs.
+
+%% Returns the variables must be saved and are currently in the
+%% x registers that are about to be overwritten by the arguments.
+
+unsaved_registers(Regs, Stk, Fb, Lf, Vdb) ->
+ [V || {V,F,L} <- Vdb,
+ F < Fb,
+ L >= Lf,
+ not on_stack(V, Stk),
+ not in_reg(V, Regs)].
+
+in_reg(V, Regs) -> keymember(V, 2, Regs).
+
+%% Move away unsaved variables from the registers that are to be
+%% overwritten by the arguments.
+move_unsaved(Vs, OrigRegs, NewRegs) ->
+ move_unsaved(Vs, OrigRegs, NewRegs, []).
+
+move_unsaved([V|Vs], OrigRegs, NewRegs0, Acc) ->
+ NewRegs = put_reg(V, NewRegs0),
+ Src = fetch_reg(V, OrigRegs),
+ Dst = fetch_reg(V, NewRegs),
+ move_unsaved(Vs, OrigRegs, NewRegs, [{move,Src,Dst}|Acc]);
+move_unsaved([], _, Regs, Acc) -> {Acc,Regs}.
+
+%% gen_moves(As, Sr)
+%% Generate the basic move instruction to move the arguments
+%% to their proper registers. The list will be sorted on
+%% destinations. (I.e. the move to {x,0} will be first --
+%% see the comment to order_moves/2.)
+
+gen_moves(As, Sr) -> gen_moves(As, Sr, 0, []).
+
+gen_moves([{var,V}|As], Sr, I, Acc) ->
+ case fetch_var(V, Sr) of
+ {x,I} -> gen_moves(As, Sr, I+1, Acc);
+ Reg -> gen_moves(As, Sr, I+1, [{move,Reg,{x,I}}|Acc])
+ end;
+gen_moves([A|As], Sr, I, Acc) ->
+ gen_moves(As, Sr, I+1, [{move,A,{x,I}}|Acc]);
+gen_moves([], _, _, Acc) -> lists:keysort(3, Acc).
+
+%% order_moves([Move], ScratchReg) -> [Move]
+%% Orders move instruction so that source registers are not
+%% destroyed before they are used. If there are cycles
+%% (such as {move,{x,0},{x,1}}, {move,{x,1},{x,1}}),
+%% the scratch register is used to break up the cycle.
+%% If possible, the first move of the input list is placed
+%% last in the result list (to make the move to {x,0} occur
+%% just before the call to allow the Beam loader to coalesce
+%% the instructions).
+
+order_moves(Ms, Scr) -> order_moves(Ms, Scr, []).
+
+order_moves([{move,_,_}=M|Ms0], ScrReg, Acc0) ->
+ {Chain,Ms} = collect_chain(Ms0, [M], ScrReg),
+ Acc = reverse(Chain, Acc0),
+ order_moves(Ms, ScrReg, Acc);
+order_moves([], _, Acc) -> Acc.
+
+collect_chain(Ms, Path, ScrReg) ->
+ collect_chain(Ms, Path, [], ScrReg).
+
+collect_chain([{move,Src,Same}=M|Ms0], [{move,Same,_}|_]=Path, Others, ScrReg) ->
+ case keysearch(Src, 3, Path) of
+ {value,_} -> %We have a cycle.
+ {break_up_cycle(M, Path, ScrReg),reverse(Others, Ms0)};
+ false ->
+ collect_chain(reverse(Others, Ms0), [M|Path], [], ScrReg)
+ end;
+collect_chain([M|Ms], Path, Others, ScrReg) ->
+ collect_chain(Ms, Path, [M|Others], ScrReg);
+collect_chain([], Path, Others, _) ->
+ {Path,Others}.
+
+break_up_cycle({move,Src,_}=M, Path, ScrReg) ->
+ [{move,ScrReg,Src},M|break_up_cycle1(Src, Path, ScrReg)].
+
+break_up_cycle1(Dst, [{move,Src,Dst}|Path], ScrReg) ->
+ [{move,Src,ScrReg}|Path];
+break_up_cycle1(Dst, [M|Path], LastMove) ->
+ [M|break_up_cycle1(Dst, Path, LastMove)].
+
+%% clear_dead(Sr, Until, Vdb) -> Aft.
+%% Remove all variables in Sr which have died AT ALL so far.
+
+clear_dead(Sr, Until, Vdb) ->
+ Sr#sr{reg=clear_dead_reg(Sr, Until, Vdb),
+ stk=clear_dead_stk(Sr#sr.stk, Until, Vdb)}.
+
+clear_dead_reg(Sr, Until, Vdb) ->
+ Reg = map(fun ({I,V}) ->
+ case vdb_find(V, Vdb) of
+ {V,_,L} when L > Until -> {I,V};
+ _ -> free %Remove anything else
+ end;
+ ({reserved,I,V}) -> {reserved,I,V};
+ (free) -> free
+ end, Sr#sr.reg),
+ reserve(Sr#sr.res, Reg, Sr#sr.stk).
+
+clear_dead_stk(Stk, Until, Vdb) ->
+ map(fun ({V}) ->
+ case vdb_find(V, Vdb) of
+ {V,_,L} when L > Until -> {V};
+ _ -> dead %Remove anything else
+ end;
+ (free) -> free;
+ (dead) -> dead
+ end, Stk).
+
+%% sr_merge(Sr1, Sr2) -> Sr.
+%% Merge two stack/register states keeping the longest of both stack
+%% and register. Perform consistency check on both, elements must be
+%% the same. Allow frame size 'void' to make easy creation of
+%% "empty" frame.
+
+sr_merge(#sr{reg=R1,stk=S1,res=[]}, #sr{reg=R2,stk=S2,res=[]}) ->
+ #sr{reg=longest(R1, R2),stk=longest(S1, S2),res=[]};
+sr_merge(void, S2) -> S2#sr{res=[]};
+sr_merge(S1, void) -> S1#sr{res=[]}.
+
+longest([H|T1], [H|T2]) -> [H|longest(T1, T2)];
+longest([dead|T1], [free|T2]) -> [dead|longest(T1, T2)];
+longest([free|T1], [dead|T2]) -> [dead|longest(T1, T2)];
+longest([dead|T1], []) -> [dead|T1];
+longest([], [dead|T2]) -> [dead|T2];
+longest([free|T1], []) -> [free|T1];
+longest([], [free|T2]) -> [free|T2];
+longest([], []) -> [].
+
+%% adjust_stack(Bef, FirstBefore, LastFrom, Vdb) -> {[Ainstr],Aft}.
+%% Do complete stack adjustment by compressing stack and adding
+%% variables to be saved. Try to optimise ordering on stack by
+%% having reverse order to their lifetimes.
+%%
+%% In Beam, there is a fixed stack frame and no need to do stack compression.
+
+adjust_stack(Bef, Fb, Lf, Vdb) ->
+ Stk0 = Bef#sr.stk,
+ {Stk1,Saves} = save_stack(Stk0, Fb, Lf, Vdb),
+ {saves(Saves, Bef#sr.reg, Stk1),
+ Bef#sr{stk=Stk1}}.
+
+%% save_stack(Stack, FirstBefore, LastFrom, Vdb) -> {[SaveVar],NewStack}.
+%% Save variables which are used past current point and which are not
+%% already on the stack.
+
+save_stack(Stk0, Fb, Lf, Vdb) ->
+ %% New variables that are in use but not on stack.
+ New = [ {V,F,L} || {V,F,L} <- Vdb,
+ F < Fb,
+ L >= Lf,
+ not on_stack(V, Stk0) ],
+ %% Add new variables that are not just dropped immediately.
+ %% N.B. foldr works backwards from the end!!
+ Saves = [ V || {V,_,_} <- keysort(3, New) ],
+ Stk1 = foldr(fun (V, Stk) -> put_stack(V, Stk) end, Stk0, Saves),
+ {Stk1,Saves}.
+
+%% saves([SaveVar], Reg, Stk) -> [{move,Reg,Stk}].
+%% Generate move instructions to save variables onto stack. The
+%% stack/reg info used is that after the new stack has been made.
+
+saves(Ss, Reg, Stk) ->
+ Res = map(fun (V) ->
+ {move,fetch_reg(V, Reg),fetch_stack(V, Stk)}
+ end, Ss),
+ Res.
+
+%% comment(C) -> ['%'{C}].
+
+%comment(C) -> [{'%',C}].
+comment(_) -> [].
+
+%% fetch_var(VarName, StkReg) -> r{R} | sp{Sp}.
+%% find_var(VarName, StkReg) -> ok{r{R} | sp{Sp}} | error.
+%% Fetch/find a variable in either the registers or on the
+%% stack. Fetch KNOWS it's there.
+
+fetch_var(V, Sr) ->
+ case find_reg(V, Sr#sr.reg) of
+ {ok,R} -> R;
+ error -> fetch_stack(V, Sr#sr.stk)
+ end.
+
+% find_var(V, Sr) ->
+% case find_reg(V, Sr#sr.reg) of
+% {ok,R} -> {ok,R};
+% error ->
+% case find_stack(V, Sr#sr.stk) of
+% {ok,S} -> {ok,S};
+% error -> error
+% end
+% end.
+
+load_vars(Vs, Regs) ->
+ foldl(fun ({var,V}, Rs) -> put_reg(V, Rs) end, Regs, Vs).
+
+%% put_reg(Val, Regs) -> Regs.
+%% load_reg(Val, Reg, Regs) -> Regs.
+%% free_reg(Val, Regs) -> Regs.
+%% find_reg(Val, Regs) -> ok{r{R}} | error.
+%% fetch_reg(Val, Regs) -> r{R}.
+%% Functions to interface the registers.
+%% put_reg puts a value into a free register,
+%% load_reg loads a value into a fixed register
+%% free_reg frees a register containing a specific value.
+
+% put_regs(Vs, Rs) -> foldl(fun put_reg/2, Rs, Vs).
+
+put_reg(V, Rs) -> put_reg_1(V, Rs, 0).
+
+put_reg_1(V, [free|Rs], I) -> [{I,V}|Rs];
+put_reg_1(V, [{reserved,I,V}|Rs], I) -> [{I,V}|Rs];
+put_reg_1(V, [R|Rs], I) -> [R|put_reg_1(V, Rs, I+1)];
+put_reg_1(V, [], I) -> [{I,V}].
+
+load_reg(V, R, Rs) -> load_reg_1(V, R, Rs, 0).
+
+load_reg_1(V, I, [_|Rs], I) -> [{I,V}|Rs];
+load_reg_1(V, I, [R|Rs], C) -> [R|load_reg_1(V, I, Rs, C+1)];
+load_reg_1(V, I, [], I) -> [{I,V}];
+load_reg_1(V, I, [], C) -> [free|load_reg_1(V, I, [], C+1)].
+
+% free_reg(V, [{I,V}|Rs]) -> [free|Rs];
+% free_reg(V, [R|Rs]) -> [R|free_reg(V, Rs)];
+% free_reg(V, []) -> [].
+
+fetch_reg(V, [{I,V}|_]) -> {x,I};
+fetch_reg(V, [_|SRs]) -> fetch_reg(V, SRs).
+
+find_reg(V, [{I,V}|_]) -> {ok,{x,I}};
+find_reg(V, [_|SRs]) -> find_reg(V, SRs);
+find_reg(_, []) -> error.
+
+%% For the bit syntax, we need a scratch register if we are constructing
+%% a binary that will not be used.
+
+find_scratch_reg(Rs) -> find_scratch_reg(Rs, 0).
+
+find_scratch_reg([free|_], I) -> {x,I};
+find_scratch_reg([_|Rs], I) -> find_scratch_reg(Rs, I+1);
+find_scratch_reg([], I) -> {x,I}.
+
+%%copy_reg(Val, R, Regs) -> load_reg(Val, R, Regs).
+%%move_reg(Val, R, Regs) -> load_reg(Val, R, free_reg(Val, Regs)).
+
+%%clear_regs(Regs) -> map(fun (R) -> free end, Regs).
+clear_regs(_) -> [].
+
+max_reg(Regs) ->
+ foldl(fun ({I,_}, _) -> I;
+ (_, Max) -> Max end,
+ -1, Regs) + 1.
+
+%% put_stack(Val, [{Val}]) -> [{Val}].
+%% fetch_stack(Var, Stk) -> sp{S}.
+%% find_stack(Var, Stk) -> ok{sp{S}} | error.
+%% Functions to interface the stack.
+
+put_stack(Val, []) -> [{Val}];
+put_stack(Val, [dead|Stk]) -> [{Val}|Stk];
+put_stack(Val, [free|Stk]) -> [{Val}|Stk];
+put_stack(Val, [NotFree|Stk]) -> [NotFree|put_stack(Val, Stk)].
+
+put_stack_carefully(Val, Stk0) ->
+ case catch put_stack_carefully1(Val, Stk0) of
+ error -> error;
+ Stk1 when list(Stk1) -> Stk1
+ end.
+
+put_stack_carefully1(_, []) -> throw(error);
+put_stack_carefully1(Val, [dead|Stk]) -> [{Val}|Stk];
+put_stack_carefully1(Val, [free|Stk]) -> [{Val}|Stk];
+put_stack_carefully1(Val, [NotFree|Stk]) ->
+ [NotFree|put_stack_carefully1(Val, Stk)].
+
+fetch_stack(Var, Stk) -> fetch_stack(Var, Stk, 0).
+
+fetch_stack(V, [{V}|_], I) -> {yy,I};
+fetch_stack(V, [_|Stk], I) -> fetch_stack(V, Stk, I+1).
+
+% find_stack(Var, Stk) -> find_stack(Var, Stk, 0).
+
+% find_stack(V, [{V}|Stk], I) -> {ok,{yy,I}};
+% find_stack(V, [O|Stk], I) -> find_stack(V, Stk, I+1);
+% find_stack(V, [], I) -> error.
+
+on_stack(V, Stk) -> keymember(V, 1, Stk).
+
+%% put_catch(CatchTag, Stack) -> Stack'
+%% drop_catch(CatchTag, Stack) -> Stack'
+%% Special interface for putting and removing catch tags, to ensure that
+%% catches nest properly. Also used for try tags.
+
+put_catch(Tag, Stk0) -> put_catch(Tag, reverse(Stk0), []).
+
+put_catch(Tag, [], Stk) ->
+ put_stack({catch_tag,Tag}, Stk);
+put_catch(Tag, [{{catch_tag,_}}|_]=RevStk, Stk) ->
+ reverse(RevStk, put_stack({catch_tag,Tag}, Stk));
+put_catch(Tag, [Other|Stk], Acc) ->
+ put_catch(Tag, Stk, [Other|Acc]).
+
+drop_catch(Tag, [{{catch_tag,Tag}}|Stk]) -> [free|Stk];
+drop_catch(Tag, [Other|Stk]) -> [Other|drop_catch(Tag, Stk)].
+
+%%%
+%%% Finish the code generation for the bit syntax matching.
+%%%
+
+bs_function({function,Name,Arity,CLabel,Asm0}=Func) ->
+ case bs_needed(Asm0, 0, false, []) of
+ {false,[]} -> Func;
+ {true,Dict} ->
+ Asm = bs_replace(Asm0, Dict, []),
+ {function,Name,Arity,CLabel,Asm}
+ end.
+
+%%%
+%%% Pass 1: Found out which bs_restore's that are needed. For now we assume
+%%% that a bs_restore is needed unless it is directly preceeded by a bs_save.
+%%%
+
+bs_needed([{bs_save,Name},{bs_restore,Name}|T], N, _BsUsed, Dict) ->
+ bs_needed(T, N, true, Dict);
+bs_needed([{bs_save,_Name}|T], N, _BsUsed, Dict) ->
+ bs_needed(T, N, true, Dict);
+bs_needed([{bs_restore,Name}|T], N, _BsUsed, Dict) ->
+ case keysearch(Name, 1, Dict) of
+ {value,{Name,_}} -> bs_needed(T, N, true, Dict);
+ false -> bs_needed(T, N+1, true, [{Name,N}|Dict])
+ end;
+bs_needed([{bs_init,_,_}|T], N, _, Dict) ->
+ bs_needed(T, N, true, Dict);
+bs_needed([{bs_init2,_,_,_,_,_}|T], N, _, Dict) ->
+ bs_needed(T, N, true, Dict);
+bs_needed([{bs_start_match,_,_}|T], N, _, Dict) ->
+ bs_needed(T, N, true, Dict);
+bs_needed([_|T], N, BsUsed, Dict) ->
+ bs_needed(T, N, BsUsed, Dict);
+bs_needed([], _, BsUsed, Dict) -> {BsUsed,Dict}.
+
+%%%
+%%% Pass 2: Only needed if there were some bs_* instructions found.
+%%%
+%%% Remove any bs_save with a name that never were found to be restored
+%%% in the first pass.
+%%%
+
+bs_replace([{bs_save,Name}=Save,{bs_restore,Name}|T], Dict, Acc) ->
+ bs_replace([Save|T], Dict, Acc);
+bs_replace([{bs_save,Name}|T], Dict, Acc) ->
+ case keysearch(Name, 1, Dict) of
+ {value,{Name,N}} ->
+ bs_replace(T, Dict, [{bs_save,N}|Acc]);
+ false ->
+ bs_replace(T, Dict, Acc)
+ end;
+bs_replace([{bs_restore,Name}|T], Dict, Acc) ->
+ case keysearch(Name, 1, Dict) of
+ {value,{Name,N}} ->
+ bs_replace(T, Dict, [{bs_restore,N}|Acc]);
+ false ->
+ bs_replace(T, Dict, Acc)
+ end;
+bs_replace([{bs_init2,Fail,Bytes,Regs,Flags,Dst}|T0], Dict, Acc) ->
+ case bs_find_test_heap(T0) of
+ none ->
+ bs_replace(T0, Dict, [{bs_init2,Fail,Bytes,0,Regs,Flags,Dst}|Acc]);
+ {T,Words} ->
+ bs_replace(T, Dict, [{bs_init2,Fail,Bytes,Words,Regs,Flags,Dst}|Acc])
+ end;
+bs_replace([H|T], Dict, Acc) ->
+ bs_replace(T, Dict, [H|Acc]);
+bs_replace([], _, Acc) -> reverse(Acc).
+
+bs_find_test_heap(Is) ->
+ bs_find_test_heap_1(Is, []).
+
+bs_find_test_heap_1([{bs_put_integer,_,_,_,_,_}=I|Is], Acc) ->
+ bs_find_test_heap_1(Is, [I|Acc]);
+bs_find_test_heap_1([{bs_put_float,_,_,_,_,_}=I|Is], Acc) ->
+ bs_find_test_heap_1(Is, [I|Acc]);
+bs_find_test_heap_1([{bs_put_binary,_,_,_,_,_}=I|Is], Acc) ->
+ bs_find_test_heap_1(Is, [I|Acc]);
+bs_find_test_heap_1([{test_heap,Words,_}|Is], Acc) ->
+ {reverse(Acc, Is),Words};
+bs_find_test_heap_1(_, _) -> none.
+
+%% new_label(St) -> {L,St}.
+
+new_label(St) ->
+ L = St#cg.lcount,
+ {L,St#cg{lcount=L+1}}.
+
+flatmapfoldl(F, Accu0, [Hd|Tail]) ->
+ {R,Accu1} = F(Hd, Accu0),
+ {Rs,Accu2} = flatmapfoldl(F, Accu1, Tail),
+ {R++Rs,Accu2};
+flatmapfoldl(_, Accu, []) -> {[],Accu}.
+
+flatmapfoldr(F, Accu0, [Hd|Tail]) ->
+ {Rs,Accu1} = flatmapfoldr(F, Accu0, Tail),
+ {R,Accu2} = F(Hd, Accu1),
+ {R++Rs,Accu2};
+flatmapfoldr(_, Accu, []) -> {[],Accu}.
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_core.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_core.erl
new file mode 100644
index 0000000000..c96837ab5e
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_core.erl
@@ -0,0 +1,1319 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: v3_core.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose : Transform normal Erlang to Core Erlang
+
+%% At this stage all preprocessing has been done. All that is left are
+%% "pure" Erlang functions.
+%%
+%% Core transformation is done in three stages:
+%%
+%% 1. Flatten expressions into an internal core form without doing
+%% matching.
+%%
+%% 2. Step "forwards" over the icore code annotating each "top-level"
+%% thing with variable usage. Detect bound variables in matching
+%% and replace with explicit guard test. Annotate "internal-core"
+%% expressions with variables they use and create. Convert matches
+%% to cases when not pure assignments.
+%%
+%% 3. Step "backwards" over icore code using variable usage
+%% annotations to change implicit exported variables to explicit
+%% returns.
+%%
+%% To ensure the evaluation order we ensure that all arguments are
+%% safe. A "safe" is basically a core_lib simple with VERY restricted
+%% binaries.
+%%
+%% We have to be very careful with matches as these create variables.
+%% While we try not to flatten things more than necessary we must make
+%% sure that all matches are at the top level. For this we use the
+%% type "novars" which are non-match expressions. Cases and receives
+%% can also create problems due to exports variables so they are not
+%% "novars" either. I.e. a novars will not export variables.
+%%
+%% Annotations in the #iset, #iletrec, and all other internal records
+%% is kept in a record, #a, not in a list as in proper core. This is
+%% easier and faster and creates no problems as we have complete control
+%% over all annotations.
+%%
+%% On output, the annotation for most Core Erlang terms will contain
+%% the source line number. A few terms will be marked with the atom
+%% atom 'compiler_generated', to indicate that the compiler has generated
+%% them and that no warning should be generated if they are optimized
+%% away.
+%%
+%%
+%% In this translation:
+%%
+%% call ops are safes
+%% call arguments are safes
+%% match arguments are novars
+%% case arguments are novars
+%% receive timeouts are novars
+%% let/set arguments are expressions
+%% fun is not a safe
+
+-module(v3_core).
+
+-export([module/2,format_error/1]).
+
+-import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,splitwith/2]).
+-import(ordsets, [add_element/2,del_element/2,is_element/2,
+ union/1,union/2,intersection/2,subtract/2]).
+
+-include("core_parse.hrl").
+
+-record(a, {us=[],ns=[],anno=[]}). %Internal annotation
+
+%% Internal core expressions and help functions.
+%% N.B. annotations fields in place as normal Core expressions.
+
+-record(iset, {anno=#a{},var,arg}).
+-record(iletrec, {anno=#a{},defs,body}).
+-record(imatch, {anno=#a{},pat,guard=[],arg,fc}).
+-record(icase, {anno=#a{},args,clauses,fc}).
+-record(iclause, {anno=#a{},pats,pguard=[],guard,body}).
+-record(ifun, {anno=#a{},id,vars,clauses,fc}).
+-record(iapply, {anno=#a{},op,args}).
+-record(icall, {anno=#a{},module,name,args}).
+-record(iprimop, {anno=#a{},name,args}).
+-record(itry, {anno=#a{},args,vars,body,evars,handler}).
+-record(icatch, {anno=#a{},body}).
+-record(ireceive1, {anno=#a{},clauses}).
+-record(ireceive2, {anno=#a{},clauses,timeout,action}).
+-record(iprotect, {anno=#a{},body}).
+-record(ibinary, {anno=#a{},segments}). %Not used in patterns.
+
+-record(core, {vcount=0, %Variable counter
+ fcount=0, %Function counter
+ ws=[]}). %Warnings.
+
+module({Mod,Exp,Forms}, _Opts) ->
+ Cexp = map(fun ({N,A}) -> #c_fname{id=N,arity=A} end, Exp),
+ {Kfs,As,Ws} = foldr(fun form/2, {[],[],[]}, Forms),
+ {ok,#c_module{name=#c_atom{val=Mod},exports=Cexp,attrs=As,defs=Kfs},Ws}.
+
+form({function,_,_,_,_}=F0, {Fs,As,Ws0}) ->
+ {F,Ws} = function(F0, Ws0),
+ {[F|Fs],As,Ws};
+form({attribute,_,_,_}=F, {Fs,As,Ws}) ->
+ {Fs,[attribute(F)|As],Ws}.
+
+attribute({attribute,_,Name,Val}) ->
+ #c_def{name=core_lib:make_literal(Name),
+ val=core_lib:make_literal(Val)}.
+
+function({function,_,Name,Arity,Cs0}, Ws0) ->
+ %%ok = io:fwrite("~p - ", [{Name,Arity}]),
+ St0 = #core{vcount=0,ws=Ws0},
+ {B0,St1} = body(Cs0, Arity, St0),
+ %%ok = io:fwrite("1", []),
+ %%ok = io:fwrite("~w:~p~n", [?LINE,B0]),
+ {B1,St2} = ubody(B0, St1),
+ %%ok = io:fwrite("2", []),
+ %%ok = io:fwrite("~w:~p~n", [?LINE,B1]),
+ {B2,#core{ws=Ws}} = cbody(B1, St2),
+ %%ok = io:fwrite("3~n", []),
+ {#c_def{name=#c_fname{id=Name,arity=Arity},val=B2},Ws}.
+
+body(Cs0, Arity, St0) ->
+ Anno = [element(2, hd(Cs0))],
+ {Args,St1} = new_vars(Anno, Arity, St0),
+ {Cs1,St2} = clauses(Cs0, St1),
+ {Ps,St3} = new_vars(Arity, St2), %Need new variables here
+ Fc = fail_clause(Ps, #c_tuple{es=[#c_atom{val=function_clause}|Ps]}),
+ {#ifun{anno=#a{anno=Anno},id=[],vars=Args,clauses=Cs1,fc=Fc},St3}.
+
+%% clause(Clause, State) -> {Cclause,State} | noclause.
+%% clauses([Clause], State) -> {[Cclause],State}.
+%% Convert clauses. Trap bad pattern aliases and remove clause from
+%% clause list.
+
+clauses([C0|Cs0], St0) ->
+ case clause(C0, St0) of
+ {noclause,St} -> clauses(Cs0, St);
+ {C,St1} ->
+ {Cs,St2} = clauses(Cs0, St1),
+ {[C|Cs],St2}
+ end;
+clauses([], St) -> {[],St}.
+
+clause({clause,Lc,H0,G0,B0}, St0) ->
+ case catch head(H0) of
+ {'EXIT',_}=Exit -> exit(Exit); %Propagate error
+ nomatch ->
+ St = add_warning(Lc, nomatch, St0),
+ {noclause,St}; %Bad pattern
+ H1 ->
+ {G1,St1} = guard(G0, St0),
+ {B1,St2} = exprs(B0, St1),
+ {#iclause{anno=#a{anno=[Lc]},pats=H1,guard=G1,body=B1},St2}
+ end.
+
+%% head([P]) -> [P].
+
+head(Ps) -> pattern_list(Ps).
+
+%% guard([Expr], State) -> {[Cexpr],State}.
+%% Build an explict and/or tree of guard alternatives, then traverse
+%% top-level and/or tree and "protect" inner tests.
+
+guard([], St) -> {[],St};
+guard(Gs0, St) ->
+ Gs = foldr(fun (Gt0, Rhs) ->
+ Gt1 = guard_tests(Gt0),
+ L = element(2, Gt1),
+ {op,L,'or',Gt1,Rhs}
+ end, guard_tests(last(Gs0)), first(Gs0)),
+ gexpr_top(Gs, St).
+
+guard_tests([]) -> [];
+guard_tests(Gs) ->
+ L = element(2, hd(Gs)),
+ {protect,L,foldr(fun (G, Rhs) -> {op,L,'and',G,Rhs} end, last(Gs), first(Gs))}.
+
+%% gexpr_top(Expr, State) -> {Cexpr,State}.
+%% Generate an internal core expression of a guard test. Explicitly
+%% handle outer boolean expressions and "protect" inner tests in a
+%% reasonably smart way.
+
+gexpr_top(E0, St0) ->
+ {E1,Eps0,Bools,St1} = gexpr(E0, [], St0),
+ {E,Eps,St} = force_booleans(Bools, E1, Eps0, St1),
+ {Eps++[E],St}.
+
+%% gexpr(Expr, Bools, State) -> {Cexpr,[PreExp],Bools,State}.
+%% Generate an internal core expression of a guard test.
+
+gexpr({protect,Line,Arg}, Bools0, St0) ->
+ case gexpr(Arg, [], St0) of
+ {E0,[],Bools,St1} ->
+ {E,Eps,St} = force_booleans(Bools, E0, [], St1),
+ {E,Eps,Bools0,St};
+ {E0,Eps0,Bools,St1} ->
+ {E,Eps,St} = force_booleans(Bools, E0, Eps0, St1),
+ {#iprotect{anno=#a{anno=[Line]},body=Eps++[E]},[],Bools0,St}
+ end;
+gexpr({op,Line,Op,L,R}=Call, Bools0, St0) ->
+ case erl_internal:bool_op(Op, 2) of
+ true ->
+ {Le,Lps,Bools1,St1} = gexpr(L, Bools0, St0),
+ {Ll,Llps,St2} = force_safe(Le, St1),
+ {Re,Rps,Bools,St3} = gexpr(R, Bools1, St2),
+ {Rl,Rlps,St4} = force_safe(Re, St3),
+ Anno = [Line],
+ {#icall{anno=#a{anno=Anno}, %Must have an #a{}
+ module=#c_atom{anno=Anno,val=erlang},name=#c_atom{anno=Anno,val=Op},
+ args=[Ll,Rl]},Lps ++ Llps ++ Rps ++ Rlps,Bools,St4};
+ false ->
+ gexpr_test(Call, Bools0, St0)
+ end;
+gexpr({op,Line,Op,A}=Call, Bools0, St0) ->
+ case erl_internal:bool_op(Op, 1) of
+ true ->
+ {Ae,Aps,Bools,St1} = gexpr(A, Bools0, St0),
+ {Al,Alps,St2} = force_safe(Ae, St1),
+ Anno = [Line],
+ {#icall{anno=#a{anno=Anno}, %Must have an #a{}
+ module=#c_atom{anno=Anno,val=erlang},name=#c_atom{anno=Anno,val=Op},
+ args=[Al]},Aps ++ Alps,Bools,St2};
+ false ->
+ gexpr_test(Call, Bools0, St0)
+ end;
+gexpr(E0, Bools, St0) ->
+ gexpr_test(E0, Bools, St0).
+
+%% gexpr_test(Expr, Bools, State) -> {Cexpr,[PreExp],Bools,State}.
+%% Generate a guard test. At this stage we must be sure that we have
+%% a proper boolean value here so wrap things with an true test if we
+%% don't know, i.e. if it is not a comparison or a type test.
+
+gexpr_test({atom,L,true}, Bools, St0) ->
+ {#c_atom{anno=[L],val=true},[],Bools,St0};
+gexpr_test({atom,L,false}, Bools, St0) ->
+ {#c_atom{anno=[L],val=false},[],Bools,St0};
+gexpr_test(E0, Bools0, St0) ->
+ {E1,Eps0,St1} = expr(E0, St0),
+ %% Generate "top-level" test and argument calls.
+ case E1 of
+ #icall{anno=Anno,module=#c_atom{val=erlang},name=#c_atom{val=N},args=As} ->
+ Ar = length(As),
+ case erl_internal:type_test(N, Ar) orelse
+ erl_internal:comp_op(N, Ar) orelse
+ (N == internal_is_record andalso Ar == 3) of
+ true -> {E1,Eps0,Bools0,St1};
+ false ->
+ Lanno = Anno#a.anno,
+ {New,St2} = new_var(Lanno, St1),
+ Bools = [New|Bools0],
+ {#icall{anno=Anno, %Must have an #a{}
+ module=#c_atom{anno=Lanno,val=erlang},
+ name=#c_atom{anno=Lanno,val='=:='},
+ args=[New,#c_atom{anno=Lanno,val=true}]},
+ Eps0 ++ [#iset{anno=Anno,var=New,arg=E1}],Bools,St2}
+ end;
+ _ ->
+ Anno = get_ianno(E1),
+ Lanno = get_lineno_anno(E1),
+ case core_lib:is_simple(E1) of
+ true ->
+ Bools = [E1|Bools0],
+ {#icall{anno=Anno, %Must have an #a{}
+ module=#c_atom{anno=Lanno,val=erlang},
+ name=#c_atom{anno=Lanno,val='=:='},
+ args=[E1,#c_atom{anno=Lanno,val=true}]},Eps0,Bools,St1};
+ false ->
+ {New,St2} = new_var(Lanno, St1),
+ Bools = [New|Bools0],
+ {#icall{anno=Anno, %Must have an #a{}
+ module=#c_atom{anno=Lanno,val=erlang},
+ name=#c_atom{anno=Lanno,val='=:='},
+ args=[New,#c_atom{anno=Lanno,val=true}]},
+ Eps0 ++ [#iset{anno=Anno,var=New,arg=E1}],Bools,St2}
+ end
+ end.
+
+force_booleans([], E, Eps, St) ->
+ {E,Eps,St};
+force_booleans([V|Vs], E0, Eps0, St0) ->
+ {E1,Eps1,St1} = force_safe(E0, St0),
+ Lanno = element(2, V),
+ Anno = #a{anno=Lanno},
+ Call = #icall{anno=Anno,module=#c_atom{anno=Lanno,val=erlang},
+ name=#c_atom{anno=Lanno,val=is_boolean},
+ args=[V]},
+ {New,St} = new_var(Lanno, St1),
+ Iset = #iset{anno=Anno,var=New,arg=Call},
+ Eps = Eps0 ++ Eps1 ++ [Iset],
+ E = #icall{anno=Anno,
+ module=#c_atom{anno=Lanno,val=erlang},name=#c_atom{anno=Lanno,val='and'},
+ args=[E1,New]},
+ force_booleans(Vs, E, Eps, St).
+
+%% exprs([Expr], State) -> {[Cexpr],State}.
+%% Flatten top-level exprs.
+
+exprs([E0|Es0], St0) ->
+ {E1,Eps,St1} = expr(E0, St0),
+ {Es1,St2} = exprs(Es0, St1),
+ {Eps ++ [E1] ++ Es1,St2};
+exprs([], St) -> {[],St}.
+
+%% expr(Expr, State) -> {Cexpr,[PreExp],State}.
+%% Generate an internal core expression.
+
+expr({var,L,V}, St) -> {#c_var{anno=[L],name=V},[],St};
+expr({char,L,C}, St) -> {#c_char{anno=[L],val=C},[],St};
+expr({integer,L,I}, St) -> {#c_int{anno=[L],val=I},[],St};
+expr({float,L,F}, St) -> {#c_float{anno=[L],val=F},[],St};
+expr({atom,L,A}, St) -> {#c_atom{anno=[L],val=A},[],St};
+expr({nil,L}, St) -> {#c_nil{anno=[L]},[],St};
+expr({string,L,S}, St) -> {#c_string{anno=[L],val=S},[],St};
+expr({cons,L,H0,T0}, St0) ->
+ {H1,Hps,St1} = safe(H0, St0),
+ {T1,Tps,St2} = safe(T0, St1),
+ {#c_cons{anno=[L],hd=H1,tl=T1},Hps ++ Tps,St2};
+expr({lc,L,E,Qs}, St) ->
+ lc_tq(L, E, Qs, {nil,L}, St);
+expr({tuple,L,Es0}, St0) ->
+ {Es1,Eps,St1} = safe_list(Es0, St0),
+ {#c_tuple{anno=[L],es=Es1},Eps,St1};
+expr({bin,L,Es0}, St0) ->
+ {Es1,Eps,St1} = expr_bin(Es0, St0),
+ {#ibinary{anno=#a{anno=[L]},segments=Es1},Eps,St1};
+expr({block,_,Es0}, St0) ->
+ %% Inline the block directly.
+ {Es1,St1} = exprs(first(Es0), St0),
+ {E1,Eps,St2} = expr(last(Es0), St1),
+ {E1,Es1 ++ Eps,St2};
+expr({'if',L,Cs0}, St0) ->
+ {Cs1,St1} = clauses(Cs0, St0),
+ Fc = fail_clause([], #c_atom{val=if_clause}),
+ {#icase{anno=#a{anno=[L]},args=[],clauses=Cs1,fc=Fc},[],St1};
+expr({'case',L,E0,Cs0}, St0) ->
+ {E1,Eps,St1} = novars(E0, St0),
+ {Cs1,St2} = clauses(Cs0, St1),
+ {Fpat,St3} = new_var(St2),
+ Fc = fail_clause([Fpat], #c_tuple{es=[#c_atom{val=case_clause},Fpat]}),
+ {#icase{anno=#a{anno=[L]},args=[E1],clauses=Cs1,fc=Fc},Eps,St3};
+expr({'receive',L,Cs0}, St0) ->
+ {Cs1,St1} = clauses(Cs0, St0),
+ {#ireceive1{anno=#a{anno=[L]},clauses=Cs1}, [], St1};
+expr({'receive',L,Cs0,Te0,Tes0}, St0) ->
+ {Te1,Teps,St1} = novars(Te0, St0),
+ {Tes1,St2} = exprs(Tes0, St1),
+ {Cs1,St3} = clauses(Cs0, St2),
+ {#ireceive2{anno=#a{anno=[L]},
+ clauses=Cs1,timeout=Te1,action=Tes1},Teps,St3};
+expr({'try',L,Es0,[],Ecs,[]}, St0) ->
+ %% 'try ... catch ... end'
+ {Es1,St1} = exprs(Es0, St0),
+ {V,St2} = new_var(St1), %This name should be arbitrary
+ {Evs,Hs,St3} = try_exception(Ecs, St2),
+ {#itry{anno=#a{anno=[L]},args=Es1,vars=[V],body=[V],
+ evars=Evs,handler=Hs},
+ [],St3};
+expr({'try',L,Es0,Cs0,Ecs,[]}, St0) ->
+ %% 'try ... of ... catch ... end'
+ {Es1,St1} = exprs(Es0, St0),
+ {V,St2} = new_var(St1), %This name should be arbitrary
+ {Cs1,St3} = clauses(Cs0, St2),
+ {Fpat,St4} = new_var(St3),
+ Fc = fail_clause([Fpat], #c_tuple{es=[#c_atom{val=try_clause},Fpat]}),
+ {Evs,Hs,St5} = try_exception(Ecs, St4),
+ {#itry{anno=#a{anno=[L]},args=Es1,
+ vars=[V],body=[#icase{anno=#a{},args=[V],clauses=Cs1,fc=Fc}],
+ evars=Evs,handler=Hs},
+ [],St5};
+expr({'try',L,Es0,[],[],As0}, St0) ->
+ %% 'try ... after ... end'
+ {Es1,St1} = exprs(Es0, St0),
+ {As1,St2} = exprs(As0, St1),
+ {Evs,Hs,St3} = try_after(As1,St2),
+ {V,St4} = new_var(St3), % (must not exist in As1)
+ %% TODO: this duplicates the 'after'-code; should lift to function.
+ {#itry{anno=#a{anno=[L]},args=Es1,vars=[V],body=As1++[V],
+ evars=Evs,handler=Hs},
+ [],St4};
+expr({'try',L,Es,Cs,Ecs,As}, St0) ->
+ %% 'try ... [of ...] [catch ...] after ... end'
+ expr({'try',L,[{'try',L,Es,Cs,Ecs,[]}],[],[],As}, St0);
+expr({'catch',L,E0}, St0) ->
+ {E1,Eps,St1} = expr(E0, St0),
+ {#icatch{anno=#a{anno=[L]},body=Eps ++ [E1]},[],St1};
+expr({'fun',L,{function,F,A},{_,_,_}=Id}, St) ->
+ {#c_fname{anno=[L,{id,Id}],id=F,arity=A},[],St};
+expr({'fun',L,{clauses,Cs},Id}, St) ->
+ fun_tq(Id, Cs, L, St);
+expr({call,L0,{remote,_,{atom,_,erlang},{atom,_,is_record}},[_,_,_]=As}, St)
+ when L0 < 0 ->
+ %% Compiler-generated erlang:is_record/3 should be converted to
+ %% erlang:internal_is_record/3.
+ L = -L0,
+ expr({call,L,{remote,L,{atom,L,erlang},{atom,L,internal_is_record}},As}, St);
+expr({call,L,{remote,_,M,F},As0}, St0) ->
+ {[M1,F1|As1],Aps,St1} = safe_list([M,F|As0], St0),
+ {#icall{anno=#a{anno=[L]},module=M1,name=F1,args=As1},Aps,St1};
+expr({call,Lc,{atom,Lf,F},As0}, St0) ->
+ {As1,Aps,St1} = safe_list(As0, St0),
+ Op = #c_fname{anno=[Lf],id=F,arity=length(As1)},
+ {#iapply{anno=#a{anno=[Lc]},op=Op,args=As1},Aps,St1};
+expr({call,L,FunExp,As0}, St0) ->
+ {Fun,Fps,St1} = safe(FunExp, St0),
+ {As1,Aps,St2} = safe_list(As0, St1),
+ {#iapply{anno=#a{anno=[L]},op=Fun,args=As1},Fps ++ Aps,St2};
+expr({match,L,P0,E0}, St0) ->
+ %% First fold matches together to create aliases.
+ {P1,E1} = fold_match(E0, P0),
+ {E2,Eps,St1} = novars(E1, St0),
+ P2 = (catch pattern(P1)),
+ {Fpat,St2} = new_var(St1),
+ Fc = fail_clause([Fpat], #c_tuple{es=[#c_atom{val=badmatch},Fpat]}),
+ case P2 of
+ {'EXIT',_}=Exit -> exit(Exit); %Propagate error
+ nomatch ->
+ St = add_warning(L, nomatch, St2),
+ {#icase{anno=#a{anno=[L]},
+ args=[E2],clauses=[],fc=Fc},Eps,St};
+ _Other ->
+ {#imatch{anno=#a{anno=[L]},pat=P2,arg=E2,fc=Fc},Eps,St2}
+ end;
+expr({op,_,'++',{lc,Llc,E,Qs},L2}, St) ->
+ %% Optimise this here because of the list comprehension algorithm.
+ lc_tq(Llc, E, Qs, L2, St);
+expr({op,L,Op,A0}, St0) ->
+ {A1,Aps,St1} = safe(A0, St0),
+ LineAnno = [L],
+ {#icall{anno=#a{anno=LineAnno}, %Must have an #a{}
+ module=#c_atom{anno=LineAnno,val=erlang},
+ name=#c_atom{anno=LineAnno,val=Op},args=[A1]},Aps,St1};
+expr({op,L,Op,L0,R0}, St0) ->
+ {As,Aps,St1} = safe_list([L0,R0], St0),
+ LineAnno = [L],
+ {#icall{anno=#a{anno=LineAnno}, %Must have an #a{}
+ module=#c_atom{anno=LineAnno,val=erlang},
+ name=#c_atom{anno=LineAnno,val=Op},args=As},Aps,St1}.
+
+%% try_exception([ExcpClause], St) -> {[ExcpVar],Handler,St}.
+
+try_exception(Ecs0, St0) ->
+ %% Note that Tag is not needed for rethrow - it is already in Info.
+ {Evs,St1} = new_vars(3, St0), % Tag, Value, Info
+ {Ecs1,St2} = clauses(Ecs0, St1),
+ [_,Value,Info] = Evs,
+ Ec = #iclause{anno=#a{anno=[compiler_generated]},
+ pats=[#c_tuple{es=Evs}],guard=[#c_atom{val=true}],
+ body=[#iprimop{anno=#a{}, %Must have an #a{}
+ name=#c_atom{val=raise},
+ args=[Info,Value]}]},
+ Hs = [#icase{anno=#a{},args=[#c_tuple{es=Evs}],clauses=Ecs1,fc=Ec}],
+ {Evs,Hs,St2}.
+
+try_after(As, St0) ->
+ %% See above.
+ {Evs,St1} = new_vars(3, St0), % Tag, Value, Info
+ [_,Value,Info] = Evs,
+ B = As ++ [#iprimop{anno=#a{}, %Must have an #a{}
+ name=#c_atom{val=raise},
+ args=[Info,Value]}],
+ Ec = #iclause{anno=#a{anno=[compiler_generated]},
+ pats=[#c_tuple{es=Evs}],guard=[#c_atom{val=true}],
+ body=B},
+ Hs = [#icase{anno=#a{},args=[#c_tuple{es=Evs}],clauses=[],fc=Ec}],
+ {Evs,Hs,St1}.
+
+%% expr_bin([ArgExpr], St) -> {[Arg],[PreExpr],St}.
+%% Flatten the arguments of a bin. Do this straight left to right!
+
+expr_bin(Es, St) ->
+ foldr(fun (E, {Ces,Esp,St0}) ->
+ {Ce,Ep,St1} = bitstr(E, St0),
+ {[Ce|Ces],Ep ++ Esp,St1}
+ end, {[],[],St}, Es).
+
+bitstr({bin_element,_,E0,Size0,[Type,{unit,Unit}|Flags]}, St0) ->
+ {E1,Eps,St1} = safe(E0, St0),
+ {Size1,Eps2,St2} = safe(Size0, St1),
+ {#c_bitstr{val=E1,size=Size1,
+ unit=core_lib:make_literal(Unit),
+ type=core_lib:make_literal(Type),
+ flags=core_lib:make_literal(Flags)},
+ Eps ++ Eps2,St2}.
+
+%% fun_tq(Id, [Clauses], Line, State) -> {Fun,[PreExp],State}.
+
+fun_tq(Id, Cs0, L, St0) ->
+ {Cs1,St1} = clauses(Cs0, St0),
+ Arity = length((hd(Cs1))#iclause.pats),
+ {Args,St2} = new_vars(Arity, St1),
+ {Ps,St3} = new_vars(Arity, St2), %Need new variables here
+ Fc = fail_clause(Ps, #c_tuple{es=[#c_atom{val=function_clause}|Ps]}),
+ Fun = #ifun{anno=#a{anno=[L]},
+ id=[{id,Id}], %We KNOW!
+ vars=Args,clauses=Cs1,fc=Fc},
+ {Fun,[],St3}.
+
+%% lc_tq(Line, Exp, [Qualifier], More, State) -> {LetRec,[PreExp],State}.
+%% This TQ from Simon PJ pp 127-138.
+%% This gets a bit messy as we must transform all directly here. We
+%% recognise guard tests and try to fold them together and join to a
+%% preceding generators, this should give us better and more compact
+%% code.
+%% More could be transformed before calling lc_tq.
+
+lc_tq(Line, E, [{generate,Lg,P,G}|Qs0], More, St0) ->
+ {Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0),
+ {Name,St1} = new_fun_name("lc", St0),
+ {Head,St2} = new_var(St1),
+ {Tname,St3} = new_var_name(St2),
+ LA = [Line],
+ LAnno = #a{anno=LA},
+ Tail = #c_var{anno=LA,name=Tname},
+ {Arg,St4} = new_var(St3),
+ NewMore = {call,Lg,{atom,Lg,Name},[{var,Lg,Tname}]},
+ {Guardc,St5} = lc_guard_tests(Gs, St4), %These are always flat!
+ {Lc,Lps,St6} = lc_tq(Line, E, Qs1, NewMore, St5),
+ {Mc,Mps,St7} = expr(More, St6),
+ {Nc,Nps,St8} = expr(NewMore, St7),
+ case catch pattern(P) of
+ {'EXIT',_}=Exit ->
+ St9 = St8,
+ Pc = nomatch,
+ exit(Exit); %Propagate error
+ nomatch ->
+ St9 = add_warning(Line, nomatch, St8),
+ Pc = nomatch;
+ Pc ->
+ St9 = St8
+ end,
+ {Gc,Gps,St10} = safe(G, St9), %Will be a function argument!
+ Fc = fail_clause([Arg], #c_tuple{anno=LA,
+ es=[#c_atom{val=function_clause},Arg]}),
+ Cs0 = [#iclause{anno=#a{anno=[compiler_generated|LA]},
+ pats=[#c_cons{anno=LA,hd=Head,tl=Tail}],
+ guard=[],
+ body=Nps ++ [Nc]},
+ #iclause{anno=LAnno,
+ pats=[#c_nil{anno=LA}],guard=[],
+ body=Mps ++ [Mc]}],
+ Cs = case Pc of
+ nomatch -> Cs0;
+ _ ->
+ [#iclause{anno=LAnno,
+ pats=[#c_cons{anno=LA,hd=Pc,tl=Tail}],
+ guard=Guardc,
+ body=Lps ++ [Lc]}|Cs0]
+ end,
+ Fun = #ifun{anno=LAnno,id=[],vars=[Arg],clauses=Cs,fc=Fc},
+ {#iletrec{anno=LAnno,defs=[{Name,Fun}],
+ body=Gps ++ [#iapply{anno=LAnno,
+ op=#c_fname{anno=LA,id=Name,arity=1},
+ args=[Gc]}]},
+ [],St10};
+lc_tq(Line, E, [Fil0|Qs0], More, St0) ->
+ %% Special case sequences guard tests.
+ LA = [Line],
+ LAnno = #a{anno=LA},
+ case is_guard_test(Fil0) of
+ true ->
+ {Gs0,Qs1} = splitwith(fun is_guard_test/1, Qs0),
+ {Lc,Lps,St1} = lc_tq(Line, E, Qs1, More, St0),
+ {Mc,Mps,St2} = expr(More, St1),
+ {Gs,St3} = lc_guard_tests([Fil0|Gs0], St2), %These are always flat!
+ {#icase{anno=LAnno,
+ args=[],
+ clauses=[#iclause{anno=LAnno,pats=[],
+ guard=Gs,body=Lps ++ [Lc]}],
+ fc=#iclause{anno=LAnno,pats=[],guard=[],body=Mps ++ [Mc]}},
+ [],St3};
+ false ->
+ {Lc,Lps,St1} = lc_tq(Line, E, Qs0, More, St0),
+ {Mc,Mps,St2} = expr(More, St1),
+ {Fpat,St3} = new_var(St2),
+ Fc = fail_clause([Fpat], #c_tuple{es=[#c_atom{val=case_clause},Fpat]}),
+ %% Do a novars little optimisation here.
+ case Fil0 of
+ {op,_,'not',Fil1} ->
+ {Filc,Fps,St4} = novars(Fil1, St3),
+ {#icase{anno=LAnno,
+ args=[Filc],
+ clauses=[#iclause{anno=LAnno,
+ pats=[#c_atom{anno=LA,val=true}],
+ guard=[],
+ body=Mps ++ [Mc]},
+ #iclause{anno=LAnno,
+ pats=[#c_atom{anno=LA,val=false}],
+ guard=[],
+ body=Lps ++ [Lc]}],
+ fc=Fc},
+ Fps,St4};
+ _Other ->
+ {Filc,Fps,St4} = novars(Fil0, St3),
+ {#icase{anno=LAnno,
+ args=[Filc],
+ clauses=[#iclause{anno=LAnno,
+ pats=[#c_atom{anno=LA,val=true}],
+ guard=[],
+ body=Lps ++ [Lc]},
+ #iclause{anno=LAnno,
+ pats=[#c_atom{anno=LA,val=false}],
+ guard=[],
+ body=Mps ++ [Mc]}],
+ fc=Fc},
+ Fps,St4}
+ end
+ end;
+lc_tq(Line, E, [], More, St) ->
+ expr({cons,Line,E,More}, St).
+
+lc_guard_tests([], St) -> {[],St};
+lc_guard_tests(Gs0, St) ->
+ Gs = guard_tests(Gs0),
+ gexpr_top(Gs, St).
+
+%% is_guard_test(Expression) -> true | false.
+%% Test if a general expression is a guard test. Use erl_lint here
+%% as it now allows sys_pre_expand transformed source.
+
+is_guard_test(E) -> erl_lint:is_guard_test(E).
+
+%% novars(Expr, State) -> {Novars,[PreExpr],State}.
+%% Generate a novars expression, basically a call or a safe. At this
+%% level we do not need to do a deep check.
+
+novars(E0, St0) ->
+ {E1,Eps,St1} = expr(E0, St0),
+ {Se,Sps,St2} = force_novars(E1, St1),
+ {Se,Eps ++ Sps,St2}.
+
+force_novars(#iapply{}=App, St) -> {App,[],St};
+force_novars(#icall{}=Call, St) -> {Call,[],St};
+force_novars(#iprimop{}=Prim, St) -> {Prim,[],St};
+force_novars(#ifun{}=Fun, St) -> {Fun,[],St}; %These are novars too
+force_novars(#ibinary{}=Bin, St) -> {Bin,[],St};
+force_novars(Ce, St) ->
+ force_safe(Ce, St).
+
+%% safe(Expr, State) -> {Safe,[PreExpr],State}.
+%% Generate an internal safe expression. These are simples without
+%% binaries which can fail. At this level we do not need to do a
+%% deep check. Must do special things with matches here.
+
+safe(E0, St0) ->
+ {E1,Eps,St1} = expr(E0, St0),
+ {Se,Sps,St2} = force_safe(E1, St1),
+ {Se,Eps ++ Sps,St2}.
+
+safe_list(Es, St) ->
+ foldr(fun (E, {Ces,Esp,St0}) ->
+ {Ce,Ep,St1} = safe(E, St0),
+ {[Ce|Ces],Ep ++ Esp,St1}
+ end, {[],[],St}, Es).
+
+force_safe(#imatch{anno=Anno,pat=P,arg=E,fc=Fc}, St0) ->
+ {Le,Lps,St1} = force_safe(E, St0),
+ {Le,Lps ++ [#imatch{anno=Anno,pat=P,arg=Le,fc=Fc}],St1};
+force_safe(Ce, St0) ->
+ case is_safe(Ce) of
+ true -> {Ce,[],St0};
+ false ->
+ {V,St1} = new_var(St0),
+ {V,[#iset{var=V,arg=Ce}],St1}
+ end.
+
+is_safe(#c_cons{}) -> true;
+is_safe(#c_tuple{}) -> true;
+is_safe(#c_var{}) -> true;
+is_safe(E) -> core_lib:is_atomic(E).
+
+%%% %% variable(Expr, State) -> {Variable,[PreExpr],State}.
+%%% %% force_variable(Expr, State) -> {Variable,[PreExpr],State}.
+%%% %% Generate a variable.
+
+%%% variable(E0, St0) ->
+%%% {E1,Eps,St1} = expr(E0, St0),
+%%% {V,Vps,St2} = force_variable(E1, St1),
+%%% {V,Eps ++ Vps,St2}.
+
+%%% force_variable(#c_var{}=Var, St) -> {Var,[],St};
+%%% force_variable(Ce, St0) ->
+%%% {V,St1} = new_var(St0),
+%%% {V,[#iset{var=V,arg=Ce}],St1}.
+
+%%% %% atomic(Expr, State) -> {Atomic,[PreExpr],State}.
+%%% %% force_atomic(Expr, State) -> {Atomic,[PreExpr],State}.
+
+%%% atomic(E0, St0) ->
+%%% {E1,Eps,St1} = expr(E0, St0),
+%%% {A,Aps,St2} = force_atomic(E1, St1),
+%%% {A,Eps ++ Aps,St2}.
+
+%%% force_atomic(Ce, St0) ->
+%%% case core_lib:is_atomic(Ce) of
+%%% true -> {Ce,[],St0};
+%%% false ->
+%%% {V,St1} = new_var(St0),
+%%% {V,[#iset{var=V,arg=Ce}],St1}
+%%% end.
+
+%% fold_match(MatchExpr, Pat) -> {MatchPat,Expr}.
+%% Fold nested matches into one match with aliased patterns.
+
+fold_match({match,L,P0,E0}, P) ->
+ {P1,E1} = fold_match(E0, P),
+ {{match,L,P0,P1},E1};
+fold_match(E, P) -> {P,E}.
+
+%% pattern(Pattern) -> CorePat.
+%% Transform a pattern by removing line numbers. We also normalise
+%% aliases in patterns to standard form, {alias,Pat,[Var]}.
+
+pattern({var,L,V}) -> #c_var{anno=[L],name=V};
+pattern({char,L,C}) -> #c_char{anno=[L],val=C};
+pattern({integer,L,I}) -> #c_int{anno=[L],val=I};
+pattern({float,L,F}) -> #c_float{anno=[L],val=F};
+pattern({atom,L,A}) -> #c_atom{anno=[L],val=A};
+pattern({string,L,S}) -> #c_string{anno=[L],val=S};
+pattern({nil,L}) -> #c_nil{anno=[L]};
+pattern({cons,L,H,T}) ->
+ #c_cons{anno=[L],hd=pattern(H),tl=pattern(T)};
+pattern({tuple,L,Ps}) ->
+ #c_tuple{anno=[L],es=pattern_list(Ps)};
+pattern({bin,L,Ps}) ->
+ %% We don't create a #ibinary record here, since there is
+ %% no need to hold any used/new annoations in a pattern.
+ #c_binary{anno=[L],segments=pat_bin(Ps)};
+pattern({match,_,P1,P2}) ->
+ pat_alias(pattern(P1), pattern(P2)).
+
+%% bin_pattern_list([BinElement]) -> [BinSeg].
+
+pat_bin(Ps) -> map(fun pat_segment/1, Ps).
+
+pat_segment({bin_element,_,Term,Size,[Type,{unit,Unit}|Flags]}) ->
+ #c_bitstr{val=pattern(Term),size=pattern(Size),
+ unit=core_lib:make_literal(Unit),
+ type=core_lib:make_literal(Type),
+ flags=core_lib:make_literal(Flags)}.
+
+%% pat_alias(CorePat, CorePat) -> AliasPat.
+%% Normalise aliases. Trap bad aliases by throwing 'nomatch'.
+
+pat_alias(#c_var{name=V1}, P2) -> #c_alias{var=#c_var{name=V1},pat=P2};
+pat_alias(P1, #c_var{name=V2}) -> #c_alias{var=#c_var{name=V2},pat=P1};
+pat_alias(#c_cons{}=Cons, #c_string{anno=A,val=[H|T]}=S) ->
+ pat_alias(Cons, #c_cons{anno=A,hd=#c_char{anno=A,val=H},
+ tl=S#c_string{val=T}});
+pat_alias(#c_string{anno=A,val=[H|T]}=S, #c_cons{}=Cons) ->
+ pat_alias(#c_cons{anno=A,hd=#c_char{anno=A,val=H},
+ tl=S#c_string{val=T}}, Cons);
+pat_alias(#c_nil{}=Nil, #c_string{val=[]}) ->
+ Nil;
+pat_alias(#c_string{val=[]}, #c_nil{}=Nil) ->
+ Nil;
+pat_alias(#c_cons{anno=A,hd=H1,tl=T1}, #c_cons{hd=H2,tl=T2}) ->
+ #c_cons{anno=A,hd=pat_alias(H1, H2),tl=pat_alias(T1, T2)};
+pat_alias(#c_tuple{es=Es1}, #c_tuple{es=Es2}) ->
+ #c_tuple{es=pat_alias_list(Es1, Es2)};
+pat_alias(#c_char{val=C}=Char, #c_int{val=C}) ->
+ Char;
+pat_alias(#c_int{val=C}, #c_char{val=C}=Char) ->
+ Char;
+pat_alias(#c_alias{var=V1,pat=P1},
+ #c_alias{var=V2,pat=P2}) ->
+ if V1 == V2 -> pat_alias(P1, P2);
+ true -> #c_alias{var=V1,pat=#c_alias{var=V2,pat=pat_alias(P1, P2)}}
+ end;
+pat_alias(#c_alias{var=V1,pat=P1}, P2) ->
+ #c_alias{var=V1,pat=pat_alias(P1, P2)};
+pat_alias(P1, #c_alias{var=V2,pat=P2}) ->
+ #c_alias{var=V2,pat=pat_alias(P1, P2)};
+pat_alias(P, P) -> P;
+pat_alias(_, _) -> throw(nomatch).
+
+%% pat_alias_list([A1], [A2]) -> [A].
+
+pat_alias_list([A1|A1s], [A2|A2s]) ->
+ [pat_alias(A1, A2)|pat_alias_list(A1s, A2s)];
+pat_alias_list([], []) -> [];
+pat_alias_list(_, _) -> throw(nomatch).
+
+%% pattern_list([P]) -> [P].
+
+pattern_list(Ps) -> map(fun pattern/1, Ps).
+
+%% first([A]) -> [A].
+%% last([A]) -> A.
+
+first([_]) -> [];
+first([H|T]) -> [H|first(T)].
+
+last([L]) -> L;
+last([_|T]) -> last(T).
+
+%% make_vars([Name]) -> [{Var,Name}].
+
+make_vars(Vs) -> [ #c_var{name=V} || V <- Vs ].
+
+%% new_fun_name(Type, State) -> {FunName,State}.
+
+new_fun_name(Type, #core{fcount=C}=St) ->
+ {list_to_atom(Type ++ "$^" ++ integer_to_list(C)),St#core{fcount=C+1}}.
+
+%% new_var_name(State) -> {VarName,State}.
+
+new_var_name(#core{vcount=C}=St) ->
+ {list_to_atom("cor" ++ integer_to_list(C)),St#core{vcount=C + 1}}.
+
+%% new_var(State) -> {{var,Name},State}.
+%% new_var(LineAnno, State) -> {{var,Name},State}.
+
+new_var(St) ->
+ new_var([], St).
+
+new_var(Anno, St0) ->
+ {New,St} = new_var_name(St0),
+ {#c_var{anno=Anno,name=New},St}.
+
+%% new_vars(Count, State) -> {[Var],State}.
+%% new_vars(Anno, Count, State) -> {[Var],State}.
+%% Make Count new variables.
+
+new_vars(N, St) -> new_vars_1(N, [], St, []).
+new_vars(Anno, N, St) -> new_vars_1(N, Anno, St, []).
+
+new_vars_1(N, Anno, St0, Vs) when N > 0 ->
+ {V,St1} = new_var(Anno, St0),
+ new_vars_1(N-1, Anno, St1, [V|Vs]);
+new_vars_1(0, _, St, Vs) -> {Vs,St}.
+
+fail_clause(Pats, A) ->
+ #iclause{anno=#a{anno=[compiler_generated]},
+ pats=Pats,guard=[],
+ body=[#iprimop{anno=#a{},name=#c_atom{val=match_fail},args=[A]}]}.
+
+ubody(B, St) -> uexpr(B, [], St).
+
+%% uclauses([Lclause], [KnownVar], State) -> {[Lclause],State}.
+
+uclauses(Lcs, Ks, St0) ->
+ mapfoldl(fun (Lc, St) -> uclause(Lc, Ks, St) end, St0, Lcs).
+
+%% uclause(Lclause, [KnownVar], State) -> {Lclause,State}.
+
+uclause(Cl0, Ks, St0) ->
+ {Cl1,_Pvs,Used,New,St1} = uclause(Cl0, Ks, Ks, St0),
+ A0 = get_ianno(Cl1),
+ A = A0#a{us=Used,ns=New},
+ {Cl1#iclause{anno=A},St1}.
+
+uclause(#iclause{anno=Anno,pats=Ps0,guard=G0,body=B0}, Pks, Ks0, St0) ->
+ {Ps1,Pg,Pvs,Pus,St1} = upattern_list(Ps0, Pks, St0),
+ Pu = union(Pus, intersection(Pvs, Ks0)),
+ Pn = subtract(Pvs, Pu),
+ Ks1 = union(Pn, Ks0),
+ {G1,St2} = uguard(Pg, G0, Ks1, St1),
+ Gu = used_in_any(G1),
+ Gn = new_in_any(G1),
+ Ks2 = union(Gn, Ks1),
+ {B1,St3} = uexprs(B0, Ks2, St2),
+ Used = intersection(union([Pu,Gu,used_in_any(B1)]), Ks0),
+ New = union([Pn,Gn,new_in_any(B1)]),
+ {#iclause{anno=Anno,pats=Ps1,guard=G1,body=B1},Pvs,Used,New,St3}.
+
+%% uguard([Test], [Kexpr], [KnownVar], State) -> {[Kexpr],State}.
+%% Build a guard expression list by folding in the equality tests.
+
+uguard([], [], _, St) -> {[],St};
+uguard(Pg, [], Ks, St) ->
+ %% No guard, so fold together equality tests.
+ uguard(first(Pg), [last(Pg)], Ks, St);
+uguard(Pg, Gs0, Ks, St0) ->
+ %% Gs0 must contain at least one element here.
+ {Gs3,St5} = foldr(fun (T, {Gs1,St1}) ->
+ {L,St2} = new_var(St1),
+ {R,St3} = new_var(St2),
+ {[#iset{var=L,arg=T}] ++ first(Gs1) ++
+ [#iset{var=R,arg=last(Gs1)},
+ #icall{anno=#a{}, %Must have an #a{}
+ module=#c_atom{val=erlang},
+ name=#c_atom{val='and'},
+ args=[L,R]}],
+ St3}
+ end, {Gs0,St0}, Pg),
+ %%ok = io:fwrite("core ~w: ~p~n", [?LINE,Gs3]),
+ uexprs(Gs3, Ks, St5).
+
+%% uexprs([Kexpr], [KnownVar], State) -> {[Kexpr],State}.
+
+uexprs([#imatch{anno=A,pat=P0,arg=Arg,fc=Fc}|Les], Ks, St0) ->
+ %% Optimise for simple set of unbound variable.
+ case upattern(P0, Ks, St0) of
+ {#c_var{},[],_Pvs,_Pus,_} ->
+ %% Throw our work away and just set to iset.
+ uexprs([#iset{var=P0,arg=Arg}|Les], Ks, St0);
+ _Other ->
+ %% Throw our work away and set to icase.
+ if
+ Les == [] ->
+ %% Need to explicitly return match "value", make
+ %% safe for efficiency.
+ {La,Lps,St1} = force_safe(Arg, St0),
+ Mc = #iclause{anno=A,pats=[P0],guard=[],body=[La]},
+ uexprs(Lps ++ [#icase{anno=A,
+ args=[La],clauses=[Mc],fc=Fc}], Ks, St1);
+ true ->
+ Mc = #iclause{anno=A,pats=[P0],guard=[],body=Les},
+ uexprs([#icase{anno=A,args=[Arg],
+ clauses=[Mc],fc=Fc}], Ks, St0)
+ end
+ end;
+uexprs([Le0|Les0], Ks, St0) ->
+ {Le1,St1} = uexpr(Le0, Ks, St0),
+ {Les1,St2} = uexprs(Les0, union((core_lib:get_anno(Le1))#a.ns, Ks), St1),
+ {[Le1|Les1],St2};
+uexprs([], _, St) -> {[],St}.
+
+uexpr(#iset{anno=A,var=V,arg=A0}, Ks, St0) ->
+ {A1,St1} = uexpr(A0, Ks, St0),
+ {#iset{anno=A#a{us=del_element(V#c_var.name, (core_lib:get_anno(A1))#a.us),
+ ns=add_element(V#c_var.name, (core_lib:get_anno(A1))#a.ns)},
+ var=V,arg=A1},St1};
+%% imatch done in uexprs.
+uexpr(#iletrec{anno=A,defs=Fs0,body=B0}, Ks, St0) ->
+ %%ok = io:fwrite("~w: ~p~n", [?LINE,{Fs0,B0}]),
+ {Fs1,St1} = mapfoldl(fun ({Name,F0}, St0) ->
+ {F1,St1} = uexpr(F0, Ks, St0),
+ {{Name,F1},St1}
+ end, St0, Fs0),
+ {B1,St2} = uexprs(B0, Ks, St1),
+ Used = used_in_any(map(fun ({_,F}) -> F end, Fs1) ++ B1),
+ {#iletrec{anno=A#a{us=Used,ns=[]},defs=Fs1,body=B1},St2};
+uexpr(#icase{anno=A,args=As0,clauses=Cs0,fc=Fc0}, Ks, St0) ->
+ %% As0 will never generate new variables.
+ {As1,St1} = uexpr_list(As0, Ks, St0),
+ {Cs1,St2} = uclauses(Cs0, Ks, St1),
+ {Fc1,St3} = uclause(Fc0, Ks, St2),
+ Used = union(used_in_any(As1), used_in_any(Cs1)),
+ New = new_in_all(Cs1),
+ {#icase{anno=A#a{us=Used,ns=New},args=As1,clauses=Cs1,fc=Fc1},St3};
+uexpr(#ifun{anno=A,id=Id,vars=As,clauses=Cs0,fc=Fc0}, Ks0, St0) ->
+ Avs = lit_list_vars(As),
+ Ks1 = union(Avs, Ks0),
+ {Cs1,St1} = ufun_clauses(Cs0, Ks1, St0),
+ {Fc1,St2} = ufun_clause(Fc0, Ks1, St1),
+ Used = subtract(intersection(used_in_any(Cs1), Ks0), Avs),
+ {#ifun{anno=A#a{us=Used,ns=[]},id=Id,vars=As,clauses=Cs1,fc=Fc1},St2};
+uexpr(#iapply{anno=A,op=Op,args=As}, _, St) ->
+ Used = union(lit_vars(Op), lit_list_vars(As)),
+ {#iapply{anno=A#a{us=Used},op=Op,args=As},St};
+uexpr(#iprimop{anno=A,name=Name,args=As}, _, St) ->
+ Used = lit_list_vars(As),
+ {#iprimop{anno=A#a{us=Used},name=Name,args=As},St};
+uexpr(#icall{anno=A,module=Mod,name=Name,args=As}, _, St) ->
+ Used = union([lit_vars(Mod),lit_vars(Name),lit_list_vars(As)]),
+ {#icall{anno=A#a{us=Used},module=Mod,name=Name,args=As},St};
+uexpr(#itry{anno=A,args=As0,vars=Vs,body=Bs0,evars=Evs,handler=Hs0}, Ks, St0) ->
+ %% Note that we export only from body and exception.
+ {As1,St1} = uexprs(As0, Ks, St0),
+ {Bs1,St2} = uexprs(Bs0, Ks, St1),
+ {Hs1,St3} = uexprs(Hs0, Ks, St2),
+ Used = intersection(used_in_any(Bs1++Hs1++As1), Ks),
+ New = new_in_all(Bs1++Hs1),
+ {#itry{anno=A#a{us=Used,ns=New},
+ args=As1,vars=Vs,body=Bs1,evars=Evs,handler=Hs1},St3};
+uexpr(#icatch{anno=A,body=Es0}, Ks, St0) ->
+ {Es1,St1} = uexprs(Es0, Ks, St0),
+ {#icatch{anno=A#a{us=used_in_any(Es1)},body=Es1},St1};
+uexpr(#ireceive1{anno=A,clauses=Cs0}, Ks, St0) ->
+ {Cs1,St1} = uclauses(Cs0, Ks, St0),
+ {#ireceive1{anno=A#a{us=used_in_any(Cs1),ns=new_in_all(Cs1)},
+ clauses=Cs1},St1};
+uexpr(#ireceive2{anno=A,clauses=Cs0,timeout=Te0,action=Tes0}, Ks, St0) ->
+ %% Te0 will never generate new variables.
+ {Te1,St1} = uexpr(Te0, Ks, St0),
+ {Cs1,St2} = uclauses(Cs0, Ks, St1),
+ {Tes1,St3} = uexprs(Tes0, Ks, St2),
+ Used = union([used_in_any(Cs1),used_in_any(Tes1),
+ (core_lib:get_anno(Te1))#a.us]),
+ New = case Cs1 of
+ [] -> new_in_any(Tes1);
+ _ -> intersection(new_in_all(Cs1), new_in_any(Tes1))
+ end,
+ {#ireceive2{anno=A#a{us=Used,ns=New},
+ clauses=Cs1,timeout=Te1,action=Tes1},St3};
+uexpr(#iprotect{anno=A,body=Es0}, Ks, St0) ->
+ {Es1,St1} = uexprs(Es0, Ks, St0),
+ Used = used_in_any(Es1),
+ {#iprotect{anno=A#a{us=Used},body=Es1},St1}; %No new variables escape!
+uexpr(#ibinary{anno=A,segments=Ss}, _, St) ->
+ Used = bitstr_vars(Ss),
+ {#ibinary{anno=A#a{us=Used},segments=Ss},St};
+uexpr(Lit, _, St) ->
+ true = core_lib:is_simple(Lit), %Sanity check!
+ Vs = lit_vars(Lit),
+ Anno = core_lib:get_anno(Lit),
+ {core_lib:set_anno(Lit, #a{us=Vs,anno=Anno}),St}.
+
+uexpr_list(Les0, Ks, St0) ->
+ mapfoldl(fun (Le, St) -> uexpr(Le, Ks, St) end, St0, Les0).
+
+%% ufun_clauses([Lclause], [KnownVar], State) -> {[Lclause],State}.
+
+ufun_clauses(Lcs, Ks, St0) ->
+ mapfoldl(fun (Lc, St) -> ufun_clause(Lc, Ks, St) end, St0, Lcs).
+
+%% ufun_clause(Lclause, [KnownVar], State) -> {Lclause,State}.
+
+ufun_clause(Cl0, Ks, St0) ->
+ {Cl1,Pvs,Used,_,St1} = uclause(Cl0, [], Ks, St0),
+ A0 = get_ianno(Cl1),
+ A = A0#a{us=subtract(intersection(Used, Ks), Pvs),ns=[]},
+ {Cl1#iclause{anno=A},St1}.
+
+%% upattern(Pat, [KnownVar], State) ->
+%% {Pat,[GuardTest],[NewVar],[UsedVar],State}.
+
+upattern(#c_var{name='_'}, _, St0) ->
+ {New,St1} = new_var_name(St0),
+ {#c_var{name=New},[],[New],[],St1};
+upattern(#c_var{name=V}=Var, Ks, St0) ->
+ case is_element(V, Ks) of
+ true ->
+ {N,St1} = new_var_name(St0),
+ New = #c_var{name=N},
+ Test = #icall{anno=#a{us=add_element(N, [V])},
+ module=#c_atom{val=erlang},
+ name=#c_atom{val='=:='},
+ args=[New,Var]},
+ %% Test doesn't need protecting.
+ {New,[Test],[N],[],St1};
+ false -> {Var,[],[V],[],St0}
+ end;
+upattern(#c_cons{hd=H0,tl=T0}=Cons, Ks, St0) ->
+ {H1,Hg,Hv,Hu,St1} = upattern(H0, Ks, St0),
+ {T1,Tg,Tv,Tu,St2} = upattern(T0, union(Hv, Ks), St1),
+ {Cons#c_cons{hd=H1,tl=T1},Hg ++ Tg,union(Hv, Tv),union(Hu, Tu),St2};
+upattern(#c_tuple{es=Es0}=Tuple, Ks, St0) ->
+ {Es1,Esg,Esv,Eus,St1} = upattern_list(Es0, Ks, St0),
+ {Tuple#c_tuple{es=Es1},Esg,Esv,Eus,St1};
+upattern(#c_binary{segments=Es0}=Bin, Ks, St0) ->
+ {Es1,Esg,Esv,Eus,St1} = upat_bin(Es0, Ks, St0),
+ {Bin#c_binary{segments=Es1},Esg,Esv,Eus,St1};
+upattern(#c_alias{var=V0,pat=P0}=Alias, Ks, St0) ->
+ {V1,Vg,Vv,Vu,St1} = upattern(V0, Ks, St0),
+ {P1,Pg,Pv,Pu,St2} = upattern(P0, union(Vv, Ks), St1),
+ {Alias#c_alias{var=V1,pat=P1},Vg ++ Pg,union(Vv, Pv),union(Vu, Pu),St2};
+upattern(Other, _, St) -> {Other,[],[],[],St}. %Constants
+
+%% upattern_list([Pat], [KnownVar], State) ->
+%% {[Pat],[GuardTest],[NewVar],[UsedVar],State}.
+
+upattern_list([P0|Ps0], Ks, St0) ->
+ {P1,Pg,Pv,Pu,St1} = upattern(P0, Ks, St0),
+ {Ps1,Psg,Psv,Psu,St2} = upattern_list(Ps0, union(Pv, Ks), St1),
+ {[P1|Ps1],Pg ++ Psg,union(Pv, Psv),union(Pu, Psu),St2};
+upattern_list([], _, St) -> {[],[],[],[],St}.
+
+%% upat_bin([Pat], [KnownVar], State) ->
+%% {[Pat],[GuardTest],[NewVar],[UsedVar],State}.
+upat_bin(Es0, Ks, St0) ->
+ upat_bin(Es0, Ks, [], St0).
+
+%% upat_bin([Pat], [KnownVar], [LocalVar], State) ->
+%% {[Pat],[GuardTest],[NewVar],[UsedVar],State}.
+upat_bin([P0|Ps0], Ks, Bs, St0) ->
+ {P1,Pg,Pv,Pu,Bs1,St1} = upat_element(P0, Ks, Bs, St0),
+ {Ps1,Psg,Psv,Psu,St2} = upat_bin(Ps0, union(Pv, Ks), Bs1, St1),
+ {[P1|Ps1],Pg ++ Psg,union(Pv, Psv),union(Pu, Psu),St2};
+upat_bin([], _, _, St) -> {[],[],[],[],St}.
+
+
+%% upat_element(Segment, [KnownVar], [LocalVar], State) ->
+%% {Segment,[GuardTest],[NewVar],[UsedVar],[LocalVar],State}
+upat_element(#c_bitstr{val=H0,size=Sz}=Seg, Ks, Bs, St0) ->
+ {H1,Hg,Hv,[],St1} = upattern(H0, Ks, St0),
+ Bs1 = case H0 of
+ #c_var{name=Hname} ->
+ case H1 of
+ #c_var{name=Hname} ->
+ Bs;
+ #c_var{name=Other} ->
+ [{Hname, Other}|Bs]
+ end;
+ _ ->
+ Bs
+ end,
+ {Sz1, Us} = case Sz of
+ #c_var{name=Vname} ->
+ rename_bitstr_size(Vname, Bs);
+ _Other -> {Sz, []}
+ end,
+ {Seg#c_bitstr{val=H1, size=Sz1},Hg,Hv,Us,Bs1,St1}.
+
+rename_bitstr_size(V, [{V, N}|_]) ->
+ New = #c_var{name=N},
+ {New, [N]};
+rename_bitstr_size(V, [_|Rest]) ->
+ rename_bitstr_size(V, Rest);
+rename_bitstr_size(V, []) ->
+ Old = #c_var{name=V},
+ {Old, [V]}.
+
+used_in_any(Les) ->
+ foldl(fun (Le, Ns) -> union((core_lib:get_anno(Le))#a.us, Ns) end,
+ [], Les).
+
+new_in_any(Les) ->
+ foldl(fun (Le, Ns) -> union((core_lib:get_anno(Le))#a.ns, Ns) end,
+ [], Les).
+
+new_in_all([Le|Les]) ->
+ foldl(fun (L, Ns) -> intersection((core_lib:get_anno(L))#a.ns, Ns) end,
+ (core_lib:get_anno(Le))#a.ns, Les);
+new_in_all([]) -> [].
+
+%% The AfterVars are the variables which are used afterwards. We need
+%% this to work out which variables are actually exported and used
+%% from case/receive. In subblocks/clauses the AfterVars of the block
+%% are just the exported variables.
+
+cbody(B0, St0) ->
+ {B1,_,_,St1} = cexpr(B0, [], St0),
+ {B1,St1}.
+
+%% cclause(Lclause, [AfterVar], State) -> {Cclause,State}.
+%% The AfterVars are the exported variables.
+
+cclause(#iclause{anno=#a{anno=Anno},pats=Ps,guard=G0,body=B0}, Exp, St0) ->
+ {B1,_Us1,St1} = cexprs(B0, Exp, St0),
+ {G1,St2} = cguard(G0, St1),
+ {#c_clause{anno=Anno,pats=Ps,guard=G1,body=B1},St2}.
+
+cclauses(Lcs, Es, St0) ->
+ mapfoldl(fun (Lc, St) -> cclause(Lc, Es, St) end, St0, Lcs).
+
+cguard([], St) -> {#c_atom{val=true},St};
+cguard(Gs, St0) ->
+ {G,_,St1} = cexprs(Gs, [], St0),
+ {G,St1}.
+
+%% cexprs([Lexpr], [AfterVar], State) -> {Cexpr,[AfterVar],State}.
+%% Must be sneaky here at the last expr when combining exports for the
+%% whole sequence and exports for that expr.
+
+cexprs([#iset{var=#c_var{name=Name}=Var}=Iset], As, St) ->
+ %% Make return value explicit, and make Var true top level.
+ cexprs([Iset,Var#c_var{anno=#a{us=[Name]}}], As, St);
+cexprs([Le], As, St0) ->
+ {Ce,Es,Us,St1} = cexpr(Le, As, St0),
+ Exp = make_vars(As), %The export variables
+ if
+ Es == [] -> {core_lib:make_values([Ce|Exp]),union(Us, As),St1};
+ true ->
+ {R,St2} = new_var(St1),
+ {#c_let{anno=get_lineno_anno(Ce),
+ vars=[R|make_vars(Es)],arg=Ce,
+ body=core_lib:make_values([R|Exp])},
+ union(Us, As),St2}
+ end;
+cexprs([#iset{anno=#a{anno=A},var=V,arg=A0}|Les], As0, St0) ->
+ {Ces,As1,St1} = cexprs(Les, As0, St0),
+ {A1,Es,Us,St2} = cexpr(A0, As1, St1),
+ {#c_let{anno=A,vars=[V|make_vars(Es)],arg=A1,body=Ces},
+ union(Us, As1),St2};
+cexprs([Le|Les], As0, St0) ->
+ {Ces,As1,St1} = cexprs(Les, As0, St0),
+ {Ce,Es,Us,St2} = cexpr(Le, As1, St1),
+ if
+ Es == [] ->
+ {#c_seq{arg=Ce,body=Ces},union(Us, As1),St2};
+ true ->
+ {R,St3} = new_var(St2),
+ {#c_let{vars=[R|make_vars(Es)],arg=Ce,body=Ces},
+ union(Us, As1),St3}
+ end.
+
+%% cexpr(Lexpr, [AfterVar], State) -> {Cexpr,[ExpVar],[UsedVar],State}.
+
+cexpr(#iletrec{anno=A,defs=Fs0,body=B0}, As, St0) ->
+ {Fs1,{_,St1}} = mapfoldl(fun ({Name,F0}, {Used,St0}) ->
+ {F1,[],Us,St1} = cexpr(F0, [], St0),
+ {#c_def{name=#c_fname{id=Name,arity=1},
+ val=F1},
+ {union(Us, Used),St1}}
+ end, {[],St0}, Fs0),
+ Exp = intersection(A#a.ns, As),
+ {B1,_Us,St2} = cexprs(B0, Exp, St1),
+ {#c_letrec{anno=A#a.anno,defs=Fs1,body=B1},Exp,A#a.us,St2};
+cexpr(#icase{anno=A,args=Largs,clauses=Lcs,fc=Lfc}, As, St0) ->
+ Exp = intersection(A#a.ns, As), %Exports
+ {Cargs,St1} = foldr(fun (La, {Cas,Sta}) ->
+ {Ca,[],_Us1,Stb} = cexpr(La, As, Sta),
+ {[Ca|Cas],Stb}
+ end, {[],St0}, Largs),
+ {Ccs,St2} = cclauses(Lcs, Exp, St1),
+ {Cfc,St3} = cclause(Lfc, [], St2), %Never exports
+ {#c_case{anno=A#a.anno,
+ arg=core_lib:make_values(Cargs),clauses=Ccs ++ [Cfc]},
+ Exp,A#a.us,St3};
+cexpr(#ireceive1{anno=A,clauses=Lcs}, As, St0) ->
+ Exp = intersection(A#a.ns, As), %Exports
+ {Ccs,St1} = cclauses(Lcs, Exp, St0),
+ {#c_receive{anno=A#a.anno,
+ clauses=Ccs,
+ timeout=#c_atom{val=infinity},action=#c_atom{val=true}},
+ Exp,A#a.us,St1};
+cexpr(#ireceive2{anno=A,clauses=Lcs,timeout=Lto,action=Les}, As, St0) ->
+ Exp = intersection(A#a.ns, As), %Exports
+ {Cto,[],_Us1,St1} = cexpr(Lto, As, St0),
+ {Ccs,St2} = cclauses(Lcs, Exp, St1),
+ {Ces,_Us2,St3} = cexprs(Les, Exp, St2),
+ {#c_receive{anno=A#a.anno,
+ clauses=Ccs,timeout=Cto,action=Ces},
+ Exp,A#a.us,St3};
+cexpr(#itry{anno=A,args=La,vars=Vs,body=Lb,evars=Evs,handler=Lh}, As, St0) ->
+ Exp = intersection(A#a.ns, As), %Exports
+ {Ca,_Us1,St1} = cexprs(La, [], St0),
+ {Cb,_Us2,St2} = cexprs(Lb, Exp, St1),
+ {Ch,_Us3,St3} = cexprs(Lh, Exp, St2),
+ {#c_try{anno=A#a.anno,arg=Ca,vars=Vs,body=Cb,evars=Evs,handler=Ch},
+ Exp,A#a.us,St3};
+cexpr(#icatch{anno=A,body=Les}, _As, St0) ->
+ {Ces,_Us1,St1} = cexprs(Les, [], St0), %Never export!
+ {#c_catch{body=Ces},[],A#a.us,St1};
+cexpr(#ifun{anno=A,id=Id,vars=Args,clauses=Lcs,fc=Lfc}, _As, St0) ->
+ {Ccs,St1} = cclauses(Lcs, [], St0), %NEVER export!
+ {Cfc,St2} = cclause(Lfc, [], St1),
+ Anno = A#a.anno,
+ {#c_fun{anno=Id++Anno,vars=Args,
+ body=#c_case{anno=Anno,
+ arg=core_lib:set_anno(core_lib:make_values(Args), Anno),
+ clauses=Ccs ++ [Cfc]}},
+ [],A#a.us,St2};
+cexpr(#iapply{anno=A,op=Op,args=Args}, _As, St) ->
+ {#c_apply{anno=A#a.anno,op=Op,args=Args},[],A#a.us,St};
+cexpr(#icall{anno=A,module=Mod,name=Name,args=Args}, _As, St) ->
+ {#c_call{anno=A#a.anno,module=Mod,name=Name,args=Args},[],A#a.us,St};
+cexpr(#iprimop{anno=A,name=Name,args=Args}, _As, St) ->
+ {#c_primop{anno=A#a.anno,name=Name,args=Args},[],A#a.us,St};
+cexpr(#iprotect{anno=A,body=Es}, _As, St0) ->
+ {Ce,_,St1} = cexprs(Es, [], St0),
+ V = #c_var{name='Try'}, %The names are arbitrary
+ Vs = [#c_var{name='T'},#c_var{name='R'}],
+ {#c_try{anno=A#a.anno,arg=Ce,vars=[V],body=V,
+ evars=Vs,handler=#c_atom{val=false}},
+ [],A#a.us,St1};
+cexpr(#ibinary{anno=#a{anno=Anno,us=Us},segments=Segs}, _As, St) ->
+ {#c_binary{anno=Anno,segments=Segs},[],Us,St};
+cexpr(Lit, _As, St) ->
+ true = core_lib:is_simple(Lit), %Sanity check!
+ Anno = core_lib:get_anno(Lit),
+ Vs = Anno#a.us,
+ %%Vs = lit_vars(Lit),
+ {core_lib:set_anno(Lit, Anno#a.anno),[],Vs,St}.
+
+%% lit_vars(Literal) -> [Var].
+
+lit_vars(Lit) -> lit_vars(Lit, []).
+
+lit_vars(#c_cons{hd=H,tl=T}, Vs) -> lit_vars(H, lit_vars(T, Vs));
+lit_vars(#c_tuple{es=Es}, Vs) -> lit_list_vars(Es, Vs);
+lit_vars(#c_var{name=V}, Vs) -> add_element(V, Vs);
+lit_vars(_, Vs) -> Vs. %These are atomic
+
+% lit_bin_vars(Segs, Vs) ->
+% foldl(fun (#c_bitstr{val=V,size=S}, Vs0) ->
+% lit_vars(V, lit_vars(S, Vs0))
+% end, Vs, Segs).
+
+lit_list_vars(Ls) -> lit_list_vars(Ls, []).
+
+lit_list_vars(Ls, Vs) ->
+ foldl(fun (L, Vs0) -> lit_vars(L, Vs0) end, Vs, Ls).
+
+bitstr_vars(Segs) ->
+ bitstr_vars(Segs, []).
+
+bitstr_vars(Segs, Vs) ->
+ foldl(fun (#c_bitstr{val=V,size=S}, Vs0) ->
+ lit_vars(V, lit_vars(S, Vs0))
+ end, Vs, Segs).
+
+get_ianno(Ce) ->
+ case core_lib:get_anno(Ce) of
+ #a{}=A -> A;
+ A when is_list(A) -> #a{anno=A}
+ end.
+
+get_lineno_anno(Ce) ->
+ case core_lib:get_anno(Ce) of
+ #a{anno=A} -> A;
+ A when is_list(A) -> A
+ end.
+
+
+%%%
+%%% Handling of warnings.
+%%%
+
+format_error(nomatch) -> "pattern cannot possibly match".
+
+add_warning(Line, Term, #core{ws=Ws}=St) when Line >= 0 ->
+ St#core{ws=[{Line,?MODULE,Term}|Ws]};
+add_warning(_, _, St) -> St.
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_kernel.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_kernel.erl
new file mode 100644
index 0000000000..d7c3e1add9
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_kernel.erl
@@ -0,0 +1,1567 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: v3_kernel.erl,v 1.3 2010/03/04 13:54:20 maria Exp $
+%%
+%% Purpose : Transform Core Erlang to Kernel Erlang
+
+%% Kernel erlang is like Core Erlang with a few significant
+%% differences:
+%%
+%% 1. It is flat! There are no nested calls or sub-blocks.
+%%
+%% 2. All variables are unique in a function. There is no scoping, or
+%% rather the scope is the whole function.
+%%
+%% 3. Pattern matching (in cases and receives) has been compiled.
+%%
+%% 4. The annotations contain variable usages. Seeing we have to work
+%% this out anyway for funs we might as well pass it on for free to
+%% later passes.
+%%
+%% 5. All remote-calls are to statically named m:f/a. Meta-calls are
+%% passed via erlang:apply/3.
+%%
+%% The translation is done in two passes:
+%%
+%% 1. Basic translation, translate variable/function names, flatten
+%% completely, pattern matching compilation.
+%%
+%% 2. Fun-lifting (lambda-lifting), variable usage annotation and
+%% last-call handling.
+%%
+%% All new Kexprs are created in the first pass, they are just
+%% annotated in the second.
+%%
+%% Functions and BIFs
+%%
+%% Functions are "call"ed or "enter"ed if it is a last call, their
+%% return values may be ignored. BIFs are things which are known to
+%% be internal by the compiler and can only be called, their return
+%% values cannot be ignored.
+%%
+%% Letrec's are handled rather naively. All the functions in one
+%% letrec are handled as one block to find the free variables. While
+%% this is not optimal it reflects how letrec's often are used. We
+%% don't have to worry about variable shadowing and nested letrec's as
+%% this is handled in the variable/function name translation. There
+%% is a little bit of trickery to ensure letrec transformations fit
+%% into the scheme of things.
+%%
+%% To ensure unique variable names we use a variable substitution
+%% table and keep the set of all defined variables. The nested
+%% scoping of Core means that we must also nest the substitution
+%% tables, but the defined set must be passed through to match the
+%% flat structure of Kernel and to make sure variables with the same
+%% name from different scopes get different substitutions.
+%%
+%% We also use these substitutions to handle the variable renaming
+%% necessary in pattern matching compilation.
+%%
+%% The pattern matching compilation assumes that the values of
+%% different types don't overlap. This means that as there is no
+%% character type yet in the machine all characters must be converted
+%% to integers!
+
+-module(v3_kernel).
+
+-export([module/2,format_error/1]).
+
+-import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,splitwith/2,
+ member/2,reverse/1,reverse/2]).
+-import(ordsets, [add_element/2,del_element/2,union/2,union/1,subtract/2]).
+
+-include("core_parse.hrl").
+-include("v3_kernel.hrl").
+
+%% These are not defined in v3_kernel.hrl.
+get_kanno(Kthing) -> element(2, Kthing).
+set_kanno(Kthing, Anno) -> setelement(2, Kthing, Anno).
+
+%% Internal kernel expressions and help functions.
+%% N.B. the annotation field is ALWAYS the first field!
+
+-record(ivalues, {anno=[],args}).
+-record(ifun, {anno=[],vars,body}).
+-record(iset, {anno=[],vars,arg,body}).
+-record(iletrec, {anno=[],defs}).
+-record(ialias, {anno=[],vars,pat}).
+-record(iclause, {anno=[],sub,pats,guard,body}).
+-record(ireceive_accept, {anno=[],arg}).
+-record(ireceive_next, {anno=[],arg}).
+
+%% State record for kernel translator.
+-record(kern, {func, %Current function
+ vcount=0, %Variable counter
+ fcount=0, %Fun counter
+ ds=[], %Defined variables
+ funs=[], %Fun functions
+ free=[], %Free variables
+ ws=[], %Warnings.
+ extinstr=false}). %Generate extended instructions
+
+module(#c_module{anno=A,name=M,exports=Es,attrs=As,defs=Fs}, Options) ->
+ ExtInstr = not member(no_new_apply, Options),
+ {Kfs,St} = mapfoldl(fun function/2, #kern{extinstr=ExtInstr}, Fs),
+ Kes = map(fun (#c_fname{id=N,arity=Ar}) -> {N,Ar} end, Es),
+ Kas = map(fun (#c_def{name=#c_atom{val=N},val=V}) ->
+ {N,core_lib:literal_value(V)} end, As),
+ {ok,#k_mdef{anno=A,name=M#c_atom.val,exports=Kes,attributes=Kas,
+ body=Kfs ++ St#kern.funs},St#kern.ws}.
+
+function(#c_def{anno=Af,name=#c_fname{id=F,arity=Arity},val=Body}, St0) ->
+ %%ok = io:fwrite("kern: ~p~n", [{F,Arity}]),
+ St1 = St0#kern{func={F,Arity},vcount=0,fcount=0,ds=sets:new()},
+ {#ifun{anno=Ab,vars=Kvs,body=B0},[],St2} = expr(Body, new_sub(), St1),
+ {B1,_,St3} = ubody(B0, return, St2),
+ %%B1 = B0, St3 = St2, %Null second pass
+ {#k_fdef{anno=#k{us=[],ns=[],a=Af ++ Ab},
+ func=F,arity=Arity,vars=Kvs,body=B1},St3}.
+
+%% body(Cexpr, Sub, State) -> {Kexpr,[PreKepxr],State}.
+%% Do the main sequence of a body. A body ends in an atomic value or
+%% values. Must check if vector first so do expr.
+
+body(#c_values{anno=A,es=Ces}, Sub, St0) ->
+ %% Do this here even if only in bodies.
+ {Kes,Pe,St1} = atomic_list(Ces, Sub, St0),
+ %%{Kes,Pe,St1} = expr_list(Ces, Sub, St0),
+ {#ivalues{anno=A,args=Kes},Pe,St1};
+body(#ireceive_next{anno=A}, _, St) ->
+ {#k_receive_next{anno=A},[],St};
+body(Ce, Sub, St0) ->
+ expr(Ce, Sub, St0).
+
+%% guard(Cexpr, Sub, State) -> {Kexpr,State}.
+%% We handle guards almost as bodies. The only special thing we
+%% must do is to make the final Kexpr a #k_test{}.
+%% Also, we wrap the entire guard in a try/catch which is
+%% not strictly needed, but makes sure that every 'bif' instruction
+%% will get a proper failure label.
+
+guard(G0, Sub, St0) ->
+ {G1,St1} = wrap_guard(G0, St0),
+ {Ge0,Pre,St2} = expr(G1, Sub, St1),
+ {Ge,St} = gexpr_test(Ge0, St2),
+ {pre_seq(Pre, Ge),St}.
+
+%% Wrap the entire guard in a try/catch if needed.
+
+wrap_guard(#c_try{}=Try, St) -> {Try,St};
+wrap_guard(Core, St0) ->
+ {VarName,St} = new_var_name(St0),
+ Var = #c_var{name=VarName},
+ Try = #c_try{arg=Core,vars=[Var],body=Var,evars=[],handler=#c_atom{val=false}},
+ {Try,St}.
+
+%% gexpr_test(Kexpr, State) -> {Kexpr,State}.
+%% Builds the final boolean test from the last Kexpr in a guard test.
+%% Must enter try blocks and isets and find the last Kexpr in them.
+%% This must end in a recognised BEAM test!
+
+gexpr_test(#k_bif{anno=A,op=#k_remote{mod=#k_atom{val=erlang},
+ name=#k_atom{val=is_boolean},arity=1}=Op,
+ args=Kargs}, St) ->
+ %% XXX Remove this clause in R11. For bootstrap purposes, we must
+ %% recognize erlang:is_boolean/1 here.
+ {#k_test{anno=A,op=Op,args=Kargs},St};
+gexpr_test(#k_bif{anno=A,op=#k_remote{mod=#k_atom{val=erlang},
+ name=#k_atom{val=internal_is_record},arity=3}=Op,
+ args=Kargs}, St) ->
+ {#k_test{anno=A,op=Op,args=Kargs},St};
+gexpr_test(#k_bif{anno=A,op=#k_remote{mod=#k_atom{val=erlang},
+ name=#k_atom{val=F},arity=Ar}=Op,
+ args=Kargs}=Ke, St) ->
+ %% Either convert to test if ok, or add test.
+ %% At this stage, erlang:float/1 is not a type test. (It should
+ %% have been converted to erlang:is_float/1.)
+ case erl_internal:new_type_test(F, Ar) orelse
+ erl_internal:comp_op(F, Ar) of
+ true -> {#k_test{anno=A,op=Op,args=Kargs},St};
+ false -> gexpr_test_add(Ke, St) %Add equality test
+ end;
+gexpr_test(#k_try{arg=B0,vars=[#k_var{name=X}],body=#k_var{name=X},
+ handler=#k_atom{val=false}}=Try, St0) ->
+ {B,St} = gexpr_test(B0, St0),
+ %%ok = io:fwrite("~w: ~p~n", [?LINE,{B0,B}]),
+ {Try#k_try{arg=B},St};
+gexpr_test(#iset{body=B0}=Iset, St0) ->
+ {B1,St1} = gexpr_test(B0, St0),
+ {Iset#iset{body=B1},St1};
+gexpr_test(Ke, St) -> gexpr_test_add(Ke, St). %Add equality test
+
+gexpr_test_add(Ke, St0) ->
+ Test = #k_remote{mod=#k_atom{val='erlang'},
+ name=#k_atom{val='=:='},
+ arity=2},
+ {Ae,Ap,St1} = force_atomic(Ke, St0),
+ {pre_seq(Ap, #k_test{anno=get_kanno(Ke),
+ op=Test,args=[Ae,#k_atom{val='true'}]}),St1}.
+
+%% expr(Cexpr, Sub, State) -> {Kexpr,[PreKexpr],State}.
+%% Convert a Core expression, flattening it at the same time.
+
+expr(#c_var{anno=A,name=V}, Sub, St) ->
+ {#k_var{anno=A,name=get_vsub(V, Sub)},[],St};
+expr(#c_char{anno=A,val=C}, _Sub, St) ->
+ {#k_int{anno=A,val=C},[],St}; %Convert to integers!
+expr(#c_int{anno=A,val=I}, _Sub, St) ->
+ {#k_int{anno=A,val=I},[],St};
+expr(#c_float{anno=A,val=F}, _Sub, St) ->
+ {#k_float{anno=A,val=F},[],St};
+expr(#c_atom{anno=A,val=At}, _Sub, St) ->
+ {#k_atom{anno=A,val=At},[],St};
+expr(#c_string{anno=A,val=S}, _Sub, St) ->
+ {#k_string{anno=A,val=S},[],St};
+expr(#c_nil{anno=A}, _Sub, St) ->
+ {#k_nil{anno=A},[],St};
+expr(#c_cons{anno=A,hd=Ch,tl=Ct}, Sub, St0) ->
+ %% Do cons in two steps, first the expressions left to right, then
+ %% any remaining literals right to left.
+ {Kh0,Hp0,St1} = expr(Ch, Sub, St0),
+ {Kt0,Tp0,St2} = expr(Ct, Sub, St1),
+ {Kt1,Tp1,St3} = force_atomic(Kt0, St2),
+ {Kh1,Hp1,St4} = force_atomic(Kh0, St3),
+ {#k_cons{anno=A,hd=Kh1,tl=Kt1},Hp0 ++ Tp0 ++ Tp1 ++ Hp1,St4};
+expr(#c_tuple{anno=A,es=Ces}, Sub, St0) ->
+ {Kes,Ep,St1} = atomic_list(Ces, Sub, St0),
+ {#k_tuple{anno=A,es=Kes},Ep,St1};
+expr(#c_binary{anno=A,segments=Cv}, Sub, St0) ->
+ case catch atomic_bin(Cv, Sub, St0, 0) of
+ {'EXIT',R} -> exit(R);
+ bad_element_size ->
+ Erl = #c_atom{val=erlang},
+ Name = #c_atom{val=error},
+ Args = [#c_atom{val=badarg}],
+ Fault = #c_call{module=Erl,name=Name,args=Args},
+ expr(Fault, Sub, St0);
+ {Kv,Ep,St1} ->
+ {#k_binary{anno=A,segs=Kv},Ep,St1}
+ end;
+expr(#c_fname{anno=A,arity=Ar}=Fname, Sub, St) ->
+ %% A local in an expression.
+ %% For now, these are wrapped into a fun by reverse
+ %% etha-conversion, but really, there should be exactly one
+ %% such "lambda function" for each escaping local name,
+ %% instead of one for each occurrence as done now.
+ Vs = [#c_var{name=list_to_atom("V" ++ integer_to_list(V))} ||
+ V <- integers(1, Ar)],
+ Fun = #c_fun{anno=A,vars=Vs,body=#c_apply{op=Fname,args=Vs}},
+ expr(Fun, Sub, St);
+expr(#c_fun{anno=A,vars=Cvs,body=Cb}, Sub0, St0) ->
+ {Kvs,Sub1,St1} = pattern_list(Cvs, Sub0, St0),
+ %%ok = io:fwrite("~w: ~p~n", [?LINE,{{Cvs,Sub0,St0},{Kvs,Sub1,St1}}]),
+ {Kb,Pb,St2} = body(Cb, Sub1, St1),
+ {#ifun{anno=A,vars=Kvs,body=pre_seq(Pb, Kb)},[],St2};
+expr(#c_seq{arg=Ca,body=Cb}, Sub, St0) ->
+ {Ka,Pa,St1} = body(Ca, Sub, St0),
+ case is_exit_expr(Ka) of
+ true -> {Ka,Pa,St1};
+ false ->
+ {Kb,Pb,St2} = body(Cb, Sub, St1),
+ {Kb,Pa ++ [Ka] ++ Pb,St2}
+ end;
+expr(#c_let{anno=A,vars=Cvs,arg=Ca,body=Cb}, Sub0, St0) ->
+ %%ok = io:fwrite("~w: ~p~n", [?LINE,{Cvs,Sub0,St0}]),
+ {Ka,Pa,St1} = body(Ca, Sub0, St0),
+ case is_exit_expr(Ka) of
+ true -> {Ka,Pa,St1};
+ false ->
+ {Kps,Sub1,St2} = pattern_list(Cvs, Sub0, St1),
+ %%ok = io:fwrite("~w: ~p~n", [?LINE,{Kps,Sub1,St1,St2}]),
+ %% Break known multiple values into separate sets.
+ Sets = case Ka of
+ #ivalues{args=Kas} ->
+ foldr2(fun (V, Val, Sb) ->
+ [#iset{vars=[V],arg=Val}|Sb] end,
+ [], Kps, Kas);
+ _Other ->
+ [#iset{anno=A,vars=Kps,arg=Ka}]
+ end,
+ {Kb,Pb,St3} = body(Cb, Sub1, St2),
+ {Kb,Pa ++ Sets ++ Pb,St3}
+ end;
+expr(#c_letrec{anno=A,defs=Cfs,body=Cb}, Sub0, St0) ->
+ %% Make new function names and store substitution.
+ {Fs0,{Sub1,St1}} =
+ mapfoldl(fun (#c_def{name=#c_fname{id=F,arity=Ar},val=B}, {Sub,St0}) ->
+ {N,St1} = new_fun_name(atom_to_list(F)
+ ++ "/" ++
+ integer_to_list(Ar),
+ St0),
+ {{N,B},{set_fsub(F, Ar, N, Sub),St1}}
+ end, {Sub0,St0}, Cfs),
+ %% Run translation on functions and body.
+ {Fs1,St2} = mapfoldl(fun ({N,Fd0}, St1) ->
+ {Fd1,[],St2} = expr(Fd0, Sub1, St1),
+ Fd = set_kanno(Fd1, A),
+ {{N,Fd},St2}
+ end, St1, Fs0),
+ {Kb,Pb,St3} = body(Cb, Sub1, St2),
+ {Kb,[#iletrec{anno=A,defs=Fs1}|Pb],St3};
+expr(#c_case{arg=Ca,clauses=Ccs}, Sub, St0) ->
+ {Ka,Pa,St1} = body(Ca, Sub, St0), %This is a body!
+ {Kvs,Pv,St2} = match_vars(Ka, St1), %Must have variables here!
+ {Km,St3} = kmatch(Kvs, Ccs, Sub, St2),
+ Match = flatten_seq(build_match(Kvs, Km)),
+ {last(Match),Pa ++ Pv ++ first(Match),St3};
+expr(#c_receive{anno=A,clauses=Ccs0,timeout=Ce,action=Ca}, Sub, St0) ->
+ {Ke,Pe,St1} = atomic_lit(Ce, Sub, St0), %Force this to be atomic!
+ {Rvar,St2} = new_var(St1),
+ %% Need to massage accept clauses and add reject clause before matching.
+ Ccs1 = map(fun (#c_clause{anno=Banno,body=B0}=C) ->
+ B1 = #c_seq{arg=#ireceive_accept{anno=A},body=B0},
+ C#c_clause{anno=Banno,body=B1}
+ end, Ccs0),
+ {Mpat,St3} = new_var_name(St2),
+ Rc = #c_clause{anno=[compiler_generated|A],
+ pats=[#c_var{name=Mpat}],guard=#c_atom{anno=A,val=true},
+ body=#ireceive_next{anno=A}},
+ {Km,St4} = kmatch([Rvar], Ccs1 ++ [Rc], Sub, add_var_def(Rvar, St3)),
+ {Ka,Pa,St5} = body(Ca, Sub, St4),
+ {#k_receive{anno=A,var=Rvar,body=Km,timeout=Ke,action=pre_seq(Pa, Ka)},
+ Pe,St5};
+expr(#c_apply{anno=A,op=Cop,args=Cargs}, Sub, St) ->
+ c_apply(A, Cop, Cargs, Sub, St);
+expr(#c_call{anno=A,module=M0,name=F0,args=Cargs}, Sub, St0) ->
+ {[M1,F1|Kargs],Ap,St1} = atomic_list([M0,F0|Cargs], Sub, St0),
+ Ar = length(Cargs),
+ case {M1,F1} of
+ {#k_atom{val=Ma},#k_atom{val=Fa}} ->
+ Call = case is_remote_bif(Ma, Fa, Ar) of
+ true ->
+ #k_bif{anno=A,
+ op=#k_remote{mod=M1,name=F1,arity=Ar},
+ args=Kargs};
+ false ->
+ #k_call{anno=A,
+ op=#k_remote{mod=M1,name=F1,arity=Ar},
+ args=Kargs}
+ end,
+ {Call,Ap,St1};
+ _Other when St0#kern.extinstr == false -> %Old explicit apply
+ Call = #c_call{anno=A,
+ module=#c_atom{val=erlang},
+ name=#c_atom{val=apply},
+ args=[M0,F0,make_list(Cargs)]},
+ expr(Call, Sub, St0);
+ _Other -> %New instruction in R10.
+ Call = #k_call{anno=A,
+ op=#k_remote{mod=M1,name=F1,arity=Ar},
+ args=Kargs},
+ {Call,Ap,St1}
+ end;
+expr(#c_primop{anno=A,name=#c_atom{val=match_fail},args=Cargs}, Sub, St0) ->
+ %% This special case will disappear.
+ {Kargs,Ap,St1} = atomic_list(Cargs, Sub, St0),
+ Ar = length(Cargs),
+ Call = #k_call{anno=A,op=#k_internal{name=match_fail,arity=Ar},args=Kargs},
+ {Call,Ap,St1};
+expr(#c_primop{anno=A,name=#c_atom{val=N},args=Cargs}, Sub, St0) ->
+ {Kargs,Ap,St1} = atomic_list(Cargs, Sub, St0),
+ Ar = length(Cargs),
+ {#k_bif{anno=A,op=#k_internal{name=N,arity=Ar},args=Kargs},Ap,St1};
+expr(#c_try{anno=A,arg=Ca,vars=Cvs,body=Cb,evars=Evs,handler=Ch}, Sub0, St0) ->
+ %% The normal try expression. The body and exception handler
+ %% variables behave as let variables.
+ {Ka,Pa,St1} = body(Ca, Sub0, St0),
+ {Kcvs,Sub1,St2} = pattern_list(Cvs, Sub0, St1),
+ {Kb,Pb,St3} = body(Cb, Sub1, St2),
+ {Kevs,Sub2,St4} = pattern_list(Evs, Sub0, St3),
+ {Kh,Ph,St5} = body(Ch, Sub2, St4),
+ {#k_try{anno=A,arg=pre_seq(Pa, Ka),
+ vars=Kcvs,body=pre_seq(Pb, Kb),
+ evars=Kevs,handler=pre_seq(Ph, Kh)},[],St5};
+expr(#c_catch{anno=A,body=Cb}, Sub, St0) ->
+ {Kb,Pb,St1} = body(Cb, Sub, St0),
+ {#k_catch{anno=A,body=pre_seq(Pb, Kb)},[],St1};
+%% Handle internal expressions.
+expr(#ireceive_accept{anno=A}, _Sub, St) -> {#k_receive_accept{anno=A},[],St}.
+
+%% expr_list([Cexpr], Sub, State) -> {[Kexpr],[PreKexpr],State}.
+
+% expr_list(Ces, Sub, St) ->
+% foldr(fun (Ce, {Kes,Esp,St0}) ->
+% {Ke,Ep,St1} = expr(Ce, Sub, St0),
+% {[Ke|Kes],Ep ++ Esp,St1}
+% end, {[],[],St}, Ces).
+
+%% match_vars(Kexpr, State) -> {[Kvar],[PreKexpr],State}.
+%% Force return from body into a list of variables.
+
+match_vars(#ivalues{args=As}, St) ->
+ foldr(fun (Ka, {Vs,Vsp,St0}) ->
+ {V,Vp,St1} = force_variable(Ka, St0),
+ {[V|Vs],Vp ++ Vsp,St1}
+ end, {[],[],St}, As);
+match_vars(Ka, St0) ->
+ {V,Vp,St1} = force_variable(Ka, St0),
+ {[V],Vp,St1}.
+
+%% c_apply(A, Op, [Carg], Sub, State) -> {Kexpr,[PreKexpr],State}.
+%% Transform application, detect which are guaranteed to be bifs.
+
+c_apply(A, #c_fname{anno=Ra,id=F0,arity=Ar}, Cargs, Sub, St0) ->
+ {Kargs,Ap,St1} = atomic_list(Cargs, Sub, St0),
+ F1 = get_fsub(F0, Ar, Sub), %Has it been rewritten
+ {#k_call{anno=A,op=#k_local{anno=Ra,name=F1,arity=Ar},args=Kargs},
+ Ap,St1};
+c_apply(A, Cop, Cargs, Sub, St0) ->
+ {Kop,Op,St1} = variable(Cop, Sub, St0),
+ {Kargs,Ap,St2} = atomic_list(Cargs, Sub, St1),
+ {#k_call{anno=A,op=Kop,args=Kargs},Op ++ Ap,St2}.
+
+flatten_seq(#iset{anno=A,vars=Vs,arg=Arg,body=B}) ->
+ [#iset{anno=A,vars=Vs,arg=Arg}|flatten_seq(B)];
+flatten_seq(Ke) -> [Ke].
+
+pre_seq([#iset{anno=A,vars=Vs,arg=Arg,body=B}|Ps], K) ->
+ B = undefined, %Assertion.
+ #iset{anno=A,vars=Vs,arg=Arg,body=pre_seq(Ps, K)};
+pre_seq([P|Ps], K) ->
+ #iset{vars=[],arg=P,body=pre_seq(Ps, K)};
+pre_seq([], K) -> K.
+
+%% atomic_lit(Cexpr, Sub, State) -> {Katomic,[PreKexpr],State}.
+%% Convert a Core expression making sure the result is an atomic
+%% literal.
+
+atomic_lit(Ce, Sub, St0) ->
+ {Ke,Kp,St1} = expr(Ce, Sub, St0),
+ {Ka,Ap,St2} = force_atomic(Ke, St1),
+ {Ka,Kp ++ Ap,St2}.
+
+force_atomic(Ke, St0) ->
+ case is_atomic(Ke) of
+ true -> {Ke,[],St0};
+ false ->
+ {V,St1} = new_var(St0),
+ {V,[#iset{vars=[V],arg=Ke}],St1}
+ end.
+
+% force_atomic_list(Kes, St) ->
+% foldr(fun (Ka, {As,Asp,St0}) ->
+% {A,Ap,St1} = force_atomic(Ka, St0),
+% {[A|As],Ap ++ Asp,St1}
+% end, {[],[],St}, Kes).
+
+atomic_bin([#c_bitstr{anno=A,val=E0,size=S0,unit=U,type=T,flags=Fs}|Es0],
+ Sub, St0, B0) ->
+ {E,Ap1,St1} = atomic_lit(E0, Sub, St0),
+ {S1,Ap2,St2} = atomic_lit(S0, Sub, St1),
+ validate_bin_element_size(S1),
+ U0 = core_lib:literal_value(U),
+ Fs0 = core_lib:literal_value(Fs),
+ {B1,Fs1} = aligned(B0, S1, U0, Fs0),
+ {Es,Ap3,St3} = atomic_bin(Es0, Sub, St2, B1),
+ {#k_bin_seg{anno=A,size=S1,
+ unit=U0,
+ type=core_lib:literal_value(T),
+ flags=Fs1,
+ seg=E,next=Es},
+ Ap1++Ap2++Ap3,St3};
+atomic_bin([], _Sub, St, _Bits) -> {#k_bin_end{},[],St}.
+
+validate_bin_element_size(#k_var{}) -> ok;
+validate_bin_element_size(#k_int{val=V}) when V >= 0 -> ok;
+validate_bin_element_size(#k_atom{val=all}) -> ok;
+validate_bin_element_size(_) -> throw(bad_element_size).
+
+%% atomic_list([Cexpr], Sub, State) -> {[Kexpr],[PreKexpr],State}.
+
+atomic_list(Ces, Sub, St) ->
+ foldr(fun (Ce, {Kes,Esp,St0}) ->
+ {Ke,Ep,St1} = atomic_lit(Ce, Sub, St0),
+ {[Ke|Kes],Ep ++ Esp,St1}
+ end, {[],[],St}, Ces).
+
+%% is_atomic(Kexpr) -> boolean().
+%% Is a Kexpr atomic? Strings are NOT considered atomic!
+
+is_atomic(#k_int{}) -> true;
+is_atomic(#k_float{}) -> true;
+is_atomic(#k_atom{}) -> true;
+%%is_atomic(#k_char{}) -> true; %No characters
+%%is_atomic(#k_string{}) -> true;
+is_atomic(#k_nil{}) -> true;
+is_atomic(#k_var{}) -> true;
+is_atomic(_) -> false.
+
+%% variable(Cexpr, Sub, State) -> {Kvar,[PreKexpr],State}.
+%% Convert a Core expression making sure the result is a variable.
+
+variable(Ce, Sub, St0) ->
+ {Ke,Kp,St1} = expr(Ce, Sub, St0),
+ {Kv,Vp,St2} = force_variable(Ke, St1),
+ {Kv,Kp ++ Vp,St2}.
+
+force_variable(#k_var{}=Ke, St) -> {Ke,[],St};
+force_variable(Ke, St0) ->
+ {V,St1} = new_var(St0),
+ {V,[#iset{vars=[V],arg=Ke}],St1}.
+
+%% pattern(Cpat, Sub, State) -> {Kpat,Sub,State}.
+%% Convert patterns. Variables shadow so rename variables that are
+%% already defined.
+
+pattern(#c_var{anno=A,name=V}, Sub, St0) ->
+ case sets:is_element(V, St0#kern.ds) of
+ true ->
+ {New,St1} = new_var_name(St0),
+ {#k_var{anno=A,name=New},
+ set_vsub(V, New, Sub),
+ St1#kern{ds=sets:add_element(New, St1#kern.ds)}};
+ false ->
+ {#k_var{anno=A,name=V},Sub,
+ St0#kern{ds=sets:add_element(V, St0#kern.ds)}}
+ end;
+pattern(#c_char{anno=A,val=C}, Sub, St) ->
+ {#k_int{anno=A,val=C},Sub,St}; %Convert to integers!
+pattern(#c_int{anno=A,val=I}, Sub, St) ->
+ {#k_int{anno=A,val=I},Sub,St};
+pattern(#c_float{anno=A,val=F}, Sub, St) ->
+ {#k_float{anno=A,val=F},Sub,St};
+pattern(#c_atom{anno=A,val=At}, Sub, St) ->
+ {#k_atom{anno=A,val=At},Sub,St};
+pattern(#c_string{val=S}, Sub, St) ->
+ L = foldr(fun (C, T) -> #k_cons{hd=#k_int{val=C},tl=T} end,
+ #k_nil{}, S),
+ {L,Sub,St};
+pattern(#c_nil{anno=A}, Sub, St) ->
+ {#k_nil{anno=A},Sub,St};
+pattern(#c_cons{anno=A,hd=Ch,tl=Ct}, Sub0, St0) ->
+ {Kh,Sub1,St1} = pattern(Ch, Sub0, St0),
+ {Kt,Sub2,St2} = pattern(Ct, Sub1, St1),
+ {#k_cons{anno=A,hd=Kh,tl=Kt},Sub2,St2};
+pattern(#c_tuple{anno=A,es=Ces}, Sub0, St0) ->
+ {Kes,Sub1,St1} = pattern_list(Ces, Sub0, St0),
+ {#k_tuple{anno=A,es=Kes},Sub1,St1};
+pattern(#c_binary{anno=A,segments=Cv}, Sub0, St0) ->
+ {Kv,Sub1,St1} = pattern_bin(Cv, Sub0, St0),
+ {#k_binary{anno=A,segs=Kv},Sub1,St1};
+pattern(#c_alias{anno=A,var=Cv,pat=Cp}, Sub0, St0) ->
+ {Cvs,Cpat} = flatten_alias(Cp),
+ {Kvs,Sub1,St1} = pattern_list([Cv|Cvs], Sub0, St0),
+ {Kpat,Sub2,St2} = pattern(Cpat, Sub1, St1),
+ {#ialias{anno=A,vars=Kvs,pat=Kpat},Sub2,St2}.
+
+flatten_alias(#c_alias{var=V,pat=P}) ->
+ {Vs,Pat} = flatten_alias(P),
+ {[V|Vs],Pat};
+flatten_alias(Pat) -> {[],Pat}.
+
+pattern_bin(Es, Sub, St) -> pattern_bin(Es, Sub, St, 0).
+
+pattern_bin([#c_bitstr{anno=A,val=E0,size=S0,unit=U,type=T,flags=Fs}|Es0],
+ Sub0, St0, B0) ->
+ {S1,[],St1} = expr(S0, Sub0, St0),
+ U0 = core_lib:literal_value(U),
+ Fs0 = core_lib:literal_value(Fs),
+ %%ok= io:fwrite("~w: ~p~n", [?LINE,{B0,S1,U0,Fs0}]),
+ {B1,Fs1} = aligned(B0, S1, U0, Fs0),
+ {E,Sub1,St2} = pattern(E0, Sub0, St1),
+ {Es,Sub2,St3} = pattern_bin(Es0, Sub1, St2, B1),
+ {#k_bin_seg{anno=A,size=S1,
+ unit=U0,
+ type=core_lib:literal_value(T),
+ flags=Fs1,
+ seg=E,next=Es},
+ Sub2,St3};
+pattern_bin([], Sub, St, _Bits) -> {#k_bin_end{},Sub,St}.
+
+%% pattern_list([Cexpr], Sub, State) -> {[Kexpr],Sub,State}.
+
+pattern_list(Ces, Sub, St) ->
+ foldr(fun (Ce, {Kes,Sub0,St0}) ->
+ {Ke,Sub1,St1} = pattern(Ce, Sub0, St0),
+ {[Ke|Kes],Sub1,St1}
+ end, {[],Sub,St}, Ces).
+
+%% new_sub() -> Subs.
+%% set_vsub(Name, Sub, Subs) -> Subs.
+%% subst_vsub(Name, Sub, Subs) -> Subs.
+%% get_vsub(Name, Subs) -> SubName.
+%% Add/get substitute Sub for Name to VarSub. Use orddict so we know
+%% the format is a list {Name,Sub} pairs. When adding a new
+%% substitute we fold substitute chains so we never have to search
+%% more than once.
+
+new_sub() -> orddict:new().
+
+get_vsub(V, Vsub) ->
+ case orddict:find(V, Vsub) of
+ {ok,Val} -> Val;
+ error -> V
+ end.
+
+set_vsub(V, S, Vsub) ->
+ orddict:store(V, S, Vsub).
+
+subst_vsub(V, S, Vsub0) ->
+ %% Fold chained substitutions.
+ Vsub1 = orddict:map(fun (_, V1) when V1 =:= V -> S;
+ (_, V1) -> V1
+ end, Vsub0),
+ orddict:store(V, S, Vsub1).
+
+get_fsub(F, A, Fsub) ->
+ case orddict:find({F,A}, Fsub) of
+ {ok,Val} -> Val;
+ error -> F
+ end.
+
+set_fsub(F, A, S, Fsub) ->
+ orddict:store({F,A}, S, Fsub).
+
+new_fun_name(St) ->
+ new_fun_name("anonymous", St).
+
+%% new_fun_name(Type, State) -> {FunName,State}.
+
+new_fun_name(Type, #kern{func={F,Arity},fcount=C}=St) ->
+ Name = "-" ++ atom_to_list(F) ++ "/" ++ integer_to_list(Arity) ++
+ "-" ++ Type ++ "-" ++ integer_to_list(C) ++ "-",
+ {list_to_atom(Name),St#kern{fcount=C+1}}.
+
+%% new_var_name(State) -> {VarName,State}.
+
+new_var_name(#kern{vcount=C}=St) ->
+ {list_to_atom("ker" ++ integer_to_list(C)),St#kern{vcount=C+1}}.
+
+%% new_var(State) -> {#k_var{},State}.
+
+new_var(St0) ->
+ {New,St1} = new_var_name(St0),
+ {#k_var{name=New},St1}.
+
+%% new_vars(Count, State) -> {[#k_var{}],State}.
+%% Make Count new variables.
+
+new_vars(N, St) -> new_vars(N, St, []).
+
+new_vars(N, St0, Vs) when N > 0 ->
+ {V,St1} = new_var(St0),
+ new_vars(N-1, St1, [V|Vs]);
+new_vars(0, St, Vs) -> {Vs,St}.
+
+make_vars(Vs) -> [ #k_var{name=V} || V <- Vs ].
+
+add_var_def(V, St) ->
+ St#kern{ds=sets:add_element(V#k_var.name, St#kern.ds)}.
+
+%%add_vars_def(Vs, St) ->
+%% Ds = foldl(fun (#k_var{name=V}, Ds) -> add_element(V, Ds) end,
+%% St#kern.ds, Vs),
+%% St#kern{ds=Ds}.
+
+%% is_remote_bif(Mod, Name, Arity) -> true | false.
+%% Test if function is really a BIF.
+
+is_remote_bif(erlang, is_boolean, 1) ->
+ %% XXX Remove this clause in R11. For bootstrap purposes, we must
+ %% recognize erlang:is_boolean/1 here.
+ true;
+is_remote_bif(erlang, internal_is_record, 3) -> true;
+is_remote_bif(erlang, get, 1) -> true;
+is_remote_bif(erlang, N, A) ->
+ case erl_internal:guard_bif(N, A) of
+ true -> true;
+ false ->
+ case erl_internal:type_test(N, A) of
+ true -> true;
+ false ->
+ case catch erl_internal:op_type(N, A) of
+ arith -> true;
+ bool -> true;
+ comp -> true;
+ _Other -> false %List, send or not an op
+ end
+ end
+ end;
+is_remote_bif(_, _, _) -> false.
+
+%% bif_vals(Name, Arity) -> integer().
+%% bif_vals(Mod, Name, Arity) -> integer().
+%% Determine how many return values a BIF has. Provision for BIFs to
+%% return multiple values. Only used in bodies where a BIF may be
+%% called for effect only.
+
+bif_vals(dsetelement, 3) -> 0;
+bif_vals(_, _) -> 1.
+
+bif_vals(_, _, _) -> 1.
+
+%% foldr2(Fun, Acc, List1, List2) -> Acc.
+%% Fold over two lists.
+
+foldr2(Fun, Acc0, [E1|L1], [E2|L2]) ->
+ Acc1 = Fun(E1, E2, Acc0),
+ foldr2(Fun, Acc1, L1, L2);
+foldr2(_, Acc, [], []) -> Acc.
+
+%% first([A]) -> [A].
+%% last([A]) -> A.
+
+last([L]) -> L;
+last([_|T]) -> last(T).
+
+first([_]) -> [];
+first([H|T]) -> [H|first(T)].
+
+%% This code implements the algorithm for an optimizing compiler for
+%% pattern matching given "The Implementation of Functional
+%% Programming Languages" by Simon Peyton Jones. The code is much
+%% longer as the meaning of constructors is different from the book.
+%%
+%% In Erlang many constructors can have different values, e.g. 'atom'
+%% or 'integer', whereas in the original algorithm thse would be
+%% different constructors. Our view makes it easier in later passes to
+%% handle indexing over each type.
+%%
+%% Patterns are complicated by having alias variables. The form of a
+%% pattern is Pat | {alias,Pat,[AliasVar]}. This is hidden by access
+%% functions to pattern arguments but the code must be aware of it.
+%%
+%% The compilation proceeds in two steps:
+%%
+%% 1. The patterns in the clauses to converted to lists of kernel
+%% patterns. The Core clause is now hybrid, this is easier to work
+%% with. Remove clauses with trivially false guards, this simplifies
+%% later passes. Add local defined vars and variable subs to each
+%% clause for later use.
+%%
+%% 2. The pattern matching is optimised. Variable substitutions are
+%% added to the VarSub structure and new variables are made visible.
+%% The guard and body are then converted to Kernel form.
+
+%% kmatch([Var], [Clause], Sub, State) -> {Kexpr,[PreExpr],State}.
+
+kmatch(Us, Ccs, Sub, St0) ->
+ {Cs,St1} = match_pre(Ccs, Sub, St0), %Convert clauses
+ %%Def = kernel_match_error, %The strict case
+ %% This should be a kernel expression from the first pass.
+ Def = #k_call{anno=[compiler_generated],
+ op=#k_remote{mod=#k_atom{val=erlang},
+ name=#k_atom{val=exit},
+ arity=1},
+ args=[#k_atom{val=kernel_match_error}]},
+ {Km,St2} = match(Us, Cs, Def, St1), %Do the match.
+ {Km,St2}.
+
+%% match_pre([Cclause], Sub, State) -> {[Clause],State}.
+%% Must be careful not to generate new substitutions here now!
+%% Remove clauses with trivially false guards which will never
+%% succeed.
+
+match_pre(Cs, Sub0, St) ->
+ foldr(fun (#c_clause{anno=A,pats=Ps,guard=G,body=B}, {Cs0,St0}) ->
+ case is_false_guard(G) of
+ true -> {Cs0,St0};
+ false ->
+ {Kps,Sub1,St1} = pattern_list(Ps, Sub0, St0),
+ {[#iclause{anno=A,sub=Sub1,pats=Kps,guard=G,body=B}|
+ Cs0],St1}
+ end
+ end, {[],St}, Cs).
+
+%% match([Var], [Clause], Default, State) -> {MatchExpr,State}.
+
+match([U|Us], Cs, Def, St0) ->
+ %%ok = io:format("match ~p~n", [Cs]),
+ Pcss = partition(Cs),
+ foldr(fun (Pcs, {D,St}) -> match_varcon([U|Us], Pcs, D, St) end,
+ {Def,St0}, Pcss);
+match([], Cs, Def, St) ->
+ match_guard(Cs, Def, St).
+
+%% match_guard([Clause], Default, State) -> {IfExpr,State}.
+%% Build a guard to handle guards. A guard *ALWAYS* fails if no
+%% clause matches, there will be a surrounding 'alt' to catch the
+%% failure. Drop redundant cases, i.e. those after a true guard.
+
+match_guard(Cs0, Def0, St0) ->
+ {Cs1,Def1,St1} = match_guard_1(Cs0, Def0, St0),
+ {build_alt(build_guard(Cs1), Def1),St1}.
+
+match_guard_1([#iclause{anno=A,sub=Sub,guard=G,body=B}|Cs0], Def0, St0) ->
+ case is_true_guard(G) of
+ true ->
+ %% The true clause body becomes the default.
+ {Kb,Pb,St1} = body(B, Sub, St0),
+ Line = get_line(A),
+ St2 = maybe_add_warning(Cs0, Line, St1),
+ St = maybe_add_warning(Def0, Line, St2),
+ {[],pre_seq(Pb, Kb),St};
+ false ->
+ {Kg,St1} = guard(G, Sub, St0),
+ {Kb,Pb,St2} = body(B, Sub, St1),
+ {Cs1,Def1,St3} = match_guard_1(Cs0, Def0, St2),
+ {[#k_guard_clause{guard=Kg,body=pre_seq(Pb, Kb)}|Cs1],
+ Def1,St3}
+ end;
+match_guard_1([], Def, St) -> {[],Def,St}.
+
+maybe_add_warning([C|_], Line, St) ->
+ maybe_add_warning(C, Line, St);
+maybe_add_warning([], _Line, St) -> St;
+maybe_add_warning(fail, _Line, St) -> St;
+maybe_add_warning(Ke, MatchLine, St) ->
+ case get_kanno(Ke) of
+ [compiler_generated|_] -> St;
+ Anno ->
+ Line = get_line(Anno),
+ Warn = case MatchLine of
+ none -> nomatch_shadow;
+ _ -> {nomatch_shadow,MatchLine}
+ end,
+ add_warning(Line, Warn, St)
+ end.
+
+get_line([Line|_]) when is_integer(Line) -> Line;
+get_line([_|T]) -> get_line(T);
+get_line([]) -> none.
+
+
+%% is_true_guard(Guard) -> boolean().
+%% is_false_guard(Guard) -> boolean().
+%% Test if a guard is either trivially true/false. This has probably
+%% already been optimised away, but what the heck!
+
+is_true_guard(G) -> guard_value(G) == true.
+is_false_guard(G) -> guard_value(G) == false.
+
+%% guard_value(Guard) -> true | false | unknown.
+
+guard_value(#c_atom{val=true}) -> true;
+guard_value(#c_atom{val=false}) -> false;
+guard_value(#c_call{module=#c_atom{val=erlang},
+ name=#c_atom{val='not'},
+ args=[A]}) ->
+ case guard_value(A) of
+ true -> false;
+ false -> true;
+ unknown -> unknown
+ end;
+guard_value(#c_call{module=#c_atom{val=erlang},
+ name=#c_atom{val='and'},
+ args=[Ca,Cb]}) ->
+ case guard_value(Ca) of
+ true -> guard_value(Cb);
+ false -> false;
+ unknown ->
+ case guard_value(Cb) of
+ false -> false;
+ _Other -> unknown
+ end
+ end;
+guard_value(#c_call{module=#c_atom{val=erlang},
+ name=#c_atom{val='or'},
+ args=[Ca,Cb]}) ->
+ case guard_value(Ca) of
+ true -> true;
+ false -> guard_value(Cb);
+ unknown ->
+ case guard_value(Cb) of
+ true -> true;
+ _Other -> unknown
+ end
+ end;
+guard_value(#c_try{arg=E,vars=[#c_var{name=X}],body=#c_var{name=X},
+ handler=#c_atom{val=false}}) ->
+ guard_value(E);
+guard_value(_) -> unknown.
+
+%% partition([Clause]) -> [[Clause]].
+%% Partition a list of clauses into groups which either contain
+%% clauses with a variable first argument, or with a "constructor".
+
+partition([C1|Cs]) ->
+ V1 = is_var_clause(C1),
+ {More,Rest} = splitwith(fun (C) -> is_var_clause(C) == V1 end, Cs),
+ [[C1|More]|partition(Rest)];
+partition([]) -> [].
+
+%% match_varcon([Var], [Clause], Def, [Var], Sub, State) ->
+%% {MatchExpr,State}.
+
+match_varcon(Us, [C|_]=Cs, Def, St) ->
+ case is_var_clause(C) of
+ true -> match_var(Us, Cs, Def, St);
+ false -> match_con(Us, Cs, Def, St)
+ end.
+
+%% match_var([Var], [Clause], Def, State) -> {MatchExpr,State}.
+%% Build a call to "select" from a list of clauses all containing a
+%% variable as the first argument. We must rename the variable in
+%% each clause to be the match variable as these clause will share
+%% this variable and may have different names for it. Rename aliases
+%% as well.
+
+match_var([U|Us], Cs0, Def, St) ->
+ Cs1 = map(fun (#iclause{sub=Sub0,pats=[Arg|As]}=C) ->
+ Vs = [arg_arg(Arg)|arg_alias(Arg)],
+ Sub1 = foldl(fun (#k_var{name=V}, Acc) ->
+ subst_vsub(V, U#k_var.name, Acc)
+ end, Sub0, Vs),
+ C#iclause{sub=Sub1,pats=As}
+ end, Cs0),
+ match(Us, Cs1, Def, St).
+
+%% match_con(Variables, [Clause], Default, State) -> {SelectExpr,State}.
+%% Build call to "select" from a list of clauses all containing a
+%% constructor/constant as first argument. Group the constructors
+%% according to type, the order is really irrelevant but tries to be
+%% smart.
+
+match_con([U|Us], Cs, Def, St0) ->
+ %% Extract clauses for different constructors (types).
+ %%ok = io:format("match_con ~p~n", [Cs]),
+ Ttcs = [ {T,Tcs} || T <- [k_cons,k_tuple,k_atom,k_float,k_int,k_nil,
+ k_binary,k_bin_end],
+ begin Tcs = select(T, Cs),
+ Tcs /= []
+ end ] ++ select_bin_con(Cs),
+ %%ok = io:format("ttcs = ~p~n", [Ttcs]),
+ {Scs,St1} =
+ mapfoldl(fun ({T,Tcs}, St) ->
+ {[S|_]=Sc,S1} = match_value([U|Us], T, Tcs, fail, St),
+ %%ok = io:format("match_con type2 ~p~n", [T]),
+ Anno = get_kanno(S),
+ {#k_type_clause{anno=Anno,type=T,values=Sc},S1} end,
+ St0, Ttcs),
+ {build_alt_1st_no_fail(build_select(U, Scs), Def),St1}.
+
+%% select_bin_con([Clause]) -> [{Type,[Clause]}].
+%% Extract clauses for the k_bin_seg constructor. As k_bin_seg
+%% matching can overlap, the k_bin_seg constructors cannot be
+%% reordered, only grouped.
+
+select_bin_con(Cs0) ->
+ Cs1 = lists:filter(fun (C) ->
+ clause_con(C) == k_bin_seg
+ end, Cs0),
+ select_bin_con_1(Cs1).
+
+select_bin_con_1([C1|Cs]) ->
+ Con = clause_con(C1),
+ {More,Rest} = splitwith(fun (C) -> clause_con(C) == Con end, Cs),
+ [{Con,[C1|More]}|select_bin_con_1(Rest)];
+select_bin_con_1([]) -> [].
+
+%% select(Con, [Clause]) -> [Clause].
+
+select(T, Cs) -> [ C || C <- Cs, clause_con(C) == T ].
+
+%% match_value([Var], Con, [Clause], Default, State) -> {SelectExpr,State}.
+%% At this point all the clauses have the same constructor, we must
+%% now separate them according to value.
+
+match_value(_, _, [], _, St) -> {[],St};
+match_value(Us, T, Cs0, Def, St0) ->
+ Css = group_value(T, Cs0),
+ %%ok = io:format("match_value ~p ~p~n", [T, Css]),
+ {Css1,St1} = mapfoldl(fun (Cs, St) ->
+ match_clause(Us, Cs, Def, St) end,
+ St0, Css),
+ {Css1,St1}.
+ %%{#k_select_val{type=T,var=hd(Us),clauses=Css1},St1}.
+
+%% group_value([Clause]) -> [[Clause]].
+%% Group clauses according to value. Here we know that
+%% 1. Some types are singled valued
+%% 2. The clauses in bin_segs cannot be reordered only grouped
+%% 3. Other types are disjoint and can be reordered
+
+group_value(k_cons, Cs) -> [Cs]; %These are single valued
+group_value(k_nil, Cs) -> [Cs];
+group_value(k_binary, Cs) -> [Cs];
+group_value(k_bin_end, Cs) -> [Cs];
+group_value(k_bin_seg, Cs) ->
+ group_bin_seg(Cs);
+group_value(_, Cs) ->
+ %% group_value(Cs).
+ Cd = foldl(fun (C, Gcs0) -> dict:append(clause_val(C), C, Gcs0) end,
+ dict:new(), Cs),
+ dict:fold(fun (_, Vcs, Css) -> [Vcs|Css] end, [], Cd).
+
+group_bin_seg([C1|Cs]) ->
+ V1 = clause_val(C1),
+ {More,Rest} = splitwith(fun (C) -> clause_val(C) == V1 end, Cs),
+ [[C1|More]|group_bin_seg(Rest)];
+group_bin_seg([]) -> [].
+
+%% Profiling shows that this quadratic implementation account for a big amount
+%% of the execution time if there are many values.
+% group_value([C|Cs]) ->
+% V = clause_val(C),
+% Same = [ Cv || Cv <- Cs, clause_val(Cv) == V ], %Same value
+% Rest = [ Cv || Cv <- Cs, clause_val(Cv) /= V ], % and all the rest
+% [[C|Same]|group_value(Rest)];
+% group_value([]) -> [].
+
+%% match_clause([Var], [Clause], Default, State) -> {Clause,State}.
+%% At this point all the clauses have the same "value". Build one
+%% select clause for this value and continue matching. Rename
+%% aliases as well.
+
+match_clause([U|Us], [C|_]=Cs0, Def, St0) ->
+ Anno = get_kanno(C),
+ {Match0,Vs,St1} = get_match(get_con(Cs0), St0),
+ Match = sub_size_var(Match0, Cs0),
+ {Cs1,St2} = new_clauses(Cs0, U, St1),
+ {B,St3} = match(Vs ++ Us, Cs1, Def, St2),
+ {#k_val_clause{anno=Anno,val=Match,body=B},St3}.
+
+sub_size_var(#k_bin_seg{size=#k_var{name=Name}=Kvar}=BinSeg, [#iclause{sub=Sub}|_]) ->
+ BinSeg#k_bin_seg{size=Kvar#k_var{name=get_vsub(Name, Sub)}};
+sub_size_var(K, _) -> K.
+
+get_con([C|_]) -> arg_arg(clause_arg(C)). %Get the constructor
+
+get_match(#k_cons{}, St0) ->
+ {[H,T],St1} = new_vars(2, St0),
+ {#k_cons{hd=H,tl=T},[H,T],St1};
+get_match(#k_binary{}, St0) ->
+ {[V]=Mes,St1} = new_vars(1, St0),
+ {#k_binary{segs=V},Mes,St1};
+get_match(#k_bin_seg{}=Seg, St0) ->
+ {[S,N]=Mes,St1} = new_vars(2, St0),
+ {Seg#k_bin_seg{seg=S,next=N},Mes,St1};
+get_match(#k_tuple{es=Es}, St0) ->
+ {Mes,St1} = new_vars(length(Es), St0),
+ {#k_tuple{es=Mes},Mes,St1};
+get_match(M, St) ->
+ {M,[],St}.
+
+new_clauses(Cs0, U, St) ->
+ Cs1 = map(fun (#iclause{sub=Sub0,pats=[Arg|As]}=C) ->
+ Head = case arg_arg(Arg) of
+ #k_cons{hd=H,tl=T} -> [H,T|As];
+ #k_tuple{es=Es} -> Es ++ As;
+ #k_binary{segs=E} -> [E|As];
+ #k_bin_seg{seg=S,next=N} ->
+ [S,N|As];
+ _Other -> As
+ end,
+ Vs = arg_alias(Arg),
+ Sub1 = foldl(fun (#k_var{name=V}, Acc) ->
+ subst_vsub(V, U#k_var.name, Acc)
+ end, Sub0, Vs),
+ C#iclause{sub=Sub1,pats=Head}
+ end, Cs0),
+ {Cs1,St}.
+
+%% build_guard([GuardClause]) -> GuardExpr.
+
+build_guard([]) -> fail;
+build_guard(Cs) -> #k_guard{clauses=Cs}.
+
+%% build_select(Var, [ConClause]) -> SelectExpr.
+
+build_select(V, [Tc|_]=Tcs) ->
+ Anno = get_kanno(Tc),
+ #k_select{anno=Anno,var=V,types=Tcs}.
+
+%% build_alt(First, Then) -> AltExpr.
+%% Build an alt, attempt some simple optimisation.
+
+build_alt(fail, Then) -> Then;
+build_alt(First,Then) -> build_alt_1st_no_fail(First, Then).
+
+build_alt_1st_no_fail(First, fail) -> First;
+build_alt_1st_no_fail(First, Then) -> #k_alt{first=First,then=Then}.
+
+%% build_match([MatchVar], MatchExpr) -> Kexpr.
+%% Build a match expr if there is a match.
+
+build_match(Us, #k_alt{}=Km) -> #k_match{vars=Us,body=Km};
+build_match(Us, #k_select{}=Km) -> #k_match{vars=Us,body=Km};
+build_match(Us, #k_guard{}=Km) -> #k_match{vars=Us,body=Km};
+build_match(_, Km) -> Km.
+
+%% clause_arg(Clause) -> FirstArg.
+%% clause_con(Clause) -> Constructor.
+%% clause_val(Clause) -> Value.
+%% is_var_clause(Clause) -> boolean().
+
+clause_arg(#iclause{pats=[Arg|_]}) -> Arg.
+
+clause_con(C) -> arg_con(clause_arg(C)).
+
+clause_val(C) -> arg_val(clause_arg(C)).
+
+is_var_clause(C) -> clause_con(C) == k_var.
+
+%% arg_arg(Arg) -> Arg.
+%% arg_alias(Arg) -> Aliases.
+%% arg_con(Arg) -> Constructor.
+%% arg_val(Arg) -> Value.
+%% These are the basic functions for obtaining fields in an argument.
+
+arg_arg(#ialias{pat=Con}) -> Con;
+arg_arg(Con) -> Con.
+
+arg_alias(#ialias{vars=As}) -> As;
+arg_alias(_Con) -> [].
+
+arg_con(Arg) ->
+ case arg_arg(Arg) of
+ #k_int{} -> k_int;
+ #k_float{} -> k_float;
+ #k_atom{} -> k_atom;
+ #k_nil{} -> k_nil;
+ #k_cons{} -> k_cons;
+ #k_tuple{} -> k_tuple;
+ #k_binary{} -> k_binary;
+ #k_bin_end{} -> k_bin_end;
+ #k_bin_seg{} -> k_bin_seg;
+ #k_var{} -> k_var
+ end.
+
+arg_val(Arg) ->
+ case arg_arg(Arg) of
+ #k_int{val=I} -> I;
+ #k_float{val=F} -> F;
+ #k_atom{val=A} -> A;
+ #k_nil{} -> 0;
+ #k_cons{} -> 2;
+ #k_tuple{es=Es} -> length(Es);
+ #k_bin_seg{size=S,unit=U,type=T,flags=Fs} ->
+ {set_kanno(S, []),U,T,Fs};
+ #k_bin_end{} -> 0;
+ #k_binary{} -> 0
+ end.
+
+%% ubody(Expr, Break, State) -> {Expr,[UsedVar],State}.
+%% Tag the body sequence with its used variables. These bodies
+%% either end with a #k_break{}, or with #k_return{} or an expression
+%% which itself can return, #k_enter{}, #k_match{} ... .
+
+ubody(#iset{vars=[],arg=#iletrec{}=Let,body=B0}, Br, St0) ->
+ %% An iletrec{} should never be last.
+ St1 = iletrec_funs(Let, St0),
+ ubody(B0, Br, St1);
+ubody(#iset{anno=A,vars=Vs,arg=E0,body=B0}, Br, St0) ->
+ {E1,Eu,St1} = uexpr(E0, {break,Vs}, St0),
+ {B1,Bu,St2} = ubody(B0, Br, St1),
+ Ns = lit_list_vars(Vs),
+ Used = union(Eu, subtract(Bu, Ns)), %Used external vars
+ {#k_seq{anno=#k{us=Used,ns=Ns,a=A},arg=E1,body=B1},Used,St2};
+ubody(#ivalues{anno=A,args=As}, return, St) ->
+ Au = lit_list_vars(As),
+ {#k_return{anno=#k{us=Au,ns=[],a=A},args=As},Au,St};
+ubody(#ivalues{anno=A,args=As}, {break,_Vbs}, St) ->
+ Au = lit_list_vars(As),
+ {#k_break{anno=#k{us=Au,ns=[],a=A},args=As},Au,St};
+ubody(E, return, St0) ->
+ %% Enterable expressions need no trailing return.
+ case is_enter_expr(E) of
+ true -> uexpr(E, return, St0);
+ false ->
+ {Ea,Pa,St1} = force_atomic(E, St0),
+ ubody(pre_seq(Pa, #ivalues{args=[Ea]}), return, St1)
+ end;
+ubody(E, {break,Rs}, St0) ->
+ %%ok = io:fwrite("ubody ~w:~p~n", [?LINE,{E,Br}]),
+ %% Exiting expressions need no trailing break.
+ case is_exit_expr(E) of
+ true -> uexpr(E, return, St0);
+ false ->
+ {Ea,Pa,St1} = force_atomic(E, St0),
+ ubody(pre_seq(Pa, #ivalues{args=[Ea]}), {break,Rs}, St1)
+ end.
+
+iletrec_funs(#iletrec{defs=Fs}, St0) ->
+ %% Use union of all free variables.
+ %% First just work out free variables for all functions.
+ Free = foldl(fun ({_,#ifun{vars=Vs,body=Fb0}}, Free0) ->
+ {_,Fbu,_} = ubody(Fb0, return, St0),
+ Ns = lit_list_vars(Vs),
+ Free1 = subtract(Fbu, Ns),
+ union(Free1, Free0)
+ end, [], Fs),
+ FreeVs = make_vars(Free),
+ %% Add this free info to State.
+ St1 = foldl(fun ({N,#ifun{vars=Vs}}, Lst) ->
+ store_free(N, length(Vs), FreeVs, Lst)
+ end, St0, Fs),
+ %% Now regenerate local functions to use free variable information.
+ St2 = foldl(fun ({N,#ifun{anno=Fa,vars=Vs,body=Fb0}}, Lst0) ->
+ {Fb1,_,Lst1} = ubody(Fb0, return, Lst0),
+ Arity = length(Vs) + length(FreeVs),
+ Fun = #k_fdef{anno=#k{us=[],ns=[],a=Fa},
+ func=N,arity=Arity,
+ vars=Vs ++ FreeVs,body=Fb1},
+ Lst1#kern{funs=[Fun|Lst1#kern.funs]}
+ end, St1, Fs),
+ St2.
+
+%% is_exit_expr(Kexpr) -> boolean().
+%% Test whether Kexpr always exits and never returns.
+
+is_exit_expr(#k_call{op=#k_remote{mod=erlang,name=throw,arity=1}}) -> true;
+is_exit_expr(#k_call{op=#k_remote{mod=erlang,name=exit,arity=1}}) -> true;
+is_exit_expr(#k_call{op=#k_remote{mod=erlang,name=error,arity=1}}) -> true;
+is_exit_expr(#k_call{op=#k_remote{mod=erlang,name=error,arity=2}}) -> true;
+is_exit_expr(#k_call{op=#k_remote{mod=erlang,name=fault,arity=1}}) -> true;
+is_exit_expr(#k_call{op=#k_remote{mod=erlang,name=fault,arity=2}}) -> true;
+is_exit_expr(#k_call{op=#k_internal{name=match_fail,arity=1}}) -> true;
+is_exit_expr(#k_bif{op=#k_internal{name=rethrow,arity=2}}) -> true;
+is_exit_expr(#k_receive_next{}) -> true;
+is_exit_expr(_) -> false.
+
+%% is_enter_expr(Kexpr) -> boolean().
+%% Test whether Kexpr is "enterable", i.e. can handle return from
+%% within itself without extra #k_return{}.
+
+is_enter_expr(#k_call{}) -> true;
+is_enter_expr(#k_match{}) -> true;
+is_enter_expr(#k_receive{}) -> true;
+is_enter_expr(#k_receive_next{}) -> true;
+%%is_enter_expr(#k_try{}) -> true; %Soon
+is_enter_expr(_) -> false.
+
+%% uguard(Expr, State) -> {Expr,[UsedVar],State}.
+%% Tag the guard sequence with its used variables.
+
+uguard(#k_try{anno=A,arg=B0,vars=[#k_var{name=X}],body=#k_var{name=X},
+ handler=#k_atom{val=false}}=Try, St0) ->
+ {B1,Bu,St1} = uguard(B0, St0),
+ {Try#k_try{anno=#k{us=Bu,ns=[],a=A},arg=B1},Bu,St1};
+uguard(T, St) ->
+ %%ok = io:fwrite("~w: ~p~n", [?LINE,T]),
+ uguard_test(T, St).
+
+%% uguard_test(Expr, State) -> {Test,[UsedVar],State}.
+%% At this stage tests are just expressions which don't return any
+%% values.
+
+uguard_test(T, St) -> uguard_expr(T, [], St).
+
+uguard_expr(#iset{anno=A,vars=Vs,arg=E0,body=B0}, Rs, St0) ->
+ Ns = lit_list_vars(Vs),
+ {E1,Eu,St1} = uguard_expr(E0, Vs, St0),
+ {B1,Bu,St2} = uguard_expr(B0, Rs, St1),
+ Used = union(Eu, subtract(Bu, Ns)),
+ {#k_seq{anno=#k{us=Used,ns=Ns,a=A},arg=E1,body=B1},Used,St2};
+uguard_expr(#k_try{anno=A,arg=B0,vars=[#k_var{name=X}],body=#k_var{name=X},
+ handler=#k_atom{val=false}}=Try, Rs, St0) ->
+ {B1,Bu,St1} = uguard_expr(B0, Rs, St0),
+ {Try#k_try{anno=#k{us=Bu,ns=lit_list_vars(Rs),a=A},arg=B1,ret=Rs},
+ Bu,St1};
+uguard_expr(#k_test{anno=A,op=Op,args=As}=Test, Rs, St) ->
+ [] = Rs, %Sanity check
+ Used = union(op_vars(Op), lit_list_vars(As)),
+ {Test#k_test{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A}},
+ Used,St};
+uguard_expr(#k_bif{anno=A,op=Op,args=As}=Bif, Rs, St) ->
+ Used = union(op_vars(Op), lit_list_vars(As)),
+ {Bif#k_bif{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A},ret=Rs},
+ Used,St};
+uguard_expr(#ivalues{anno=A,args=As}, Rs, St) ->
+ Sets = foldr2(fun (V, Arg, Rhs) ->
+ #iset{anno=A,vars=[V],arg=Arg,body=Rhs}
+ end, #k_atom{val=true}, Rs, As),
+ uguard_expr(Sets, [], St);
+uguard_expr(#k_match{anno=A,vars=Vs,body=B0}, Rs, St0) ->
+ %% Experimental support for andalso/orelse in guards.
+ Br = case Rs of
+ [] -> return;
+ _ -> {break,Rs}
+ end,
+ {B1,Bu,St1} = umatch(B0, Br, St0),
+ {#k_match{anno=#k{us=Bu,ns=lit_list_vars(Rs),a=A},
+ vars=Vs,body=B1,ret=Rs},Bu,St1};
+uguard_expr(Lit, Rs, St) ->
+ %% Transform literals to puts here.
+ Used = lit_vars(Lit),
+ {#k_put{anno=#k{us=Used,ns=lit_list_vars(Rs),a=get_kanno(Lit)},
+ arg=Lit,ret=Rs},Used,St}.
+
+%% uexpr(Expr, Break, State) -> {Expr,[UsedVar],State}.
+%% Tag an expression with its used variables.
+%% Break = return | {break,[RetVar]}.
+
+uexpr(#k_call{anno=A,op=#k_local{name=F,arity=Ar}=Op,args=As0}=Call, Br, St) ->
+ Free = get_free(F, Ar, St),
+ As1 = As0 ++ Free, %Add free variables LAST!
+ Used = lit_list_vars(As1),
+ {case Br of
+ {break,Rs} ->
+ Call#k_call{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A},
+ op=Op#k_local{arity=Ar + length(Free)},
+ args=As1,ret=Rs};
+ return ->
+ #k_enter{anno=#k{us=Used,ns=[],a=A},
+ op=Op#k_local{arity=Ar + length(Free)},
+ args=As1}
+ end,Used,St};
+uexpr(#k_call{anno=A,op=Op,args=As}=Call, {break,Rs}, St) ->
+ Used = union(op_vars(Op), lit_list_vars(As)),
+ {Call#k_call{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A},ret=Rs},
+ Used,St};
+uexpr(#k_call{anno=A,op=Op,args=As}, return, St) ->
+ Used = union(op_vars(Op), lit_list_vars(As)),
+ {#k_enter{anno=#k{us=Used,ns=[],a=A},op=Op,args=As},
+ Used,St};
+uexpr(#k_bif{anno=A,op=Op,args=As}=Bif, {break,Rs}, St0) ->
+ Used = union(op_vars(Op), lit_list_vars(As)),
+ {Brs,St1} = bif_returns(Op, Rs, St0),
+ {Bif#k_bif{anno=#k{us=Used,ns=lit_list_vars(Brs),a=A},ret=Brs},
+ Used,St1};
+uexpr(#k_match{anno=A,vars=Vs,body=B0}, Br, St0) ->
+ Rs = break_rets(Br),
+ {B1,Bu,St1} = umatch(B0, Br, St0),
+ {#k_match{anno=#k{us=Bu,ns=lit_list_vars(Rs),a=A},
+ vars=Vs,body=B1,ret=Rs},Bu,St1};
+uexpr(#k_receive{anno=A,var=V,body=B0,timeout=T,action=A0}, Br, St0) ->
+ Rs = break_rets(Br),
+ Tu = lit_vars(T), %Timeout is atomic
+ {B1,Bu,St1} = umatch(B0, Br, St0),
+ {A1,Au,St2} = ubody(A0, Br, St1),
+ Used = del_element(V#k_var.name, union(Bu, union(Tu, Au))),
+ {#k_receive{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A},
+ var=V,body=B1,timeout=T,action=A1,ret=Rs},
+ Used,St2};
+uexpr(#k_receive_accept{anno=A}, _, St) ->
+ {#k_receive_accept{anno=#k{us=[],ns=[],a=A}},[],St};
+uexpr(#k_receive_next{anno=A}, _, St) ->
+ {#k_receive_next{anno=#k{us=[],ns=[],a=A}},[],St};
+uexpr(#k_try{anno=A,arg=A0,vars=Vs,body=B0,evars=Evs,handler=H0},
+ {break,Rs0}, St0) ->
+ {Avs,St1} = new_vars(length(Vs), St0), %Need dummy names here
+ {A1,Au,St2} = ubody(A0, {break,Avs}, St1), %Must break to clean up here!
+ {B1,Bu,St3} = ubody(B0, {break,Rs0}, St2),
+ {H1,Hu,St4} = ubody(H0, {break,Rs0}, St3),
+ %% Guarantee ONE return variable.
+ NumNew = if
+ Rs0 =:= [] -> 1;
+ true -> 0
+ end,
+ {Ns,St5} = new_vars(NumNew, St4),
+ Rs1 = Rs0 ++ Ns,
+ Used = union([Au,subtract(Bu, lit_list_vars(Vs)),
+ subtract(Hu, lit_list_vars(Evs))]),
+ {#k_try{anno=#k{us=Used,ns=lit_list_vars(Rs1),a=A},
+ arg=A1,vars=Vs,body=B1,evars=Evs,handler=H1,ret=Rs1},
+ Used,St5};
+uexpr(#k_catch{anno=A,body=B0}, {break,Rs0}, St0) ->
+ {Rb,St1} = new_var(St0),
+ {B1,Bu,St2} = ubody(B0, {break,[Rb]}, St1),
+ %% Guarantee ONE return variable.
+ {Ns,St3} = new_vars(1 - length(Rs0), St2),
+ Rs1 = Rs0 ++ Ns,
+ {#k_catch{anno=#k{us=Bu,ns=lit_list_vars(Rs1),a=A},body=B1,ret=Rs1},Bu,St3};
+uexpr(#ifun{anno=A,vars=Vs,body=B0}=IFun, {break,Rs}, St0) ->
+ {B1,Bu,St1} = ubody(B0, return, St0), %Return out of new function
+ Ns = lit_list_vars(Vs),
+ Free = subtract(Bu, Ns), %Free variables in fun
+ Fvs = make_vars(Free),
+ Arity = length(Vs) + length(Free),
+ {{Index,Uniq,Fname}, St3} =
+ case lists:keysearch(id, 1, A) of
+ {value,{id,Id}} ->
+ {Id, St1};
+ false ->
+ %% No id annotation. Must invent one.
+ I = St1#kern.fcount,
+ U = erlang:hash(IFun, (1 bsl 27)-1),
+ {N, St2} = new_fun_name(St1),
+ {{I,U,N}, St2}
+ end,
+ Fun = #k_fdef{anno=#k{us=[],ns=[],a=A},func=Fname,arity=Arity,
+ vars=Vs ++ Fvs,body=B1},
+ {#k_bif{anno=#k{us=Free,ns=lit_list_vars(Rs),a=A},
+ op=#k_internal{name=make_fun,arity=length(Free)+3},
+ args=[#k_atom{val=Fname},#k_int{val=Arity},
+ #k_int{val=Index},#k_int{val=Uniq}|Fvs],
+ ret=Rs},
+% {#k_call{anno=#k{us=Free,ns=lit_list_vars(Rs),a=A},
+% op=#k_internal{name=make_fun,arity=length(Free)+3},
+% args=[#k_atom{val=Fname},#k_int{val=Arity},
+% #k_int{val=Index},#k_int{val=Uniq}|Fvs],
+% ret=Rs},
+ Free,St3#kern{funs=[Fun|St3#kern.funs]}};
+uexpr(Lit, {break,Rs}, St) ->
+ %% Transform literals to puts here.
+ %%ok = io:fwrite("uexpr ~w:~p~n", [?LINE,Lit]),
+ Used = lit_vars(Lit),
+ {#k_put{anno=#k{us=Used,ns=lit_list_vars(Rs),a=get_kanno(Lit)},
+ arg=Lit,ret=Rs},Used,St}.
+
+%% get_free(Name, Arity, State) -> [Free].
+%% store_free(Name, Arity, [Free], State) -> State.
+
+get_free(F, A, St) ->
+ case orddict:find({F,A}, St#kern.free) of
+ {ok,Val} -> Val;
+ error -> []
+ end.
+
+store_free(F, A, Free, St) ->
+ St#kern{free=orddict:store({F,A}, Free, St#kern.free)}.
+
+break_rets({break,Rs}) -> Rs;
+break_rets(return) -> [].
+
+%% bif_returns(Op, [Ret], State) -> {[Ret],State}.
+
+bif_returns(#k_remote{mod=M,name=N,arity=Ar}, Rs, St0) ->
+ %%ok = io:fwrite("uexpr ~w:~p~n", [?LINE,{M,N,Ar,Rs}]),
+ {Ns,St1} = new_vars(bif_vals(M, N, Ar) - length(Rs), St0),
+ {Rs ++ Ns,St1};
+bif_returns(#k_internal{name=N,arity=Ar}, Rs, St0) ->
+ %%ok = io:fwrite("uexpr ~w:~p~n", [?LINE,{N,Ar,Rs}]),
+ {Ns,St1} = new_vars(bif_vals(N, Ar) - length(Rs), St0),
+ {Rs ++ Ns,St1}.
+
+%% umatch(Match, Break, State) -> {Match,[UsedVar],State}.
+%% Tag a match expression with its used variables.
+
+umatch(#k_alt{anno=A,first=F0,then=T0}, Br, St0) ->
+ {F1,Fu,St1} = umatch(F0, Br, St0),
+ {T1,Tu,St2} = umatch(T0, Br, St1),
+ Used = union(Fu, Tu),
+ {#k_alt{anno=#k{us=Used,ns=[],a=A},first=F1,then=T1},
+ Used,St2};
+umatch(#k_select{anno=A,var=V,types=Ts0}, Br, St0) ->
+ {Ts1,Tus,St1} = umatch_list(Ts0, Br, St0),
+ Used = add_element(V#k_var.name, Tus),
+ {#k_select{anno=#k{us=Used,ns=[],a=A},var=V,types=Ts1},Used,St1};
+umatch(#k_type_clause{anno=A,type=T,values=Vs0}, Br, St0) ->
+ {Vs1,Vus,St1} = umatch_list(Vs0, Br, St0),
+ {#k_type_clause{anno=#k{us=Vus,ns=[],a=A},type=T,values=Vs1},Vus,St1};
+umatch(#k_val_clause{anno=A,val=P,body=B0}, Br, St0) ->
+ {U0,Ps} = pat_vars(P),
+ {B1,Bu,St1} = umatch(B0, Br, St0),
+ Used = union(U0, subtract(Bu, Ps)),
+ {#k_val_clause{anno=#k{us=Used,ns=[],a=A},val=P,body=B1},
+ Used,St1};
+umatch(#k_guard{anno=A,clauses=Gs0}, Br, St0) ->
+ {Gs1,Gus,St1} = umatch_list(Gs0, Br, St0),
+ {#k_guard{anno=#k{us=Gus,ns=[],a=A},clauses=Gs1},Gus,St1};
+umatch(#k_guard_clause{anno=A,guard=G0,body=B0}, Br, St0) ->
+ %%ok = io:fwrite("~w: ~p~n", [?LINE,G0]),
+ {G1,Gu,St1} = uguard(G0, St0),
+ %%ok = io:fwrite("~w: ~p~n", [?LINE,G1]),
+ {B1,Bu,St2} = umatch(B0, Br, St1),
+ Used = union(Gu, Bu),
+ {#k_guard_clause{anno=#k{us=Used,ns=[],a=A},guard=G1,body=B1},Used,St2};
+umatch(B0, Br, St0) -> ubody(B0, Br, St0).
+
+umatch_list(Ms0, Br, St) ->
+ foldr(fun (M0, {Ms1,Us,Sta}) ->
+ {M1,Mu,Stb} = umatch(M0, Br, Sta),
+ {[M1|Ms1],union(Mu, Us),Stb}
+ end, {[],[],St}, Ms0).
+
+%% op_vars(Op) -> [VarName].
+
+op_vars(#k_local{}) -> [];
+op_vars(#k_remote{mod=Mod,name=Name}) ->
+ ordsets:from_list([V || #k_var{name=V} <- [Mod,Name]]);
+op_vars(#k_internal{}) -> [];
+op_vars(Atomic) -> lit_vars(Atomic).
+
+%% lit_vars(Literal) -> [VarName].
+%% Return the variables in a literal.
+
+lit_vars(#k_var{name=N}) -> [N];
+lit_vars(#k_int{}) -> [];
+lit_vars(#k_float{}) -> [];
+lit_vars(#k_atom{}) -> [];
+%%lit_vars(#k_char{}) -> [];
+lit_vars(#k_string{}) -> [];
+lit_vars(#k_nil{}) -> [];
+lit_vars(#k_cons{hd=H,tl=T}) ->
+ union(lit_vars(H), lit_vars(T));
+lit_vars(#k_binary{segs=V}) -> lit_vars(V);
+lit_vars(#k_bin_end{}) -> [];
+lit_vars(#k_bin_seg{size=Size,seg=S,next=N}) ->
+ union(lit_vars(Size), union(lit_vars(S), lit_vars(N)));
+lit_vars(#k_tuple{es=Es}) ->
+ lit_list_vars(Es).
+
+lit_list_vars(Ps) ->
+ foldl(fun (P, Vs) -> union(lit_vars(P), Vs) end, [], Ps).
+
+%% pat_vars(Pattern) -> {[UsedVarName],[NewVarName]}.
+%% Return variables in a pattern. All variables are new variables
+%% except those in the size field of binary segments.
+
+pat_vars(#k_var{name=N}) -> {[],[N]};
+%%pat_vars(#k_char{}) -> {[],[]};
+pat_vars(#k_int{}) -> {[],[]};
+pat_vars(#k_float{}) -> {[],[]};
+pat_vars(#k_atom{}) -> {[],[]};
+pat_vars(#k_string{}) -> {[],[]};
+pat_vars(#k_nil{}) -> {[],[]};
+pat_vars(#k_cons{hd=H,tl=T}) ->
+ pat_list_vars([H,T]);
+pat_vars(#k_binary{segs=V}) ->
+ pat_vars(V);
+pat_vars(#k_bin_seg{size=Size,seg=S,next=N}) ->
+ {U1,New} = pat_list_vars([S,N]),
+ {[],U2} = pat_vars(Size),
+ {union(U1, U2),New};
+pat_vars(#k_bin_end{}) -> {[],[]};
+pat_vars(#k_tuple{es=Es}) ->
+ pat_list_vars(Es).
+
+pat_list_vars(Ps) ->
+ foldl(fun (P, {Used0,New0}) ->
+ {Used,New} = pat_vars(P),
+ {union(Used0, Used),union(New0, New)} end,
+ {[],[]}, Ps).
+
+%% aligned(Bits, Size, Unit, Flags) -> {Size,Flags}
+%% Add 'aligned' to the flags if the current field is aligned.
+%% Number of bits correct modulo 8.
+
+aligned(B, S, U, Fs) when B rem 8 =:= 0 ->
+ {incr_bits(B, S, U),[aligned|Fs]};
+aligned(B, S, U, Fs) ->
+ {incr_bits(B, S, U),Fs}.
+
+incr_bits(B, #k_int{val=S}, U) when integer(B) -> B + S*U;
+incr_bits(_, #k_atom{val=all}, _) -> 0; %Always aligned
+incr_bits(B, _, 8) -> B;
+incr_bits(_, _, _) -> unknown.
+
+make_list(Es) ->
+ foldr(fun (E, Acc) -> #c_cons{hd=E,tl=Acc} end, #c_nil{}, Es).
+
+%% List of integers in interval [N,M]. Empty list if N > M.
+
+integers(N, M) when N =< M ->
+ [N|integers(N + 1, M)];
+integers(_, _) -> [].
+
+%%%
+%%% Handling of warnings.
+%%%
+
+format_error({nomatch_shadow,Line}) ->
+ M = io_lib:format("this clause cannot match because a previous clause at line ~p "
+ "always matches", [Line]),
+ lists:flatten(M);
+format_error(nomatch_shadow) ->
+ "this clause cannot match because a previous clause always matches".
+
+add_warning(none, Term, #kern{ws=Ws}=St) ->
+ St#kern{ws=[{?MODULE,Term}|Ws]};
+add_warning(Line, Term, #kern{ws=Ws}=St) when Line >= 0 ->
+ St#kern{ws=[{Line,?MODULE,Term}|Ws]};
+add_warning(_, _, St) -> St.
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_kernel.hrl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_kernel.hrl
new file mode 100644
index 0000000000..6e97d4d66a
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_kernel.hrl
@@ -0,0 +1,77 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: v3_kernel.hrl,v 1.1 2008/12/17 09:53:43 mikpe Exp $
+%%
+
+%% Purpose : Kernel Erlang as records.
+
+%% It would be nice to incorporate some generic functions as well but
+%% this could make including this file difficult.
+%% N.B. the annotation field is ALWAYS the first field!
+
+%% Kernel annotation record.
+-record(k, {us, %Used variables
+ ns, %New variables
+ a}). %Core annotation
+
+%% Literals
+%% NO CHARACTERS YET.
+%%-record(k_char, {anno=[],val}).
+-record(k_int, {anno=[],val}).
+-record(k_float, {anno=[],val}).
+-record(k_atom, {anno=[],val}).
+-record(k_string, {anno=[],val}).
+-record(k_nil, {anno=[]}).
+
+-record(k_tuple, {anno=[],es}).
+-record(k_cons, {anno=[],hd,tl}).
+-record(k_binary, {anno=[],segs}).
+-record(k_bin_seg, {anno=[],size,unit,type,flags,seg,next}).
+-record(k_bin_end, {anno=[]}).
+-record(k_var, {anno=[],name}).
+
+-record(k_local, {anno=[],name,arity}).
+-record(k_remote, {anno=[],mod,name,arity}).
+-record(k_internal, {anno=[],name,arity}).
+
+-record(k_mdef, {anno=[],name,exports,attributes,body}).
+-record(k_fdef, {anno=[],func,arity,vars,body}).
+
+-record(k_seq, {anno=[],arg,body}).
+-record(k_put, {anno=[],arg,ret=[]}).
+-record(k_bif, {anno=[],op,args,ret=[]}).
+-record(k_test, {anno=[],op,args}).
+-record(k_call, {anno=[],op,args,ret=[]}).
+-record(k_enter, {anno=[],op,args}).
+-record(k_receive, {anno=[],var,body,timeout,action,ret=[]}).
+-record(k_receive_accept, {anno=[]}).
+-record(k_receive_next, {anno=[]}).
+-record(k_try, {anno=[],arg,vars,body,evars,handler,ret=[]}).
+-record(k_catch, {anno=[],body,ret=[]}).
+
+-record(k_match, {anno=[],vars,body,ret=[]}).
+-record(k_alt, {anno=[],first,then}).
+-record(k_select, {anno=[],var,types}).
+-record(k_type_clause, {anno=[],type,values}).
+-record(k_val_clause, {anno=[],val,body}).
+-record(k_guard, {anno=[],clauses}).
+-record(k_guard_clause, {anno=[],guard,body}).
+
+-record(k_break, {anno=[],args=[]}).
+-record(k_return, {anno=[],args=[]}).
+
+%%k_get_anno(Thing) -> element(2, Thing).
+%%k_set_anno(Thing, Anno) -> setelement(2, Thing, Anno).
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_kernel_pp.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_kernel_pp.erl
new file mode 100644
index 0000000000..41f59b7a81
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_kernel_pp.erl
@@ -0,0 +1,444 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: v3_kernel_pp.erl,v 1.1 2008/12/17 09:53:43 mikpe Exp $
+%%
+%% Purpose : Kernel Erlang (naive) prettyprinter
+
+-module(v3_kernel_pp).
+
+-include("v3_kernel.hrl").
+
+-export([format/1]).
+
+%% These are "internal" structures in sys_kernel which are here for
+%% debugging purposes.
+-record(iset, {anno=[],vars,arg,body}).
+-record(ifun, {anno=[],vars,body}).
+
+%% ====================================================================== %%
+%% format(Node) -> Text
+%% Node = coreErlang()
+%% Text = string() | [Text]
+%%
+%% Prettyprint-formats (naively) an abstract Core Erlang syntax
+%% tree.
+
+-record(ctxt, {indent = 0,
+ item_indent = 2,
+ body_indent = 2,
+ tab_width = 8}).
+
+canno(Cthing) -> element(2, Cthing).
+
+format(Node) -> format(Node, #ctxt{}).
+
+format(Node, Ctxt) ->
+ case canno(Node) of
+ [] ->
+ format_1(Node, Ctxt);
+ List ->
+ format_anno(List, Ctxt, fun (Ctxt1) -> format_1(Node, Ctxt1) end)
+ end.
+
+format_anno(Anno, Ctxt, ObjFun) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, 2),
+ ["( ",
+ ObjFun(Ctxt1),
+ nl_indent(Ctxt1),
+ "-| ",io_lib:write(Anno),
+ " )"].
+
+%% format_1(Kexpr, Context) -> string().
+
+format_1(#k_atom{val=A}, _Ctxt) -> core_atom(A);
+%%format_1(#k_char{val=C}, _Ctxt) -> io_lib:write_char(C);
+format_1(#k_float{val=F}, _Ctxt) -> float_to_list(F);
+format_1(#k_int{val=I}, _Ctxt) -> integer_to_list(I);
+format_1(#k_nil{}, _Ctxt) -> "[]";
+format_1(#k_string{val=S}, _Ctxt) -> io_lib:write_string(S);
+format_1(#k_var{name=V}, _Ctxt) ->
+ if atom(V) ->
+ case atom_to_list(V) of
+ [$_|Cs] -> "_X" ++ Cs;
+ [C|Cs] when C >= $A, C =< $Z -> [C|Cs];
+ Cs -> [$_|Cs]
+ end;
+ integer(V) -> [$_|integer_to_list(V)]
+ end;
+format_1(#k_cons{hd=H,tl=T}, Ctxt) ->
+ Txt = ["["|format(H, ctxt_bump_indent(Ctxt, 1))],
+ [Txt|format_list_tail(T, ctxt_bump_indent(Ctxt, width(Txt, Ctxt)))];
+format_1(#k_tuple{es=Es}, Ctxt) ->
+ [${,
+ format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
+ $}
+ ];
+format_1(#k_binary{segs=S}, Ctxt) ->
+ ["#<",format(S, ctxt_bump_indent(Ctxt, 2)),">#"];
+format_1(#k_bin_seg{}=S, Ctxt) ->
+ [format_bin_seg_1(S, Ctxt),
+ format_bin_seg(S#k_bin_seg.next, ctxt_bump_indent(Ctxt, 2))];
+format_1(#k_bin_end{}, _Ctxt) -> "#<>#";
+format_1(#k_local{name=N,arity=A}, Ctxt) ->
+ "local " ++ format_fa_pair({N,A}, Ctxt);
+format_1(#k_remote{mod=M,name=N,arity=A}, _Ctxt) ->
+ %% This is for our internal translator.
+ io_lib:format("remote ~s:~s/~w", [format(M),format(N),A]);
+format_1(#k_internal{name=N,arity=A}, Ctxt) ->
+ "internal " ++ format_fa_pair({N,A}, Ctxt);
+format_1(#k_seq{arg=A,body=B}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, 2),
+ ["do",
+ nl_indent(Ctxt1),
+ format(A, Ctxt1),
+ nl_indent(Ctxt),
+ "then",
+ nl_indent(Ctxt)
+ | format(B, Ctxt)
+ ];
+format_1(#k_match{vars=Vs,body=Bs,ret=Rs}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent),
+ ["match ",
+ format_hseq(Vs, ",", ctxt_bump_indent(Ctxt, 6), fun format/2),
+ nl_indent(Ctxt1),
+ format(Bs, Ctxt1),
+ nl_indent(Ctxt),
+ "end",
+ format_ret(Rs, Ctxt1)
+ ];
+format_1(#k_alt{first=O,then=T}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent),
+ ["alt",
+ nl_indent(Ctxt1),
+ format(O, Ctxt1),
+ nl_indent(Ctxt1),
+ format(T, Ctxt1)];
+format_1(#k_select{var=V,types=Cs}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, 2),
+ ["select ",
+ format(V, Ctxt),
+ nl_indent(Ctxt1),
+ format_vseq(Cs, "", "", Ctxt1, fun format/2)
+ ];
+format_1(#k_type_clause{type=T,values=Cs}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["type ",
+ io_lib:write(T),
+ nl_indent(Ctxt1),
+ format_vseq(Cs, "", "", Ctxt1, fun format/2)
+ ];
+format_1(#k_val_clause{val=Val,body=B}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
+ [format(Val, Ctxt),
+ " ->",
+ nl_indent(Ctxt1)
+ | format(B, Ctxt1)
+ ];
+format_1(#k_guard{clauses=Gs}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, 5),
+ ["when ",
+ nl_indent(Ctxt1),
+ format_vseq(Gs, "", "", Ctxt1, fun format/2)];
+format_1(#k_guard_clause{guard=G,body=B}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
+ [format(G, Ctxt),
+ nl_indent(Ctxt),
+ "->",
+ nl_indent(Ctxt1)
+ | format(B, Ctxt1)
+ ];
+format_1(#k_call{op=Op,args=As,ret=Rs}, Ctxt) ->
+ Txt = ["call (",format(Op, ctxt_bump_indent(Ctxt, 6)),$)],
+ Ctxt1 = ctxt_bump_indent(Ctxt, 2),
+ [Txt,format_args(As, Ctxt1),
+ format_ret(Rs, Ctxt1)
+ ];
+format_1(#k_enter{op=Op,args=As}, Ctxt) ->
+ Txt = ["enter (",format(Op, ctxt_bump_indent(Ctxt, 7)),$)],
+ Ctxt1 = ctxt_bump_indent(Ctxt, 2),
+ [Txt,format_args(As, Ctxt1)];
+format_1(#k_bif{op=Op,args=As,ret=Rs}, Ctxt) ->
+ Txt = ["bif (",format(Op, ctxt_bump_indent(Ctxt, 5)),$)],
+ Ctxt1 = ctxt_bump_indent(Ctxt, 2),
+ [Txt,format_args(As, Ctxt1),
+ format_ret(Rs, Ctxt1)
+ ];
+format_1(#k_test{op=Op,args=As}, Ctxt) ->
+ Txt = ["test (",format(Op, ctxt_bump_indent(Ctxt, 6)),$)],
+ Ctxt1 = ctxt_bump_indent(Ctxt, 2),
+ [Txt,format_args(As, Ctxt1)];
+format_1(#k_put{arg=A,ret=Rs}, Ctxt) ->
+ [format(A, Ctxt),
+ format_ret(Rs, ctxt_bump_indent(Ctxt, 1))
+ ];
+format_1(#k_try{arg=A,vars=Vs,body=B,evars=Evs,handler=H,ret=Rs}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["try",
+ nl_indent(Ctxt1),
+ format(A, Ctxt1),
+ nl_indent(Ctxt),
+ "of ",
+ format_hseq(Vs, ", ", ctxt_bump_indent(Ctxt, 3), fun format/2),
+ nl_indent(Ctxt1),
+ format(B, Ctxt1),
+ nl_indent(Ctxt),
+ "catch ",
+ format_hseq(Evs, ", ", ctxt_bump_indent(Ctxt, 6), fun format/2),
+ nl_indent(Ctxt1),
+ format(H, Ctxt1),
+ nl_indent(Ctxt),
+ "end",
+ format_ret(Rs, Ctxt1)
+ ];
+format_1(#k_catch{body=B,ret=Rs}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["catch",
+ nl_indent(Ctxt1),
+ format(B, Ctxt1),
+ nl_indent(Ctxt),
+ "end",
+ format_ret(Rs, Ctxt1)
+ ];
+format_1(#k_receive{var=V,body=B,timeout=T,action=A,ret=Rs}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent),
+ ["receive ",
+ format(V, Ctxt),
+ nl_indent(Ctxt1),
+ format(B, Ctxt1),
+ nl_indent(Ctxt),
+ "after ",
+ format(T, ctxt_bump_indent(Ctxt, 6)),
+ " ->",
+ nl_indent(Ctxt1),
+ format(A, Ctxt1),
+ nl_indent(Ctxt),
+ "end",
+ format_ret(Rs, Ctxt1)
+ ];
+format_1(#k_receive_accept{}, _Ctxt) -> "receive_accept";
+format_1(#k_receive_next{}, _Ctxt) -> "receive_next";
+format_1(#k_break{args=As}, Ctxt) ->
+ ["<",
+ format_hseq(As, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
+ ">"
+ ];
+format_1(#k_return{args=As}, Ctxt) ->
+ ["<<",
+ format_hseq(As, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
+ ">>"
+ ];
+format_1(#k_fdef{func=F,arity=A,vars=Vs,body=B}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["fdef ",
+ format_fa_pair({F,A}, ctxt_bump_indent(Ctxt, 5)),
+ format_args(Vs, ctxt_bump_indent(Ctxt, 14)),
+ " =",
+ nl_indent(Ctxt1),
+ format(B, Ctxt1)
+ ];
+format_1(#k_mdef{name=N,exports=Es,attributes=As,body=B}, Ctxt) ->
+ ["module ",
+ format(#k_atom{val=N}, ctxt_bump_indent(Ctxt, 7)),
+ nl_indent(Ctxt),
+ "export [",
+ format_vseq(Es,
+ "", ",",
+ ctxt_bump_indent(Ctxt, 8),
+ fun format_fa_pair/2),
+ "]",
+ nl_indent(Ctxt),
+ "attributes [",
+ format_vseq(As,
+ "", ",",
+ ctxt_bump_indent(Ctxt, 12),
+ fun format_attribute/2),
+ "]",
+ nl_indent(Ctxt),
+ format_vseq(B,
+ "", "",
+ Ctxt,
+ fun format/2),
+ nl_indent(Ctxt)
+ | "end"
+ ];
+%% Internal sys_kernel structures.
+format_1(#iset{vars=Vs,arg=A,body=B}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["set <",
+ format_hseq(Vs, ", ", ctxt_bump_indent(Ctxt, 5), fun format/2),
+ "> =",
+ nl_indent(Ctxt1),
+ format(A, Ctxt1),
+ nl_indent(Ctxt),
+ "in "
+ | format(B, ctxt_bump_indent(Ctxt, 2))
+ ];
+format_1(#ifun{vars=Vs,body=B}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["fun ",
+ format_args(Vs, ctxt_bump_indent(Ctxt, 4)),
+ " ->",
+ nl_indent(Ctxt1)
+ | format(B, Ctxt1)
+ ];
+format_1(Type, _Ctxt) ->
+ ["** Unsupported type: ",
+ io_lib:write(Type)
+ | " **"
+ ].
+
+%% format_ret([RetVar], Context) -> Txt.
+%% Format the return vars of kexpr.
+
+format_ret(Rs, Ctxt) ->
+ [" >> ",
+ "<",
+ format_hseq(Rs, ",", ctxt_bump_indent(Ctxt, 5), fun format/2),
+ ">"].
+
+%% format_args([Arg], Context) -> Txt.
+%% Format arguments.
+
+format_args(As, Ctxt) ->
+ [$(,format_hseq(As, ", ", ctxt_bump_indent(Ctxt, 1), fun format/2),$)].
+
+%% format_hseq([Thing], Separator, Context, Fun) -> Txt.
+%% Format a sequence horizontally.
+
+format_hseq([H], _Sep, Ctxt, Fun) ->
+ Fun(H, Ctxt);
+format_hseq([H|T], Sep, Ctxt, Fun) ->
+ Txt = [Fun(H, Ctxt)|Sep],
+ Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt, Ctxt)),
+ [Txt|format_hseq(T, Sep, Ctxt1, Fun)];
+format_hseq([], _, _, _) -> "".
+
+%% format_vseq([Thing], LinePrefix, LineSuffix, Context, Fun) -> Txt.
+%% Format a sequence vertically.
+
+format_vseq([H], _Pre, _Suf, Ctxt, Fun) ->
+ Fun(H, Ctxt);
+format_vseq([H|T], Pre, Suf, Ctxt, Fun) ->
+ [Fun(H, Ctxt),Suf,nl_indent(Ctxt),Pre|
+ format_vseq(T, Pre, Suf, Ctxt, Fun)];
+format_vseq([], _, _, _, _) -> "".
+
+format_fa_pair({F,A}, _Ctxt) -> [core_atom(F),$/,integer_to_list(A)].
+
+%% format_attribute({Name,Val}, Context) -> Txt.
+
+format_attribute({Name,Val}, Ctxt) when list(Val) ->
+ Txt = format(#k_atom{val=Name}, Ctxt),
+ Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt,Ctxt)+4),
+ [Txt," = ",
+ $[,format_vseq(Val, "", ",", Ctxt1,
+ fun (A, _C) -> io_lib:write(A) end),$]
+ ];
+format_attribute({Name,Val}, Ctxt) ->
+ Txt = format(#k_atom{val=Name}, Ctxt),
+ [Txt," = ",io_lib:write(Val)].
+
+format_list_tail(#k_nil{anno=[]}, _Ctxt) -> "]";
+format_list_tail(#k_cons{anno=[],hd=H,tl=T}, Ctxt) ->
+ Txt = [$,|format(H, Ctxt)],
+ Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt, Ctxt)),
+ [Txt|format_list_tail(T, Ctxt1)];
+format_list_tail(Tail, Ctxt) ->
+ ["|",format(Tail, ctxt_bump_indent(Ctxt, 1)), "]"].
+
+format_bin_seg(#k_bin_end{anno=[]}, _Ctxt) -> "";
+format_bin_seg(#k_bin_seg{anno=[],next=N}=Seg, Ctxt) ->
+ Txt = [$,|format_bin_seg_1(Seg, Ctxt)],
+ [Txt|format_bin_seg(N, ctxt_bump_indent(Ctxt, width(Txt, Ctxt)))];
+format_bin_seg(Seg, Ctxt) ->
+ ["|",format(Seg, ctxt_bump_indent(Ctxt, 2))].
+
+format_bin_seg_1(#k_bin_seg{size=S,unit=U,type=T,flags=Fs,seg=Seg}, Ctxt) ->
+ [format(Seg, Ctxt),
+ ":",format(S, Ctxt),"*",io_lib:write(U),
+ ":",io_lib:write(T),
+ lists:map(fun (F) -> [$-,io_lib:write(F)] end, Fs)
+ ].
+
+% format_bin_elements(#k_binary_cons{hd=H,tl=T,size=S,info=I}, Ctxt) ->
+% A = canno(T),
+% Fe = fun (Eh, Es, Ei, Ct) ->
+% [format(Eh, Ct),":",format(Es, Ct),"/",io_lib:write(Ei)]
+% end,
+% case T of
+% #k_zero_binary{} when A == [] ->
+% Fe(H, S, I, Ctxt);
+% #k_binary_cons{} when A == [] ->
+% Txt = [Fe(H, S, I, Ctxt)|","],
+% Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt, Ctxt)),
+% [Txt|format_bin_elements(T, Ctxt1)];
+% _ ->
+% Txt = [Fe(H, S, I, Ctxt)|"|"],
+% [Txt|format(T, ctxt_bump_indent(Ctxt, width(Txt, Ctxt)))]
+% end.
+
+indent(Ctxt) -> indent(Ctxt#ctxt.indent, Ctxt).
+
+indent(N, _Ctxt) when N =< 0 -> "";
+indent(N, Ctxt) ->
+ T = Ctxt#ctxt.tab_width,
+ string:chars($\t, N div T, string:chars($\s, N rem T)).
+
+nl_indent(Ctxt) -> [$\n|indent(Ctxt)].
+
+
+unindent(T, Ctxt) ->
+ unindent(T, Ctxt#ctxt.indent, Ctxt, []).
+
+unindent(T, N, _Ctxt, C) when N =< 0 ->
+ [T|C];
+unindent([$\s|T], N, Ctxt, C) ->
+ unindent(T, N - 1, Ctxt, C);
+unindent([$\t|T], N, Ctxt, C) ->
+ Tab = Ctxt#ctxt.tab_width,
+ if N >= Tab ->
+ unindent(T, N - Tab, Ctxt, C);
+ true ->
+ unindent([string:chars($\s, Tab - N)|T], 0, Ctxt, C)
+ end;
+unindent([L|T], N, Ctxt, C) when list(L) ->
+ unindent(L, N, Ctxt, [T|C]);
+unindent([H|T], _N, _Ctxt, C) ->
+ [H|[T|C]];
+unindent([], N, Ctxt, [H|T]) ->
+ unindent(H, N, Ctxt, T);
+unindent([], _, _, []) -> [].
+
+
+width(Txt, Ctxt) ->
+ width(Txt, 0, Ctxt, []).
+
+width([$\t|T], A, Ctxt, C) ->
+ width(T, A + Ctxt#ctxt.tab_width, Ctxt, C);
+width([$\n|T], _A, Ctxt, C) ->
+ width(unindent([T|C], Ctxt), Ctxt);
+width([H|T], A, Ctxt, C) when list(H) ->
+ width(H, A, Ctxt, [T|C]);
+width([_|T], A, Ctxt, C) ->
+ width(T, A + 1, Ctxt, C);
+width([], A, Ctxt, [H|T]) ->
+ width(H, A, Ctxt, T);
+width([], A, _, []) -> A.
+
+ctxt_bump_indent(Ctxt, Dx) ->
+ Ctxt#ctxt{indent=Ctxt#ctxt.indent + Dx}.
+
+core_atom(A) -> io_lib:write_string(atom_to_list(A), $').
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_life.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_life.erl
new file mode 100644
index 0000000000..9579b5f46a
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_life.erl
@@ -0,0 +1,448 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: v3_life.erl,v 1.2 2010/03/04 13:54:20 maria Exp $
+%%
+%% Purpose : Convert annotated kernel expressions to annotated beam format.
+
+%% This module creates beam format annotated with variable lifetime
+%% information. Each thing is given an index and for each variable we
+%% store the first and last index for its occurrence. The variable
+%% database, VDB, attached to each thing is only relevant internally
+%% for that thing.
+%%
+%% For nested things like matches the numbering continues locally and
+%% the VDB for that thing refers to the variable usage within that
+%% thing. Variables which live through a such a thing are internally
+%% given a very large last index. Internally the indexes continue
+%% after the index of that thing. This creates no problems as the
+%% internal variable info never escapes and externally we only see
+%% variable which are alive both before or after.
+%%
+%% This means that variables never "escape" from a thing and the only
+%% way to get values from a thing is to "return" them, with 'break' or
+%% 'return'. Externally these values become the return values of the
+%% thing. This is no real limitation as most nested things have
+%% multiple threads so working out a common best variable usage is
+%% difficult.
+
+-module(v3_life).
+
+-export([module/2]).
+
+-export([vdb_find/2]).
+
+-import(lists, [map/2,foldl/3]).
+-import(ordsets, [add_element/2,intersection/2,union/2,union/1]).
+
+-include("v3_kernel.hrl").
+-include("v3_life.hrl").
+
+%% These are not defined in v3_kernel.hrl.
+get_kanno(Kthing) -> element(2, Kthing).
+%%set_kanno(Kthing, Anno) -> setelement(2, Kthing, Anno).
+
+module(#k_mdef{name=M,exports=Es,attributes=As,body=Fs0}, Opts) ->
+ put(?MODULE, Opts),
+ Fs1 = map(fun function/1, Fs0),
+ erase(?MODULE),
+ {ok,{M,Es,As,Fs1}}.
+
+%% function(Kfunc) -> Func.
+
+function(#k_fdef{func=F,arity=Ar,vars=Vs,body=Kb}) ->
+ %%ok = io:fwrite("life ~w: ~p~n", [?LINE,{F,Ar}]),
+ As = var_list(Vs),
+ Vdb0 = foldl(fun ({var,N}, Vdb) -> new_var(N, 0, Vdb) end, [], As),
+ %% Force a top-level match!
+ B0 = case Kb of
+ #k_match{} -> Kb;
+ _ ->
+ Ka = get_kanno(Kb),
+ #k_match{anno=#k{us=Ka#k.us,ns=[],a=Ka#k.a},
+ vars=Vs,body=Kb,ret=[]}
+ end,
+ {B1,_,Vdb1} = body(B0, 1, Vdb0),
+ {function,F,Ar,As,B1,Vdb1}.
+
+%% body(Kbody, I, Vdb) -> {[Expr],MaxI,Vdb}.
+%% Handle a body, need special cases for transforming match_fails.
+%% We KNOW that they only occur last in a body.
+
+body(#k_seq{arg=#k_put{anno=Pa,arg=Arg,ret=[R]},
+ body=#k_enter{anno=Ea,op=#k_internal{name=match_fail,arity=1},
+ args=[R]}},
+ I, Vdb0) ->
+ Vdb1 = use_vars(Pa#k.us, I, Vdb0), %All used here
+ {[match_fail(Arg, I, Pa#k.a ++ Ea#k.a)],I,Vdb1};
+body(#k_enter{anno=Ea,op=#k_internal{name=match_fail,arity=1},args=[Arg]},
+ I, Vdb0) ->
+ Vdb1 = use_vars(Ea#k.us, I, Vdb0),
+ {[match_fail(Arg, I, Ea#k.a)],I,Vdb1};
+body(#k_seq{arg=Ke,body=Kb}, I, Vdb0) ->
+ %%ok = io:fwrite("life ~w:~p~n", [?LINE,{Ke,I,Vdb0}]),
+ A = get_kanno(Ke),
+ Vdb1 = use_vars(A#k.us, I, new_vars(A#k.ns, I, Vdb0)),
+ {Es,MaxI,Vdb2} = body(Kb, I+1, Vdb1),
+ E = expr(Ke, I, Vdb2),
+ {[E|Es],MaxI,Vdb2};
+body(Ke, I, Vdb0) ->
+ %%ok = io:fwrite("life ~w:~p~n", [?LINE,{Ke,I,Vdb0}]),
+ A = get_kanno(Ke),
+ Vdb1 = use_vars(A#k.us, I, new_vars(A#k.ns, I, Vdb0)),
+ E = expr(Ke, I, Vdb1),
+ {[E],I,Vdb1}.
+
+%% guard(Kguard, I, Vdb) -> Guard.
+
+guard(#k_try{anno=A,arg=Ts,vars=[#k_var{name=X}],body=#k_var{name=X},
+ handler=#k_atom{val=false},ret=Rs}, I, Vdb) ->
+ %% Lock variables that are alive before try and used afterwards.
+ %% Don't lock variables that are only used inside the try expression.
+ Pdb0 = vdb_sub(I, I+1, Vdb),
+ {T,MaxI,Pdb1} = guard_body(Ts, I+1, Pdb0),
+ Pdb2 = use_vars(A#k.ns, MaxI+1, Pdb1), %Save "return" values
+ #l{ke={protected,T,var_list(Rs)},i=I,a=A#k.a,vdb=Pdb2};
+guard(#k_seq{}=G, I, Vdb0) ->
+ {Es,_,Vdb1} = guard_body(G, I, Vdb0),
+ #l{ke={block,Es},i=I,vdb=Vdb1,a=[]};
+guard(G, I, Vdb) -> guard_expr(G, I, Vdb).
+
+%% guard_body(Kbody, I, Vdb) -> {[Expr],MaxI,Vdb}.
+
+guard_body(#k_seq{arg=Ke,body=Kb}, I, Vdb0) ->
+ A = get_kanno(Ke),
+ Vdb1 = use_vars(A#k.us, I, new_vars(A#k.ns, I, Vdb0)),
+ {Es,MaxI,Vdb2} = guard_body(Kb, I+1, Vdb1),
+ E = guard_expr(Ke, I, Vdb2),
+ {[E|Es],MaxI,Vdb2};
+guard_body(Ke, I, Vdb0) ->
+ A = get_kanno(Ke),
+ Vdb1 = use_vars(A#k.us, I, new_vars(A#k.ns, I, Vdb0)),
+ E = guard_expr(Ke, I, Vdb1),
+ {[E],I,Vdb1}.
+
+%% guard_expr(Call, I, Vdb) -> Expr
+
+guard_expr(#k_test{anno=A,op=Op,args=As}, I, _Vdb) ->
+ #l{ke={test,test_op(Op),atomic_list(As)},i=I,a=A#k.a};
+guard_expr(#k_bif{anno=A,op=Op,args=As,ret=Rs}, I, _Vdb) ->
+ #l{ke={bif,bif_op(Op),atomic_list(As),var_list(Rs)},i=I,a=A#k.a};
+guard_expr(#k_put{anno=A,arg=Arg,ret=Rs}, I, _Vdb) ->
+ #l{ke={set,var_list(Rs),literal(Arg)},i=I,a=A#k.a};
+guard_expr(#k_match{anno=A,body=Kb,ret=Rs}, I, Vdb) ->
+ %% Experimental support for andalso/orelse in guards.
+ %% Work out imported variables which need to be locked.
+ Mdb = vdb_sub(I, I+1, Vdb),
+ M = match(Kb, A#k.us, I+1, Mdb),
+ #l{ke={match,M,var_list(Rs)},i=I,vdb=use_vars(A#k.us, I+1, Mdb),a=A#k.a};
+guard_expr(G, I, Vdb) -> guard(G, I, Vdb).
+
+%% expr(Kexpr, I, Vdb) -> Expr.
+
+expr(#k_call{anno=A,op=Op,args=As,ret=Rs}, I, _Vdb) ->
+ #l{ke={call,call_op(Op),atomic_list(As),var_list(Rs)},i=I,a=A#k.a};
+expr(#k_enter{anno=A,op=Op,args=As}, I, _Vdb) ->
+ #l{ke={enter,call_op(Op),atomic_list(As)},i=I,a=A#k.a};
+expr(#k_bif{anno=A,op=Op,args=As,ret=Rs}, I, _Vdb) ->
+ Bif = k_bif(A, Op, As, Rs),
+ #l{ke=Bif,i=I,a=A#k.a};
+expr(#k_match{anno=A,body=Kb,ret=Rs}, I, Vdb) ->
+ %% Work out imported variables which need to be locked.
+ Mdb = vdb_sub(I, I+1, Vdb),
+ M = match(Kb, A#k.us, I+1, Mdb),
+ #l{ke={match,M,var_list(Rs)},i=I,vdb=use_vars(A#k.us, I+1, Mdb),a=A#k.a};
+expr(#k_try{anno=A,arg=Ka,vars=Vs,body=Kb,evars=Evs,handler=Kh,ret=Rs}, I, Vdb) ->
+ %% Lock variables that are alive before the catch and used afterwards.
+ %% Don't lock variables that are only used inside the try.
+ Tdb0 = vdb_sub(I, I+1, Vdb),
+ %% This is the tricky bit. Lock variables in Arg that are used in
+ %% the body and handler. Add try tag 'variable'.
+ Ab = get_kanno(Kb),
+ Ah = get_kanno(Kh),
+ Tdb1 = use_vars(Ab#k.us, I+3, use_vars(Ah#k.us, I+3, Tdb0)),
+ Tdb2 = vdb_sub(I, I+2, Tdb1),
+ Vnames = fun (Kvar) -> Kvar#k_var.name end, %Get the variable names
+ {Aes,_,Adb} = body(Ka, I+2, add_var({catch_tag,I+1}, I+1, 1000000, Tdb2)),
+ {Bes,_,Bdb} = body(Kb, I+4, new_vars(map(Vnames, Vs), I+3, Tdb2)),
+ {Hes,_,Hdb} = body(Kh, I+4, new_vars(map(Vnames, Evs), I+3, Tdb2)),
+ #l{ke={'try',#l{ke={block,Aes},i=I+1,vdb=Adb,a=[]},
+ var_list(Vs),#l{ke={block,Bes},i=I+3,vdb=Bdb,a=[]},
+ var_list(Evs),#l{ke={block,Hes},i=I+3,vdb=Hdb,a=[]},
+ var_list(Rs)},
+ i=I,vdb=Tdb1,a=A#k.a};
+expr(#k_catch{anno=A,body=Kb,ret=[R]}, I, Vdb) ->
+ %% Lock variables that are alive before the catch and used afterwards.
+ %% Don't lock variables that are only used inside the catch.
+ %% Add catch tag 'variable'.
+ Cdb0 = vdb_sub(I, I+1, Vdb),
+ {Es,_,Cdb1} = body(Kb, I+1, add_var({catch_tag,I}, I, 1000000, Cdb0)),
+ #l{ke={'catch',Es,variable(R)},i=I,vdb=Cdb1,a=A#k.a};
+expr(#k_receive{anno=A,var=V,body=Kb,timeout=T,action=Ka,ret=Rs}, I, Vdb) ->
+ %% Work out imported variables which need to be locked.
+ Rdb = vdb_sub(I, I+1, Vdb),
+ M = match(Kb, add_element(V#k_var.name, A#k.us), I+1,
+ new_var(V#k_var.name, I, Rdb)),
+ {Tes,_,Adb} = body(Ka, I+1, Rdb),
+ #l{ke={receive_loop,atomic_lit(T),variable(V),M,
+ #l{ke=Tes,i=I+1,vdb=Adb,a=[]},var_list(Rs)},
+ i=I,vdb=use_vars(A#k.us, I+1, Vdb),a=A#k.a};
+expr(#k_receive_accept{anno=A}, I, _Vdb) ->
+ #l{ke=receive_accept,i=I,a=A#k.a};
+expr(#k_receive_next{anno=A}, I, _Vdb) ->
+ #l{ke=receive_next,i=I,a=A#k.a};
+expr(#k_put{anno=A,arg=Arg,ret=Rs}, I, _Vdb) ->
+ #l{ke={set,var_list(Rs),literal(Arg)},i=I,a=A#k.a};
+expr(#k_break{anno=A,args=As}, I, _Vdb) ->
+ #l{ke={break,atomic_list(As)},i=I,a=A#k.a};
+expr(#k_return{anno=A,args=As}, I, _Vdb) ->
+ #l{ke={return,atomic_list(As)},i=I,a=A#k.a}.
+
+%% call_op(Op) -> Op.
+%% bif_op(Op) -> Op.
+%% test_op(Op) -> Op.
+%% Do any necessary name translations here to munge into beam format.
+
+call_op(#k_local{name=N}) -> N;
+call_op(#k_remote{mod=M,name=N}) -> {remote,atomic_lit(M),atomic_lit(N)};
+call_op(Other) -> variable(Other).
+
+bif_op(#k_remote{mod=#k_atom{val=erlang},name=#k_atom{val=N}}) -> N;
+bif_op(#k_internal{name=N}) -> N.
+
+test_op(#k_remote{mod=#k_atom{val=erlang},name=#k_atom{val=N}}) -> N.
+
+%% k_bif(Anno, Op, [Arg], [Ret]) -> Expr.
+%% Build bifs, do special handling of internal some calls.
+
+k_bif(_A, #k_internal{name=dsetelement,arity=3}, As, []) ->
+ {bif,dsetelement,atomic_list(As),[]};
+k_bif(_A, #k_internal{name=make_fun},
+ [#k_atom{val=Fun},#k_int{val=Arity},
+ #k_int{val=Index},#k_int{val=Uniq}|Free],
+ Rs) ->
+ {bif,{make_fun,Fun,Arity,Index,Uniq},var_list(Free),var_list(Rs)};
+k_bif(_A, Op, As, Rs) ->
+ %% The general case.
+ {bif,bif_op(Op),atomic_list(As),var_list(Rs)}.
+
+%% match(Kexpr, [LockVar], I, Vdb) -> Expr.
+%% Convert match tree to old format.
+
+match(#k_alt{anno=A,first=Kf,then=Kt}, Ls, I, Vdb0) ->
+ Vdb1 = use_vars(union(A#k.us, Ls), I, Vdb0),
+ F = match(Kf, Ls, I+1, Vdb1),
+ T = match(Kt, Ls, I+1, Vdb1),
+ #l{ke={alt,F,T},i=I,vdb=Vdb1,a=A#k.a};
+match(#k_select{anno=A,var=V,types=Kts}, Ls0, I, Vdb0) ->
+ Ls1 = add_element(V#k_var.name, Ls0),
+ Vdb1 = use_vars(union(A#k.us, Ls1), I, Vdb0),
+ Ts = map(fun (Tc) -> type_clause(Tc, Ls1, I+1, Vdb1) end, Kts),
+ #l{ke={select,literal(V),Ts},i=I,vdb=Vdb1,a=A#k.a};
+match(#k_guard{anno=A,clauses=Kcs}, Ls, I, Vdb0) ->
+ Vdb1 = use_vars(union(A#k.us, Ls), I, Vdb0),
+ Cs = map(fun (G) -> guard_clause(G, Ls, I+1, Vdb1) end, Kcs),
+ #l{ke={guard,Cs},i=I,vdb=Vdb1,a=A#k.a};
+match(Other, Ls, I, Vdb0) ->
+ Vdb1 = use_vars(Ls, I, Vdb0),
+ {B,_,Vdb2} = body(Other, I+1, Vdb1),
+ #l{ke={block,B},i=I,vdb=Vdb2,a=[]}.
+
+type_clause(#k_type_clause{anno=A,type=T,values=Kvs}, Ls, I, Vdb0) ->
+ %%ok = io:format("life ~w: ~p~n", [?LINE,{T,Kvs}]),
+ Vdb1 = use_vars(union(A#k.us, Ls), I+1, Vdb0),
+ Vs = map(fun (Vc) -> val_clause(Vc, Ls, I+1, Vdb1) end, Kvs),
+ #l{ke={type_clause,type(T),Vs},i=I,vdb=Vdb1,a=A#k.a}.
+
+val_clause(#k_val_clause{anno=A,val=V,body=Kb}, Ls0, I, Vdb0) ->
+ {_Used,New} = match_pat_vars(V),
+ %% Not clear yet how Used should be used.
+ Bus = (get_kanno(Kb))#k.us,
+ %%ok = io:format("Ls0 = ~p, Used=~p\n New=~p, Bus=~p\n", [Ls0,Used,New,Bus]),
+ Ls1 = union(intersection(New, Bus), Ls0), %Lock for safety
+ Vdb1 = use_vars(union(A#k.us, Ls1), I+1, new_vars(New, I, Vdb0)),
+ B = match(Kb, Ls1, I+1, Vdb1),
+ #l{ke={val_clause,literal(V),B},i=I,vdb=use_vars(Bus, I+1, Vdb1),a=A#k.a}.
+
+guard_clause(#k_guard_clause{anno=A,guard=Kg,body=Kb}, Ls, I, Vdb0) ->
+ Vdb1 = use_vars(union(A#k.us, Ls), I+2, Vdb0),
+ Gdb = vdb_sub(I+1, I+2, Vdb1),
+ G = guard(Kg, I+1, Gdb),
+ B = match(Kb, Ls, I+2, Vdb1),
+ #l{ke={guard_clause,G,B},
+ i=I,vdb=use_vars((get_kanno(Kg))#k.us, I+2, Vdb1),
+ a=A#k.a}.
+
+%% match_fail(FailValue, I, Anno) -> Expr.
+%% Generate the correct match_fail instruction. N.B. there is no
+%% generic case for when the fail value has been created elsewhere.
+
+match_fail(#k_tuple{es=[#k_atom{val=function_clause}|As]}, I, A) ->
+ #l{ke={match_fail,{function_clause,literal_list(As)}},i=I,a=A};
+match_fail(#k_tuple{es=[#k_atom{val=badmatch},Val]}, I, A) ->
+ #l{ke={match_fail,{badmatch,literal(Val)}},i=I,a=A};
+match_fail(#k_tuple{es=[#k_atom{val=case_clause},Val]}, I, A) ->
+ #l{ke={match_fail,{case_clause,literal(Val)}},i=I,a=A};
+match_fail(#k_atom{val=if_clause}, I, A) ->
+ #l{ke={match_fail,if_clause},i=I,a=A};
+match_fail(#k_tuple{es=[#k_atom{val=try_clause},Val]}, I, A) ->
+ #l{ke={match_fail,{try_clause,literal(Val)}},i=I,a=A}.
+
+%% type(Ktype) -> Type.
+
+type(k_int) -> integer;
+type(k_char) -> integer; %Hhhmmm???
+type(k_float) -> float;
+type(k_atom) -> atom;
+type(k_nil) -> nil;
+type(k_cons) -> cons;
+type(k_tuple) -> tuple;
+type(k_binary) -> binary;
+type(k_bin_seg) -> bin_seg;
+type(k_bin_end) -> bin_end.
+
+%% variable(Klit) -> Lit.
+%% var_list([Klit]) -> [Lit].
+
+variable(#k_var{name=N}) -> {var,N}.
+
+var_list(Ks) -> map(fun variable/1, Ks).
+
+%% atomic_lit(Klit) -> Lit.
+%% atomic_list([Klit]) -> [Lit].
+
+atomic_lit(#k_var{name=N}) -> {var,N};
+atomic_lit(#k_int{val=I}) -> {integer,I};
+atomic_lit(#k_float{val=F}) -> {float,F};
+atomic_lit(#k_atom{val=N}) -> {atom,N};
+%%atomic_lit(#k_char{val=C}) -> {char,C};
+%%atomic_lit(#k_string{val=S}) -> {string,S};
+atomic_lit(#k_nil{}) -> nil.
+
+atomic_list(Ks) -> map(fun atomic_lit/1, Ks).
+
+%% literal(Klit) -> Lit.
+%% literal_list([Klit]) -> [Lit].
+
+literal(#k_var{name=N}) -> {var,N};
+literal(#k_int{val=I}) -> {integer,I};
+literal(#k_float{val=F}) -> {float,F};
+literal(#k_atom{val=N}) -> {atom,N};
+%%literal(#k_char{val=C}) -> {char,C};
+literal(#k_string{val=S}) -> {string,S};
+literal(#k_nil{}) -> nil;
+literal(#k_cons{hd=H,tl=T}) ->
+ {cons,[literal(H),literal(T)]};
+literal(#k_binary{segs=V}) ->
+ case proplists:get_bool(no_new_binaries, get(?MODULE)) of
+ true ->
+ {old_binary,literal(V)};
+ false ->
+ {binary,literal(V)}
+ end;
+literal(#k_bin_seg{size=S,unit=U,type=T,flags=Fs,seg=Seg,next=N}) ->
+ {bin_seg,literal(S),U,T,Fs,[literal(Seg),literal(N)]};
+literal(#k_bin_end{}) -> bin_end;
+literal(#k_tuple{es=Es}) ->
+ {tuple,literal_list(Es)}.
+
+literal_list(Ks) -> map(fun literal/1, Ks).
+
+%% match_pat_vars(Pattern) -> {[UsedVarName],[NewVarName]}.
+
+match_pat_vars(#k_var{name=N}) -> {[],[N]};
+match_pat_vars(#k_int{}) -> {[],[]};
+match_pat_vars(#k_float{}) -> {[],[]};
+match_pat_vars(#k_atom{}) -> {[],[]};
+%%match_pat_vars(#k_char{}) -> {[],[]};
+match_pat_vars(#k_string{}) -> {[],[]};
+match_pat_vars(#k_nil{}) -> {[],[]};
+match_pat_vars(#k_cons{hd=H,tl=T}) ->
+ match_pat_list_vars([H,T]);
+match_pat_vars(#k_binary{segs=V}) ->
+ match_pat_vars(V);
+match_pat_vars(#k_bin_seg{size=S,seg=Seg,next=N}) ->
+ {U1,New1} = match_pat_vars(Seg),
+ {U2,New2} = match_pat_vars(N),
+ {[],U3} = match_pat_vars(S),
+ {union([U1,U2,U3]),union(New1, New2)};
+match_pat_vars(#k_bin_end{}) -> {[],[]};
+match_pat_vars(#k_tuple{es=Es}) ->
+ match_pat_list_vars(Es).
+
+match_pat_list_vars(Ps) ->
+ foldl(fun (P, {Used0,New0}) ->
+ {Used,New} = match_pat_vars(P),
+ {union(Used0, Used),union(New0, New)} end,
+ {[],[]}, Ps).
+
+%% new_var(VarName, I, Vdb) -> Vdb.
+%% new_vars([VarName], I, Vdb) -> Vdb.
+%% use_var(VarName, I, Vdb) -> Vdb.
+%% use_vars([VarName], I, Vdb) -> Vdb.
+%% add_var(VarName, F, L, Vdb) -> Vdb.
+
+new_var(V, I, Vdb) ->
+ case vdb_find(V, Vdb) of
+ {V,F,L} when I < F -> vdb_store(V, I, L, Vdb);
+ {V,_,_} -> Vdb;
+ error -> vdb_store(V, I, I, Vdb)
+ end.
+
+new_vars(Vs, I, Vdb0) ->
+ foldl(fun (V, Vdb) -> new_var(V, I, Vdb) end, Vdb0, Vs).
+
+use_var(V, I, Vdb) ->
+ case vdb_find(V, Vdb) of
+ {V,F,L} when I > L -> vdb_store(V, F, I, Vdb);
+ {V,_,_} -> Vdb;
+ error -> vdb_store(V, I, I, Vdb)
+ end.
+
+use_vars(Vs, I, Vdb0) ->
+ foldl(fun (V, Vdb) -> use_var(V, I, Vdb) end, Vdb0, Vs).
+
+add_var(V, F, L, Vdb) ->
+ use_var(V, L, new_var(V, F, Vdb)).
+
+vdb_find(V, Vdb) ->
+ %% Peformance note: Profiling shows that this function accounts for
+ %% a lot of the execution time when huge constants terms are built.
+ %% Using the BIF lists:keysearch/3 is a lot faster than the
+ %% original Erlang version.
+ case lists:keysearch(V, 1, Vdb) of
+ {value,Vd} -> Vd;
+ false -> error
+ end.
+
+%vdb_find(V, [{V1,F,L}=Vd|Vdb]) when V < V1 -> error;
+%vdb_find(V, [{V1,F,L}=Vd|Vdb]) when V == V1 -> Vd;
+%vdb_find(V, [{V1,F,L}=Vd|Vdb]) when V > V1 -> vdb_find(V, Vdb);
+%vdb_find(V, []) -> error.
+
+vdb_store(V, F, L, [{V1,_,_}=Vd|Vdb]) when V > V1 ->
+ [Vd|vdb_store(V, F, L, Vdb)];
+vdb_store(V, F, L, [{V1,_,_}=Vd|Vdb]) when V < V1 -> [{V,F,L},Vd|Vdb];
+vdb_store(V, F, L, [{_V1,_,_}|Vdb]) -> [{V,F,L}|Vdb]; %V == V1
+vdb_store(V, F, L, []) -> [{V,F,L}].
+
+%% vdb_sub(Min, Max, Vdb) -> Vdb.
+%% Extract variables which are used before and after Min. Lock
+%% variables alive after Max.
+
+vdb_sub(Min, Max, Vdb) ->
+ [ if L >= Max -> {V,F,1000000};
+ true -> Vd
+ end || {V,F,L}=Vd <- Vdb, F < Min, L >= Min ].
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_life.hrl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_life.hrl
new file mode 100644
index 0000000000..4d183b7234
--- /dev/null
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/v3_life.hrl
@@ -0,0 +1,24 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: v3_life.hrl,v 1.1 2008/12/17 09:53:43 mikpe Exp $
+%%
+%% This record contains variable life-time annotation for a
+%% kernel expression. Added by v3_life, used by v3_codegen.
+
+-record(l, {ke, %Kernel expression
+ i=0, %Op number
+ vdb=[], %Variable database
+ a}). %Core annotation
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/dialyzer_options b/lib/dialyzer/test/options1_tests_SUITE_data/dialyzer_options
deleted file mode 100644
index c612e77d3e..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/dialyzer_options
+++ /dev/null
@@ -1,2 +0,0 @@
-{dialyzer_options, [{include_dirs, ["my_include"]}, {defines, [{'COMPILER_VSN', 42}]}, {warnings, [no_improper_lists]}]}.
-{time_limit, 30}.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Entries b/lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Entries
deleted file mode 100644
index 513d4a315a..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Entries
+++ /dev/null
@@ -1,3 +0,0 @@
-/erl_bits.hrl/1.1/Wed Dec 17 09:53:40 2008//
-/erl_compile.hrl/1.1/Wed Dec 17 09:53:40 2008//
-D
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Repository b/lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Repository
deleted file mode 100644
index 1c6511fec3..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Repository
+++ /dev/null
@@ -1 +0,0 @@
-dialyzer_tests/option_tests/compiler/my_include
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Root b/lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Root
deleted file mode 100644
index f6cdd6158b..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Root
+++ /dev/null
@@ -1 +0,0 @@
-:pserver:stavros@cvs.srv.it.uu.se:/hipe
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/my_include/erl_bits.hrl b/lib/dialyzer/test/options1_tests_SUITE_data/my_include/erl_bits.hrl
deleted file mode 100644
index 96d5cec268..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/my_include/erl_bits.hrl
+++ /dev/null
@@ -1,43 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.0, (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License at
-%% http://www.erlang.org/EPL1_0.txt
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Original Code is Erlang-4.7.3, December, 1998.
-%%
-%% The Initial Developer of the Original Code is Ericsson Telecom
-%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson
-%% Telecom AB. All Rights Reserved.
-%%
-%% Contributor(s): ______________________________________.''
-%%
-%% This is an -*- erlang -*- file.
-%% Generic compiler options, passed from the erl_compile module.
-
--record(bittype, {
- type, %% integer/float/binary
- unit, %% element unit
- sign, %% signed/unsigned
- endian %% big/little
- }).
-
--record(bitdefault, {
- integer, %% default type for integer
- float, %% default type for float
- binary %% default type for binary
- }).
-
-%%% (From config.hrl in the bitsyntax branch.)
--define(SYS_ENDIAN, big).
--define(SIZEOF_CHAR, 1).
--define(SIZEOF_DOUBLE, 8).
--define(SIZEOF_FLOAT, 4).
--define(SIZEOF_INT, 4).
--define(SIZEOF_LONG, 4).
--define(SIZEOF_LONG_LONG, 8).
--define(SIZEOF_SHORT, 2).
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/my_include/erl_compile.hrl b/lib/dialyzer/test/options1_tests_SUITE_data/my_include/erl_compile.hrl
deleted file mode 100644
index ef2b68ac9a..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/my_include/erl_compile.hrl
+++ /dev/null
@@ -1,42 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: erl_compile.hrl,v 1.1 2008/12/17 09:53:40 mikpe Exp $
-%%
-
-%% Generic compiler options, passed from the erl_compile module.
-
--record(options,
- {includes=[], % Include paths (list of absolute
- % directory names).
- outdir=".", % Directory for result (absolute
- % path).
- output_type=undefined, % Type of output file (atom).
- defines=[], % Preprocessor defines. Each
- % element is an atom (the name to
- % define), or a {Name, Value}
- % tuple.
- warning=1, % Warning level (0 - no
- % warnings, 1 - standard level,
- % 2, 3, ... - more warnings).
- verbose=false, % Verbose (true/false).
- optimize=999, % Optimize options.
- specific=[], % Compiler specific options.
- outfile="", % Name of output file (internal
- % use in erl_compile.erl).
- cwd % Current working directory
- % for erlc.
- }).
-
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/results/compiler b/lib/dialyzer/test/options1_tests_SUITE_data/results/compiler
deleted file mode 100644
index 924ef389df..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/results/compiler
+++ /dev/null
@@ -1,35 +0,0 @@
-
-beam_asm.erl:32: The pattern {'error', Error} can never match the type <<_:64,_:_*8>>
-beam_bool.erl:193: The pattern {[], _} can never match the type {[{_,_,_,_},...],[any()]}
-beam_bool.erl:510: The pattern [{'set', [Dst], _, _}, {'%live', _}] can never match the type [{_,_,_,_}]
-beam_disasm.erl:537: The variable X can never match since previous clauses completely covered the type 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
-beam_type.erl:284: The pattern <'pi', 0> can never match the type <_,1 | 2>
-beam_validator.erl:396: The pattern <{'jump', {'f', _}}, Vst = {'vst', 'none', _}> can never match the type <_,#vst{current::#st{ct::[]}}>
-beam_validator.erl:690: The pattern <'term', OldT> can never match the type <{'tuple',[any(),...]},_>
-beam_validator.erl:692: Clause guard cannot succeed. The pattern was matched against the type <{'tuple',[any(),...]},_>
-beam_validator.erl:699: Clause guard cannot succeed. The pattern was matched against the type <{'tuple',[any(),...]},_>
-beam_validator.erl:702: The pattern <'number', OldT = {Type, _}> can never match the type <{'tuple',[any(),...]},_>
-beam_validator.erl:705: The pattern <'bool', {'atom', A}> can never match the type <{'tuple',[any(),...]},_>
-beam_validator.erl:707: The pattern <{'atom', A}, 'bool'> can never match the type <{'tuple',[any(),...]},_>
-beam_validator.erl:713: Guard test is_integer(Sz::[any(),...]) can never succeed
-beam_validator.erl:727: Function upgrade_bool/1 will never be called
-cerl_inline.erl:190: The pattern 'true' can never match the type 'false'
-cerl_inline.erl:219: The pattern 'true' can never match the type 'false'
-cerl_inline.erl:230: The pattern 'true' can never match the type 'false'
-cerl_inline.erl:2333: The pattern 'true' can never match the type 'false'
-cerl_inline.erl:2355: The pattern 'true' can never match the type 'false'
-cerl_inline.erl:238: The pattern 'true' can never match the type 'false'
-cerl_inline.erl:2436: Function filename/1 will never be called
-cerl_inline.erl:2700: The pattern 'true' can never match the type 'false'
-cerl_inline.erl:2730: The pattern <{F, L, D}, Vs> can never match the type <[1..255,...],[any()]>
-cerl_inline.erl:2738: The pattern <{F, L, D}, Vs> can never match the type <[1..255,...],[any()]>
-cerl_inline.erl:2750: The pattern <{[], L, D}, Vs> can never match the type <[1..255,...],[any()]>
-cerl_inline.erl:2752: The pattern <{[], _L, D}, Vs> can never match the type <[1..255,...],[any()]>
-cerl_inline.erl:2754: The pattern <{F, L, D}, Vs> can never match the type <[1..255,...],[any()]>
-cerl_inline.erl:2756: The pattern <{F, _L, D}, Vs> can never match the type <[1..255,...],[any()]>
-compile.erl:788: The pattern {'error', Es} can never match the type {'ok',<<_:64,_:_*8>>}
-core_lint.erl:473: The pattern <{'c_atom', _, 'all'}, 'binary', _Def, St> can never match the type <_,#c_nil{} | {'c_atom' | 'c_char' | 'c_float' | 'c_int' | 'c_string' | 'c_tuple',_,_} | #c_cons{hd::#c_nil{} | {'c_atom' | 'c_char' | 'c_float' | 'c_int' | 'c_string' | 'c_tuple',_,_} | #c_cons{hd::{_,_} | {_,_,_} | {_,_,_,_},tl::{_,_} | {_,_,_} | {_,_,_,_}},tl::#c_nil{} | {'c_atom' | 'c_char' | 'c_float' | 'c_int' | 'c_string' | 'c_tuple',_,_} | #c_cons{hd::{_,_} | {_,_,_} | {_,_,_,_},tl::{_,_} | {_,_,_} | {_,_,_,_}}},[any()],_>
-core_lint.erl:505: The pattern <_Req, 'unknown', St> can never match the type
-v3_codegen.erl:1569: The call v3_codegen:load_reg_1(V::any(),I::0,Rs::any(),pos_integer()) will never return since it differs in the 4th argument from the success typing arguments: (any(),0,maybe_improper_list(),0)
-v3_codegen.erl:1571: The call v3_codegen:load_reg_1(V::any(),I::0,[],pos_integer()) will never return since it differs in the 4th argument from the success typing arguments: (any(),0,maybe_improper_list(),0)
-v3_core.erl:646: The pattern can never match the type <#c_nil{anno::[any(),...]} | {'c_atom' | 'c_char' | 'c_float' | 'c_int' | 'c_string' | 'c_tuple' | 'c_var' | 'ibinary' | 'icatch' | 'ireceive1',[any(),...] | {_,_,_,_},_} | #c_cons{anno::[any(),...]} | #c_fname{anno::[any(),...]} | #iletrec{anno::{_,_,_,_},defs::[any(),...],body::[any(),...]} | #icase{anno::{_,_,_,_},args::[any()],clauses::[any()],fc::{_,_,_,_,_,_}} | #ireceive2{anno::{_,_,_,_},clauses::[any()],action::[any()]} | #ifun{anno::{_,_,_,_},id::[any(),...],vars::[any()],clauses::[any(),...],fc::{_,_,_,_,_,_}} | #imatch{anno::{_,_,_,_},guard::[],fc::{_,_,_,_,_,_}} | #itry{anno::{_,_,_,_},args::[any()],vars::[any(),...],body::[any(),...],evars::[any(),...],handler::[any(),...]},_>
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_asm.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_asm.erl
deleted file mode 100644
index c2d9edcaa7..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_asm.erl
+++ /dev/null
@@ -1,358 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: beam_asm.erl,v 1.1 2008/12/17 09:53:40 mikpe Exp $
-%%
-%% Purpose : Assembler for threaded Beam.
-
--module(beam_asm).
-
--export([module/4,format_error/1]).
--export([encode/2]).
-
--import(lists, [map/2,member/2,keymember/3,duplicate/2]).
--include("beam_opcodes.hrl").
-
--define(bs_aligned, 1).
-
-module(Code, Abst, SourceFile, Opts) ->
- case assemble(Code, Abst, SourceFile, Opts) of
- {error, Error} ->
- {error, [{none, ?MODULE, Error}]};
- Bin when binary(Bin) ->
- {ok, Bin}
- end.
-
-format_error({crashed, Why}) ->
- io_lib:format("beam_asm_int: EXIT: ~p", [Why]).
-
-assemble({Mod,Exp,Attr,Asm,NumLabels}, Abst, SourceFile, Opts) ->
- {1,Dict0} = beam_dict:atom(Mod, beam_dict:new()),
- NumFuncs = length(Asm),
- {Code,Dict1} = assemble_1(Asm, Exp, Dict0, []),
- build_file(Code, Attr, Dict1, NumLabels, NumFuncs, Abst, SourceFile, Opts).
-
-assemble_1([{function,Name,Arity,Entry,Asm}|T], Exp, Dict0, Acc) ->
- Dict1 = case member({Name,Arity}, Exp) of
- true ->
- beam_dict:export(Name, Arity, Entry, Dict0);
- false ->
- beam_dict:local(Name, Arity, Entry, Dict0)
- end,
- {Code, Dict2} = assemble_function(Asm, Acc, Dict1),
- assemble_1(T, Exp, Dict2, Code);
-assemble_1([], _Exp, Dict0, Acc) ->
- {IntCodeEnd,Dict1} = make_op(int_code_end, Dict0),
- {list_to_binary(lists:reverse(Acc, [IntCodeEnd])),Dict1}.
-
-assemble_function([H|T], Acc, Dict0) ->
- {Code, Dict} = make_op(H, Dict0),
- assemble_function(T, [Code| Acc], Dict);
-assemble_function([], Code, Dict) ->
- {Code, Dict}.
-
-build_file(Code, Attr, Dict, NumLabels, NumFuncs, Abst, SourceFile, Opts) ->
- %% Create the code chunk.
-
- CodeChunk = chunk(<<"Code">>,
- <<16:32,
- (beam_opcodes:format_number()):32,
- (beam_dict:highest_opcode(Dict)):32,
- NumLabels:32,
- NumFuncs:32>>,
- Code),
-
- %% Create the atom table chunk.
-
- {NumAtoms, AtomTab} = beam_dict:atom_table(Dict),
- AtomChunk = chunk(<<"Atom">>, <>, AtomTab),
-
- %% Create the import table chunk.
-
- {NumImps, ImpTab0} = beam_dict:import_table(Dict),
- Imp = flatten_imports(ImpTab0),
- ImportChunk = chunk(<<"ImpT">>, <>, Imp),
-
- %% Create the export table chunk.
-
- {NumExps, ExpTab0} = beam_dict:export_table(Dict),
- Exp = flatten_exports(ExpTab0),
- ExpChunk = chunk(<<"ExpT">>, <>, Exp),
-
- %% Create the local function table chunk.
-
- {NumLocals, Locals} = beam_dict:local_table(Dict),
- Loc = flatten_exports(Locals),
- LocChunk = chunk(<<"LocT">>, <>, Loc),
-
- %% Create the string table chunk.
-
- {_,StringTab} = beam_dict:string_table(Dict),
- StringChunk = chunk(<<"StrT">>, StringTab),
-
- %% Create the fun table chunk. It is important not to build an empty chunk,
- %% as that would change the MD5.
-
- LambdaChunk = case beam_dict:lambda_table(Dict) of
- {0,[]} -> [];
- {NumLambdas,LambdaTab} ->
- chunk(<<"FunT">>, <>, LambdaTab)
- end,
-
- %% Create the attributes and compile info chunks.
-
- Essentials = [AtomChunk,CodeChunk,StringChunk,ImportChunk,ExpChunk,LambdaChunk],
- {Attributes,Compile} = build_attributes(Opts, SourceFile, Attr, Essentials),
- AttrChunk = chunk(<<"Attr">>, Attributes),
- CompileChunk = chunk(<<"CInf">>, Compile),
-
- %% Create the abstract code chunk.
-
- AbstChunk = chunk(<<"Abst">>, Abst),
-
- %% Create IFF chunk.
-
- Chunks = case member(slim, Opts) of
- true -> [Essentials,AttrChunk,CompileChunk,AbstChunk];
- false -> [Essentials,LocChunk,AttrChunk,CompileChunk,AbstChunk]
- end,
- build_form(<<"BEAM">>, Chunks).
-
-%% Build an IFF form.
-
-build_form(Id, Chunks0) when size(Id) == 4, list(Chunks0) ->
- Chunks = list_to_binary(Chunks0),
- Size = size(Chunks),
- 0 = Size rem 4, % Assertion: correct padding?
- <<"FOR1",(Size+4):32,Id/binary,Chunks/binary>>.
-
-%% Build a correctly padded chunk (with no sub-header).
-
-chunk(Id, Contents) when size(Id) == 4, binary(Contents) ->
- Size = size(Contents),
- [<>,Contents|pad(Size)];
-chunk(Id, Contents) when list(Contents) ->
- chunk(Id, list_to_binary(Contents)).
-
-%% Build a correctly padded chunk (with a sub-header).
-
-chunk(Id, Head, Contents) when size(Id) == 4, is_binary(Head), is_binary(Contents) ->
- Size = size(Head)+size(Contents),
- [<>,Contents|pad(Size)];
-chunk(Id, Head, Contents) when list(Contents) ->
- chunk(Id, Head, list_to_binary(Contents)).
-
-pad(Size) ->
- case Size rem 4 of
- 0 -> [];
- Rem -> duplicate(4 - Rem, 0)
- end.
-
-flatten_exports(Exps) ->
- list_to_binary(map(fun({F,A,L}) -> <> end, Exps)).
-
-flatten_imports(Imps) ->
- list_to_binary(map(fun({M,F,A}) -> <> end, Imps)).
-
-build_attributes(Opts, SourceFile, Attr, Essentials) ->
- Misc = case member(slim, Opts) of
- false ->
- {{Y,Mo,D},{H,Mi,S}} = erlang:universaltime(),
- [{time,{Y,Mo,D,H,Mi,S}},{source,SourceFile}];
- true -> []
- end,
- Compile = [{options,Opts},{version,?COMPILER_VSN}|Misc],
- {term_to_binary(calc_vsn(Attr, Essentials)),term_to_binary(Compile)}.
-
-%%
-%% If the attributes contains no 'vsn' attribute, we'll insert one
-%% with an MD5 "checksum" calculated on the code as its value.
-%% We'll not change an existing 'vsn' attribute.
-%%
-
-calc_vsn(Attr, Essentials) ->
- case keymember(vsn, 1, Attr) of
- true -> Attr;
- false ->
- <> = erlang:md5(Essentials),
- [{vsn,[Number]}|Attr]
- end.
-
-bif_type('-', 1) -> negate;
-bif_type('+', 2) -> {op, m_plus};
-bif_type('-', 2) -> {op, m_minus};
-bif_type('*', 2) -> {op, m_times};
-bif_type('/', 2) -> {op, m_div};
-bif_type('div', 2) -> {op, int_div};
-bif_type('rem', 2) -> {op, int_rem};
-bif_type('band', 2) -> {op, int_band};
-bif_type('bor', 2) -> {op, int_bor};
-bif_type('bxor', 2) -> {op, int_bxor};
-bif_type('bsl', 2) -> {op, int_bsl};
-bif_type('bsr', 2) -> {op, int_bsr};
-bif_type('bnot', 1) -> {op, int_bnot};
-bif_type(fnegate, 1) -> {op, fnegate};
-bif_type(fadd, 2) -> {op, fadd};
-bif_type(fsub, 2) -> {op, fsub};
-bif_type(fmul, 2) -> {op, fmul};
-bif_type(fdiv, 2) -> {op, fdiv};
-bif_type(_, _) -> bif.
-
-make_op(Comment, Dict) when element(1, Comment) == '%' ->
- {[],Dict};
-make_op({'%live',_R}, Dict) ->
- {[],Dict};
-make_op({bif, Bif, nofail, [], Dest}, Dict) ->
- encode_op(bif0, [{extfunc, erlang, Bif, 0}, Dest], Dict);
-make_op({bif, raise, _Fail, [A1,A2], _Dest}, Dict) ->
- encode_op(raise, [A1,A2], Dict);
-make_op({bif, Bif, Fail, Args, Dest}, Dict) ->
- Arity = length(Args),
- case bif_type(Bif, Arity) of
- {op, Op} ->
- make_op(list_to_tuple([Op, Fail|Args++[Dest]]), Dict);
- negate ->
- %% Fake negation operator.
- make_op({m_minus, Fail, {integer,0}, hd(Args), Dest}, Dict);
- bif ->
- BifOp = list_to_atom(lists:concat([bif, Arity])),
- encode_op(BifOp, [Fail, {extfunc, erlang, Bif, Arity}|Args++[Dest]],
- Dict)
- end;
-make_op({bs_add=Op,Fail,[Src1,Src2,Unit],Dest}, Dict) ->
- encode_op(Op, [Fail,Src1,Src2,Unit,Dest], Dict);
-make_op({test,Cond,Fail,Ops}, Dict) when list(Ops) ->
- encode_op(Cond, [Fail|Ops], Dict);
-make_op({make_fun2,{f,Lbl},Index,OldUniq,NumFree}, Dict0) ->
- {Fun,Dict} = beam_dict:lambda(Lbl, Index, OldUniq, NumFree, Dict0),
- make_op({make_fun2,Fun}, Dict);
-make_op(Op, Dict) when atom(Op) ->
- encode_op(Op, [], Dict);
-make_op({kill,Y}, Dict) ->
- make_op({init,Y}, Dict);
-make_op({Name,Arg1}, Dict) ->
- encode_op(Name, [Arg1], Dict);
-make_op({Name,Arg1,Arg2}, Dict) ->
- encode_op(Name, [Arg1,Arg2], Dict);
-make_op({Name,Arg1,Arg2,Arg3}, Dict) ->
- encode_op(Name, [Arg1,Arg2,Arg3], Dict);
-make_op({Name,Arg1,Arg2,Arg3,Arg4}, Dict) ->
- encode_op(Name, [Arg1,Arg2,Arg3,Arg4], Dict);
-make_op({Name,Arg1,Arg2,Arg3,Arg4,Arg5}, Dict) ->
- encode_op(Name, [Arg1,Arg2,Arg3,Arg4,Arg5], Dict);
-make_op({Name,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6}, Dict) ->
- encode_op(Name, [Arg1,Arg2,Arg3,Arg4,Arg5,Arg6], Dict).
-
-encode_op(Name, Args, Dict0) when atom(Name) ->
- {EncArgs,Dict1} = encode_args(Args, Dict0),
- Op = beam_opcodes:opcode(Name, length(Args)),
- Dict2 = beam_dict:opcode(Op, Dict1),
- {list_to_binary([Op|EncArgs]),Dict2}.
-
-encode_args([Arg| T], Dict0) ->
- {EncArg, Dict1} = encode_arg(Arg, Dict0),
- {EncTail, Dict2} = encode_args(T, Dict1),
- {[EncArg| EncTail], Dict2};
-encode_args([], Dict) ->
- {[], Dict}.
-
-encode_arg({x, X}, Dict) when X >= 0 ->
- {encode(?tag_x, X), Dict};
-encode_arg({y, Y}, Dict) when Y >= 0 ->
- {encode(?tag_y, Y), Dict};
-encode_arg({atom, Atom}, Dict0) when atom(Atom) ->
- {Index, Dict} = beam_dict:atom(Atom, Dict0),
- {encode(?tag_a, Index), Dict};
-encode_arg({integer, N}, Dict) ->
- {encode(?tag_i, N), Dict};
-encode_arg(nil, Dict) ->
- {encode(?tag_a, 0), Dict};
-encode_arg({f, W}, Dict) ->
- {encode(?tag_f, W), Dict};
-encode_arg({'char', C}, Dict) ->
- {encode(?tag_h, C), Dict};
-encode_arg({string, String}, Dict0) ->
- {Offset, Dict} = beam_dict:string(String, Dict0),
- {encode(?tag_u, Offset), Dict};
-encode_arg({extfunc, M, F, A}, Dict0) ->
- {Index, Dict} = beam_dict:import(M, F, A, Dict0),
- {encode(?tag_u, Index), Dict};
-encode_arg({list, List}, Dict0) ->
- {L, Dict} = encode_list(List, Dict0, []),
- {[encode(?tag_z, 1), encode(?tag_u, length(List))|L], Dict};
-encode_arg({float, Float}, Dict) when float(Float) ->
- {[encode(?tag_z, 0)|<>], Dict};
-encode_arg({fr,Fr}, Dict) ->
- {[encode(?tag_z, 2),encode(?tag_u,Fr)], Dict};
-encode_arg({field_flags,Flags0}, Dict) ->
- Flags = lists:foldl(fun (F, S) -> S bor flag_to_bit(F) end, 0, Flags0),
- {encode(?tag_u, Flags), Dict};
-encode_arg({alloc,List}, Dict) ->
- {encode_alloc_list(List),Dict};
-encode_arg(Int, Dict) when is_integer(Int) ->
- {encode(?tag_u, Int),Dict}.
-
-flag_to_bit(aligned) -> 16#01;
-flag_to_bit(little) -> 16#02;
-flag_to_bit(big) -> 16#00;
-flag_to_bit(signed) -> 16#04;
-flag_to_bit(unsigned)-> 16#00;
-flag_to_bit(exact) -> 16#08;
-flag_to_bit(native) -> 16#10.
-
-encode_list([H|T], _Dict, _Acc) when is_list(H) ->
- exit({illegal_nested_list,encode_arg,[H|T]});
-encode_list([H|T], Dict0, Acc) ->
- {Enc,Dict} = encode_arg(H, Dict0),
- encode_list(T, Dict, [Enc|Acc]);
-encode_list([], Dict, Acc) ->
- {lists:reverse(Acc), Dict}.
-
-encode_alloc_list(L0) ->
- L = encode_alloc_list_1(L0),
- [encode(?tag_z, 3),encode(?tag_u, length(L0))|L].
-
-encode_alloc_list_1([{words,Words}|T]) ->
- [encode(?tag_u, 0),encode(?tag_u, Words)|encode_alloc_list_1(T)];
-encode_alloc_list_1([{floats,Floats}|T]) ->
- [encode(?tag_u, 1),encode(?tag_u, Floats)|encode_alloc_list_1(T)];
-encode_alloc_list_1([]) -> [].
-
-encode(Tag, N) when N < 0 ->
- encode1(Tag, negative_to_bytes(N, []));
-encode(Tag, N) when N < 16 ->
- (N bsl 4) bor Tag;
-encode(Tag, N) when N < 16#800 ->
- [((N bsr 3) band 2#11100000) bor Tag bor 2#00001000, N band 16#ff];
-encode(Tag, N) ->
- encode1(Tag, to_bytes(N, [])).
-
-encode1(Tag, Bytes) ->
- case length(Bytes) of
- Num when 2 =< Num, Num =< 8 ->
- [((Num-2) bsl 5) bor 2#00011000 bor Tag| Bytes];
- Num when 8 < Num ->
- [2#11111000 bor Tag, encode(?tag_u, Num-9)| Bytes]
- end.
-
-to_bytes(0, [B|Acc]) when B < 128 ->
- [B|Acc];
-to_bytes(N, Acc) ->
- to_bytes(N bsr 8, [N band 16#ff| Acc]).
-
-negative_to_bytes(-1, [B1, B2|T]) when B1 > 127 ->
- [B1, B2|T];
-negative_to_bytes(N, Acc) ->
- negative_to_bytes(N bsr 8, [N band 16#ff|Acc]).
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_block.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_block.erl
deleted file mode 100644
index b0dd3e6380..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_block.erl
+++ /dev/null
@@ -1,601 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: beam_block.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
-%%
-%% Purpose : Partitions assembly instructions into basic blocks and
-%% optimizes them.
-
--module(beam_block).
-
--export([module/2]).
--export([live_at_entry/1]). %Used by beam_type, beam_bool.
--export([is_killed/2]). %Used by beam_dead, beam_type, beam_bool.
--export([is_not_used/2]). %Used by beam_bool.
--export([merge_blocks/2]). %Used by beam_jump.
--import(lists, [map/2,mapfoldr/3,reverse/1,reverse/2,foldl/3,
- member/2,sort/1,all/2]).
--define(MAXREG, 1024).
-
-module({Mod,Exp,Attr,Fs,Lc}, _Opt) ->
- {ok,{Mod,Exp,Attr,map(fun function/1, Fs),Lc}}.
-
-function({function,Name,Arity,CLabel,Is0}) ->
- %% Collect basic blocks and optimize them.
- Is = blockify(Is0),
-
- %% Done.
- {function,Name,Arity,CLabel,Is}.
-
-%% blockify(Instructions0) -> Instructions
-%% Collect sequences of instructions to basic blocks and
-%% optimize the contents of the blocks. Also do some simple
-%% optimations on instructions outside the blocks.
-
-blockify(Is) ->
- blockify(Is, []).
-
-blockify([{loop_rec,{f,Fail},{x,0}},{loop_rec_end,_Lbl},{label,Fail}|Is], Acc) ->
- %% Useless instruction sequence.
- blockify(Is, Acc);
-blockify([{test,bs_test_tail,F,[Bits]}|Is],
- [{test,bs_skip_bits,F,[{integer,I},Unit,_Flags]}|Acc]) ->
- blockify(Is, [{test,bs_test_tail,F,[Bits+I*Unit]}|Acc]);
-blockify([{test,bs_skip_bits,F,[{integer,I1},Unit1,_]}|Is],
- [{test,bs_skip_bits,F,[{integer,I2},Unit2,Flags]}|Acc]) ->
- blockify(Is, [{test,bs_skip_bits,F,
- [{integer,I1*Unit1+I2*Unit2},1,Flags]}|Acc]);
-blockify([{test,is_atom,{f,Fail},[Reg]}=I|
- [{select_val,Reg,{f,Fail},
- {list,[{atom,false},{f,_}=BrFalse,
- {atom,true}=AtomTrue,{f,_}=BrTrue]}}|Is]=Is0],
- [{block,Bl}|_]=Acc) ->
- case is_last_bool(Bl, Reg) of
- false ->
- blockify(Is0, [I|Acc]);
- true ->
- blockify(Is, [{jump,BrTrue},
- {test,is_eq_exact,BrFalse,[Reg,AtomTrue]}|Acc])
- end;
-blockify([{test,is_atom,{f,Fail},[Reg]}=I|
- [{select_val,Reg,{f,Fail},
- {list,[{atom,true}=AtomTrue,{f,_}=BrTrue,
- {atom,false},{f,_}=BrFalse]}}|Is]=Is0],
- [{block,Bl}|_]=Acc) ->
- case is_last_bool(Bl, Reg) of
- false ->
- blockify(Is0, [I|Acc]);
- true ->
- blockify(Is, [{jump,BrTrue},
- {test,is_eq_exact,BrFalse,[Reg,AtomTrue]}|Acc])
- end;
-blockify([I|Is0]=IsAll, Acc) ->
- case is_bs_put(I) of
- true ->
- {BsPuts0,Is} = collect_bs_puts(IsAll),
- BsPuts = opt_bs_puts(BsPuts0),
- blockify(Is, reverse(BsPuts, Acc));
- false ->
- case collect(I) of
- error -> blockify(Is0, [I|Acc]);
- Instr when is_tuple(Instr) ->
- {Block0,Is} = collect_block(IsAll),
- Block = opt_block(Block0),
- blockify(Is, [{block,Block}|Acc])
- end
- end;
-blockify([], Acc) -> reverse(Acc).
-
-is_last_bool([I,{'%live',_}], Reg) ->
- is_last_bool([I], Reg);
-is_last_bool([{set,[Reg],As,{bif,N,_}}], Reg) ->
- Ar = length(As),
- erl_internal:new_type_test(N, Ar) orelse erl_internal:comp_op(N, Ar)
- orelse erl_internal:bool_op(N, Ar);
-is_last_bool([_|Is], Reg) -> is_last_bool(Is, Reg);
-is_last_bool([], _) -> false.
-
-collect_block(Is) ->
- collect_block(Is, []).
-
-collect_block([{allocate_zero,Ns,R},{test_heap,Nh,R}|Is], Acc) ->
- collect_block(Is, [{allocate,R,{no_opt,Ns,Nh,[]}}|Acc]);
-collect_block([I|Is]=Is0, Acc) ->
- case collect(I) of
- error -> {reverse(Acc),Is0};
- Instr -> collect_block(Is, [Instr|Acc])
- end;
-collect_block([], Acc) -> {reverse(Acc),[]}.
-
-collect({allocate_zero,N,R}) -> {allocate,R,{zero,N,0,[]}};
-collect({test_heap,N,R}) -> {allocate,R,{nozero,nostack,N,[]}};
-collect({bif,N,nofail,As,D}) -> {set,[D],As,{bif,N}};
-collect({bif,N,F,As,D}) -> {set,[D],As,{bif,N,F}};
-collect({move,S,D}) -> {set,[D],[S],move};
-collect({put_list,S1,S2,D}) -> {set,[D],[S1,S2],put_list};
-collect({put_tuple,A,D}) -> {set,[D],[],{put_tuple,A}};
-collect({put,S}) -> {set,[],[S],put};
-collect({put_string,L,S,D}) -> {set,[D],[],{put_string,L,S}};
-collect({get_tuple_element,S,I,D}) -> {set,[D],[S],{get_tuple_element,I}};
-collect({set_tuple_element,S,D,I}) -> {set,[],[S,D],{set_tuple_element,I}};
-collect({get_list,S,D1,D2}) -> {set,[D1,D2],[S],get_list};
-collect(remove_message) -> {set,[],[],remove_message};
-collect({'catch',R,L}) -> {set,[R],[],{'catch',L}};
-collect({'%live',_}=Live) -> Live;
-collect(_) -> error.
-
-opt_block(Is0) ->
- %% We explicitly move any allocate instruction upwards before optimising
- %% moves, to avoid any potential problems with the calculation of live
- %% registers.
- Is1 = find_fixpoint(fun move_allocates/1, Is0),
- Is2 = find_fixpoint(fun opt/1, Is1),
- Is = opt_alloc(Is2),
- share_floats(Is).
-
-find_fixpoint(OptFun, Is0) ->
- case OptFun(Is0) of
- Is0 -> Is0;
- Is1 -> find_fixpoint(OptFun, Is1)
- end.
-
-move_allocates([{set,_Ds,_Ss,{set_tuple_element,_}}|_]=Is) -> Is;
-move_allocates([{set,Ds,Ss,_Op}=Set,{allocate,R,Alloc}|Is]) when is_integer(R) ->
- [{allocate,live_regs(Ds, Ss, R),Alloc},Set|Is];
-move_allocates([{allocate,R1,Alloc1},{allocate,R2,Alloc2}|Is]) ->
- R1 = R2, % Assertion.
- move_allocates([{allocate,R1,combine_alloc(Alloc1, Alloc2)}|Is]);
-move_allocates([I|Is]) ->
- [I|move_allocates(Is)];
-move_allocates([]) -> [].
-
-combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) ->
- {zero,Ns,Nh1+Nh2,Init}.
-
-merge_blocks([{allocate,R,{Attr,Ns,Nh1,Init}}|B1],
- [{allocate,_,{_,nostack,Nh2,[]}}|B2]) ->
- Alloc = {allocate,R,{Attr,Ns,Nh1+Nh2,Init}},
- [Alloc|merge_blocks(B1, B2)];
-merge_blocks(B1, B2) -> merge_blocks_1(B1++[{set,[],[],stop_here}|B2]).
-
-merge_blocks_1([{set,[],_,stop_here}|Is]) -> Is;
-merge_blocks_1([{set,[D],_,move}=I|Is]) ->
- case is_killed(D, Is) of
- true -> merge_blocks_1(Is);
- false -> [I|merge_blocks_1(Is)]
- end;
-merge_blocks_1([I|Is]) -> [I|merge_blocks_1(Is)].
-
-opt([{set,[Dst],As,{bif,Bif,Fail}}=I1,
- {set,[Dst],[Dst],{bif,'not',Fail}}=I2|Is]) ->
- %% Get rid of the 'not' if the operation can be inverted.
- case inverse_comp_op(Bif) of
- none -> [I1,I2|opt(Is)];
- RevBif -> [{set,[Dst],As,{bif,RevBif,Fail}}|opt(Is)]
- end;
-opt([{set,[X],[X],move}|Is]) -> opt(Is);
-opt([{set,[D1],[{integer,Idx1},Reg],{bif,element,{f,0}}}=I1,
- {set,[D2],[{integer,Idx2},Reg],{bif,element,{f,0}}}=I2|Is])
- when Idx1 < Idx2, D1 =/= D2, D1 =/= Reg, D2 =/= Reg ->
- opt([I2,I1|Is]);
-opt([{set,Ds0,Ss,Op}|Is0]) ->
- {Ds,Is} = opt_moves(Ds0, Is0),
- [{set,Ds,Ss,Op}|opt(Is)];
-opt([I|Is]) -> [I|opt(Is)];
-opt([]) -> [].
-
-opt_moves([], Is0) -> {[],Is0};
-opt_moves([D0], Is0) ->
- {D1,Is1} = opt_move(D0, Is0),
- {[D1],Is1};
-opt_moves([X0,Y0]=Ds, Is0) ->
- {X1,Is1} = opt_move(X0, Is0),
- case opt_move(Y0, Is1) of
- {Y1,Is2} when X1 =/= Y1 -> {[X1,Y1],Is2};
- _Other when X1 =/= Y0 -> {[X1,Y0],Is1};
- _Other -> {Ds,Is0}
- end.
-
-opt_move(R, [{set,[D],[R],move}|Is]=Is0) ->
- case is_killed(R, Is) of
- true -> {D,Is};
- false -> {R,Is0}
- end;
-opt_move(R, [I|Is0]) ->
- case is_transparent(R, I) of
- true ->
- {D,Is1} = opt_move(R, Is0),
- case is_transparent(D, I) of
- true -> {D,[I|Is1]};
- false -> {R,[I|Is0]}
- end;
- false -> {R,[I|Is0]}
- end;
-opt_move(R, []) -> {R,[]}.
-
-is_transparent(R, {set,Ds,Ss,_Op}) ->
- case member(R, Ds) of
- true -> false;
- false -> not member(R, Ss)
- end;
-is_transparent(_, _) -> false.
-
-%% is_killed(Register, [Instruction]) -> true|false
-%% Determine whether a register is killed by the instruction sequence.
-%% If true is returned, it means that the register will not be
-%% referenced in ANY way (not even indirectly by an allocate instruction);
-%% i.e. it is OK to enter the instruction sequence with Register
-%% containing garbage.
-
-is_killed({x,N}=R, [{block,Blk}|Is]) ->
- case is_killed(R, Blk) of
- true -> true;
- false ->
- %% Before looking beyond the block, we must be
- %% sure that the register is not referenced by
- %% any allocate instruction in the block.
- case all(fun({allocate,Live,_}) when N < Live -> false;
- (_) -> true
- end, Blk) of
- true -> is_killed(R, Is);
- false -> false
- end
- end;
-is_killed(R, [{block,Blk}|Is]) ->
- case is_killed(R, Blk) of
- true -> true;
- false -> is_killed(R, Is)
- end;
-is_killed(R, [{set,Ds,Ss,_Op}|Is]) ->
- case member(R, Ss) of
- true -> false;
- false ->
- case member(R, Ds) of
- true -> true;
- false -> is_killed(R, Is)
- end
- end;
-is_killed(R, [{case_end,Used}|_]) -> R =/= Used;
-is_killed(R, [{badmatch,Used}|_]) -> R =/= Used;
-is_killed(_, [if_end|_]) -> true;
-is_killed(R, [{func_info,_,_,Ar}|_]) ->
- case R of
- {x,X} when X < Ar -> false;
- _ -> true
- end;
-is_killed(R, [{kill,R}|_]) -> true;
-is_killed(R, [{kill,_}|Is]) -> is_killed(R, Is);
-is_killed(R, [{bs_init2,_,_,_,_,_,Dst}|Is]) ->
- if
- R =:= Dst -> true;
- true -> is_killed(R, Is)
- end;
-is_killed(R, [{bs_put_string,_,_}|Is]) -> is_killed(R, Is);
-is_killed({x,R}, [{'%live',Live}|_]) when R >= Live -> true;
-is_killed({x,R}, [{'%live',_}|Is]) -> is_killed(R, Is);
-is_killed({x,R}, [{allocate,Live,_}|_]) ->
- %% Note: To be safe here, we must return either true or false,
- %% not looking further at the instructions beyond the allocate
- %% instruction.
- R >= Live;
-is_killed({x,R}, [{call,Live,_}|_]) when R >= Live -> true;
-is_killed({x,R}, [{call_last,Live,_,_}|_]) when R >= Live -> true;
-is_killed({x,R}, [{call_only,Live,_}|_]) when R >= Live -> true;
-is_killed({x,R}, [{call_ext,Live,_}|_]) when R >= Live -> true;
-is_killed({x,R}, [{call_ext_last,Live,_,_}|_]) when R >= Live -> true;
-is_killed({x,R}, [{call_ext_only,Live,_}|_]) when R >= Live -> true;
-is_killed({x,R}, [return|_]) when R > 0 -> true;
-is_killed(_, _) -> false.
-
-%% is_not_used(Register, [Instruction]) -> true|false
-%% Determine whether a register is used by the instruction sequence.
-%% If true is returned, it means that the register will not be
-%% referenced directly, but it may be referenced by an allocate
-%% instruction (meaning that it is NOT allowed to contain garbage).
-
-is_not_used(R, [{block,Blk}|Is]) ->
- case is_not_used(R, Blk) of
- true -> true;
- false -> is_not_used(R, Is)
- end;
-is_not_used({x,R}=Reg, [{allocate,Live,_}|Is]) ->
- if
- R >= Live -> true;
- true -> is_not_used(Reg, Is)
- end;
-is_not_used(R, [{set,Ds,Ss,_Op}|Is]) ->
- case member(R, Ss) of
- true -> false;
- false ->
- case member(R, Ds) of
- true -> true;
- false -> is_not_used(R, Is)
- end
- end;
-is_not_used(R, Is) -> is_killed(R, Is).
-
-%% opt_alloc(Instructions) -> Instructions'
-%% Optimises all allocate instructions.
-
-opt_alloc([{allocate,R,{_,Ns,Nh,[]}}|Is]) ->
- [opt_alloc(Is, Ns, Nh, R)|opt(Is)];
-opt_alloc([I|Is]) -> [I|opt_alloc(Is)];
-opt_alloc([]) -> [].
-
-%% opt_alloc(Instructions, FrameSize, HeapNeed, LivingRegs) -> [Instr]
-%% Generates the optimal sequence of instructions for
-%% allocating and initalizing the stack frame and needed heap.
-
-opt_alloc(_Is, nostack, Nh, LivingRegs) ->
- {allocate,LivingRegs,{nozero,nostack,Nh,[]}};
-opt_alloc(Is, Ns, Nh, LivingRegs) ->
- InitRegs = init_yreg(Is, 0),
- case count_ones(InitRegs) of
- N when N*2 > Ns ->
- {allocate,LivingRegs,{nozero,Ns,Nh,gen_init(Ns, InitRegs)}};
- _ ->
- {allocate,LivingRegs,{zero,Ns,Nh,[]}}
- end.
-
-gen_init(Fs, Regs) -> gen_init(Fs, Regs, 0, []).
-
-gen_init(SameFs, _Regs, SameFs, Acc) -> reverse(Acc);
-gen_init(Fs, Regs, Y, Acc) when Regs band 1 == 0 ->
- gen_init(Fs, Regs bsr 1, Y+1, [{init, {y,Y}}|Acc]);
-gen_init(Fs, Regs, Y, Acc) ->
- gen_init(Fs, Regs bsr 1, Y+1, Acc).
-
-%% init_yreg(Instructions, RegSet) -> RegSetInitialized
-%% Calculate the set of initialized y registers.
-
-init_yreg([{set,_,_,{bif,_,_}}|_], Reg) -> Reg;
-init_yreg([{set,Ds,_,_}|Is], Reg) -> init_yreg(Is, add_yregs(Ds, Reg));
-init_yreg(_Is, Reg) -> Reg.
-
-add_yregs(Ys, Reg) -> foldl(fun(Y, R0) -> add_yreg(Y, R0) end, Reg, Ys).
-
-add_yreg({y,Y}, Reg) -> Reg bor (1 bsl Y);
-add_yreg(_, Reg) -> Reg.
-
-count_ones(Bits) -> count_ones(Bits, 0).
-count_ones(0, Acc) -> Acc;
-count_ones(Bits, Acc) ->
- count_ones(Bits bsr 1, Acc + (Bits band 1)).
-
-%% live_at_entry(Is) -> NumberOfRegisters
-%% Calculate the number of register live at the entry to the code
-%% sequence.
-
-live_at_entry([{block,[{allocate,R,_}|_]}|_]) ->
- R;
-live_at_entry([{label,_}|Is]) ->
- live_at_entry(Is);
-live_at_entry([{block,Bl}|_]) ->
- live_at_entry(Bl);
-live_at_entry([{func_info,_,_,Ar}|_]) ->
- Ar;
-live_at_entry(Is0) ->
- case reverse(Is0) of
- [{'%live',Regs}|Is] -> live_at_entry_1(Is, (1 bsl Regs)-1);
- _ -> unknown
- end.
-
-live_at_entry_1([{set,Ds,Ss,_}|Is], Rset0) ->
- Rset = x_live(Ss, x_dead(Ds, Rset0)),
- live_at_entry_1(Is, Rset);
-live_at_entry_1([{allocate,_,_}|Is], Rset) ->
- live_at_entry_1(Is, Rset);
-live_at_entry_1([], Rset) -> live_regs_1(0, Rset).
-
-%% Calculate the new number of live registers when we move an allocate
-%% instruction upwards, passing a 'set' instruction.
-
-live_regs(Ds, Ss, Regs0) ->
- Rset = x_live(Ss, x_dead(Ds, (1 bsl Regs0)-1)),
- live_regs_1(0, Rset).
-
-live_regs_1(N, 0) -> N;
-live_regs_1(N, Regs) -> live_regs_1(N+1, Regs bsr 1).
-
-x_dead([{x,N}|Rs], Regs) -> x_dead(Rs, Regs band (bnot (1 bsl N)));
-x_dead([_|Rs], Regs) -> x_dead(Rs, Regs);
-x_dead([], Regs) -> Regs.
-
-x_live([{x,N}|Rs], Regs) -> x_live(Rs, Regs bor (1 bsl N));
-x_live([_|Rs], Regs) -> x_live(Rs, Regs);
-x_live([], Regs) -> Regs.
-
-%%
-%% If a floating point literal occurs more than once, move it into
-%% a free register and re-use it.
-%%
-
-share_floats([{allocate,_,_}=Alloc|Is]) ->
- [Alloc|share_floats(Is)];
-share_floats(Is0) ->
- All = get_floats(Is0, []),
- MoreThanOnce0 = more_than_once(sort(All), gb_sets:empty()),
- case gb_sets:is_empty(MoreThanOnce0) of
- true -> Is0;
- false ->
- MoreThanOnce = gb_sets:to_list(MoreThanOnce0),
- FreeX = highest_used(Is0, -1) + 1,
- Regs0 = make_reg_map(MoreThanOnce, FreeX, []),
- Regs = gb_trees:from_orddict(Regs0),
- Is = map(fun({set,Ds,[{float,F}],Op}=I) ->
- case gb_trees:lookup(F, Regs) of
- none -> I;
- {value,R} -> {set,Ds,[R],Op}
- end;
- (I) -> I
- end, Is0),
- [{set,[R],[{float,F}],move} || {F,R} <- Regs0] ++ Is
- end.
-
-get_floats([{set,_,[{float,F}],_}|Is], Acc) ->
- get_floats(Is, [F|Acc]);
-get_floats([_|Is], Acc) ->
- get_floats(Is, Acc);
-get_floats([], Acc) -> Acc.
-
-more_than_once([F,F|Fs], Set) ->
- more_than_once(Fs, gb_sets:add(F, Set));
-more_than_once([_|Fs], Set) ->
- more_than_once(Fs, Set);
-more_than_once([], Set) -> Set.
-
-highest_used([{set,Ds,Ss,_}|Is], High) ->
- highest_used(Is, highest(Ds, highest(Ss, High)));
-highest_used([{'%live',Live}|Is], High) when Live > High ->
- highest_used(Is, Live);
-highest_used([_|Is], High) ->
- highest_used(Is, High);
-highest_used([], High) -> High.
-
-highest([{x,R}|Rs], High) when R > High ->
- highest(Rs, R);
-highest([_|Rs], High) ->
- highest(Rs, High);
-highest([], High) -> High.
-
-make_reg_map([F|Fs], R, Acc) when R < ?MAXREG ->
- make_reg_map(Fs, R+1, [{F,{x,R}}|Acc]);
-make_reg_map(_, _, Acc) -> sort(Acc).
-
-%% inverse_comp_op(Op) -> none|RevOp
-
-inverse_comp_op('=:=') -> '=/=';
-inverse_comp_op('=/=') -> '=:=';
-inverse_comp_op('==') -> '/=';
-inverse_comp_op('/=') -> '==';
-inverse_comp_op('>') -> '=<';
-inverse_comp_op('<') -> '>=';
-inverse_comp_op('>=') -> '<';
-inverse_comp_op('=<') -> '>';
-inverse_comp_op(_) -> none.
-
-%%%
-%%% Evaluation of constant bit fields.
-%%%
-
-is_bs_put({bs_put_integer,_,_,_,_,_}) -> true;
-is_bs_put({bs_put_float,_,_,_,_,_}) -> true;
-is_bs_put(_) -> false.
-
-collect_bs_puts(Is) ->
- collect_bs_puts_1(Is, []).
-
-collect_bs_puts_1([I|Is]=Is0, Acc) ->
- case is_bs_put(I) of
- false -> {reverse(Acc),Is0};
- true -> collect_bs_puts_1(Is, [I|Acc])
- end;
-collect_bs_puts_1([], Acc) -> {reverse(Acc),[]}.
-
-opt_bs_puts(Is) ->
- opt_bs_1(Is, []).
-
-opt_bs_1([{bs_put_float,Fail,{integer,Sz},1,Flags0,Src}=I0|Is], Acc) ->
- case catch eval_put_float(Src, Sz, Flags0) of
- {'EXIT',_} ->
- opt_bs_1(Is, [I0|Acc]);
- <> ->
- Flags = force_big(Flags0),
- I = {bs_put_integer,Fail,{integer,Sz},1,Flags,{integer,Int}},
- opt_bs_1([I|Is], Acc)
- end;
-opt_bs_1([{bs_put_integer,_,{integer,8},1,_,{integer,_}}|_]=IsAll, Acc0) ->
- {Is,Acc} = bs_collect_string(IsAll, Acc0),
- opt_bs_1(Is, Acc);
-opt_bs_1([{bs_put_integer,Fail,{integer,Sz},1,F,{integer,N}}=I|Is0], Acc) when Sz > 8 ->
- case field_endian(F) of
- big ->
- case bs_split_int(N, Sz, Fail, Is0) of
- no_split -> opt_bs_1(Is0, [I|Acc]);
- Is -> opt_bs_1(Is, Acc)
- end;
- little ->
- case catch <> of
- {'EXIT',_} ->
- opt_bs_1(Is0, [I|Acc]);
- <> ->
- Flags = force_big(F),
- Is = [{bs_put_integer,Fail,{integer,Sz},1,
- Flags,{integer,Int}}|Is0],
- opt_bs_1(Is, Acc)
- end;
- native -> opt_bs_1(Is0, [I|Acc])
- end;
-opt_bs_1([{Op,Fail,{integer,Sz},U,F,Src}|Is], Acc) when U > 1 ->
- opt_bs_1([{Op,Fail,{integer,U*Sz},1,F,Src}|Is], Acc);
-opt_bs_1([I|Is], Acc) ->
- opt_bs_1(Is, [I|Acc]);
-opt_bs_1([], Acc) -> reverse(Acc).
-
-eval_put_float(Src, Sz, Flags) ->
- Val = value(Src),
- case field_endian(Flags) of
- little -> <>;
- big -> <>
- %% native intentionally not handled here - we can't optimize it.
- end.
-
-value({integer,I}) -> I;
-value({float,F}) -> F;
-value({atom,A}) -> A.
-
-bs_collect_string(Is, [{bs_put_string,Len,{string,Str}}|Acc]) ->
- bs_coll_str_1(Is, Len, reverse(Str), Acc);
-bs_collect_string(Is, Acc) ->
- bs_coll_str_1(Is, 0, [], Acc).
-
-bs_coll_str_1([{bs_put_integer,_,{integer,Sz},U,_,{integer,V}}|Is],
- Len, StrAcc, IsAcc) when U*Sz =:= 8 ->
- Byte = V band 16#FF,
- bs_coll_str_1(Is, Len+1, [Byte|StrAcc], IsAcc);
-bs_coll_str_1(Is, Len, StrAcc, IsAcc) ->
- {Is,[{bs_put_string,Len,{string,reverse(StrAcc)}}|IsAcc]}.
-
-field_endian({field_flags,F}) -> field_endian_1(F).
-
-field_endian_1([big=E|_]) -> E;
-field_endian_1([little=E|_]) -> E;
-field_endian_1([native=E|_]) -> E;
-field_endian_1([_|Fs]) -> field_endian_1(Fs).
-
-force_big({field_flags,F}) ->
- {field_flags,force_big_1(F)}.
-
-force_big_1([big|_]=Fs) -> Fs;
-force_big_1([little|Fs]) -> [big|Fs];
-force_big_1([F|Fs]) -> [F|force_big_1(Fs)].
-
-bs_split_int(0, Sz, _, _) when Sz > 64 ->
- %% We don't want to split in this case because the
- %% string will consist of only zeroes.
- no_split;
-bs_split_int(N, Sz, Fail, Acc) ->
- FirstByteSz = case Sz rem 8 of
- 0 -> 8;
- Rem -> Rem
- end,
- bs_split_int_1(N, FirstByteSz, Sz, Fail, Acc).
-
-bs_split_int_1(N, ByteSz, Sz, Fail, Acc) when Sz > 0 ->
- Mask = (1 bsl ByteSz) - 1,
- I = {bs_put_integer,Fail,{integer,ByteSz},1,
- {field_flags,[big]},{integer,N band Mask}},
- bs_split_int_1(N bsr ByteSz, 8, Sz-ByteSz, Fail, [I|Acc]);
-bs_split_int_1(_, _, _, _, Acc) -> Acc.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_bool.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_bool.erl
deleted file mode 100644
index 3180a22433..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_bool.erl
+++ /dev/null
@@ -1,617 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: beam_bool.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
-%%
-%% Purpose: Optimizes booleans in guards.
-
--module(beam_bool).
-
--export([module/2]).
-
--import(lists, [reverse/1,foldl/3,mapfoldl/3,sort/1,member/2]).
--define(MAXREG, 1024).
-
--record(st,
- {next, %Next label number.
- ll %Live regs at labels.
- }).
-
-module({Mod,Exp,Attr,Fs0,Lc}, _Opts) ->
- %%io:format("~p:\n", [Mod]),
- {Fs,_} = mapfoldl(fun(Fn, Lbl) -> function(Fn, Lbl) end, 100000000, Fs0),
- {ok,{Mod,Exp,Attr,Fs,Lc}}.
-
-function({function,Name,Arity,CLabel,Is0}, Lbl0) ->
- %%io:format("~p/~p:\n", [Name,Arity]),
- {Is,#st{next=Lbl}} = bool_opt(Is0, Lbl0),
- {{function,Name,Arity,CLabel,Is},Lbl}.
-
-%%
-%% Optimize boolean expressions that use guard bifs. Rewrite to
-%% use test instructions if possible.
-%%
-
-bool_opt(Asm, Lbl) ->
- LiveInfo = index_instructions(Asm),
- bopt(Asm, [], #st{next=Lbl,ll=LiveInfo}).
-
-bopt([{block,Bl0}=Block|
- [{jump,{f,Succ}},
- {label,Fail},
- {block,[{set,[Dst],[{atom,false}],move},{'%live',Live}]},
- {label,Succ}|Is]=Is0], Acc0, St) ->
- case split_block(Bl0, Dst, Fail) of
- failed ->
- bopt(Is0, [Block|Acc0], St);
- {Bl,PreBlock} ->
- Acc1 = case PreBlock of
- [] -> Acc0;
- _ -> [{block,PreBlock}|Acc0]
- end,
- Acc = [{protected,[Dst],Bl,{Fail,Succ,Live}}|Acc1],
- bopt(Is, Acc, St)
- end;
-bopt([{test,is_eq_exact,{f,Fail},[Reg,{atom,true}]}=I|Is], [{block,_}|_]=Acc0, St0) ->
- case bopt_block(Reg, Fail, Is, Acc0, St0) of
- failed -> bopt(Is, [I|Acc0], St0);
- {Acc,St} -> bopt(Is, Acc, St)
- end;
-bopt([I|Is], Acc, St) ->
- bopt(Is, [I|Acc], St);
-bopt([], Acc, St) ->
- {bopt_reverse(Acc, []),St}.
-
-bopt_reverse([{protected,[Dst],Block,{Fail,Succ,Live}}|Is], Acc0) ->
- Acc = [{block,Block},{jump,{f,Succ}},
- {label,Fail},
- {block,[{set,[Dst],[{atom,false}],move},{'%live',Live}]},
- {label,Succ}|Acc0],
- bopt_reverse(Is, Acc);
-bopt_reverse([I|Is], Acc) ->
- bopt_reverse(Is, [I|Acc]);
-bopt_reverse([], Acc) -> Acc.
-
-%% bopt_block(Reg, Fail, OldIs, Accumulator, St) -> failed | {NewAcc,St}
-%% Attempt to optimized a block of guard BIFs followed by a test
-%% instruction.
-bopt_block(Reg, Fail, OldIs, [{block,Bl0}|Acc0], St0) ->
- case split_block(Bl0, Reg, Fail) of
- failed ->
- %% Reason for failure: The block either contained no
- %% guard BIFs with the failure label Fail, or the final
- %% instruction in the block did not assign the Reg register.
-
- %%io:format("split ~p: ~P\n", [Reg,Bl0,20]),
- failed;
- {Bl1,BlPre} ->
- %% The block has been splitted. Bl1 is a non-empty list
- %% of guard BIF instructions having the failure label Fail.
- %% BlPre is a (possibly empty list) of instructions preceeding
- %% Bl1.
- Acc1 = make_block(BlPre, Acc0),
- {Bl,Acc} = extend_block(Bl1, Fail, Acc1),
- case catch bopt_block_1(Bl, Fail, St0) of
- {'EXIT',_Reason} ->
- %% Optimization failed for one of the following reasons:
- %%
- %% 1. Not possible to rewrite because a boolean value is
- %% passed to another guard bif, e.g. 'abs(A > B)'
- %% (in this case, obviously nonsense code). Rare in
- %% practice.
- %%
- %% 2. Not possible to rewrite because we have not seen
- %% the complete boolan expression (it is spread out
- %% over several blocks with jumps and labels).
- %% The 'or' and 'and' instructions need to that fully
- %% known operands in order to be eliminated.
- %%
- %% 3. Other bug or limitation.
-
- %%io:format("~P\n", [_Reason,20]),
- failed;
- {NewCode,St} ->
- case is_opt_safe(Bl, NewCode, OldIs, St) of
- false ->
- %% The optimization is not safe. (A register
- %% used by the instructions following the
- %% optimized code is either not assigned a
- %% value at all or assigned a different value.)
-
- %%io:format("\nNot safe:\n"),
- %%io:format("~p\n", [Bl]),
- %%io:format("~p\n", [reverse(NewCode)]),
- failed;
- true -> {NewCode++Acc,St}
- end
- end
- end.
-
-bopt_block_1(Block, Fail, St) ->
- {Pre0,[{_,Tree}]} = bopt_tree(Block),
- Pre = update_fail_label(Pre0, Fail, []),
- bopt_cg(Tree, Fail, make_block(Pre, []), St).
-
-%% is_opt_safe(OriginalCode, OptCode, FollowingCode, State) -> true|false
-%% Comparing the original code to the optimized code, determine
-%% whether the optimized code is guaranteed to work in the same
-%% way as the original code.
-
-is_opt_safe(Bl, NewCode, OldIs, St) ->
- %% Here are the conditions that must be true for the
- %% optimization to be safe.
- %%
- %% 1. Any register that was assigned a value in the original
- %% code, but is not in the optimized code, must be guaranteed
- %% to be KILLED in the following code. (NotSet below.)
- %%
- %% 2. Any register that is assigned a value in the optimized
- %% code must be UNUSED in the following code. (NewDst, Set.)
- %% (Possible future improvement: Registers that are known
- %% to be assigned the SAME value in the original and optimized
- %% code don't need to be unused in the following code.)
-
- PrevDst = dst_regs(Bl),
- NewDst = dst_regs(NewCode),
- NotSet = ordsets:subtract(PrevDst, NewDst),
-
- %% Note: The following line is an optimization. We don't need
- %% to test whether variables in NotSet for being unused, because
- %% they will all be tested for being killed (a stronger condition
- %% than being unused).
-
- Set = ordsets:subtract(NewDst, NotSet),
-
- all_killed(NotSet, OldIs, St) andalso
- none_used(Set, OldIs, St).
-
-% update_fail_label([{set,_,_,{bif,_,{f,0}}}=I|Is], Fail, Acc) ->
-% update_fail_label(Is, Fail, [I|Acc]);
-update_fail_label([{set,Ds,As,{bif,N,{f,_}}}|Is], Fail, Acc) ->
- update_fail_label(Is, Fail, [{set,Ds,As,{bif,N,{f,Fail}}}|Acc]);
-update_fail_label([], _, Acc) -> Acc.
-
-make_block([], Acc) -> Acc;
-make_block(Bl, Acc) -> [{block,Bl}|Acc].
-
-extend_block(BlAcc, Fail, [{protected,_,_,_}=Prot|OldAcc]) ->
- extend_block([Prot|BlAcc], Fail, OldAcc);
-extend_block(BlAcc0, Fail, [{block,Is0}|OldAcc]=OldAcc0) ->
- case extend_block_1(reverse(Is0), Fail, BlAcc0) of
- {[],_} -> {BlAcc0,OldAcc0};
- {BlAcc,[]} -> extend_block(BlAcc, Fail, OldAcc);
- {BlAcc,Is} -> {BlAcc,[{block,Is}|OldAcc]}
- end;
-extend_block(BlAcc, _, OldAcc) -> {BlAcc,OldAcc}.
-
-extend_block_1([{set,[_],_,{bif,_,{f,Fail}}}=I|Is], Fail, Acc) ->
- extend_block_1(Is, Fail, [I|Acc]);
-extend_block_1([{set,[_],As,{bif,Bif,_}}=I|Is]=Is0, Fail, Acc) ->
- case safe_bool_op(Bif, length(As)) of
- false -> {Acc,reverse(Is0)};
- true -> extend_block_1(Is, Fail, [I|Acc])
- end;
-extend_block_1([_|_]=Is, _, Acc) -> {Acc,reverse(Is)};
-extend_block_1([], _, Acc) -> {Acc,[]}.
-
-split_block(Is0, Dst, Fail) ->
- case reverse(Is0) of
- [{'%live',_}|[{set,[Dst],_,_}|_]=Is] ->
- split_block_1(Is, Fail);
- [{set,[Dst],_,_}|_]=Is ->
- split_block_1(Is, Fail);
- _ -> failed
- end.
-
-split_block_1(Is, Fail) ->
- case split_block_2(Is, Fail, []) of
- {[],_} -> failed;
- {_,_}=Res -> Res
- end.
-
-% split_block_2([{set,[_],_,{bif,_,{f,0}}}=I|Is], Fail, Acc) ->
-% split_block_2(Is, Fail, [I|Acc]);
-split_block_2([{set,[_],_,{bif,_,{f,Fail}}}=I|Is], Fail, Acc) ->
- split_block_2(Is, Fail, [I|Acc]);
-split_block_2([{'%live',_}|Is], Fail, Acc) ->
- split_block_2(Is, Fail, Acc);
-split_block_2(Is, _, Acc) -> {Acc,reverse(Is)}.
-
-dst_regs(Is) ->
- dst_regs(Is, []).
-
-dst_regs([{block,Bl}|Is], Acc) ->
- dst_regs(Bl, dst_regs(Is, Acc));
-dst_regs([{set,[D],_,{bif,_,{f,_}}}|Is], Acc) ->
- dst_regs(Is, [D|Acc]);
-dst_regs([_|Is], Acc) ->
- dst_regs(Is, Acc);
-dst_regs([], Acc) -> ordsets:from_list(Acc).
-
-all_killed([R|Rs], OldIs, St) ->
- case is_killed(R, OldIs, St) of
- false -> false;
- true -> all_killed(Rs, OldIs, St)
- end;
-all_killed([], _, _) -> true.
-
-none_used([R|Rs], OldIs, St) ->
- case is_not_used(R, OldIs, St) of
- false -> false;
- true -> none_used(Rs, OldIs, St)
- end;
-none_used([], _, _) -> true.
-
-bopt_tree(Block0) ->
- Block = ssa_block(Block0),
- Reg = free_variables(Block),
- %%io:format("~p\n", [Block]),
- %%io:format("~p\n", [Reg]),
- Res = bopt_tree_1(Block, Reg, []),
- %%io:format("~p\n", [Res]),
- Res.
-
-bopt_tree_1([{set,[Dst],As0,{bif,'not',_}}|Is], Forest0, Pre) ->
- {[Arg],Forest1} = bopt_bool_args(As0, Forest0),
- Forest = gb_trees:enter(Dst, {'not',Arg}, Forest1),
- bopt_tree_1(Is, Forest, Pre);
-bopt_tree_1([{set,[Dst],As0,{bif,'and',_}}|Is], Forest0, Pre) ->
- {As,Forest1} = bopt_bool_args(As0, Forest0),
- AndList = make_and_list(As),
- Forest = gb_trees:enter(Dst, {'and',AndList}, Forest1),
- bopt_tree_1(Is, Forest, Pre);
-bopt_tree_1([{set,[Dst],[L0,R0],{bif,'or',_}}|Is], Forest0, Pre) ->
- L = gb_trees:get(L0, Forest0),
- R = gb_trees:get(R0, Forest0),
- Forest1 = gb_trees:delete(L0, gb_trees:delete(R0, Forest0)),
- OrList = make_or_list([L,R]),
- Forest = gb_trees:enter(Dst, {'or',OrList}, Forest1),
- bopt_tree_1(Is, Forest, Pre);
-bopt_tree_1([{protected,[Dst],_,_}=Prot|Is], Forest0, Pre) ->
- Forest = gb_trees:enter(Dst, Prot, Forest0),
- bopt_tree_1(Is, Forest, Pre);
-bopt_tree_1([{set,[Dst],As,{bif,N,_}}=Bif|Is], Forest0, Pre) ->
- Ar = length(As),
- case safe_bool_op(N, Ar) of
- false ->
- bopt_good_args(As, Forest0),
- Forest = gb_trees:enter(Dst, any, Forest0),
- bopt_tree_1(Is, Forest, [Bif|Pre]);
- true ->
- bopt_good_args(As, Forest0),
- Test = bif_to_test(Dst, N, As),
- Forest = gb_trees:enter(Dst, Test, Forest0),
- bopt_tree_1(Is, Forest, Pre)
- end;
-bopt_tree_1([], Forest, Pre) ->
- {Pre,[R || {_,V}=R <- gb_trees:to_list(Forest), V =/= any]}.
-
-safe_bool_op(internal_is_record, 3) -> true;
-safe_bool_op(N, Ar) ->
- erl_internal:new_type_test(N, Ar) orelse erl_internal:comp_op(N, Ar).
-
-bopt_bool_args(As, Forest) ->
- mapfoldl(fun bopt_bool_arg/2, Forest, As).
-
-bopt_bool_arg({T,_}=R, Forest) when T == x; T == y ->
- {gb_trees:get(R, Forest),gb_trees:delete(R, Forest)};
-bopt_bool_arg(Term, Forest) ->
- {Term,Forest}.
-
-bopt_good_args([A|As], Regs) ->
- bopt_good_arg(A, Regs),
- bopt_good_args(As, Regs);
-bopt_good_args([], _) -> ok.
-
-bopt_good_arg({x,_}=X, Regs) ->
- case gb_trees:get(X, Regs) of
- any -> ok;
- _Other ->
- %%io:format("not any: ~p: ~p\n", [X,_Other]),
- exit(bad_contents)
- end;
-bopt_good_arg(_, _) -> ok.
-
-bif_to_test(_, N, As) ->
- bif_to_test(N, As).
-
-bif_to_test(internal_is_record, [_,_,_]=As) ->
- {test,internal_is_record,fail,As};
-bif_to_test('=:=', As) -> {test,is_eq_exact,fail,As};
-bif_to_test('=/=', As) -> {test,is_ne_exact,fail,As};
-bif_to_test('==', As) -> {test,is_eq,fail,As};
-bif_to_test('/=', As) -> {test,is_ne,fail,As};
-bif_to_test('=<', [L,R]) -> {test,is_ge,fail,[R,L]};
-bif_to_test('>=', As) -> {test,is_ge,fail,As};
-bif_to_test('>', [L,R]) -> {test,is_lt,fail,[R,L]};
-bif_to_test('<', As) -> {test,is_lt,fail,As};
-bif_to_test(Name, [_]=As) ->
- case erl_internal:new_type_test(Name, 1) of
- false -> exit({bif_to_test,Name,As,failed});
- true -> {test,Name,fail,As}
- end.
-
-make_and_list([{'and',As}|Is]) ->
- make_and_list(As++Is);
-make_and_list([I|Is]) ->
- [I|make_and_list(Is)];
-make_and_list([]) -> [].
-
-make_or_list([{'or',As}|Is]) ->
- make_or_list(As++Is);
-make_or_list([I|Is]) ->
- [I|make_or_list(Is)];
-make_or_list([]) -> [].
-
-%% Code generation for a boolean tree.
-
-bopt_cg({'not',Arg}, Fail, Acc, St) ->
- I = bopt_cg_not(Arg),
- bopt_cg(I, Fail, Acc, St);
-bopt_cg({'and',As}, Fail, Acc, St) ->
- bopt_cg_and(As, Fail, Acc, St);
-bopt_cg({'or',As}, Fail, Acc, St0) ->
- {Succ,St} = new_label(St0),
- bopt_cg_or(As, Succ, Fail, Acc, St);
-bopt_cg({test,is_tuple_element,fail,[Tmp,Tuple,RecordTag]}, Fail, Acc, St) ->
- {[{test,is_eq_exact,{f,Fail},[Tmp,RecordTag]},
- {get_tuple_element,Tuple,0,Tmp}|Acc],St};
-bopt_cg({inverted_test,is_tuple_element,fail,[Tmp,Tuple,RecordTag]}, Fail, Acc, St) ->
- {[{test,is_ne_exact,{f,Fail},[Tmp,RecordTag]},
- {get_tuple_element,Tuple,0,Tmp}|Acc],St};
-bopt_cg({test,N,fail,As}, Fail, Acc, St) ->
- Test = {test,N,{f,Fail},As},
- {[Test|Acc],St};
-bopt_cg({inverted_test,N,fail,As}, Fail, Acc, St0) ->
- {Lbl,St} = new_label(St0),
- {[{label,Lbl},{jump,{f,Fail}},{test,N,{f,Lbl},As}|Acc],St};
-bopt_cg({protected,_,Bl0,{_,_,_}}, Fail, Acc, St0) ->
- {Bl,St} = bopt_block_1(Bl0, Fail, St0),
- {Bl++Acc,St};
-bopt_cg([_|_]=And, Fail, Acc, St) ->
- bopt_cg_and(And, Fail, Acc, St).
-
-bopt_cg_not({'and',As0}) ->
- As = [bopt_cg_not(A) || A <- As0],
- {'or',As};
-bopt_cg_not({'or',As0}) ->
- As = [bopt_cg_not(A) || A <- As0],
- {'and',As};
-bopt_cg_not({test,Test,Fail,As}) ->
- {inverted_test,Test,Fail,As}.
-
-bopt_cg_and([{atom,false}|_], Fail, _, St) ->
- {[{jump,{f,Fail}}],St};
-bopt_cg_and([{atom,true}|Is], Fail, Acc, St) ->
- bopt_cg_and(Is, Fail, Acc, St);
-bopt_cg_and([I|Is], Fail, Acc0, St0) ->
- {Acc,St} = bopt_cg(I, Fail, Acc0, St0),
- bopt_cg_and(Is, Fail, Acc, St);
-bopt_cg_and([], _, Acc, St) -> {Acc,St}.
-
-bopt_cg_or([I], Succ, Fail, Acc0, St0) ->
- {Acc,St} = bopt_cg(I, Fail, Acc0, St0),
- {[{label,Succ}|Acc],St};
-bopt_cg_or([I|Is], Succ, Fail, Acc0, St0) ->
- {Lbl,St1} = new_label(St0),
- {Acc,St} = bopt_cg(I, Lbl, Acc0, St1),
- bopt_cg_or(Is, Succ, Fail, [{label,Lbl},{jump,{f,Succ}}|Acc], St).
-
-new_label(#st{next=LabelNum}=St) when is_integer(LabelNum) ->
- {LabelNum,St#st{next=LabelNum+1}}.
-
-free_variables(Is) ->
- E = gb_sets:empty(),
- free_vars_1(Is, E, E).
-
-free_vars_1([{set,[Dst],As,{bif,_,_}}|Is], F0, N0) ->
- F = gb_sets:union(F0, gb_sets:difference(var_list(As), N0)),
- N = gb_sets:union(N0, var_list([Dst])),
- free_vars_1(Is, F, N);
-free_vars_1([{protected,_,Pa,_}|Is], F, N) ->
- free_vars_1(Pa++Is, F, N);
-free_vars_1([], F, _) ->
- gb_trees:from_orddict([{K,any} || K <- gb_sets:to_list(F)]).
-
-var_list(Is) ->
- var_list_1(Is, gb_sets:empty()).
-
-var_list_1([{x,_}=X|Is], D) ->
- var_list_1(Is, gb_sets:add(X, D));
-var_list_1([_|Is], D) ->
- var_list_1(Is, D);
-var_list_1([], D) -> D.
-
-%%%
-%%% Convert a block to Static Single Assignment (SSA) form.
-%%%
-
--record(ssa,
- {live,
- sub}).
-
-ssa_block(Is0) ->
- Next = ssa_first_free(Is0, 0),
- {Is,_} = ssa_block_1(Is0, #ssa{live=Next,sub=gb_trees:empty()}, []),
- Is.
-
-ssa_block_1([{protected,[_],Pa0,Pb}|Is], Sub0, Acc) ->
- {Pa,Sub} = ssa_block_1(Pa0, Sub0, []),
- Dst = ssa_last_target(Pa),
- ssa_block_1(Is, Sub, [{protected,[Dst],Pa,Pb}|Acc]);
-ssa_block_1([{set,[Dst],As,Bif}|Is], Sub0, Acc0) ->
- Sub1 = ssa_in_use_list(As, Sub0),
- Sub = ssa_assign(Dst, Sub1),
- Acc = [{set,[ssa_sub(Dst, Sub)],ssa_sub_list(As, Sub0),Bif}|Acc0],
- ssa_block_1(Is, Sub, Acc);
-ssa_block_1([], Sub, Acc) -> {reverse(Acc),Sub}.
-
-ssa_in_use_list(As, Sub) ->
- foldl(fun ssa_in_use/2, Sub, As).
-
-ssa_in_use({x,_}=R, #ssa{sub=Sub0}=Ssa) ->
- case gb_trees:is_defined(R, Sub0) of
- true -> Ssa;
- false ->
- Sub = gb_trees:insert(R, R, Sub0),
- Ssa#ssa{sub=Sub}
- end;
-ssa_in_use(_, Ssa) -> Ssa.
-
-ssa_assign({x,_}=R, #ssa{sub=Sub0}=Ssa0) ->
- case gb_trees:is_defined(R, Sub0) of
- false ->
- Sub = gb_trees:insert(R, R, Sub0),
- Ssa0#ssa{sub=Sub};
- true ->
- {NewReg,Ssa} = ssa_new_reg(Ssa0),
- Sub1 = gb_trees:update(R, NewReg, Sub0),
- Sub = gb_trees:insert(NewReg, NewReg, Sub1),
- Ssa#ssa{sub=Sub}
- end;
-ssa_assign(_, Ssa) -> Ssa.
-
-ssa_sub_list(List, Sub) ->
- [ssa_sub(E, Sub) || E <- List].
-
-ssa_sub(R0, #ssa{sub=Sub}) ->
- case gb_trees:lookup(R0, Sub) of
- none -> R0;
- {value,R} -> R
- end.
-
-ssa_new_reg(#ssa{live=Reg}=Ssa) ->
- {{x,Reg},Ssa#ssa{live=Reg+1}}.
-
-ssa_first_free([{protected,Ds,_,_}|Is], Next0) ->
- Next = ssa_first_free_list(Ds, Next0),
- ssa_first_free(Is, Next);
-ssa_first_free([{set,[Dst],As,_}|Is], Next0) ->
- Next = ssa_first_free_list([Dst|As], Next0),
- ssa_first_free(Is, Next);
-ssa_first_free([], Next) -> Next.
-
-ssa_first_free_list(Regs, Next) ->
- foldl(fun({x,R}, N) when R >= N -> R+1;
- (_, N) -> N end, Next, Regs).
-
-ssa_last_target([{set,[Dst],_,_},{'%live',_}]) -> Dst;
-ssa_last_target([{set,[Dst],_,_}]) -> Dst;
-ssa_last_target([_|Is]) -> ssa_last_target(Is).
-
-%% index_instructions(FunctionIs) -> GbTree([{Label,Is}])
-%% Index the instruction sequence so that we can quickly
-%% look up the instruction following a specific label.
-
-index_instructions(Is) ->
- ii_1(Is, []).
-
-ii_1([{label,Lbl}|Is0], Acc) ->
- Is = lists:dropwhile(fun({label,_}) -> true;
- (_) -> false end, Is0),
- ii_1(Is0, [{Lbl,Is}|Acc]);
-ii_1([_|Is], Acc) ->
- ii_1(Is, Acc);
-ii_1([], Acc) -> gb_trees:from_orddict(sort(Acc)).
-
-%% is_killed(Register, [Instruction], State) -> true|false
-%% Determine whether a register is killed in the instruction sequence.
-%% The state is used to allow us to determine the kill state
-%% across branches.
-
-is_killed(R, Is, St) ->
- case is_killed_1(R, Is, St) of
- false ->
- %%io:format("nk ~p: ~P\n", [R,Is,15]),
- false;
- true -> true
- end.
-
-is_killed_1(R, [{block,Blk}|Is], St) ->
- case is_killed_1(R, Blk, St) of
- true -> true;
- false -> is_killed_1(R, Is, St)
- end;
-is_killed_1(R, [{test,_,{f,Fail},As}|Is], St) ->
- case not member(R, As) andalso is_reg_killed_at(R, Fail, St) of
- false -> false;
- true -> is_killed_1(R, Is, St)
- end;
-is_killed_1(R, [{select_val,R,_,_}|_], _) -> false;
-is_killed_1(R, [{select_val,_,Fail,{list,Branches}}|_], St) ->
- is_killed_at_all(R, [Fail|Branches], St);
-is_killed_1(R, [{jump,{f,F}}|_], St) ->
- is_reg_killed_at(R, F, St);
-is_killed_1(Reg, Is, _) ->
- beam_block:is_killed(Reg, Is).
-
-is_reg_killed_at(R, Lbl, #st{ll=Ll}=St) ->
- Is = gb_trees:get(Lbl, Ll),
- is_killed_1(R, Is, St).
-
-is_killed_at_all(R, [{f,Lbl}|T], St) ->
- case is_reg_killed_at(R, Lbl, St) of
- false -> false;
- true -> is_killed_at_all(R, T, St)
- end;
-is_killed_at_all(R, [_|T], St) ->
- is_killed_at_all(R, T, St);
-is_killed_at_all(_, [], _) -> true.
-
-%% is_not_used(Register, [Instruction], State) -> true|false
-%% Determine whether a register is never used in the instruction sequence
-%% (it could still referenced by an allocate instruction, meaning that
-%% it MUST be initialized).
-%% The state is used to allow us to determine the usage state
-%% across branches.
-
-is_not_used(R, Is, St) ->
- case is_not_used_1(R, Is, St) of
- false ->
- %%io:format("used ~p: ~P\n", [R,Is,15]),
- false;
- true -> true
- end.
-
-is_not_used_1(R, [{block,Blk}|Is], St) ->
- case is_not_used_1(R, Blk, St) of
- true -> true;
- false -> is_not_used_1(R, Is, St)
- end;
-is_not_used_1(R, [{test,_,{f,Fail},As}|Is], St) ->
- case not member(R, As) andalso is_reg_not_used_at(R, Fail, St) of
- false -> false;
- true -> is_not_used_1(R, Is, St)
- end;
-is_not_used_1(R, [{select_val,R,_,_}|_], _) -> false;
-is_not_used_1(R, [{select_val,_,Fail,{list,Branches}}|_], St) ->
- is_used_at_none(R, [Fail|Branches], St);
-is_not_used_1(R, [{jump,{f,F}}|_], St) ->
- is_reg_not_used_at(R, F, St);
-is_not_used_1(Reg, Is, _) ->
- beam_block:is_not_used(Reg, Is).
-
-is_reg_not_used_at(R, Lbl, #st{ll=Ll}=St) ->
- Is = gb_trees:get(Lbl, Ll),
- is_not_used_1(R, Is, St).
-
-is_used_at_none(R, [{f,Lbl}|T], St) ->
- case is_reg_not_used_at(R, Lbl, St) of
- false -> false;
- true -> is_used_at_none(R, T, St)
- end;
-is_used_at_none(R, [_|T], St) ->
- is_used_at_none(R, T, St);
-is_used_at_none(_, [], _) -> true.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_clean.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_clean.erl
deleted file mode 100644
index d47ae9c896..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_clean.erl
+++ /dev/null
@@ -1,232 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: beam_clean.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
-%%
-%% Purpose : Clean up, such as removing unused labels and unused functions.
-
--module(beam_clean).
-
--export([module/2]).
--import(lists, [member/2,map/2,foldl/3,mapfoldl/3,reverse/1]).
-
-module({Mod,Exp,Attr,Fs0,_}, _Opt) ->
- Order = [Lbl || {function,_,_,Lbl,_} <- Fs0],
- All = foldl(fun({function,_,_,Lbl,_}=Func,D) -> dict:store(Lbl, Func, D) end,
- dict:new(), Fs0),
- {WorkList,Used0} = exp_to_labels(Fs0, Exp),
- Used = find_all_used(WorkList, All, Used0),
- Fs1 = remove_unused(Order, Used, All),
- {Fs,Lc} = clean_labels(Fs1),
- {ok,{Mod,Exp,Attr,Fs,Lc}}.
-
-%% Convert the export list ({Name,Arity} pairs) to a list of entry labels.
-
-exp_to_labels(Fs, Exp) -> exp_to_labels(Fs, Exp, [], sets:new()).
-
-exp_to_labels([{function,Name,Arity,Lbl,_}|Fs], Exp, Acc, Used) ->
- case member({Name,Arity}, Exp) of
- true -> exp_to_labels(Fs, Exp, [Lbl|Acc], sets:add_element(Lbl, Used));
- false -> exp_to_labels(Fs, Exp, Acc, Used)
- end;
-exp_to_labels([], _, Acc, Used) -> {Acc,Used}.
-
-%% Remove the unused functions.
-
-remove_unused([F|Fs], Used, All) ->
- case sets:is_element(F, Used) of
- false -> remove_unused(Fs, Used, All);
- true -> [dict:fetch(F, All)|remove_unused(Fs, Used, All)]
- end;
-remove_unused([], _, _) -> [].
-
-%% Find all used functions.
-
-find_all_used([F|Fs0], All, Used0) ->
- {function,_,_,_,Code} = dict:fetch(F, All),
- {Fs,Used} = update_work_list(Code, {Fs0,Used0}),
- find_all_used(Fs, All, Used);
-find_all_used([], _All, Used) -> Used.
-
-update_work_list([{call,_,{f,L}}|Is], Sets) ->
- update_work_list(Is, add_to_work_list(L, Sets));
-update_work_list([{call_last,_,{f,L},_}|Is], Sets) ->
- update_work_list(Is, add_to_work_list(L, Sets));
-update_work_list([{call_only,_,{f,L}}|Is], Sets) ->
- update_work_list(Is, add_to_work_list(L, Sets));
-update_work_list([{make_fun,{f,L},_,_}|Is], Sets) ->
- update_work_list(Is, add_to_work_list(L, Sets));
-update_work_list([{make_fun2,{f,L},_,_,_}|Is], Sets) ->
- update_work_list(Is, add_to_work_list(L, Sets));
-update_work_list([_|Is], Sets) ->
- update_work_list(Is, Sets);
-update_work_list([], Sets) -> Sets.
-
-add_to_work_list(F, {Fs,Used}=Sets) ->
- case sets:is_element(F, Used) of
- true -> Sets;
- false -> {[F|Fs],sets:add_element(F, Used)}
- end.
-
-
-%%%
-%%% Coalesce adjacent labels. Renumber all labels to eliminate gaps.
-%%% This cleanup will slightly reduce file size and slightly speed up loading.
-%%%
-%%% We also expand internal_is_record/3 to a sequence of instructions. It is done
-%%% here merely because this module will always be called even if optimization
-%%% is turned off. We don't want to do the expansion in beam_asm because we
-%%% want to see the expanded code in a .S file.
-%%%
-
--record(st, {lmap, %Translation tables for labels.
- entry, %Number of entry label.
- lc %Label counter
- }).
-
-clean_labels(Fs0) ->
- St0 = #st{lmap=dict:new(),lc=1},
- {Fs1,#st{lmap=Lmap,lc=Lc}} = mapfoldl(fun function_renumber/2, St0, Fs0),
- {map(fun(F) -> function_replace(F, Lmap) end, Fs1),Lc}.
-
-function_renumber({function,Name,Arity,_Entry,Asm0}, St0) ->
- {Asm,St} = renumber_labels(Asm0, [], St0),
- {{function,Name,Arity,St#st.entry,Asm},St}.
-
-renumber_labels([{bif,internal_is_record,{f,_},
- [Term,Tag,{integer,Arity}],Dst}|Is], Acc, St) ->
- ContLabel = 900000000+2*St#st.lc,
- FailLabel = ContLabel+1,
- Fail = {f,FailLabel},
- Tmp = Dst,
- renumber_labels([{test,is_tuple,Fail,[Term]},
- {test,test_arity,Fail,[Term,Arity]},
- {get_tuple_element,Term,0,Tmp},
- {test,is_eq_exact,Fail,[Tmp,Tag]},
- {move,{atom,true},Dst},
- {jump,{f,ContLabel}},
- {label,FailLabel},
- {move,{atom,false},Dst},
- {label,ContLabel}|Is], Acc, St);
-renumber_labels([{test,internal_is_record,{f,_}=Fail,
- [Term,Tag,{integer,Arity}]}|Is], Acc, St) ->
- Tmp = {x,1023},
- case Term of
- {Reg,_} when Reg == x; Reg == y ->
- renumber_labels([{test,is_tuple,Fail,[Term]},
- {test,test_arity,Fail,[Term,Arity]},
- {get_tuple_element,Term,0,Tmp},
- {test,is_eq_exact,Fail,[Tmp,Tag]}|Is], Acc, St);
- _ ->
- renumber_labels([{jump,Fail}|Is], Acc, St)
- end;
-renumber_labels([{label,Old}|Is], [{label,New}|_]=Acc, #st{lmap=D0}=St) ->
- D = dict:store(Old, New, D0),
- renumber_labels(Is, Acc, St#st{lmap=D});
-renumber_labels([{label,Old}|Is], Acc, St0) ->
- New = St0#st.lc,
- D = dict:store(Old, New, St0#st.lmap),
- renumber_labels(Is, [{label,New}|Acc], St0#st{lmap=D,lc=New+1});
-renumber_labels([{func_info,_,_,_}=Fi|Is], Acc, St0) ->
- renumber_labels(Is, [Fi|Acc], St0#st{entry=St0#st.lc});
-renumber_labels([I|Is], Acc, St0) ->
- renumber_labels(Is, [I|Acc], St0);
-renumber_labels([], Acc, St0) -> {Acc,St0}.
-
-function_replace({function,Name,Arity,Entry,Asm0}, Dict) ->
- Asm = case catch replace(Asm0, [], Dict) of
- {'EXIT',_}=Reason ->
- exit(Reason);
- {error,{undefined_label,Lbl}=Reason} ->
- io:format("Function ~s/~w refers to undefined label ~w\n",
- [Name,Arity,Lbl]),
- exit(Reason);
- Asm1 when list(Asm1) -> Asm1
- end,
- {function,Name,Arity,Entry,Asm}.
-
-replace([{test,Test,{f,Lbl},Ops}|Is], Acc, D) ->
- replace(Is, [{test,Test,{f,label(Lbl, D)},Ops}|Acc], D);
-replace([{select_val,R,{f,Fail0},{list,Vls0}}|Is], Acc, D) ->
- Vls1 = map(fun ({f,L}) -> {f,label(L, D)};
- (Other) -> Other end, Vls0),
- Fail = label(Fail0, D),
- case redundant_values(Vls1, Fail, []) of
- [] ->
- %% Oops, no choices left. The loader will not accept that.
- %% Convert to a plain jump.
- replace(Is, [{jump,{f,Fail}}|Acc], D);
- Vls ->
- replace(Is, [{select_val,R,{f,Fail},{list,Vls}}|Acc], D)
- end;
-replace([{select_tuple_arity,R,{f,Fail},{list,Vls0}}|Is], Acc, D) ->
- Vls = map(fun ({f,L}) -> {f,label(L, D)};
- (Other) -> Other end, Vls0),
- replace(Is, [{select_tuple_arity,R,{f,label(Fail, D)},{list,Vls}}|Acc], D);
-replace([{'try',R,{f,Lbl}}|Is], Acc, D) ->
- replace(Is, [{'try',R,{f,label(Lbl, D)}}|Acc], D);
-replace([{'catch',R,{f,Lbl}}|Is], Acc, D) ->
- replace(Is, [{'catch',R,{f,label(Lbl, D)}}|Acc], D);
-replace([{jump,{f,Lbl}}|Is], Acc, D) ->
- replace(Is, [{jump,{f,label(Lbl, D)}}|Acc], D);
-replace([{loop_rec,{f,Lbl},R}|Is], Acc, D) ->
- replace(Is, [{loop_rec,{f,label(Lbl, D)},R}|Acc], D);
-replace([{loop_rec_end,{f,Lbl}}|Is], Acc, D) ->
- replace(Is, [{loop_rec_end,{f,label(Lbl, D)}}|Acc], D);
-replace([{wait,{f,Lbl}}|Is], Acc, D) ->
- replace(Is, [{wait,{f,label(Lbl, D)}}|Acc], D);
-replace([{wait_timeout,{f,Lbl},To}|Is], Acc, D) ->
- replace(Is, [{wait_timeout,{f,label(Lbl, D)},To}|Acc], D);
-replace([{bif,Name,{f,Lbl},As,R}|Is], Acc, D) when Lbl =/= 0 ->
- replace(Is, [{bif,Name,{f,label(Lbl, D)},As,R}|Acc], D);
-replace([{call,Ar,{f,Lbl}}|Is], Acc, D) ->
- replace(Is, [{call,Ar,{f,label(Lbl,D)}}|Acc], D);
-replace([{call_last,Ar,{f,Lbl},N}|Is], Acc, D) ->
- replace(Is, [{call_last,Ar,{f,label(Lbl,D)},N}|Acc], D);
-replace([{call_only,Ar,{f,Lbl}}|Is], Acc, D) ->
- replace(Is, [{call_only,Ar,{f,label(Lbl, D)}}|Acc], D);
-replace([{make_fun,{f,Lbl},U1,U2}|Is], Acc, D) ->
- replace(Is, [{make_fun,{f,label(Lbl, D)},U1,U2}|Acc], D);
-replace([{make_fun2,{f,Lbl},U1,U2,U3}|Is], Acc, D) ->
- replace(Is, [{make_fun2,{f,label(Lbl, D)},U1,U2,U3}|Acc], D);
-replace([{bs_init2,{f,Lbl},Sz,Words,R,F,Dst}|Is], Acc, D) when Lbl =/= 0 ->
- replace(Is, [{bs_init2,{f,label(Lbl, D)},Sz,Words,R,F,Dst}|Acc], D);
-replace([{bs_put_integer,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 ->
- replace(Is, [{bs_put_integer,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D);
-replace([{bs_put_binary,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 ->
- replace(Is, [{bs_put_binary,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D);
-replace([{bs_put_float,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 ->
- replace(Is, [{bs_put_float,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D);
-replace([{bs_final,{f,Lbl},R}|Is], Acc, D) when Lbl =/= 0 ->
- replace(Is, [{bs_final,{f,label(Lbl, D)},R}|Acc], D);
-replace([{bs_add,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 ->
- replace(Is, [{bs_add,{f,label(Lbl, D)},Src,Dst}|Acc], D);
-replace([{bs_bits_to_bytes,{f,Lbl},Bits,Dst}|Is], Acc, D) when Lbl =/= 0 ->
- replace(Is, [{bs_bits_to_bytes,{f,label(Lbl, D)},Bits,Dst}|Acc], D);
-replace([I|Is], Acc, D) ->
- replace(Is, [I|Acc], D);
-replace([], Acc, _) -> Acc.
-
-label(Old, D) ->
- case dict:find(Old, D) of
- {ok,Val} -> Val;
- error -> throw({error,{undefined_label,Old}})
- end.
-
-redundant_values([_,{f,Fail}|Vls], Fail, Acc) ->
- redundant_values(Vls, Fail, Acc);
-redundant_values([Val,Lbl|Vls], Fail, Acc) ->
- redundant_values(Vls, Fail, [Lbl,Val|Acc]);
-redundant_values([], _, Acc) -> reverse(Acc).
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_dict.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_dict.erl
deleted file mode 100644
index ddab957704..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_dict.erl
+++ /dev/null
@@ -1,196 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: beam_dict.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
-%%
-%% Purpose : Maintain atom, import, and export tables for assembler.
-
--module(beam_dict).
-
--export([new/0, opcode/2, highest_opcode/1,
- atom/2, local/4, export/4, import/4, string/2, lambda/5,
- atom_table/1, local_table/1, export_table/1, import_table/1,
- string_table/1,lambda_table/1]).
-
--record(asm_dict,
- {atoms = [], % [{Index, Atom}]
- exports = [], % [{F, A, Label}]
- locals = [], % [{F, A, Label}]
- imports = [], % [{Index, {M, F, A}]
- strings = [], % Deep list of characters
- lambdas = [], % [{...}]
- next_atom = 1,
- next_import = 0,
- string_offset = 0,
- highest_opcode = 0
- }).
-
-new() ->
- #asm_dict{}.
-
-%% Remembers highest opcode.
-
-opcode(Op, Dict) when Dict#asm_dict.highest_opcode > Op -> Dict;
-opcode(Op, Dict) -> Dict#asm_dict{highest_opcode=Op}.
-
-%% Returns the highest opcode encountered.
-
-highest_opcode(#asm_dict{highest_opcode=Op}) -> Op.
-
-%% Returns the index for an atom (adding it to the atom table if necessary).
-%% atom(Atom, Dict) -> {Index, Dict'}
-
-atom(Atom, Dict) when atom(Atom) ->
- NextIndex = Dict#asm_dict.next_atom,
- case lookup_store(Atom, Dict#asm_dict.atoms, NextIndex) of
- {Index, _, NextIndex} ->
- {Index, Dict};
- {Index, Atoms, NewIndex} ->
- {Index, Dict#asm_dict{atoms=Atoms, next_atom=NewIndex}}
- end.
-
-%% Remembers an exported function.
-%% export(Func, Arity, Label, Dict) -> Dict'
-
-export(Func, Arity, Label, Dict0) when atom(Func), integer(Arity), integer(Label) ->
- {Index, Dict1} = atom(Func, Dict0),
- Dict1#asm_dict{exports = [{Index, Arity, Label}| Dict1#asm_dict.exports]}.
-
-%% Remembers a local function.
-%% local(Func, Arity, Label, Dict) -> Dict'
-
-local(Func, Arity, Label, Dict0) when atom(Func), integer(Arity), integer(Label) ->
- {Index,Dict1} = atom(Func, Dict0),
- Dict1#asm_dict{locals = [{Index,Arity,Label}| Dict1#asm_dict.locals]}.
-
-%% Returns the index for an import entry (adding it to the import table if necessary).
-%% import(Mod, Func, Arity, Dict) -> {Index, Dict'}
-
-import(Mod, Func, Arity, Dict) when atom(Mod), atom(Func), integer(Arity) ->
- NextIndex = Dict#asm_dict.next_import,
- case lookup_store({Mod, Func, Arity}, Dict#asm_dict.imports, NextIndex) of
- {Index, _, NextIndex} ->
- {Index, Dict};
- {Index, Imports, NewIndex} ->
- {_, D1} = atom(Mod, Dict#asm_dict{imports=Imports, next_import=NewIndex}),
- {_, D2} = atom(Func, D1),
- {Index, D2}
- end.
-
-%% Returns the index for a string in the string table (adding the string to the
-%% table if necessary).
-%% string(String, Dict) -> {Offset, Dict'}
-
-string(Str, Dict) when list(Str) ->
- #asm_dict{strings = Strings, string_offset = NextOffset} = Dict,
- case old_string(Str, Strings) of
- {true, Offset} ->
- {Offset, Dict};
- false ->
- NewDict = Dict#asm_dict{strings = Strings++Str,
- string_offset = NextOffset+length(Str)},
- {NextOffset, NewDict}
- end.
-
-%% Returns the index for a funentry (adding it to the table if necessary).
-%% lambda(Dict, Lbl, Index, Uniq, NumFree) -> {Index,Dict'}
-
-lambda(Lbl, Index, OldUniq, NumFree, #asm_dict{lambdas=Lambdas0}=Dict) ->
- OldIndex = length(Lambdas0),
- Lambdas = [{Lbl,{OldIndex,Lbl,Index,NumFree,OldUniq}}|Lambdas0],
- {OldIndex,Dict#asm_dict{lambdas=Lambdas}}.
-
-%% Returns the atom table.
-%% atom_table(Dict) -> [Length,AtomString...]
-
-atom_table(#asm_dict{atoms=Atoms, next_atom=NumAtoms}) ->
- Sorted = lists:sort(Atoms),
- Fun = fun({_, A}) ->
- L = atom_to_list(A),
- [length(L)|L]
- end,
- {NumAtoms-1, lists:map(Fun, Sorted)}.
-
-%% Returns the table of local functions.
-%% local_table(Dict) -> {NumLocals, [{Function, Arity, Label}...]}
-
-local_table(#asm_dict{locals = Locals}) ->
- {length(Locals),Locals}.
-
-%% Returns the export table.
-%% export_table(Dict) -> {NumExports, [{Function, Arity, Label}...]}
-
-export_table(#asm_dict{exports = Exports}) ->
- {length(Exports), Exports}.
-
-%% Returns the import table.
-%% import_table(Dict) -> {NumImports, [{Module, Function, Arity}...]}
-
-import_table(Dict) ->
- #asm_dict{imports = Imports, next_import = NumImports} = Dict,
- Sorted = lists:sort(Imports),
- Fun = fun({_, {Mod, Func, Arity}}) ->
- {Atom0, _} = atom(Mod, Dict),
- {Atom1, _} = atom(Func, Dict),
- {Atom0, Atom1, Arity}
- end,
- {NumImports, lists:map(Fun, Sorted)}.
-
-string_table(#asm_dict{strings = Strings, string_offset = Size}) ->
- {Size, Strings}.
-
-lambda_table(#asm_dict{locals=Loc0,lambdas=Lambdas0}) ->
- Lambdas1 = sofs:relation(Lambdas0),
- Loc = sofs:relation([{Lbl,{F,A}} || {F,A,Lbl} <- Loc0]),
- Lambdas2 = sofs:relative_product1(Lambdas1, Loc),
- Lambdas = [<> ||
- {{_,Lbl,Index,NumFree,OldUniq},{F,A}} <- sofs:to_external(Lambdas2)],
- {length(Lambdas),Lambdas}.
-
-%%% Local helper functions.
-
-lookup_store(Key, Dict, NextIndex) ->
- case catch lookup_store1(Key, Dict, NextIndex) of
- Index when integer(Index) ->
- {Index, Dict, NextIndex};
- {Index, NewDict} ->
- {Index, NewDict, NextIndex+1}
- end.
-
-lookup_store1(Key, [Pair|Dict], NextIndex) when Key > element(2, Pair) ->
- {Index, NewDict} = lookup_store1(Key, Dict, NextIndex),
- {Index, [Pair|NewDict]};
-lookup_store1(Key, [{Index, Key}|_Dict], _NextIndex) ->
- throw(Index);
-lookup_store1(Key, Dict, NextIndex) ->
- {NextIndex, [{NextIndex, Key}|Dict]}.
-
-%% Search for string Str in the string pool Pool.
-%% old_string(Str, Pool) -> false | {true, Offset}
-
-old_string(Str, Pool) ->
- old_string(Str, Pool, 0).
-
-old_string([C|Str], [C|Pool], Index) ->
- case lists:prefix(Str, Pool) of
- true ->
- {true, Index};
- false ->
- old_string([C|Str], Pool, Index+1)
- end;
-old_string(Str, [_|Pool], Index) ->
- old_string(Str, Pool, Index+1);
-old_string(_Str, [], _Index) ->
- false.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_disasm.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_disasm.erl
deleted file mode 100644
index 451b83db66..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_disasm.erl
+++ /dev/null
@@ -1,964 +0,0 @@
-%% -*- erlang-indent-level: 4 -*-
-%%=======================================================================
-%% File : beam_disasm.erl
-%% Author : Kostis Sagonas
-%% Description : Disassembles an R5-R10 .beam file into symbolic BEAM code
-%%=======================================================================
-%% $Id: beam_disasm.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
-%%=======================================================================
-%% Notes:
-%% 1. It does NOT work for .beam files of previous BEAM versions.
-%% 2. If handling of new BEAM instructions is needed, this should be
-%% inserted at the end of function resolve_inst().
-%%=======================================================================
-
--module(beam_disasm).
-
--export([file/1, format_error/1]).
-
--author("Kostis Sagonas").
-
--include("beam_opcodes.hrl").
-
-%%-----------------------------------------------------------------------
-
--define(NO_DEBUG(Str,Xs),ok).
--define(DEBUG(Str,Xs),io:format(Str,Xs)).
--define(exit(Reason),exit({?MODULE,?LINE,Reason})).
-
-%%-----------------------------------------------------------------------
-%% Error information
-
-format_error({error, Module, Error}) ->
- Module:format_error(Error);
-format_error({internal, Error}) ->
- io_lib:format("~p: disassembly failed with reason ~P.",
- [?MODULE, Error, 25]).
-
-%%-----------------------------------------------------------------------
-%% The main exported function
-%% File is either a file name or a binary containing the code.
-%% Returns `{beam_file, [...]}' or `{error, Module, Reason}'.
-%% Call `format_error({error, Module, Reason})' for an error string.
-%%-----------------------------------------------------------------------
-
-file(File) ->
- case beam_lib:info(File) of
- Info when list(Info) ->
- {value,{chunks,Chunks}} = lists:keysearch(chunks,1,Info),
- case catch process_chunks(File, Chunks) of
- {'EXIT', Error} ->
- {error, ?MODULE, {internal, Error}};
- Result ->
- Result
- end;
- Error ->
- Error
- end.
-
-%%-----------------------------------------------------------------------
-%% Interface might need to be revised -- do not depend on it.
-%%-----------------------------------------------------------------------
-
-process_chunks(F,ChunkInfoList) ->
- {ok,{_,Chunks}} = beam_lib:chunks(F, ["Atom","Code","StrT","ImpT","ExpT"]),
- [{"Atom",AtomBin},{"Code",CodeBin},{"StrT",StrBin},
- {"ImpT",ImpBin},{"ExpT",ExpBin}] = Chunks,
- LambdaBin = optional_chunk(F, "FunT", ChunkInfoList),
- LocBin = optional_chunk(F, "LocT", ChunkInfoList),
- AttrBin = optional_chunk(F, "Attr", ChunkInfoList),
- CompBin = optional_chunk(F, "CInf", ChunkInfoList),
- Atoms = beam_disasm_atoms(AtomBin),
- Exports = beam_disasm_exports(ExpBin, Atoms),
- Imports = beam_disasm_imports(ImpBin, Atoms),
- LocFuns = beam_disasm_exports(LocBin, Atoms),
- Lambdas = beam_disasm_lambdas(LambdaBin, Atoms),
- Str = beam_disasm_strings(StrBin),
- Str1 = binary_to_list(Str), %% for debugging -- use Str as far as poss.
- Sym_Code = beam_disasm_code(CodeBin,Atoms,Imports,Str,Lambdas),
- Attributes = beam_disasm_attributes(AttrBin),
- CompInfo = beam_disasm_compilation_info(CompBin),
- All = [{exports,Exports},
- {imports,Imports},
- {code,Sym_Code},
- {atoms,Atoms},
- {local_funs,LocFuns},
- {strings,Str1},
- {attributes,Attributes},
- {comp_info,CompInfo}],
- {beam_file,[Item || {_Key,Data}=Item <- All, Data =/= none]}.
-
-%%-----------------------------------------------------------------------
-%% Retrieve an optional chunk or none if the chunk doesn't exist.
-%%-----------------------------------------------------------------------
-
-optional_chunk(F, ChunkTag, ChunkInfo) ->
- case lists:keymember(ChunkTag, 1, ChunkInfo) of
- true ->
- {ok,{_,[{ChunkTag,Chunk}]}} = beam_lib:chunks(F, [ChunkTag]),
- Chunk;
- false -> none
- end.
-
-%%-----------------------------------------------------------------------
-%% UTILITIES -- these actually exist in file "beam_lib"
-%% -- they should be moved into a common utils file.
-%%-----------------------------------------------------------------------
-
-i32([X1,X2,X3,X4]) ->
- (X1 bsl 24) bor (X2 bsl 16) bor (X3 bsl 8) bor X4.
-
-get_int(B) ->
- {I, B1} = split_binary(B, 4),
- {i32(binary_to_list(I)), B1}.
-
-%%-----------------------------------------------------------------------
-%% Disassembles the atom table of a BEAM file.
-%% - atoms are stored in order 1 ... N (N = Num_atoms, in fact),
-%% - each atom name consists of a length byte, followed by that many
-%% bytes of name
-%% (nb: atom names max 255 chars?!)
-%%-----------------------------------------------------------------------
-
-beam_disasm_atoms(AtomTabBin) ->
- {_NumAtoms,B} = get_int(AtomTabBin),
- disasm_atoms(B).
-
-disasm_atoms(AtomBin) ->
- disasm_atoms(binary_to_list(AtomBin),1).
-
-disasm_atoms([Len|Xs],N) ->
- {AtomName,Rest} = get_atom_name(Len,Xs),
- [{N,list_to_atom(AtomName)}|disasm_atoms(Rest,N+1)];
-disasm_atoms([],_) ->
- [].
-
-get_atom_name(Len,Xs) ->
- get_atom_name(Len,Xs,[]).
-
-get_atom_name(N,[X|Xs],RevName) when N > 0 ->
- get_atom_name(N-1,Xs,[X|RevName]);
-get_atom_name(0,Xs,RevName) ->
- { lists:reverse(RevName), Xs }.
-
-%%-----------------------------------------------------------------------
-%% Disassembles the export table of a BEAM file.
-%%-----------------------------------------------------------------------
-
-beam_disasm_exports(none, _) -> none;
-beam_disasm_exports(ExpTabBin, Atoms) ->
- {_NumAtoms,B} = get_int(ExpTabBin),
- disasm_exports(B,Atoms).
-
-disasm_exports(Bin,Atoms) ->
- resolve_exports(collect_exports(binary_to_list(Bin)),Atoms).
-
-collect_exports([F3,F2,F1,F0,A3,A2,A1,A0,L3,L2,L1,L0|Exps]) ->
- [{i32([F3,F2,F1,F0]), % F = function (atom ID)
- i32([A3,A2,A1,A0]), % A = arity (int)
- i32([L3,L2,L1,L0])} % L = label (int)
- |collect_exports(Exps)];
-collect_exports([]) ->
- [].
-
-resolve_exports(Exps,Atoms) ->
- [ {lookup_key(F,Atoms), A, L} || {F,A,L} <- Exps ].
-
-%%-----------------------------------------------------------------------
-%% Disassembles the import table of a BEAM file.
-%%-----------------------------------------------------------------------
-
-beam_disasm_imports(ExpTabBin,Atoms) ->
- {_NumAtoms,B} = get_int(ExpTabBin),
- disasm_imports(B,Atoms).
-
-disasm_imports(Bin,Atoms) ->
- resolve_imports(collect_imports(binary_to_list(Bin)),Atoms).
-
-collect_imports([M3,M2,M1,M0,F3,F2,F1,F0,A3,A2,A1,A0|Exps]) ->
- [{i32([M3,M2,M1,M0]), % M = module (atom ID)
- i32([F3,F2,F1,F0]), % F = function (atom ID)
- i32([A3,A2,A1,A0])} % A = arity (int)
- |collect_imports(Exps)];
-collect_imports([]) ->
- [].
-
-resolve_imports(Exps,Atoms) ->
- [{extfunc,lookup_key(M,Atoms),lookup_key(F,Atoms),A} || {M,F,A} <- Exps ].
-
-%%-----------------------------------------------------------------------
-%% Disassembles the lambda (fun) table of a BEAM file.
-%%-----------------------------------------------------------------------
-
-beam_disasm_lambdas(none, _) -> none;
-beam_disasm_lambdas(<<_:32,Tab/binary>>, Atoms) ->
- disasm_lambdas(Tab, Atoms, 0).
-
-disasm_lambdas(<>,
- Atoms, OldIndex) ->
- Info = {lookup_key(F, Atoms),A,Lbl,Index,NumFree,OldUniq},
- [{OldIndex,Info}|disasm_lambdas(More, Atoms, OldIndex+1)];
-disasm_lambdas(<<>>, _, _) -> [].
-
-%%-----------------------------------------------------------------------
-%% Disassembles the code chunk of a BEAM file:
-%% - The code is first disassembled into a long list of instructions.
-%% - This list is then split into functions and all names are resolved.
-%%-----------------------------------------------------------------------
-
-beam_disasm_code(CodeBin,Atoms,Imports,Str,Lambdas) ->
- [_SS3,_SS2,_SS1,_SS0, % Sub-Size (length of information before code)
- _IS3,_IS2,_IS1,_IS0, % Instruction Set Identifier (always 0)
- _OM3,_OM2,_OM1,_OM0, % Opcode Max
- _L3,_L2,_L1,_L0,_F3,_F2,_F1,_F0|Code] = binary_to_list(CodeBin),
- case catch disasm_code(Code, Atoms) of
- {'EXIT',Rsn} ->
- ?NO_DEBUG('code disasm failed: ~p~n',[Rsn]),
- ?exit(Rsn);
- DisasmCode ->
- Functions = get_function_chunks(DisasmCode),
- LocLabels = local_labels(Functions),
- [resolve_names(F,Imports,Str,LocLabels,Lambdas) || F <- Functions]
- end.
-
-%%-----------------------------------------------------------------------
-
-disasm_code([B|Bs], Atoms) ->
- {Instr,RestBs} = disasm_instr(B, Bs, Atoms),
- [Instr|disasm_code(RestBs, Atoms)];
-disasm_code([], _) -> [].
-
-%%-----------------------------------------------------------------------
-%% Splits the code stream into chunks representing the code of functions.
-%%
-%% NOTE: code actually looks like
-%% label L1: ... label Ln:
-%% func_info ...
-%% label entry:
-%% ...
-%%
-%% ...
-%% So the labels before each func_info should be included as well.
-%% Ideally, only one such label is needed, but the BEAM compiler
-%% before R8 didn't care to remove the redundant ones.
-%%-----------------------------------------------------------------------
-
-get_function_chunks([I|Code]) ->
- {LastI,RestCode,Labs} = split_head_labels(I,Code,[]),
- get_funs(LastI,RestCode,Labs,[]);
-get_function_chunks([]) ->
- ?exit(empty_code_segment).
-
-get_funs(PrevI,[I|Is],RevF,RevFs) ->
- case I of
- {func_info,_Info} ->
- [H|T] = RevF,
- {Last,Fun,TrailingLabels} = split_head_labels(H,T,[]),
- get_funs(I, Is, [PrevI|TrailingLabels], add_funs([Last|Fun],RevFs));
- _ ->
- get_funs(I, Is, [PrevI|RevF], RevFs)
- end;
-get_funs(PrevI,[],RevF,RevFs) ->
- case PrevI of
- {int_code_end,[]} ->
- emit_funs(add_fun(RevF,RevFs));
- _ ->
- ?DEBUG('warning: code segment did not end with int_code_end~n',[]),
- emit_funs(add_funs([PrevI|RevF],RevFs))
- end.
-
-split_head_labels({label,L},[I|Code],Labs) ->
- split_head_labels(I,Code,[{label,L}|Labs]);
-split_head_labels(I,Code,Labs) ->
- {I,Code,Labs}.
-
-add_fun([],Fs) ->
- Fs;
-add_fun(F,Fs) ->
- add_funs(F,Fs).
-
-add_funs(F,Fs) ->
- [ lists:reverse(F) | Fs ].
-
-emit_funs(Fs) ->
- lists:reverse(Fs).
-
-%%-----------------------------------------------------------------------
-%% Collects local labels -- I am not sure this is 100% what is needed.
-%%-----------------------------------------------------------------------
-
-local_labels(Funs) ->
- [local_label(Fun) || Fun <- Funs].
-
-%% The first clause below attempts to provide some (limited form of)
-%% backwards compatibility; it is not needed for .beam files generated
-%% by the R8 compiler. The clause should one fine day be taken out.
-local_label([{label,_},{label,L}|Code]) ->
- local_label([{label,L}|Code]);
-local_label([{label,_},
- {func_info,[M0,F0,{u,A}]},
- {label,[{u,L1}]}|_]) ->
- {atom,M} = resolve_arg(M0),
- {atom,F} = resolve_arg(F0),
- {L1, {M, F, A}};
-local_label(Code) ->
- io:format('beam_disasm: no label in ~p~n', [Code]),
- {-666,{none,none,0}}.
-
-%%-----------------------------------------------------------------------
-%% Disassembles a single BEAM instruction; most instructions are handled
-%% in a generic way; indexing instructions are handled separately.
-%%-----------------------------------------------------------------------
-
-disasm_instr(B, Bs, Atoms) ->
- {SymOp,Arity} = beam_opcodes:opname(B),
- case SymOp of
- select_val ->
- disasm_select_inst(select_val, Bs, Atoms);
- select_tuple_arity ->
- disasm_select_inst(select_tuple_arity, Bs, Atoms);
- _ ->
- case catch decode_n_args(Arity, Bs, Atoms) of
- {'EXIT',Rsn} ->
- ?NO_DEBUG("decode_n_args(~p,~p) failed~n",[Arity,Bs]),
- {{'EXIT',{SymOp,Arity,Rsn}},[]};
- {Args,RestBs} ->
- ?NO_DEBUG("instr ~p~n",[{SymOp,Args}]),
- {{SymOp,Args}, RestBs}
- end
- end.
-
-%%-----------------------------------------------------------------------
-%% Disassembles a BEAM select_* instruction used for indexing.
-%% Currently handles {select_val,3} and {select_tuple_arity,3} insts.
-%%
-%% The arruments of a "select"-type instruction look as follows:
-%% , {f,FailLabel}, {list, , [ ... ]}
-%% where each case is of the form [symbol,{f,Label}].
-%%-----------------------------------------------------------------------
-
-disasm_select_inst(Inst, Bs, Atoms) ->
- {X, Bs1} = decode_arg(Bs, Atoms),
- {F, Bs2} = decode_arg(Bs1, Atoms),
- {Z, Bs3} = decode_arg(Bs2, Atoms),
- {U, Bs4} = decode_arg(Bs3, Atoms),
- {u,Len} = U,
- {List, RestBs} = decode_n_args(Len, Bs4, Atoms),
- {{Inst,[X,F,{Z,U,List}]},RestBs}.
-
-%%-----------------------------------------------------------------------
-%% decode_arg([Byte]) -> { Arg, [Byte] }
-%%
-%% - an arg can have variable length, so we must return arg + remaining bytes
-%% - decodes an argument into its 'raw' form: { Tag, Value }
-%% several types map to a single tag, so the byte code instr must then
-%% assign a type to it
-%%-----------------------------------------------------------------------
-
-decode_arg([B|Bs]) ->
- Tag = decode_tag(B band 2#111),
- ?NO_DEBUG('Tag = ~p, B = ~p, Bs = ~p~n',[Tag,B,Bs]),
- case Tag of
- z ->
- decode_z_tagged(Tag, B, Bs);
- _ ->
- %% all other cases are handled as if they were integers
- decode_int(Tag, B, Bs)
- end.
-
-decode_arg([B|Bs0], Atoms) ->
- Tag = decode_tag(B band 2#111),
- ?NO_DEBUG('Tag = ~p, B = ~p, Bs = ~p~n',[Tag,B,Bs]),
- case Tag of
- z ->
- decode_z_tagged(Tag, B, Bs0);
- a ->
- %% atom or nil
- case decode_int(Tag, B, Bs0) of
- {{a,0},Bs} -> {nil,Bs};
- {{a,I},Bs} -> {{atom,lookup_key(I, Atoms)},Bs}
- end;
- _ ->
- %% all other cases are handled as if they were integers
- decode_int(Tag, B, Bs0)
- end.
-
-%%-----------------------------------------------------------------------
-%% Decodes an integer value. Handles positives, negatives, and bignums.
-%%
-%% Tries to do the opposite of:
-%% beam_asm:encode(1, 5) = [81]
-%% beam_asm:encode(1, 1000) = [105,232]
-%% beam_asm:encode(1, 2047) = [233,255]
-%% beam_asm:encode(1, 2048) = [25,8,0]
-%% beam_asm:encode(1,-1) = [25,255,255]
-%% beam_asm:encode(1,-4294967295) = [121,255,0,0,0,1]
-%% beam_asm:encode(1, 4294967295) = [121,0,255,255,255,255]
-%% beam_asm:encode(1, 429496729501) = [121,99,255,255,255,157]
-%%-----------------------------------------------------------------------
-
-decode_int(Tag,B,Bs) when (B band 16#08) == 0 ->
- %% N < 16 = 4 bits, NNNN:0:TTT
- N = B bsr 4,
- {{Tag,N},Bs};
-decode_int(Tag,B,Bs) when (B band 16#10) == 0 ->
- %% N < 2048 = 11 bits = 3:8 bits, NNN:01:TTT, NNNNNNNN
- [B1|Bs1] = Bs,
- Val0 = B band 2#11100000,
- N = (Val0 bsl 3) bor B1,
- ?NO_DEBUG('NNN:01:TTT, NNNNNNNN = ~n~p:01:~p, ~p = ~p~n', [Val0,Tag,B,N]),
- {{Tag,N},Bs1};
-decode_int(Tag,B,Bs) ->
- {Len,Bs1} = decode_int_length(B,Bs),
- {IntBs,RemBs} = take_bytes(Len,Bs1),
- N = build_arg(IntBs),
- [F|_] = IntBs,
- Num = if F > 127, Tag == i -> decode_negative(N,Len);
- true -> N
- end,
- ?NO_DEBUG('Len = ~p, IntBs = ~p, Num = ~p~n', [Len,IntBs,Num]),
- {{Tag,Num},RemBs}.
-
-decode_int_length(B,Bs) ->
- %% The following imitates get_erlang_integer() in beam_load.c
- %% Len is the size of the integer value in bytes
- case B bsr 5 of
- 7 ->
- {Arg,ArgBs} = decode_arg(Bs),
- case Arg of
- {u,L} ->
- {L+9,ArgBs}; % 9 stands for 7+2
- _ ->
- ?exit({decode_int,weird_bignum_sublength,Arg})
- end;
- L ->
- {L+2,Bs}
- end.
-
-decode_negative(N,Len) ->
- N - (1 bsl (Len*8)). % 8 is number of bits in a byte
-
-%%-----------------------------------------------------------------------
-%% Decodes lists and floating point numbers.
-%%-----------------------------------------------------------------------
-
-decode_z_tagged(Tag,B,Bs) when (B band 16#08) == 0 ->
- N = B bsr 4,
- case N of
- 0 -> % float
- decode_float(Bs);
- 1 -> % list
- {{Tag,N},Bs};
- 2 -> % fr
- decode_fr(Bs);
- 3 -> % allocation list
- decode_alloc_list(Bs);
- _ ->
- ?exit({decode_z_tagged,{invalid_extended_tag,N}})
- end;
-decode_z_tagged(_,B,_) ->
- ?exit({decode_z_tagged,{weird_value,B}}).
-
-decode_float(Bs) ->
- {FL,RestBs} = take_bytes(8,Bs),
- <> = list_to_binary(FL),
- {{float,Float},RestBs}.
-
-decode_fr(Bs) ->
- {{u,Fr},RestBs} = decode_arg(Bs),
- {{fr,Fr},RestBs}.
-
-decode_alloc_list(Bs) ->
- {{u,N},RestBs} = decode_arg(Bs),
- decode_alloc_list_1(N, RestBs, []).
-
-decode_alloc_list_1(0, RestBs, Acc) ->
- {{u,{alloc,lists:reverse(Acc)}},RestBs};
-decode_alloc_list_1(N, Bs0, Acc) ->
- {{u,Type},Bs1} = decode_arg(Bs0),
- {{u,Val},Bs} = decode_arg(Bs1),
- case Type of
- 0 ->
- decode_alloc_list_1(N-1, Bs, [{words,Val}|Acc]);
- 1 ->
- decode_alloc_list_1(N-1, Bs, [{floats,Val}|Acc])
- end.
-
-%%-----------------------------------------------------------------------
-%% take N bytes from a stream, return { Taken_bytes, Remaining_bytes }
-%%-----------------------------------------------------------------------
-
-take_bytes(N,Bs) ->
- take_bytes(N,Bs,[]).
-
-take_bytes(N,[B|Bs],Acc) when N > 0 ->
- take_bytes(N-1,Bs,[B|Acc]);
-take_bytes(0,Bs,Acc) ->
- { lists:reverse(Acc), Bs }.
-
-%%-----------------------------------------------------------------------
-%% from a list of bytes Bn,Bn-1,...,B1,B0
-%% build (Bn << 8*n) bor ... bor B1 << 8 bor B0 << 0
-%%-----------------------------------------------------------------------
-
-build_arg(Bs) ->
- build_arg(Bs,0).
-
-build_arg([B|Bs],N) ->
- build_arg(Bs, (N bsl 8) bor B);
-build_arg([],N) ->
- N.
-
-%%-----------------------------------------------------------------------
-%% Decodes a bunch of arguments and returns them in a list
-%%-----------------------------------------------------------------------
-
-decode_n_args(N, Bs, Atoms) when N >= 0 ->
- decode_n_args(N, [], Bs, Atoms).
-
-decode_n_args(N, Acc, Bs0, Atoms) when N > 0 ->
- {A1,Bs} = decode_arg(Bs0, Atoms),
- decode_n_args(N-1, [A1|Acc], Bs, Atoms);
-decode_n_args(0, Acc, Bs, _) ->
- {lists:reverse(Acc),Bs}.
-
-%%-----------------------------------------------------------------------
-%% Convert a numeric tag value into a symbolic one
-%%-----------------------------------------------------------------------
-
-decode_tag(?tag_u) -> u;
-decode_tag(?tag_i) -> i;
-decode_tag(?tag_a) -> a;
-decode_tag(?tag_x) -> x;
-decode_tag(?tag_y) -> y;
-decode_tag(?tag_f) -> f;
-decode_tag(?tag_h) -> h;
-decode_tag(?tag_z) -> z;
-decode_tag(X) -> ?exit({unknown_tag,X}).
-
-%%-----------------------------------------------------------------------
-%% - replace all references {a,I} with the atom with index I (or {atom,A})
-%% - replace all references to {i,K} in an external call position with
-%% the proper MFA (position in list, first elt = 0, yields MFA to use)
-%% - resolve strings, represented as , into their
-%% actual values by using string table
-%% (note: string table should be passed as a BINARY so that we can
-%% use binary_to_list/3!)
-%% - convert instruction to its readable form ...
-%%
-%% Currently, only the first three are done (systematically, at least).
-%%
-%% Note: It MAY be premature to remove the lists of args, since that
-%% representation means it is simpler to iterate over all args, etc.
-%%-----------------------------------------------------------------------
-
-resolve_names(Fun, Imports, Str, Lbls, Lambdas) ->
- [resolve_inst(Instr, Imports, Str, Lbls, Lambdas) || Instr <- Fun].
-
-%%
-%% New make_fun2/4 instruction added in August 2001 (R8).
-%% We handle it specially here to avoid adding an argument to
-%% the clause for every instruction.
-%%
-
-resolve_inst({make_fun2,Args},_,_,Lbls,Lambdas) ->
- [OldIndex] = resolve_args(Args),
- {value,{OldIndex,{F,A,_Lbl,_Index,NumFree,OldUniq}}} =
- lists:keysearch(OldIndex, 1, Lambdas),
- [{_,{M,_,_}}|_] = Lbls, % Slighly kludgy.
- {make_fun2,{M,F,A},OldIndex,OldUniq,NumFree};
-resolve_inst(Instr, Imports, Str, Lbls, _Lambdas) ->
- resolve_inst(Instr, Imports, Str, Lbls).
-
-resolve_inst({label,[{u,L}]},_,_,_) ->
- {label,L};
-resolve_inst({func_info,RawMFA},_,_,_) ->
- {func_info,resolve_args(RawMFA)};
-% resolve_inst(int_code_end,_,_,_,_) -> % instruction already handled
-% int_code_end; % should not really be handled here
-resolve_inst({call,[{u,N},{f,L}]},_,_,Lbls) ->
- {call,N,catch lookup_key(L,Lbls)};
-resolve_inst({call_last,[{u,N},{f,L},{u,U}]},_,_,Lbls) ->
- {call_last,N,catch lookup_key(L,Lbls),U};
-resolve_inst({call_only,[{u,N},{f,L}]},_,_,Lbls) ->
- {call_only,N,catch lookup_key(L,Lbls)};
-resolve_inst({call_ext,[{u,N},{u,MFAix}]},Imports,_,_) ->
- {call_ext,N,catch lists:nth(MFAix+1,Imports)};
-resolve_inst({call_ext_last,[{u,N},{u,MFAix},{u,X}]},Imports,_,_) ->
- {call_ext_last,N,catch lists:nth(MFAix+1,Imports),X};
-resolve_inst({bif0,Args},Imports,_,_) ->
- [Bif,Reg] = resolve_args(Args),
- {extfunc,_Mod,BifName,_Arity} = lists:nth(Bif+1,Imports),
- %?NO_DEBUG('bif0(~p, ~p)~n',[BifName,Reg]),
- {bif,BifName,nofail,[],Reg};
-resolve_inst({bif1,Args},Imports,_,_) ->
- [F,Bif,A1,Reg] = resolve_args(Args),
- {extfunc,_Mod,BifName,_Arity} = lists:nth(Bif+1,Imports),
- %?NO_DEBUG('bif1(~p, ~p, ~p, ~p, ~p)~n',[Bif,BifName,F,[A1],Reg]),
- {bif,BifName,F,[A1],Reg};
-resolve_inst({bif2,Args},Imports,_,_) ->
- [F,Bif,A1,A2,Reg] = resolve_args(Args),
- {extfunc,_Mod,BifName,_Arity} = lists:nth(Bif+1,Imports),
- %?NO_DEBUG('bif2(~p, ~p, ~p, ~p, ~p)~n',[Bif,BifName,F,[A1,A2],Reg]),
- {bif,BifName,F,[A1,A2],Reg};
-resolve_inst({allocate,[{u,X0},{u,X1}]},_,_,_) ->
- {allocate,X0,X1};
-resolve_inst({allocate_heap,[{u,X0},{u,X1},{u,X2}]},_,_,_) ->
- {allocate_heap,X0,X1,X2};
-resolve_inst({allocate_zero,[{u,X0},{u,X1}]},_,_,_) ->
- {allocate_zero,X0,X1};
-resolve_inst({allocate_heap_zero,[{u,X0},{u,X1},{u,X2}]},_,_,_) ->
- {allocate_heap_zero,X0,X1,X2};
-resolve_inst({test_heap,[{u,X0},{u,X1}]},_,_,_) ->
- {test_heap,X0,X1};
-resolve_inst({init,[Dst]},_,_,_) ->
- {init,Dst};
-resolve_inst({deallocate,[{u,L}]},_,_,_) ->
- {deallocate,L};
-resolve_inst({return,[]},_,_,_) ->
- return;
-resolve_inst({send,[]},_,_,_) ->
- send;
-resolve_inst({remove_message,[]},_,_,_) ->
- remove_message;
-resolve_inst({timeout,[]},_,_,_) ->
- timeout;
-resolve_inst({loop_rec,[Lbl,Dst]},_,_,_) ->
- {loop_rec,Lbl,Dst};
-resolve_inst({loop_rec_end,[Lbl]},_,_,_) ->
- {loop_rec_end,Lbl};
-resolve_inst({wait,[Lbl]},_,_,_) ->
- {wait,Lbl};
-resolve_inst({wait_timeout,[Lbl,Int]},_,_,_) ->
- {wait_timeout,Lbl,resolve_arg(Int)};
-resolve_inst({m_plus,Args},_,_,_) ->
- [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
- {arithbif,'+',W,[SrcR1,SrcR2],DstR};
-resolve_inst({m_minus,Args},_,_,_) ->
- [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
- {arithbif,'-',W,[SrcR1,SrcR2],DstR};
-resolve_inst({m_times,Args},_,_,_) ->
- [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
- {arithbif,'*',W,[SrcR1,SrcR2],DstR};
-resolve_inst({m_div,Args},_,_,_) ->
- [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
- {arithbif,'/',W,[SrcR1,SrcR2],DstR};
-resolve_inst({int_div,Args},_,_,_) ->
- [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
- {arithbif,'div',W,[SrcR1,SrcR2],DstR};
-resolve_inst({int_rem,Args},_,_,_) ->
- [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
- {arithbif,'rem',W,[SrcR1,SrcR2],DstR};
-resolve_inst({int_band,Args},_,_,_) ->
- [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
- {arithbif,'band',W,[SrcR1,SrcR2],DstR};
-resolve_inst({int_bor,Args},_,_,_) ->
- [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
- {arithbif,'bor',W,[SrcR1,SrcR2],DstR};
-resolve_inst({int_bxor,Args},_,_,_) ->
- [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
- {arithbif,'bxor',W,[SrcR1,SrcR2],DstR};
-resolve_inst({int_bsl,Args},_,_,_) ->
- [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
- {arithbif,'bsl',W,[SrcR1,SrcR2],DstR};
-resolve_inst({int_bsr,Args},_,_,_) ->
- [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
- {arithbif,'bsr',W,[SrcR1,SrcR2],DstR};
-resolve_inst({int_bnot,Args},_,_,_) ->
- [W,SrcR,DstR] = resolve_args(Args),
- {arithbif,'bnot',W,[SrcR],DstR};
-resolve_inst({is_lt=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({is_ge=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({is_eq=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({is_ne=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({is_eq_exact=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({is_ne_exact=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({is_integer=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({is_float=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({is_number=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({is_atom=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({is_pid=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({is_reference=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({is_port=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({is_nil=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({is_binary=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({is_constant=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({is_list=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({is_nonempty_list=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({is_tuple=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({test_arity=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({select_val,Args},_,_,_) ->
- [Reg,FLbl,{{z,1},{u,_Len},List0}] = Args,
- List = resolve_args(List0),
- {select_val,Reg,FLbl,{list,List}};
-resolve_inst({select_tuple_arity,Args},_,_,_) ->
- [Reg,FLbl,{{z,1},{u,_Len},List0}] = Args,
- List = resolve_args(List0),
- {select_tuple_arity,Reg,FLbl,{list,List}};
-resolve_inst({jump,[Lbl]},_,_,_) ->
- {jump,Lbl};
-resolve_inst({'catch',[Dst,Lbl]},_,_,_) ->
- {'catch',Dst,Lbl};
-resolve_inst({catch_end,[Dst]},_,_,_) ->
- {catch_end,Dst};
-resolve_inst({move,[Src,Dst]},_,_,_) ->
- {move,resolve_arg(Src),Dst};
-resolve_inst({get_list,[Src,Dst1,Dst2]},_,_,_) ->
- {get_list,Src,Dst1,Dst2};
-resolve_inst({get_tuple_element,[Src,{u,Off},Dst]},_,_,_) ->
- {get_tuple_element,resolve_arg(Src),Off,resolve_arg(Dst)};
-resolve_inst({set_tuple_element,[Src,Dst,{u,Off}]},_,_,_) ->
- {set_tuple_element,resolve_arg(Src),resolve_arg(Dst),Off};
-resolve_inst({put_string,[{u,Len},{u,Off},Dst]},_,Strings,_) ->
- String = if Len > 0 -> binary_to_list(Strings, Off+1, Off+Len);
- true -> ""
- end,
-?NO_DEBUG('put_string(~p, {string,~p}, ~p)~n',[Len,String,Dst]),
- {put_string,Len,{string,String},Dst};
-resolve_inst({put_list,[Src1,Src2,Dst]},_,_,_) ->
- {put_list,resolve_arg(Src1),resolve_arg(Src2),Dst};
-resolve_inst({put_tuple,[{u,Arity},Dst]},_,_,_) ->
- {put_tuple,Arity,Dst};
-resolve_inst({put,[Src]},_,_,_) ->
- {put,resolve_arg(Src)};
-resolve_inst({badmatch,[X]},_,_,_) ->
- {badmatch,resolve_arg(X)};
-resolve_inst({if_end,[]},_,_,_) ->
- if_end;
-resolve_inst({case_end,[X]},_,_,_) ->
- {case_end,resolve_arg(X)};
-resolve_inst({call_fun,[{u,N}]},_,_,_) ->
- {call_fun,N};
-resolve_inst({make_fun,Args},_,_,Lbls) ->
- [{f,L},Magic,FreeVars] = resolve_args(Args),
- {make_fun,catch lookup_key(L,Lbls),Magic,FreeVars};
-resolve_inst({is_function=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-resolve_inst({call_ext_only,[{u,N},{u,MFAix}]},Imports,_,_) ->
- {call_ext_only,N,catch lists:nth(MFAix+1,Imports)};
-%%
-%% Instructions for handling binaries added in R7A & R7B
-%%
-resolve_inst({bs_start_match,[F,Reg]},_,_,_) ->
- {bs_start_match,F,Reg};
-resolve_inst({bs_get_integer=I,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
- [A2,A5] = resolve_args([Arg2,Arg5]),
- {test,I,Lbl,[A2,N,decode_field_flags(U),A5]};
-resolve_inst({bs_get_float=I,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
- [A2,A5] = resolve_args([Arg2,Arg5]),
- {test,I,Lbl,[A2,N,decode_field_flags(U),A5]};
-resolve_inst({bs_get_binary=I,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
- [A2,A5] = resolve_args([Arg2,Arg5]),
- {test,I,Lbl,[A2,N,decode_field_flags(U),A5]};
-resolve_inst({bs_skip_bits,[Lbl,Arg2,{u,N},{u,U}]},_,_,_) ->
- [A2] = resolve_args([Arg2]),
- {test,bs_skip_bits,Lbl,[A2,N,decode_field_flags(U)]};
-resolve_inst({bs_test_tail,[F,{u,N}]},_,_,_) ->
- {test,bs_test_tail,F,[N]};
-resolve_inst({bs_save,[{u,N}]},_,_,_) ->
- {bs_save,N};
-resolve_inst({bs_restore,[{u,N}]},_,_,_) ->
- {bs_restore,N};
-resolve_inst({bs_init,[{u,N},{u,U}]},_,_,_) ->
- {bs_init,N,decode_field_flags(U)};
-resolve_inst({bs_final,[F,X]},_,_,_) ->
- {bs_final,F,X};
-resolve_inst({bs_put_integer,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
- [A2,A5] = resolve_args([Arg2,Arg5]),
- {bs_put_integer,Lbl,A2,N,decode_field_flags(U),A5};
-resolve_inst({bs_put_binary,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
- [A2,A5] = resolve_args([Arg2,Arg5]),
- ?NO_DEBUG('bs_put_binary(~p,~p,~p,~p,~p})~n',[Lbl,A2,N,U,A5]),
- {bs_put_binary,Lbl,A2,N,decode_field_flags(U),A5};
-resolve_inst({bs_put_float,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
- [A2,A5] = resolve_args([Arg2,Arg5]),
- ?NO_DEBUG('bs_put_float(~p,~p,~p,~p,~p})~n',[Lbl,A2,N,U,A5]),
- {bs_put_float,Lbl,A2,N,decode_field_flags(U),A5};
-resolve_inst({bs_put_string,[{u,Len},{u,Off}]},_,Strings,_) ->
- String = if Len > 0 -> binary_to_list(Strings, Off+1, Off+Len);
- true -> ""
- end,
- ?NO_DEBUG('bs_put_string(~p, {string,~p})~n',[Len,String]),
- {bs_put_string,Len,{string,String}};
-resolve_inst({bs_need_buf,[{u,N}]},_,_,_) ->
- {bs_need_buf,N};
-
-%%
-%% Instructions for handling floating point numbers added in June 2001 (R8).
-%%
-resolve_inst({fclearerror,[]},_,_,_) ->
- fclearerror;
-resolve_inst({fcheckerror,Args},_,_,_) ->
- [Fail] = resolve_args(Args),
- {fcheckerror,Fail};
-resolve_inst({fmove,Args},_,_,_) ->
- [FR,Reg] = resolve_args(Args),
- {fmove,FR,Reg};
-resolve_inst({fconv,Args},_,_,_) ->
- [Reg,FR] = resolve_args(Args),
- {fconv,Reg,FR};
-resolve_inst({fadd=I,Args},_,_,_) ->
- [F,A1,A2,Reg] = resolve_args(Args),
- {arithfbif,I,F,[A1,A2],Reg};
-resolve_inst({fsub=I,Args},_,_,_) ->
- [F,A1,A2,Reg] = resolve_args(Args),
- {arithfbif,I,F,[A1,A2],Reg};
-resolve_inst({fmul=I,Args},_,_,_) ->
- [F,A1,A2,Reg] = resolve_args(Args),
- {arithfbif,I,F,[A1,A2],Reg};
-resolve_inst({fdiv=I,Args},_,_,_) ->
- [F,A1,A2,Reg] = resolve_args(Args),
- {arithfbif,I,F,[A1,A2],Reg};
-resolve_inst({fnegate,Args},_,_,_) ->
- [F,Arg,Reg] = resolve_args(Args),
- {arithfbif,fnegate,F,[Arg],Reg};
-
-%%
-%% Instructions for try expressions added in January 2003 (R10).
-%%
-
-resolve_inst({'try',[Reg,Lbl]},_,_,_) -> % analogous to 'catch'
- {'try',Reg,Lbl};
-resolve_inst({try_end,[Reg]},_,_,_) -> % analogous to 'catch_end'
- {try_end,Reg};
-resolve_inst({try_case,[Reg]},_,_,_) -> % analogous to 'catch_end'
- {try_case,Reg};
-resolve_inst({try_case_end,[Reg]},_,_,_) ->
- {try_case_end,Reg};
-resolve_inst({raise,[Reg1,Reg2]},_,_,_) ->
- {bif,raise,{f,0},[Reg1,Reg2],{x,0}};
-
-%%
-%% New bit syntax instructions added in February 2004 (R10B).
-%%
-
-resolve_inst({bs_init2,[Lbl,Arg2,{u,W},{u,R},{u,F},Arg6]},_,_,_) ->
- [A2,A6] = resolve_args([Arg2,Arg6]),
- {bs_init2,Lbl,A2,W,R,decode_field_flags(F),A6};
-resolve_inst({bs_bits_to_bytes,[Lbl,Arg2,Arg3]},_,_,_) ->
- [A2,A3] = resolve_args([Arg2,Arg3]),
- {bs_bits_to_bytes,Lbl,A2,A3};
-resolve_inst({bs_add=I,[Lbl,Arg2,Arg3,Arg4,Arg5]},_,_,_) ->
- [A2,A3,A4,A5] = resolve_args([Arg2,Arg3,Arg4,Arg5]),
- {I,Lbl,[A2,A3,A4],A5};
-
-%%
-%% New apply instructions added in April 2004 (R10B).
-%%
-resolve_inst({apply,[{u,Arity}]},_,_,_) ->
- {apply,Arity};
-resolve_inst({apply_last,[{u,Arity},{u,D}]},_,_,_) ->
- {apply_last,Arity,D};
-
-%%
-%% New test instruction added in April 2004 (R10B).
-%%
-resolve_inst({is_boolean=I,Args0},_,_,_) ->
- [L|Args] = resolve_args(Args0),
- {test,I,L,Args};
-
-%%
-%% Catches instructions that are not yet handled.
-%%
-
-resolve_inst(X,_,_,_) -> ?exit({resolve_inst,X}).
-
-%%-----------------------------------------------------------------------
-%% Resolves arguments in a generic way.
-%%-----------------------------------------------------------------------
-
-resolve_args(Args) -> [resolve_arg(A) || A <- Args].
-
-resolve_arg({u,N}) -> N;
-resolve_arg({i,N}) -> {integer,N};
-resolve_arg({atom,Atom}=A) when is_atom(Atom) -> A;
-resolve_arg(nil) -> nil;
-resolve_arg(Arg) -> Arg.
-
-%%-----------------------------------------------------------------------
-%% The purpose of the following is just to add a hook for future changes.
-%% Currently, field flags are numbers 1-2-4-8 and only two of these
-%% numbers (BSF_LITTLE 2 -- BSF_SIGNED 4) have a semantic significance;
-%% others are just hints for speeding up the execution; see "erl_bits.h".
-%%-----------------------------------------------------------------------
-
-decode_field_flags(FF) ->
- {field_flags,FF}.
-
-%%-----------------------------------------------------------------------
-%% Each string is denoted in the assembled code by its offset into this
-%% binary. This binary contains all strings concatenated together.
-%%-----------------------------------------------------------------------
-
-beam_disasm_strings(Bin) ->
- Bin.
-
-%%-----------------------------------------------------------------------
-%% Disassembles the attributes of a BEAM file.
-%%-----------------------------------------------------------------------
-
-beam_disasm_attributes(none) -> none;
-beam_disasm_attributes(AttrBin) -> binary_to_term(AttrBin).
-
-%%-----------------------------------------------------------------------
-%% Disassembles the compilation information of a BEAM file.
-%%-----------------------------------------------------------------------
-
-beam_disasm_compilation_info(none) -> none;
-beam_disasm_compilation_info(Bin) -> binary_to_term(Bin).
-
-%%-----------------------------------------------------------------------
-%% Private Utilities
-%%-----------------------------------------------------------------------
-
-%%-----------------------------------------------------------------------
-
-lookup_key(Key,[{Key,Val}|_]) ->
- Val;
-lookup_key(Key,[_|KVs]) ->
- lookup_key(Key,KVs);
-lookup_key(Key,[]) ->
- ?exit({lookup_key,{key_not_found,Key}}).
-
-%%-----------------------------------------------------------------------
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_flatten.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_flatten.erl
deleted file mode 100644
index a9958f87cd..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_flatten.erl
+++ /dev/null
@@ -1,137 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: beam_flatten.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
-%%
-%% Purpose : Converts intermediate assembly code to final format.
-
--module(beam_flatten).
-
--export([module/2]).
--import(lists, [reverse/1,reverse/2,map/2]).
-
-module({Mod,Exp,Attr,Fs,Lc}, _Opt) ->
- {ok,{Mod,Exp,Attr,map(fun function/1, Fs),Lc}}.
-
-function({function,Name,Arity,CLabel,Is0}) ->
- Is1 = block(Is0),
- Is = opt(Is1),
- {function,Name,Arity,CLabel,Is}.
-
-block(Is) ->
- block(Is, []).
-
-block([{block,Is0}|Is1], Acc) -> block(Is1, norm_block(Is0, Acc));
-block([I|Is], Acc) -> block(Is, [I|Acc]);
-block([], Acc) -> reverse(Acc).
-
-norm_block([{allocate,R,Alloc}|Is], Acc0) ->
- case insert_alloc_in_bs_init(Acc0, Alloc) of
- not_possible ->
- norm_block(Is, reverse(norm_allocate(Alloc, R), Acc0));
- Acc ->
- norm_block(Is, Acc)
- end;
-norm_block([I|Is], Acc) -> norm_block(Is, [norm(I)|Acc]);
-norm_block([], Acc) -> Acc.
-
-norm({set,[D],As,{bif,N}}) -> {bif,N,nofail,As,D};
-norm({set,[D],As,{bif,N,F}}) -> {bif,N,F,As,D};
-norm({set,[D],[S],move}) -> {move,S,D};
-norm({set,[D],[S],fmove}) -> {fmove,S,D};
-norm({set,[D],[S],fconv}) -> {fconv,S,D};
-norm({set,[D],[S1,S2],put_list}) -> {put_list,S1,S2,D};
-norm({set,[D],[],{put_tuple,A}}) -> {put_tuple,A,D};
-norm({set,[],[S],put}) -> {put,S};
-norm({set,[D],[],{put_string,L,S}}) -> {put_string,L,S,D};
-norm({set,[D],[S],{get_tuple_element,I}}) -> {get_tuple_element,S,I,D};
-norm({set,[],[S,D],{set_tuple_element,I}}) -> {set_tuple_element,S,D,I};
-norm({set,[D1,D2],[S],get_list}) -> {get_list,S,D1,D2};
-norm({set,[],[],remove_message}) -> remove_message;
-norm({set,[],[],fclearerror}) -> fclearerror;
-norm({set,[],[],fcheckerror}) -> {fcheckerror,{f,0}};
-norm({'%',_}=Comment) -> Comment;
-norm({'%live',R}) -> {'%live',R}.
-
-norm_allocate({_Zero,nostack,Nh,[]}, Regs) ->
- [{test_heap,Nh,Regs}];
-norm_allocate({_Zero,nostack,Nh,Nf,[]}, Regs) ->
- [{test_heap,alloc_list(Nh, Nf),Regs}];
-norm_allocate({zero,0,Nh,[]}, Regs) ->
- norm_allocate({nozero,0,Nh,[]}, Regs);
-norm_allocate({zero,0,Nh,Nf,[]}, Regs) ->
- norm_allocate({nozero,0,Nh,Nf,[]}, Regs);
-norm_allocate({zero,Ns,0,[]}, Regs) ->
- [{allocate_zero,Ns,Regs}];
-norm_allocate({zero,Ns,Nh,[]}, Regs) ->
- [{allocate_heap_zero,Ns,Nh,Regs}];
-norm_allocate({nozero,Ns,0,Inits}, Regs) ->
- [{allocate,Ns,Regs}|Inits];
-norm_allocate({nozero,Ns,Nh,Inits}, Regs) ->
- [{allocate_heap,Ns,Nh,Regs}|Inits];
-norm_allocate({nozero,Ns,Nh,Floats,Inits}, Regs) ->
- [{allocate_heap,Ns,alloc_list(Nh, Floats),Regs}|Inits];
-norm_allocate({zero,Ns,Nh,Floats,Inits}, Regs) ->
- [{allocate_heap_zero,Ns,alloc_list(Nh, Floats),Regs}|Inits].
-
-insert_alloc_in_bs_init([I|_]=Is, Alloc) ->
- case is_bs_put(I) of
- false ->
- not_possible;
- true ->
- insert_alloc_1(Is, Alloc, [])
- end.
-
-insert_alloc_1([{bs_init2,Fail,Bs,Ws,Regs,F,Dst}|Is], {_,nostack,Nh,Nf,[]}, Acc) ->
- Al = alloc_list(Ws+Nh, Nf),
- I = {bs_init2,Fail,Bs,Al,Regs,F,Dst},
- reverse(Acc, [I|Is]);
-insert_alloc_1([I|Is], Alloc, Acc) ->
- insert_alloc_1(Is, Alloc, [I|Acc]).
-
-is_bs_put({bs_put_integer,_,_,_,_,_}) -> true;
-is_bs_put({bs_put_float,_,_,_,_,_}) -> true;
-is_bs_put({bs_put_binary,_,_,_,_,_}) -> true;
-is_bs_put({bs_put_string,_,_}) -> true;
-is_bs_put(_) -> false.
-
-alloc_list(Words, Floats) ->
- {alloc,[{words,Words},{floats,Floats}]}.
-
-
-%% opt(Is0) -> Is
-%% Simple peep-hole optimization to move a {move,Any,{x,0}} past
-%% any kill up to the next call instruction.
-
-opt(Is) ->
- opt_1(Is, []).
-
-opt_1([{move,_,{x,0}}=I|Is0], Acc0) ->
- case move_past_kill(Is0, I, Acc0) of
- impossible -> opt_1(Is0, [I|Acc0]);
- {Is,Acc} -> opt_1(Is, Acc)
- end;
-opt_1([I|Is], Acc) ->
- opt_1(Is, [I|Acc]);
-opt_1([], Acc) -> reverse(Acc).
-
-move_past_kill([{'%live',_}|Is], Move, Acc) ->
- move_past_kill(Is, Move, Acc);
-move_past_kill([{kill,Src}|_], {move,Src,_}, _) ->
- impossible;
-move_past_kill([{kill,_}=I|Is], Move, Acc) ->
- move_past_kill(Is, Move, [I|Acc]);
-move_past_kill(Is, Move, Acc) ->
- {Is,[Move|Acc]}.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_jump.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_jump.erl
deleted file mode 100644
index fd005898b6..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_jump.erl
+++ /dev/null
@@ -1,477 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: beam_jump.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
-%%
-%%% Purpose : Optimise jumps and remove unreachable code.
-
--module(beam_jump).
-
--export([module/2,module_labels/1,
- is_unreachable_after/1,remove_unused_labels/1]).
-
-%%% The following optimisations are done:
-%%%
-%%% (1) This code with two identical instruction sequences
-%%%
-%%% L1:
-%%% L2:
-%%% . . .
-%%% L3:
-%%% L4:
-%%%
-%%% can be replaced with
-%%%
-%%% L1: jump L3
-%%% L2:
-%%% . . .
-%%% L3:
-%%% L4
-%%%
-%%% Note: The instruction sequence must end with an instruction
-%%% such as a jump that never transfers control to the instruction
-%%% following it.
-%%%
-%%% (2) case_end, if_end, and badmatch, and function calls that cause an
-%%% exit (such as calls to exit/1) are moved to the end of the function.
-%%% The purpose is to allow further optimizations at the place from
-%%% which the code was moved.
-%%%
-%%% (3) Any unreachable code is removed. Unreachable code is code after
-%%% jump, call_last and other instructions which never transfer control
-%%% to the following instruction. Code is unreachable up to the next
-%%% *referenced* label. Note that the optimisations below might
-%%% generate more possibilities for removing unreachable code.
-%%%
-%%% (4) This code:
-%%% L1: jump L2
-%%% . . .
-%%% L2: ...
-%%%
-%%% will be changed to
-%%%
-%%% jump L2
-%%% . . .
-%%% L1:
-%%% L2: ...
-%%%
-%%% If the jump is unreachable, it will be removed according to (1).
-%%%
-%%% (5) In
-%%%
-%%% jump L1
-%%% L1:
-%%%
-%%% the jump will be removed.
-%%%
-%%% (6) If test instructions are used to skip a single jump instruction,
-%%% the test is inverted and the jump is eliminated (provided that
-%%% the test can be inverted). Example:
-%%%
-%%% is_eq L1 {x,1} {x,2}
-%%% jump L2
-%%% L1:
-%%%
-%%% will be changed to
-%%%
-%%% is_ne L2 {x,1} {x,2}
-%%%
-%%% (The label L1 will be retained if there were previous references to it.)
-%%%
-%%% (7) Some redundant uses of is_boolean/1 is optimized away.
-%%%
-%%% Terminology note: The optimisation done here is called unreachable-code
-%%% elimination, NOT dead-code elimination. Dead code elimination
-%%% means the removal of instructions that are executed, but have no visible
-%%% effect on the program state.
-%%%
-
--import(lists, [reverse/1,reverse/2,map/2,mapfoldl/3,foldl/3,
- last/1,foreach/2,member/2]).
-
-module({Mod,Exp,Attr,Fs0,Lc}, _Opt) ->
- Fs = map(fun function/1, Fs0),
- {ok,{Mod,Exp,Attr,Fs,Lc}}.
-
-module_labels({Mod,Exp,Attr,Fs,Lc}) ->
- {Mod,Exp,Attr,map(fun function_labels/1, Fs),Lc}.
-
-function_labels({function,Name,Arity,CLabel,Asm0}) ->
- Asm = remove_unused_labels(Asm0),
- {function,Name,Arity,CLabel,Asm}.
-
-function({function,Name,Arity,CLabel,Asm0}) ->
- Asm1 = share(Asm0),
- Asm2 = bopt(Asm1),
- Asm3 = move(Asm2),
- Asm4 = opt(Asm3, CLabel),
- Asm = remove_unused_labels(Asm4),
- {function,Name,Arity,CLabel,Asm}.
-
-%%%
-%%% (1) We try to share the code for identical code segments by replacing all
-%%% occurrences except the last with jumps to the last occurrence.
-%%%
-
-share(Is) ->
- share_1(reverse(Is), gb_trees:empty(), [], []).
-
-share_1([{label,_}=Lbl|Is], Dict, [], Acc) ->
- share_1(Is, Dict, [], [Lbl|Acc]);
-share_1([{label,L}=Lbl|Is], Dict0, Seq, Acc) ->
- case is_unreachable_after(last(Seq)) of
- false ->
- share_1(Is, Dict0, [], [Lbl|Seq ++ Acc]);
- true ->
- case gb_trees:lookup(Seq, Dict0) of
- none ->
- Dict = gb_trees:insert(Seq, L, Dict0),
- share_1(Is, Dict, [], [Lbl|Seq ++ Acc]);
- {value,Label} ->
- share_1(Is, Dict0, [], [Lbl,{jump,{f,Label}}|Acc])
- end
- end;
-share_1([{func_info,_,_,_}=I|Is], _, [], Acc) ->
- Is++[I|Acc];
-share_1([I|Is], Dict, Seq, Acc) ->
- case is_unreachable_after(I) of
- false ->
- share_1(Is, Dict, [I|Seq], Acc);
- true ->
- share_1(Is, Dict, [I], Acc)
- end.
-
-%%%
-%%% (2) Move short code sequences ending in an instruction that causes an exit
-%%% to the end of the function.
-%%%
-
-move(Is) ->
- move_1(Is, [], []).
-
-move_1([I|Is], End, Acc) ->
- case is_exit_instruction(I) of
- false -> move_1(Is, End, [I|Acc]);
- true -> move_2(I, Is, End, Acc)
- end;
-move_1([], End, Acc) ->
- reverse(Acc, reverse(End)).
-
-move_2(Exit, Is, End, [{block,_},{label,_},{func_info,_,_,_}|_]=Acc) ->
- move_1(Is, End, [Exit|Acc]);
-move_2(Exit, Is, End, [{kill,_Y}|Acc]) ->
- move_2(Exit, Is, End, Acc);
-move_2(Exit, Is, End, [{block,_}=Blk,{label,_}=Lbl,Dead|More]=Acc) ->
- case is_unreachable_after(Dead) of
- false ->
- move_1(Is, End, [Exit|Acc]);
- true ->
- move_1([Dead|Is], [Exit,Blk,Lbl|End], More)
- end;
-move_2(Exit, Is, End, [{label,_}=Lbl,Dead|More]=Acc) ->
- case is_unreachable_after(Dead) of
- false ->
- move_1(Is, End, [Exit|Acc]);
- true ->
- move_1([Dead|Is], [Exit,Lbl|End], More)
- end;
-move_2(Exit, Is, End, Acc) ->
- move_1(Is, End, [Exit|Acc]).
-
-%%%
-%%% (7) Remove redundant is_boolean tests.
-%%%
-
-bopt(Is) ->
- bopt_1(Is, []).
-
-bopt_1([{test,is_boolean,_,_}=I|Is], Acc0) ->
- case opt_is_bool(I, Acc0) of
- no -> bopt_1(Is, [I|Acc0]);
- yes -> bopt_1(Is, Acc0);
- {yes,Acc} -> bopt_1(Is, Acc)
- end;
-bopt_1([I|Is], Acc) -> bopt_1(Is, [I|Acc]);
-bopt_1([], Acc) -> reverse(Acc).
-
-opt_is_bool({test,is_boolean,{f,Lbl},[Reg]}, Acc) ->
- opt_is_bool_1(Acc, Reg, Lbl).
-
-opt_is_bool_1([{test,is_eq_exact,{f,Lbl},[Reg,{atom,true}]}|_], Reg, Lbl) ->
- %% Instruction not needed in this context.
- yes;
-opt_is_bool_1([{test,is_ne_exact,{f,Lbl},[Reg,{atom,true}]}|Acc], Reg, Lbl) ->
- %% Rewrite to shorter test.
- {yes,[{test,is_eq_exact,{f,Lbl},[Reg,{atom,false}]}|Acc]};
-opt_is_bool_1([{test,_,{f,Lbl},_}=Test|Acc0], Reg, Lbl) ->
- case opt_is_bool_1(Acc0, Reg, Lbl) of
- {yes,Acc} -> {yes,[Test|Acc]};
- Other -> Other
- end;
-opt_is_bool_1(_, _, _) -> no.
-
-%%%
-%%% (3) (4) (5) (6) Jump and unreachable code optimizations.
-%%%
-
--record(st, {fc, %Label for function class errors.
- entry, %Entry label (must not be moved).
- mlbl, %Moved labels.
- labels %Set of referenced labels.
- }).
-
-opt([{label,Fc}|_]=Is, CLabel) ->
- Lbls = initial_labels(Is),
- St = #st{fc=Fc,entry=CLabel,mlbl=dict:new(),labels=Lbls},
- opt(Is, [], St).
-
-opt([{test,Test0,{f,Lnum}=Lbl,Ops}=I|Is0], Acc, St) ->
- case Is0 of
- [{jump,To}|[{label,Lnum}|Is2]=Is1] ->
- case invert_test(Test0) of
- not_possible ->
- opt(Is0, [I|Acc], label_used(Lbl, St));
- Test ->
- Is = case is_label_used(Lnum, St) of
- true -> Is1;
- false -> Is2
- end,
- opt([{test,Test,To,Ops}|Is], Acc, label_used(To, St))
- end;
- _Other ->
- opt(Is0, [I|Acc], label_used(Lbl, St))
- end;
-opt([{select_val,_R,Fail,{list,Vls}}=I|Is], Acc, St) ->
- skip_unreachable(Is, [I|Acc], label_used([Fail|Vls], St));
-opt([{select_tuple_arity,_R,Fail,{list,Vls}}=I|Is], Acc, St) ->
- skip_unreachable(Is, [I|Acc], label_used([Fail|Vls], St));
-opt([{'try',_R,Lbl}=I|Is], Acc, St) ->
- opt(Is, [I|Acc], label_used(Lbl, St));
-opt([{'catch',_R,Lbl}=I|Is], Acc, St) ->
- opt(Is, [I|Acc], label_used(Lbl, St));
-opt([{label,L}=I|Is], Acc, #st{entry=L}=St) ->
- %% NEVER move the entry label.
- opt(Is, [I|Acc], St);
-opt([{label,L1},{jump,{f,L2}}=I|Is], [Prev|Acc], St0) ->
- St = St0#st{mlbl=dict:append(L2, L1, St0#st.mlbl)},
- opt([Prev,I|Is], Acc, label_used({f,L2}, St));
-opt([{label,Lbl}=I|Is], Acc, #st{mlbl=Mlbl}=St0) ->
- case dict:find(Lbl, Mlbl) of
- {ok,Lbls} ->
- %% Essential to remove the list of labels from the dictionary,
- %% since we will rescan the inserted labels. We MUST rescan.
- St = St0#st{mlbl=dict:erase(Lbl, Mlbl)},
- insert_labels([Lbl|Lbls], Is, Acc, St);
- error -> opt(Is, [I|Acc], St0)
- end;
-opt([{jump,{f,Lbl}},{label,Lbl}=I|Is], Acc, St) ->
- opt([I|Is], Acc, St);
-opt([{jump,Lbl}=I|Is], Acc, St) ->
- skip_unreachable(Is, [I|Acc], label_used(Lbl, St));
-opt([{loop_rec,Lbl,_R}=I|Is], Acc, St) ->
- opt(Is, [I|Acc], label_used(Lbl, St));
-opt([{bif,_Name,Lbl,_As,_R}=I|Is], Acc, St) ->
- opt(Is, [I|Acc], label_used(Lbl, St));
-opt([{bs_put_integer,Lbl,_Bits,_Unit,_Fl,_Val}=I|Is], Acc, St) ->
- opt(Is, [I|Acc], label_used(Lbl, St));
-opt([{bs_put_binary,Lbl,_Bits,_Unit,_Fl,_Val}=I|Is], Acc, St) ->
- opt(Is, [I|Acc], label_used(Lbl, St));
-opt([{bs_put_float,Lbl,_Bits,_Unit,_Fl,_Val}=I|Is], Acc, St) ->
- opt(Is, [I|Acc], label_used(Lbl, St));
-opt([{bs_final,Lbl,_R}=I|Is], Acc, St) ->
- opt(Is, [I|Acc], label_used(Lbl, St));
-opt([{bs_init2,Lbl,_,_,_,_,_}=I|Is], Acc, St) ->
- opt(Is, [I|Acc], label_used(Lbl, St));
-opt([{bs_add,Lbl,_,_}=I|Is], Acc, St) ->
- opt(Is, [I|Acc], label_used(Lbl, St));
-opt([{bs_bits_to_bytes,Lbl,_,_}=I|Is], Acc, St) ->
- opt(Is, [I|Acc], label_used(Lbl, St));
-opt([I|Is], Acc, St) ->
- case is_unreachable_after(I) of
- true -> skip_unreachable(Is, [I|Acc], St);
- false -> opt(Is, [I|Acc], St)
- end;
-opt([], Acc, #st{fc=Fc,mlbl=Mlbl}) ->
- Code = reverse(Acc),
- case dict:find(Fc, Mlbl) of
- {ok,Lbls} -> insert_fc_labels(Lbls, Mlbl, Code);
- error -> Code
- end.
-
-insert_fc_labels([L|Ls], Mlbl, Acc0) ->
- Acc = [{label,L}|Acc0],
- case dict:find(L, Mlbl) of
- error ->
- insert_fc_labels(Ls, Mlbl, Acc);
- {ok,Lbls} ->
- insert_fc_labels(Lbls++Ls, Mlbl, Acc)
- end;
-insert_fc_labels([], _, Acc) -> Acc.
-
-%% invert_test(Test0) -> not_possible | Test
-
-invert_test(is_ge) -> is_lt;
-invert_test(is_lt) -> is_ge;
-invert_test(is_eq) -> is_ne;
-invert_test(is_ne) -> is_eq;
-invert_test(is_eq_exact) -> is_ne_exact;
-invert_test(is_ne_exact) -> is_eq_exact;
-invert_test(_) -> not_possible.
-
-insert_labels([L|Ls], Is, [{jump,{f,L}}|Acc], St) ->
- insert_labels(Ls, [{label,L}|Is], Acc, St);
-insert_labels([L|Ls], Is, Acc, St) ->
- insert_labels(Ls, [{label,L}|Is], Acc, St);
-insert_labels([], Is, Acc, St) ->
- opt(Is, Acc, St).
-
-%% Skip unreachable code up to the next referenced label.
-
-skip_unreachable([{label,L}|Is], [{jump,{f,L}}|Acc], St) ->
- opt([{label,L}|Is], Acc, St);
-skip_unreachable([{label,L}|Is], Acc, St) ->
- case is_label_used(L, St) of
- true -> opt([{label,L}|Is], Acc, St);
- false -> skip_unreachable(Is, Acc, St)
- end;
-skip_unreachable([_|Is], Acc, St) ->
- skip_unreachable(Is, Acc, St);
-skip_unreachable([], Acc, St) ->
- opt([], Acc, St).
-
-%% Add one or more label to the set of used labels.
-
-label_used({f,0}, St) -> St;
-label_used({f,L}, St) -> St#st{labels=gb_sets:add(L, St#st.labels)};
-label_used([H|T], St0) -> label_used(T, label_used(H, St0));
-label_used([], St) -> St;
-label_used(_Other, St) -> St.
-
-%% Test if label is used.
-
-is_label_used(L, St) ->
- gb_sets:is_member(L, St#st.labels).
-
-%% is_unreachable_after(Instruction) -> true|false
-%% Test whether the code after Instruction is unreachable.
-
-is_unreachable_after({func_info,_M,_F,_A}) -> true;
-is_unreachable_after(return) -> true;
-is_unreachable_after({call_ext_last,_Ar,_ExtFunc,_D}) -> true;
-is_unreachable_after({call_ext_only,_Ar,_ExtFunc}) -> true;
-is_unreachable_after({call_last,_Ar,_Lbl,_D}) -> true;
-is_unreachable_after({call_only,_Ar,_Lbl}) -> true;
-is_unreachable_after({apply_last,_Ar,_N}) -> true;
-is_unreachable_after({jump,_Lbl}) -> true;
-is_unreachable_after({select_val,_R,_Lbl,_Cases}) -> true;
-is_unreachable_after({select_tuple_arity,_R,_Lbl,_Cases}) -> true;
-is_unreachable_after({loop_rec_end,_}) -> true;
-is_unreachable_after({wait,_}) -> true;
-is_unreachable_after(I) -> is_exit_instruction(I).
-
-%% is_exit_instruction(Instruction) -> true|false
-%% Test whether the instruction Instruction always
-%% causes an exit/failure.
-
-is_exit_instruction({call_ext,_,{extfunc,M,F,A}}) ->
- is_exit_instruction_1(M, F, A);
-is_exit_instruction({call_ext_last,_,{extfunc,M,F,A},_}) ->
- is_exit_instruction_1(M, F, A);
-is_exit_instruction({call_ext_only,_,{extfunc,M,F,A}}) ->
- is_exit_instruction_1(M, F, A);
-is_exit_instruction(if_end) -> true;
-is_exit_instruction({case_end,_}) -> true;
-is_exit_instruction({try_case_end,_}) -> true;
-is_exit_instruction({badmatch,_}) -> true;
-is_exit_instruction(_) -> false.
-
-is_exit_instruction_1(erlang, exit, 1) -> true;
-is_exit_instruction_1(erlang, throw, 1) -> true;
-is_exit_instruction_1(erlang, error, 1) -> true;
-is_exit_instruction_1(erlang, error, 2) -> true;
-is_exit_instruction_1(erlang, fault, 1) -> true;
-is_exit_instruction_1(erlang, fault, 2) -> true;
-is_exit_instruction_1(_, _, _) -> false.
-
-%% remove_unused_labels(Instructions0) -> Instructions
-%% Remove all unused labels.
-
-remove_unused_labels(Is) ->
- Used0 = initial_labels(Is),
- Used = foldl(fun ulbl/2, Used0, Is),
- rem_unused(Is, Used, []).
-
-rem_unused([{label,Lbl}=I|Is], Used, Acc) ->
- case gb_sets:is_member(Lbl, Used) of
- false -> rem_unused(Is, Used, Acc);
- true -> rem_unused(Is, Used, [I|Acc])
- end;
-rem_unused([I|Is], Used, Acc) ->
- rem_unused(Is, Used, [I|Acc]);
-rem_unused([], _, Acc) -> reverse(Acc).
-
-initial_labels(Is) ->
- initial_labels(Is, []).
-
-initial_labels([{label,Lbl}|Is], Acc) ->
- initial_labels(Is, [Lbl|Acc]);
-initial_labels([{func_info,_,_,_},{label,Lbl}|_], Acc) ->
- gb_sets:from_list([Lbl|Acc]).
-
-ulbl({test,_,Fail,_}, Used) ->
- mark_used(Fail, Used);
-ulbl({select_val,_,Fail,{list,Vls}}, Used) ->
- mark_used_list(Vls, mark_used(Fail, Used));
-ulbl({select_tuple_arity,_,Fail,{list,Vls}}, Used) ->
- mark_used_list(Vls, mark_used(Fail, Used));
-ulbl({'try',_,Lbl}, Used) ->
- mark_used(Lbl, Used);
-ulbl({'catch',_,Lbl}, Used) ->
- mark_used(Lbl, Used);
-ulbl({jump,Lbl}, Used) ->
- mark_used(Lbl, Used);
-ulbl({loop_rec,Lbl,_}, Used) ->
- mark_used(Lbl, Used);
-ulbl({loop_rec_end,Lbl}, Used) ->
- mark_used(Lbl, Used);
-ulbl({wait,Lbl}, Used) ->
- mark_used(Lbl, Used);
-ulbl({wait_timeout,Lbl,_To}, Used) ->
- mark_used(Lbl, Used);
-ulbl({bif,_Name,Lbl,_As,_R}, Used) ->
- mark_used(Lbl, Used);
-ulbl({bs_init2,Lbl,_,_,_,_,_}, Used) ->
- mark_used(Lbl, Used);
-ulbl({bs_put_integer,Lbl,_Bits,_Unit,_Fl,_Val}, Used) ->
- mark_used(Lbl, Used);
-ulbl({bs_put_float,Lbl,_Bits,_Unit,_Fl,_Val}, Used) ->
- mark_used(Lbl, Used);
-ulbl({bs_put_binary,Lbl,_Bits,_Unit,_Fl,_Val}, Used) ->
- mark_used(Lbl, Used);
-ulbl({bs_final,Lbl,_}, Used) ->
- mark_used(Lbl, Used);
-ulbl({bs_add,Lbl,_,_}, Used) ->
- mark_used(Lbl, Used);
-ulbl({bs_bits_to_bytes,Lbl,_,_}, Used) ->
- mark_used(Lbl, Used);
-ulbl(_, Used) -> Used.
-
-mark_used({f,0}, Used) -> Used;
-mark_used({f,L}, Used) -> gb_sets:add(L, Used);
-mark_used(_, Used) -> Used.
-
-mark_used_list([H|T], Used) ->
- mark_used_list(T, mark_used(H, Used));
-mark_used_list([], Used) -> Used.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_listing.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_listing.erl
deleted file mode 100644
index 006b8c551a..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_listing.erl
+++ /dev/null
@@ -1,117 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: beam_listing.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
-%%
--module(beam_listing).
-
--export([module/2]).
-
--include("v3_life.hrl").
-
--import(lists, [foreach/2]).
-
-module(File, Core) when element(1, Core) == c_module ->
- %% This is a core module.
- io:put_chars(File, core_pp:format(Core));
-module(File, Kern) when element(1, Kern) == k_mdef ->
- %% This is a kernel module.
- io:put_chars(File, v3_kernel_pp:format(Kern));
- %%io:put_chars(File, io_lib:format("~p~n", [Kern]));
-module(File, {Mod,Exp,Attr,Kern}) ->
- %% This is output from beam_life (v3).
- io:fwrite(File, "~w.~n~p.~n~p.~n", [Mod,Exp,Attr]),
- foreach(fun (F) -> function(File, F) end, Kern);
-module(Stream, {Mod,Exp,Attr,Code,NumLabels}) ->
- %% This is output from beam_codegen.
- io:format(Stream, "{module, ~s}. %% version = ~w\n",
- [Mod, beam_opcodes:format_number()]),
- io:format(Stream, "\n{exports, ~p}.\n", [Exp]),
- io:format(Stream, "\n{attributes, ~p}.\n", [Attr]),
- io:format(Stream, "\n{labels, ~p}.\n", [NumLabels]),
- foreach(
- fun ({function,Name,Arity,Entry,Asm}) ->
- io:format(Stream, "\n\n{function, ~w, ~w, ~w}.\n",
- [Name, Arity, Entry]),
- foreach(fun(Op) -> print_op(Stream, Op) end, Asm) end,
- Code);
-module(Stream, {Mod,Exp,Inter}) ->
- %% Other kinds of intermediate formats.
- io:fwrite(Stream, "~w.~n~p.~n", [Mod,Exp]),
- foreach(fun (F) -> io:format(Stream, "~p.\n", [F]) end, Inter);
-module(Stream, [_|_]=Fs) ->
- %% Form-based abstract format.
- foreach(fun (F) -> io:format(Stream, "~p.\n", [F]) end, Fs).
-
-print_op(Stream, Label) when element(1, Label) == label ->
- io:format(Stream, " ~p.\n", [Label]);
-print_op(Stream, Op) ->
- io:format(Stream, " ~p.\n", [Op]).
-
-function(File, {function,Name,Arity,Args,Body,Vdb}) ->
- io:nl(File),
- io:format(File, "function ~p/~p.\n", [Name,Arity]),
- io:format(File, " ~p.\n", [Args]),
- print_vdb(File, Vdb),
- put(beam_listing_nl, true),
- foreach(fun(F) -> format(File, F, []) end, Body),
- nl(File),
- erase(beam_listing_nl).
-
-format(File, #l{ke=Ke,i=I,vdb=Vdb}, Ind) ->
- nl(File),
- ind_format(File, Ind, "~p ", [I]),
- print_vdb(File, Vdb),
- nl(File),
- format(File, Ke, Ind);
-format(File, Tuple, Ind) when is_tuple(Tuple) ->
- ind_format(File, Ind, "{", []),
- format_list(File, tuple_to_list(Tuple), [$\s|Ind]),
- ind_format(File, Ind, "}", []);
-format(File, List, Ind) when is_list(List) ->
- ind_format(File, Ind, "[", []),
- format_list(File, List, [$\s|Ind]),
- ind_format(File, Ind, "]", []);
-format(File, F, Ind) ->
- ind_format(File, Ind, "~p", [F]).
-
-format_list(File, [F], Ind) ->
- format(File, F, Ind);
-format_list(File, [F|Fs], Ind) ->
- format(File, F, Ind),
- ind_format(File, Ind, ",", []),
- format_list(File, Fs, Ind);
-format_list(_, [], _) -> ok.
-
-
-print_vdb(File, [{Var,F,E}|Vs]) ->
- io:format(File, "~p:~p..~p ", [Var,F,E]),
- print_vdb(File, Vs);
-print_vdb(_, []) -> ok.
-
-ind_format(File, Ind, Format, Args) ->
- case get(beam_listing_nl) of
- true ->
- put(beam_listing_nl, false),
- io:put_chars(File, Ind);
- false -> ok
- end,
- io:format(File, Format, Args).
-
-nl(File) ->
- case put(beam_listing_nl, true) of
- true -> ok;
- false -> io:nl(File)
- end.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_opcodes.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_opcodes.erl
deleted file mode 100644
index a4f5fd34d2..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_opcodes.erl
+++ /dev/null
@@ -1,240 +0,0 @@
--module(beam_opcodes).
-%% Warning: Do not edit this file. It was automatically
-%% generated by 'beam_makeops' on Wed Nov 24 17:52:43 2004.
-
--export([format_number/0]).
--export([opcode/2,opname/1]).
-
-format_number() -> 0.
-
-opcode(label, 1) -> 1;
-opcode(func_info, 3) -> 2;
-opcode(int_code_end, 0) -> 3;
-opcode(call, 2) -> 4;
-opcode(call_last, 3) -> 5;
-opcode(call_only, 2) -> 6;
-opcode(call_ext, 2) -> 7;
-opcode(call_ext_last, 3) -> 8;
-opcode(bif0, 2) -> 9;
-opcode(bif1, 4) -> 10;
-opcode(bif2, 5) -> 11;
-opcode(allocate, 2) -> 12;
-opcode(allocate_heap, 3) -> 13;
-opcode(allocate_zero, 2) -> 14;
-opcode(allocate_heap_zero, 3) -> 15;
-opcode(test_heap, 2) -> 16;
-opcode(init, 1) -> 17;
-opcode(deallocate, 1) -> 18;
-opcode(return, 0) -> 19;
-opcode(send, 0) -> 20;
-opcode(remove_message, 0) -> 21;
-opcode(timeout, 0) -> 22;
-opcode(loop_rec, 2) -> 23;
-opcode(loop_rec_end, 1) -> 24;
-opcode(wait, 1) -> 25;
-opcode(wait_timeout, 2) -> 26;
-opcode(m_plus, 4) -> 27;
-opcode(m_minus, 4) -> 28;
-opcode(m_times, 4) -> 29;
-opcode(m_div, 4) -> 30;
-opcode(int_div, 4) -> 31;
-opcode(int_rem, 4) -> 32;
-opcode(int_band, 4) -> 33;
-opcode(int_bor, 4) -> 34;
-opcode(int_bxor, 4) -> 35;
-opcode(int_bsl, 4) -> 36;
-opcode(int_bsr, 4) -> 37;
-opcode(int_bnot, 3) -> 38;
-opcode(is_lt, 3) -> 39;
-opcode(is_ge, 3) -> 40;
-opcode(is_eq, 3) -> 41;
-opcode(is_ne, 3) -> 42;
-opcode(is_eq_exact, 3) -> 43;
-opcode(is_ne_exact, 3) -> 44;
-opcode(is_integer, 2) -> 45;
-opcode(is_float, 2) -> 46;
-opcode(is_number, 2) -> 47;
-opcode(is_atom, 2) -> 48;
-opcode(is_pid, 2) -> 49;
-opcode(is_reference, 2) -> 50;
-opcode(is_port, 2) -> 51;
-opcode(is_nil, 2) -> 52;
-opcode(is_binary, 2) -> 53;
-opcode(is_constant, 2) -> 54;
-opcode(is_list, 2) -> 55;
-opcode(is_nonempty_list, 2) -> 56;
-opcode(is_tuple, 2) -> 57;
-opcode(test_arity, 3) -> 58;
-opcode(select_val, 3) -> 59;
-opcode(select_tuple_arity, 3) -> 60;
-opcode(jump, 1) -> 61;
-opcode('catch', 2) -> 62;
-opcode(catch_end, 1) -> 63;
-opcode(move, 2) -> 64;
-opcode(get_list, 3) -> 65;
-opcode(get_tuple_element, 3) -> 66;
-opcode(set_tuple_element, 3) -> 67;
-opcode(put_string, 3) -> 68;
-opcode(put_list, 3) -> 69;
-opcode(put_tuple, 2) -> 70;
-opcode(put, 1) -> 71;
-opcode(badmatch, 1) -> 72;
-opcode(if_end, 0) -> 73;
-opcode(case_end, 1) -> 74;
-opcode(call_fun, 1) -> 75;
-opcode(make_fun, 3) -> 76;
-opcode(is_function, 2) -> 77;
-opcode(call_ext_only, 2) -> 78;
-opcode(bs_start_match, 2) -> 79;
-opcode(bs_get_integer, 5) -> 80;
-opcode(bs_get_float, 5) -> 81;
-opcode(bs_get_binary, 5) -> 82;
-opcode(bs_skip_bits, 4) -> 83;
-opcode(bs_test_tail, 2) -> 84;
-opcode(bs_save, 1) -> 85;
-opcode(bs_restore, 1) -> 86;
-opcode(bs_init, 2) -> 87;
-opcode(bs_final, 2) -> 88;
-opcode(bs_put_integer, 5) -> 89;
-opcode(bs_put_binary, 5) -> 90;
-opcode(bs_put_float, 5) -> 91;
-opcode(bs_put_string, 2) -> 92;
-opcode(bs_need_buf, 1) -> 93;
-opcode(fclearerror, 0) -> 94;
-opcode(fcheckerror, 1) -> 95;
-opcode(fmove, 2) -> 96;
-opcode(fconv, 2) -> 97;
-opcode(fadd, 4) -> 98;
-opcode(fsub, 4) -> 99;
-opcode(fmul, 4) -> 100;
-opcode(fdiv, 4) -> 101;
-opcode(fnegate, 3) -> 102;
-opcode(make_fun2, 1) -> 103;
-opcode('try', 2) -> 104;
-opcode(try_end, 1) -> 105;
-opcode(try_case, 1) -> 106;
-opcode(try_case_end, 1) -> 107;
-opcode(raise, 2) -> 108;
-opcode(bs_init2, 6) -> 109;
-opcode(bs_bits_to_bytes, 3) -> 110;
-opcode(bs_add, 5) -> 111;
-opcode(apply, 1) -> 112;
-opcode(apply_last, 2) -> 113;
-opcode(is_boolean, 2) -> 114;
-opcode(Name, Arity) -> erlang:error(badarg, [Name,Arity]).
-
-opname(1) -> {label,1};
-opname(2) -> {func_info,3};
-opname(3) -> {int_code_end,0};
-opname(4) -> {call,2};
-opname(5) -> {call_last,3};
-opname(6) -> {call_only,2};
-opname(7) -> {call_ext,2};
-opname(8) -> {call_ext_last,3};
-opname(9) -> {bif0,2};
-opname(10) -> {bif1,4};
-opname(11) -> {bif2,5};
-opname(12) -> {allocate,2};
-opname(13) -> {allocate_heap,3};
-opname(14) -> {allocate_zero,2};
-opname(15) -> {allocate_heap_zero,3};
-opname(16) -> {test_heap,2};
-opname(17) -> {init,1};
-opname(18) -> {deallocate,1};
-opname(19) -> {return,0};
-opname(20) -> {send,0};
-opname(21) -> {remove_message,0};
-opname(22) -> {timeout,0};
-opname(23) -> {loop_rec,2};
-opname(24) -> {loop_rec_end,1};
-opname(25) -> {wait,1};
-opname(26) -> {wait_timeout,2};
-opname(27) -> {m_plus,4};
-opname(28) -> {m_minus,4};
-opname(29) -> {m_times,4};
-opname(30) -> {m_div,4};
-opname(31) -> {int_div,4};
-opname(32) -> {int_rem,4};
-opname(33) -> {int_band,4};
-opname(34) -> {int_bor,4};
-opname(35) -> {int_bxor,4};
-opname(36) -> {int_bsl,4};
-opname(37) -> {int_bsr,4};
-opname(38) -> {int_bnot,3};
-opname(39) -> {is_lt,3};
-opname(40) -> {is_ge,3};
-opname(41) -> {is_eq,3};
-opname(42) -> {is_ne,3};
-opname(43) -> {is_eq_exact,3};
-opname(44) -> {is_ne_exact,3};
-opname(45) -> {is_integer,2};
-opname(46) -> {is_float,2};
-opname(47) -> {is_number,2};
-opname(48) -> {is_atom,2};
-opname(49) -> {is_pid,2};
-opname(50) -> {is_reference,2};
-opname(51) -> {is_port,2};
-opname(52) -> {is_nil,2};
-opname(53) -> {is_binary,2};
-opname(54) -> {is_constant,2};
-opname(55) -> {is_list,2};
-opname(56) -> {is_nonempty_list,2};
-opname(57) -> {is_tuple,2};
-opname(58) -> {test_arity,3};
-opname(59) -> {select_val,3};
-opname(60) -> {select_tuple_arity,3};
-opname(61) -> {jump,1};
-opname(62) -> {'catch',2};
-opname(63) -> {catch_end,1};
-opname(64) -> {move,2};
-opname(65) -> {get_list,3};
-opname(66) -> {get_tuple_element,3};
-opname(67) -> {set_tuple_element,3};
-opname(68) -> {put_string,3};
-opname(69) -> {put_list,3};
-opname(70) -> {put_tuple,2};
-opname(71) -> {put,1};
-opname(72) -> {badmatch,1};
-opname(73) -> {if_end,0};
-opname(74) -> {case_end,1};
-opname(75) -> {call_fun,1};
-opname(76) -> {make_fun,3};
-opname(77) -> {is_function,2};
-opname(78) -> {call_ext_only,2};
-opname(79) -> {bs_start_match,2};
-opname(80) -> {bs_get_integer,5};
-opname(81) -> {bs_get_float,5};
-opname(82) -> {bs_get_binary,5};
-opname(83) -> {bs_skip_bits,4};
-opname(84) -> {bs_test_tail,2};
-opname(85) -> {bs_save,1};
-opname(86) -> {bs_restore,1};
-opname(87) -> {bs_init,2};
-opname(88) -> {bs_final,2};
-opname(89) -> {bs_put_integer,5};
-opname(90) -> {bs_put_binary,5};
-opname(91) -> {bs_put_float,5};
-opname(92) -> {bs_put_string,2};
-opname(93) -> {bs_need_buf,1};
-opname(94) -> {fclearerror,0};
-opname(95) -> {fcheckerror,1};
-opname(96) -> {fmove,2};
-opname(97) -> {fconv,2};
-opname(98) -> {fadd,4};
-opname(99) -> {fsub,4};
-opname(100) -> {fmul,4};
-opname(101) -> {fdiv,4};
-opname(102) -> {fnegate,3};
-opname(103) -> {make_fun2,1};
-opname(104) -> {'try',2};
-opname(105) -> {try_end,1};
-opname(106) -> {try_case,1};
-opname(107) -> {try_case_end,1};
-opname(108) -> {raise,2};
-opname(109) -> {bs_init2,6};
-opname(110) -> {bs_bits_to_bytes,3};
-opname(111) -> {bs_add,5};
-opname(112) -> {apply,1};
-opname(113) -> {apply_last,2};
-opname(114) -> {is_boolean,2};
-opname(Number) -> erlang:error(badarg, [Number]).
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_opcodes.hrl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_opcodes.hrl
deleted file mode 100644
index 1ad0887314..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_opcodes.hrl
+++ /dev/null
@@ -1,12 +0,0 @@
-%% Warning: Do not edit this file. It was automatically
-%% generated by 'beam_makeops' on Wed Nov 24 17:52:43 2004.
-
--define(tag_u, 0).
--define(tag_i, 1).
--define(tag_a, 2).
--define(tag_x, 3).
--define(tag_y, 4).
--define(tag_f, 5).
--define(tag_h, 6).
--define(tag_z, 7).
-
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_type.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_type.erl
deleted file mode 100644
index 7d288b249c..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_type.erl
+++ /dev/null
@@ -1,551 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: beam_type.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
-%%
-%% Purpose : Type-based optimisations.
-
--module(beam_type).
-
--export([module/2]).
-
--import(lists, [map/2,foldl/3,reverse/1,reverse/2,filter/2,member/2]).
-
-module({Mod,Exp,Attr,Fs0,Lc}, Opt) ->
- AllowFloatOpts = not member(no_float_opt, Opt),
- Fs = map(fun(F) -> function(F, AllowFloatOpts) end, Fs0),
- {ok,{Mod,Exp,Attr,Fs,Lc}}.
-
-function({function,Name,Arity,CLabel,Asm0}, AllowFloatOpts) ->
- Asm = opt(Asm0, AllowFloatOpts, [], tdb_new()),
- {function,Name,Arity,CLabel,Asm}.
-
-%% opt([Instruction], AllowFloatOpts, Accumulator, TypeDb) -> {[Instruction'],TypeDb'}
-%% Keep track of type information; try to simplify.
-
-opt([{block,Body1}|Is], AllowFloatOpts, [{block,Body0}|Acc], Ts0) ->
- {Body2,Ts} = simplify(Body1, Ts0, AllowFloatOpts),
- Body = beam_block:merge_blocks(Body0, Body2),
- opt(Is, AllowFloatOpts, [{block,Body}|Acc], Ts);
-opt([{block,Body0}|Is], AllowFloatOpts, Acc, Ts0) ->
- {Body,Ts} = simplify(Body0, Ts0, AllowFloatOpts),
- opt(Is, AllowFloatOpts, [{block,Body}|Acc], Ts);
-opt([I0|Is], AllowFloatOpts, Acc, Ts0) ->
- case simplify([I0], Ts0, AllowFloatOpts) of
- {[],Ts} -> opt(Is, AllowFloatOpts, Acc, Ts);
- {[I],Ts} -> opt(Is, AllowFloatOpts, [I|Acc], Ts)
- end;
-opt([], _, Acc, _) -> reverse(Acc).
-
-%% simplify(Instruction, TypeDb, AllowFloatOpts) -> NewInstruction
-%% Simplify an instruction using type information (this is
-%% technically a "strength reduction").
-
-simplify(Is, TypeDb, false) ->
- simplify(Is, TypeDb, no_float_opt, []);
-simplify(Is, TypeDb, true) ->
- case are_live_regs_determinable(Is) of
- false -> simplify(Is, TypeDb, no_float_opt, []);
- true -> simplify(Is, TypeDb, [], [])
- end.
-
-simplify([{set,[D],[{integer,Index},Reg],{bif,element,_}}=I0|Is]=Is0, Ts0, Rs0, Acc0) ->
- I = case max_tuple_size(Reg, Ts0) of
- Sz when 0 < Index, Index =< Sz ->
- {set,[D],[Reg],{get_tuple_element,Index-1}};
- _Other -> I0
- end,
- Ts = update(I, Ts0),
- {Rs,Acc} = flush(Rs0, Is0, Acc0),
- simplify(Is, Ts, Rs, [I|checkerror(Acc)]);
-simplify([{set,[D0],[A],{bif,'-',{f,0}}}=I|Is]=Is0, Ts0, Rs0, Acc0)
- when Rs0 =/= no_float_opt ->
- case tdb_find(A, Ts0) of
- float ->
- {Rs1,Acc1} = load_reg(A, Ts0, Rs0, Acc0),
- {D,Rs} = find_dest(D0, Rs1),
- Areg = fetch_reg(A, Rs),
- Acc = [{set,[D],[Areg],{bif,fnegate,{f,0}}}|clearerror(Acc1)],
- Ts = tdb_update([{D0,float}], Ts0),
- simplify(Is, Ts, Rs, Acc);
- _Other ->
- Ts = update(I, Ts0),
- {Rs,Acc} = flush(Rs0, Is0, Acc0),
- simplify(Is, Ts, Rs, [I|checkerror(Acc)])
- end;
-simplify([{set,[_],[_],{bif,_,{f,0}}}=I|Is]=Is0, Ts0, Rs0, Acc0) ->
- Ts = update(I, Ts0),
- {Rs,Acc} = flush(Rs0, Is0, Acc0),
- simplify(Is, Ts, Rs, [I|checkerror(Acc)]);
-simplify([{set,[D0],[A,B],{bif,Op0,{f,0}}}=I|Is]=Is0, Ts0, Rs0, Acc0)
- when Rs0 =/= no_float_opt ->
- case float_op(Op0, A, B, Ts0) of
- no ->
- Ts = update(I, Ts0),
- {Rs,Acc} = flush(Rs0, Is0, Acc0),
- simplify(Is, Ts, Rs, [I|checkerror(Acc)]);
- {yes,Op} ->
- {Rs1,Acc1} = load_reg(A, Ts0, Rs0, Acc0),
- {Rs2,Acc2} = load_reg(B, Ts0, Rs1, Acc1),
- {D,Rs} = find_dest(D0, Rs2),
- Areg = fetch_reg(A, Rs),
- Breg = fetch_reg(B, Rs),
- Acc = [{set,[D],[Areg,Breg],{bif,Op,{f,0}}}|clearerror(Acc2)],
- Ts = tdb_update([{D0,float}], Ts0),
- simplify(Is, Ts, Rs, Acc)
- end;
-simplify([{set,[D],[TupleReg],{get_tuple_element,0}}=I|Is0], Ts0, Rs0, Acc0) ->
- case tdb_find(TupleReg, Ts0) of
- {tuple,_,[Contents]} ->
- Ts = tdb_update([{D,Contents}], Ts0),
- {Rs,Acc} = flush(Rs0, Is0, Acc0),
- simplify(Is0, Ts, Rs, [{set,[D],[Contents],move}|Acc]);
- _ ->
- Ts = update(I, Ts0),
- {Rs,Acc} = flush(Rs0, Is0, Acc0),
- simplify(Is0, Ts, Rs, [I|checkerror(Acc)])
- end;
-simplify([{set,_,_,{'catch',_}}=I|Is]=Is0, _Ts, Rs0, Acc0) ->
- Acc = flush_all(Rs0, Is0, Acc0),
- simplify(Is, tdb_new(), Rs0, [I|Acc]);
-simplify([{test,is_tuple,_,[R]}=I|Is], Ts, Rs, Acc) ->
- case tdb_find(R, Ts) of
- {tuple,_,_} -> simplify(Is, Ts, Rs, Acc);
- _ ->
- simplify(Is, Ts, Rs, [I|Acc])
- end;
-simplify([{test,test_arity,_,[R,Arity]}=I|Is], Ts0, Rs, Acc) ->
- case tdb_find(R, Ts0) of
- {tuple,Arity,_} ->
- simplify(Is, Ts0, Rs, Acc);
- _Other ->
- Ts = update(I, Ts0),
- simplify(Is, Ts, Rs, [I|Acc])
- end;
-simplify([{test,is_eq_exact,Fail,[R,{atom,_}=Atom]}=I|Is0], Ts0, Rs0, Acc0) ->
- Acc1 = case tdb_find(R, Ts0) of
- {atom,_}=Atom -> Acc0;
- {atom,_} -> [{jump,Fail}|Acc0];
- _ -> [I|Acc0]
- end,
- Ts = update(I, Ts0),
- {Rs,Acc} = flush(Rs0, Is0, Acc1),
- simplify(Is0, Ts, Rs, Acc);
-simplify([I|Is]=Is0, Ts0, Rs0, Acc0) ->
- Ts = update(I, Ts0),
- {Rs,Acc} = flush(Rs0, Is0, Acc0),
- simplify(Is, Ts, Rs, [I|Acc]);
-simplify([], Ts, Rs, Acc) ->
- Is0 = reverse(flush_all(Rs, [], Acc)),
- Is1 = opt_fmoves(Is0, []),
- Is = add_ftest_heap(Is1),
- {Is,Ts}.
-
-opt_fmoves([{set,[{x,_}=R],[{fr,_}]=Src,fmove}=I1,
- {set,[{y,_}]=Dst,[{x,_}=R],move}=I2|Is], Acc) ->
- case beam_block:is_killed(R, Is) of
- false -> opt_fmoves(Is, [I2,I1|Acc]);
- true -> opt_fmoves(Is, [{set,Dst,Src,fmove}|Acc])
- end;
-opt_fmoves([I|Is], Acc) ->
- opt_fmoves(Is, [I|Acc]);
-opt_fmoves([], Acc) -> reverse(Acc).
-
-clearerror(Is) ->
- clearerror(Is, Is).
-
-clearerror([{set,[],[],fclearerror}|_], OrigIs) -> OrigIs;
-clearerror([{set,[],[],fcheckerror}|_], OrigIs) -> [{set,[],[],fclearerror}|OrigIs];
-clearerror([_|Is], OrigIs) -> clearerror(Is, OrigIs);
-clearerror([], OrigIs) -> [{set,[],[],fclearerror}|OrigIs].
-
-%% update(Instruction, TypeDb) -> NewTypeDb
-%% Update the type database to account for executing an instruction.
-%%
-%% First the cases for instructions inside basic blocks.
-update({set,[D],[S],move}, Ts0) ->
- Ops = case tdb_find(S, Ts0) of
- error -> [{D,kill}];
- Info -> [{D,Info}]
- end,
- tdb_update(Ops, Ts0);
-update({set,[D],[{integer,I},Reg],{bif,element,_}}, Ts0) ->
- tdb_update([{Reg,{tuple,I,[]}},{D,kill}], Ts0);
-update({set,[D],[_Index,Reg],{bif,element,_}}, Ts0) ->
- tdb_update([{Reg,{tuple,0,[]}},{D,kill}], Ts0);
-update({set,[D],[S],{get_tuple_element,0}}, Ts) ->
- tdb_update([{D,{tuple_element,S,0}}], Ts);
-update({set,[D],[S],{bif,float,{f,0}}}, Ts0) ->
- %% Make sure we reject non-numeric literal argument.
- case possibly_numeric(S) of
- true -> tdb_update([{D,float}], Ts0);
- false -> Ts0
- end;
-update({set,[D],[S1,S2],{bif,'/',{f,0}}}, Ts0) ->
- %% Make sure we reject non-numeric literals.
- case possibly_numeric(S1) andalso possibly_numeric(S2) of
- true -> tdb_update([{D,float}], Ts0);
- false -> Ts0
- end;
-update({set,[D],[S1,S2],{bif,Op,{f,0}}}, Ts0) ->
- case arith_op(Op) of
- no ->
- tdb_update([{D,kill}], Ts0);
- {yes,_} ->
- case {tdb_find(S1, Ts0),tdb_find(S2, Ts0)} of
- {float,_} -> tdb_update([{D,float}], Ts0);
- {_,float} -> tdb_update([{D,float}], Ts0);
- {_,_} -> tdb_update([{D,kill}], Ts0)
- end
- end;
-update({set,[],_Src,_Op}, Ts0) -> Ts0;
-update({set,[D],_Src,_Op}, Ts0) ->
- tdb_update([{D,kill}], Ts0);
-update({set,[D1,D2],_Src,_Op}, Ts0) ->
- tdb_update([{D1,kill},{D2,kill}], Ts0);
-update({allocate,_,_}, Ts) -> Ts;
-update({init,D}, Ts) ->
- tdb_update([{D,kill}], Ts);
-update({kill,D}, Ts) ->
- tdb_update([{D,kill}], Ts);
-update({'%live',_}, Ts) -> Ts;
-
-%% Instructions outside of blocks.
-update({test,is_float,_Fail,[Src]}, Ts0) ->
- tdb_update([{Src,float}], Ts0);
-update({test,test_arity,_Fail,[Src,Arity]}, Ts0) ->
- tdb_update([{Src,{tuple,Arity,[]}}], Ts0);
-update({test,is_eq_exact,_,[Reg,{atom,_}=Atom]}, Ts) ->
- case tdb_find(Reg, Ts) of
- error ->
- Ts;
- {tuple_element,TupleReg,0} ->
- tdb_update([{TupleReg,{tuple,1,[Atom]}}], Ts);
- _ ->
- Ts
- end;
-update({test,_Test,_Fail,_Other}, Ts) -> Ts;
-update({call_ext,1,{extfunc,math,Math,1}}, Ts) ->
- case is_math_bif(Math, 1) of
- true -> tdb_update([{{x,0},float}], Ts);
- false -> tdb_kill_xregs(Ts)
- end;
-update({call_ext,2,{extfunc,math,Math,2}}, Ts) ->
- case is_math_bif(Math, 2) of
- true -> tdb_update([{{x,0},float}], Ts);
- false -> tdb_kill_xregs(Ts)
- end;
-update({call_ext,3,{extfunc,erlang,setelement,3}}, Ts0) ->
- Op = case tdb_find({x,1}, Ts0) of
- error -> kill;
- Info -> Info
- end,
- Ts1 = tdb_kill_xregs(Ts0),
- tdb_update([{{x,0},Op}], Ts1);
-update({call,_Arity,_Func}, Ts) -> tdb_kill_xregs(Ts);
-update({call_ext,_Arity,_Func}, Ts) -> tdb_kill_xregs(Ts);
-update({make_fun2,_,_,_,_}, Ts) -> tdb_kill_xregs(Ts);
-
-%% The instruction is unknown. Kill all information.
-update(_I, _Ts) -> tdb_new().
-
-is_math_bif(cos, 1) -> true;
-is_math_bif(cosh, 1) -> true;
-is_math_bif(sin, 1) -> true;
-is_math_bif(sinh, 1) -> true;
-is_math_bif(tan, 1) -> true;
-is_math_bif(tanh, 1) -> true;
-is_math_bif(acos, 1) -> true;
-is_math_bif(acosh, 1) -> true;
-is_math_bif(asin, 1) -> true;
-is_math_bif(asinh, 1) -> true;
-is_math_bif(atan, 1) -> true;
-is_math_bif(atanh, 1) -> true;
-is_math_bif(erf, 1) -> true;
-is_math_bif(erfc, 1) -> true;
-is_math_bif(exp, 1) -> true;
-is_math_bif(log, 1) -> true;
-is_math_bif(log10, 1) -> true;
-is_math_bif(sqrt, 1) -> true;
-is_math_bif(atan2, 2) -> true;
-is_math_bif(pow, 2) -> true;
-is_math_bif(pi, 0) -> true;
-is_math_bif(_, _) -> false.
-
-%% Reject non-numeric literals.
-possibly_numeric({x,_}) -> true;
-possibly_numeric({y,_}) -> true;
-possibly_numeric({integer,_}) -> true;
-possibly_numeric({float,_}) -> true;
-possibly_numeric(_) -> false.
-
-max_tuple_size(Reg, Ts) ->
- case tdb_find(Reg, Ts) of
- {tuple,Sz,_} -> Sz;
- _Other -> 0
- end.
-
-float_op('/', A, B, _) ->
- case possibly_numeric(A) andalso possibly_numeric(B) of
- true -> {yes,fdiv};
- false -> no
- end;
-float_op(Op, {float,_}, B, _) ->
- case possibly_numeric(B) of
- true -> arith_op(Op);
- false -> no
- end;
-float_op(Op, A, {float,_}, _) ->
- case possibly_numeric(A) of
- true -> arith_op(Op);
- false -> no
- end;
-float_op(Op, A, B, Ts) ->
- case {tdb_find(A, Ts),tdb_find(B, Ts)} of
- {float,_} -> arith_op(Op);
- {_,float} -> arith_op(Op);
- {_,_} -> no
- end.
-
-find_dest(V, Rs0) ->
- case find_reg(V, Rs0) of
- {ok,FR} ->
- {FR,mark(V, Rs0, dirty)};
- error ->
- Rs = put_reg(V, Rs0, dirty),
- {ok,FR} = find_reg(V, Rs),
- {FR,Rs}
- end.
-
-load_reg({float,_}=F, _, Rs0, Is0) ->
- Rs = put_reg(F, Rs0, clean),
- {ok,FR} = find_reg(F, Rs),
- Is = [{set,[FR],[F],fmove}|Is0],
- {Rs,Is};
-load_reg(V, Ts, Rs0, Is0) ->
- case find_reg(V, Rs0) of
- {ok,_FR} -> {Rs0,Is0};
- error ->
- Rs = put_reg(V, Rs0, clean),
- {ok,FR} = find_reg(V, Rs),
- Op = case tdb_find(V, Ts) of
- float -> fmove;
- _ -> fconv
- end,
- Is = [{set,[FR],[V],Op}|Is0],
- {Rs,Is}
- end.
-
-arith_op('+') -> {yes,fadd};
-arith_op('-') -> {yes,fsub};
-arith_op('*') -> {yes,fmul};
-arith_op('/') -> {yes,fdiv};
-arith_op(_) -> no.
-
-flush(no_float_opt, _, Acc) -> {no_float_opt,Acc};
-flush(Rs, [{set,[_],[],{put_tuple,_}}|_]=Is0, Acc0) ->
- Acc = flush_all(Rs, Is0, Acc0),
- {[],Acc};
-flush(Rs0, [{set,Ds,Ss,_Op}|_], Acc0) ->
- Save = gb_sets:from_list(Ss),
- Acc = save_regs(Rs0, Save, Acc0),
- Rs1 = foldl(fun(S, A) -> mark(S, A, clean) end, Rs0, Ss),
- Kill = gb_sets:from_list(Ds),
- Rs = kill_regs(Rs1, Kill),
- {Rs,Acc};
-flush(Rs0, Is, Acc0) ->
- Acc = flush_all(Rs0, Is, Acc0),
- {[],Acc}.
-
-flush_all(no_float_opt, _, Acc) -> Acc;
-flush_all([{_,{float,_},_}|Rs], Is, Acc) ->
- flush_all(Rs, Is, Acc);
-flush_all([{I,V,dirty}|Rs], Is, Acc0) ->
- Acc = checkerror(Acc0),
- case beam_block:is_killed(V, Is) of
- true -> flush_all(Rs, Is, Acc);
- false -> flush_all(Rs, Is, [{set,[V],[{fr,I}],fmove}|Acc])
- end;
-flush_all([{_,_,clean}|Rs], Is, Acc) -> flush_all(Rs, Is, Acc);
-flush_all([free|Rs], Is, Acc) -> flush_all(Rs, Is, Acc);
-flush_all([], _, Acc) -> Acc.
-
-save_regs(Rs, Save, Acc) ->
- foldl(fun(R, A) -> save_reg(R, Save, A) end, Acc, Rs).
-
-save_reg({I,V,dirty}, Save, Acc) ->
- case gb_sets:is_member(V, Save) of
- true -> [{set,[V],[{fr,I}],fmove}|checkerror(Acc)];
- false -> Acc
- end;
-save_reg(_, _, Acc) -> Acc.
-
-kill_regs(Rs, Kill) ->
- map(fun(R) -> kill_reg(R, Kill) end, Rs).
-
-kill_reg({_,V,_}=R, Kill) ->
- case gb_sets:is_member(V, Kill) of
- true -> free;
- false -> R
- end;
-kill_reg(R, _) -> R.
-
-mark(V, [{I,V,_}|Rs], Mark) -> [{I,V,Mark}|Rs];
-mark(V, [R|Rs], Mark) -> [R|mark(V, Rs, Mark)];
-mark(_, [], _) -> [].
-
-fetch_reg(V, [{I,V,_}|_]) -> {fr,I};
-fetch_reg(V, [_|SRs]) -> fetch_reg(V, SRs).
-
-find_reg(V, [{I,V,_}|_]) -> {ok,{fr,I}};
-find_reg(V, [_|SRs]) -> find_reg(V, SRs);
-find_reg(_, []) -> error.
-
-put_reg(V, Rs, Dirty) -> put_reg_1(V, Rs, Dirty, 0).
-
-put_reg_1(V, [free|Rs], Dirty, I) -> [{I,V,Dirty}|Rs];
-put_reg_1(V, [R|Rs], Dirty, I) -> [R|put_reg_1(V, Rs, Dirty, I+1)];
-put_reg_1(V, [], Dirty, I) -> [{I,V,Dirty}].
-
-checkerror(Is) ->
- checkerror_1(Is, Is).
-
-checkerror_1([{set,[],[],fcheckerror}|_], OrigIs) -> OrigIs;
-checkerror_1([{set,[],[],fclearerror}|_], OrigIs) -> OrigIs;
-checkerror_1([{set,_,_,{bif,fadd,_}}|_], OrigIs) -> checkerror_2(OrigIs);
-checkerror_1([{set,_,_,{bif,fsub,_}}|_], OrigIs) -> checkerror_2(OrigIs);
-checkerror_1([{set,_,_,{bif,fmul,_}}|_], OrigIs) -> checkerror_2(OrigIs);
-checkerror_1([{set,_,_,{bif,fdiv,_}}|_], OrigIs) -> checkerror_2(OrigIs);
-checkerror_1([{set,_,_,{bif,fnegate,_}}|_], OrigIs) -> checkerror_2(OrigIs);
-checkerror_1([_|Is], OrigIs) -> checkerror_1(Is, OrigIs);
-checkerror_1([], OrigIs) -> OrigIs.
-
-checkerror_2(OrigIs) -> [{set,[],[],fcheckerror}|OrigIs].
-
-add_ftest_heap(Is) ->
- add_ftest_heap_1(reverse(Is), 0, []).
-
-add_ftest_heap_1([{set,_,[{fr,_}],fmove}=I|Is], Floats, Acc) ->
- add_ftest_heap_1(Is, Floats+1, [I|Acc]);
-add_ftest_heap_1([{allocate,_,_}=I|Is], 0, Acc) ->
- reverse(Is, [I|Acc]);
-add_ftest_heap_1([{allocate,Regs,{Z,Stk,Heap,Inits}}|Is], Floats, Acc) ->
- reverse(Is, [{allocate,Regs,{Z,Stk,Heap,Floats,Inits}}|Acc]);
-add_ftest_heap_1([I|Is], Floats, Acc) ->
- add_ftest_heap_1(Is, Floats, [I|Acc]);
-add_ftest_heap_1([], 0, Acc) ->
- Acc;
-add_ftest_heap_1([], Floats, Is) ->
- Regs = beam_block:live_at_entry(Is),
- [{allocate,Regs,{nozero,nostack,0,Floats,[]}}|Is].
-
-are_live_regs_determinable([{allocate,_,_}|_]) -> true;
-are_live_regs_determinable([{'%live',_}|_]) -> true;
-are_live_regs_determinable([_|Is]) -> are_live_regs_determinable(Is);
-are_live_regs_determinable([]) -> false.
-
-
-%%% Routines for maintaining a type database. The type database
-%%% associates type information with registers.
-%%%
-%%% {tuple,Size,First} means that the corresponding register contains a
-%%% tuple with *at least* Size elements. An tuple with unknown
-%%% size is represented as {tuple,0}. First is either [] (meaning that
-%%% the tuple's first element is unknown) or [FirstElement] (the contents
-%%% of the first element).
-%%%
-%%% 'float' means that the register contains a float.
-
-%% tdb_new() -> EmptyDataBase
-%% Creates a new, empty type database.
-
-tdb_new() -> [].
-
-%% tdb_find(Register, Db) -> Information|error
-%% Returns type information or the atom error if there are no type
-%% information available for Register.
-
-tdb_find(Key, [{K,_}|_]) when Key < K -> error;
-tdb_find(Key, [{Key,Info}|_]) -> Info;
-tdb_find(Key, [_|Db]) -> tdb_find(Key, Db);
-tdb_find(_, []) -> error.
-
-%% tdb_update([UpdateOp], Db) -> NewDb
-%% UpdateOp = {Register,kill}|{Register,NewInfo}
-%% Updates a type database. If a 'kill' operation is given, the type
-%% information for that register will be removed from the database.
-%% A kill operation takes precende over other operations for the same
-%% register (i.e. [{{x,0},kill},{{x,0},{tuple,5}}] means that the
-%% the existing type information, if any, will be discarded, and the
-%% the '{tuple,5}' information ignored.
-%%
-%% If NewInfo information is given and there exists information about
-%% the register, the old and new type information will be merged.
-%% For instance, {tuple,5} and {tuple,10} will be merged to produce
-%% {tuple,10}.
-
-tdb_update(Uis0, Ts0) ->
- Uis1 = filter(fun ({{x,_},_Op}) -> true;
- ({{y,_},_Op}) -> true;
- (_) -> false
- end, Uis0),
- tdb_update1(lists:sort(Uis1), Ts0).
-
-tdb_update1([{Key,kill}|Ops], [{K,_Old}|_]=Db) when Key < K ->
- tdb_update1(remove_key(Key, Ops), Db);
-tdb_update1([{Key,_New}=New|Ops], [{K,_Old}|_]=Db) when Key < K ->
- [New|tdb_update1(Ops, Db)];
-tdb_update1([{Key,kill}|Ops], [{Key,_}|Db]) ->
- tdb_update1(remove_key(Key, Ops), Db);
-tdb_update1([{Key,NewInfo}|Ops], [{Key,OldInfo}|Db]) ->
- [{Key,merge_type_info(NewInfo, OldInfo)}|tdb_update1(Ops, Db)];
-tdb_update1([{_,_}|_]=Ops, [Old|Db]) ->
- [Old|tdb_update1(Ops, Db)];
-tdb_update1([{Key,kill}|Ops], []) ->
- tdb_update1(remove_key(Key, Ops), []);
-tdb_update1([{_,_}=New|Ops], []) ->
- [New|tdb_update1(Ops, [])];
-tdb_update1([], Db) -> Db.
-
-%% tdb_kill_xregs(Db) -> NewDb
-%% Kill all information about x registers. Also kill all tuple_element
-%% dependencies from y registers to x registers.
-
-tdb_kill_xregs([{{x,_},_Type}|Db]) -> tdb_kill_xregs(Db);
-tdb_kill_xregs([{{y,_},{tuple_element,{x,_},_}}|Db]) -> tdb_kill_xregs(Db);
-tdb_kill_xregs([Any|Db]) -> [Any|tdb_kill_xregs(Db)];
-tdb_kill_xregs([]) -> [].
-
-remove_key(Key, [{Key,_Op}|Ops]) -> remove_key(Key, Ops);
-remove_key(_, Ops) -> Ops.
-
-merge_type_info(I, I) -> I;
-merge_type_info({tuple,Sz1,Same}, {tuple,Sz2,Same}=Max) when Sz1 < Sz2 ->
- Max;
-merge_type_info({tuple,Sz1,Same}=Max, {tuple,Sz2,Same}) when Sz1 > Sz2 ->
- Max;
-merge_type_info({tuple,Sz1,[]}, {tuple,Sz2,First}) ->
- merge_type_info({tuple,Sz1,First}, {tuple,Sz2,First});
-merge_type_info({tuple,Sz1,First}, {tuple,Sz2,_}) ->
- merge_type_info({tuple,Sz1,First}, {tuple,Sz2,First});
-merge_type_info(NewType, _) ->
- verify_type(NewType),
- NewType.
-
-verify_type({tuple,Sz,[]}) when is_integer(Sz) -> ok;
-verify_type({tuple,Sz,[_]}) when is_integer(Sz) -> ok;
-verify_type({tuple_element,_,_}) -> ok;
-verify_type(float) -> ok;
-verify_type({atom,_}) -> ok.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_validator.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_validator.erl
deleted file mode 100644
index a01be447b0..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_validator.erl
+++ /dev/null
@@ -1,1022 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: beam_validator.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
-
--module(beam_validator).
-
--export([file/1,files/1]).
-
-%% Interface for compiler.
--export([module/2,format_error/1]).
-
--import(lists, [reverse/1,foldl/3]).
-
--define(MAXREG, 1024).
-
--define(DEBUG, 1).
--undef(DEBUG).
--ifdef(DEBUG).
--define(DBG_FORMAT(F, D), (io:format((F), (D)))).
--else.
--define(DBG_FORMAT(F, D), ok).
--endif.
-
-%%%
-%%% API functions.
-%%%
-
-files([F|Fs]) ->
- ?DBG_FORMAT("# Verifying: ~p~n", [F]),
- case file(F) of
- ok -> ok;
- {error,Es} ->
- io:format("~p:~n~s~n", [F,format_error(Es)])
- end,
- files(Fs);
-files([]) -> ok.
-
-file(Name) when is_list(Name) ->
- case case filename:extension(Name) of
- ".S" -> s_file(Name);
- ".beam" -> beam_file(Name)
- end of
- [] -> ok;
- Es -> {error,Es}
- end.
-
-%% To be called by the compiler.
-module({Mod,Exp,Attr,Fs,Lc}=Code, _Opts)
- when is_atom(Mod), is_list(Exp), is_list(Attr), is_integer(Lc) ->
- case validate(Fs) of
- [] -> {ok,Code};
- Es0 ->
- Es = [{?MODULE,E} || E <- Es0],
- {error,[{atom_to_list(Mod),Es}]}
- end.
-
-format_error([]) -> [];
-format_error([{{M,F,A},{I,Off,Desc}}|Es]) ->
- [io_lib:format(" ~p:~p/~p+~p:~n ~p - ~p~n",
- [M,F,A,Off,I,Desc])|format_error(Es)];
-format_error({{_M,F,A},{I,Off,Desc}}) ->
- io_lib:format(
- "function ~p/~p+~p:~n"
- " Internal consistency check failed - please report this bug.~n"
- " Instruction: ~p~n"
- " Error: ~p:~n", [F,A,Off,I,Desc]).
-
-%%%
-%%% Local functions follow.
-%%%
-
-s_file(Name) ->
- {ok,Is} = file:consult(Name),
- Fs = find_functions(Is),
- validate(Fs).
-
-find_functions(Fs) ->
- find_functions_1(Fs, none, [], []).
-
-find_functions_1([{function,Name,Arity,Entry}|Is], Func, FuncAcc, Acc0) ->
- Acc = add_func(Func, FuncAcc, Acc0),
- find_functions_1(Is, {Name,Arity,Entry}, [], Acc);
-find_functions_1([I|Is], Func, FuncAcc, Acc) ->
- find_functions_1(Is, Func, [I|FuncAcc], Acc);
-find_functions_1([], Func, FuncAcc, Acc) ->
- reverse(add_func(Func, FuncAcc, Acc)).
-
-add_func(none, _, Acc) -> Acc;
-add_func({Name,Arity,Entry}, Is, Acc) ->
- [{function,Name,Arity,Entry,reverse(Is)}|Acc].
-
-beam_file(Name) ->
- try beam_disasm:file(Name) of
- {error,beam_lib,Reason} -> [{beam_lib,Reason}];
- {beam_file,L} ->
- {value,{code,Code0}} = lists:keysearch(code, 1, L),
- Code = beam_file_1(Code0, []),
- validate(Code)
- catch _:_ -> [disassembly_failed]
- end.
-
-beam_file_1([F0|Fs], Acc) ->
- F = conv_func(F0),
- beam_file_1(Fs, [F|Acc]);
-beam_file_1([], Acc) -> reverse(Acc).
-
-%% Convert from the disassembly format to the internal format
-%% used by the compiler (as passed to the assembler).
-
-conv_func(Is) ->
- conv_func_1(labels(Is)).
-
-conv_func_1({Ls,[{func_info,[{atom,M},{atom,F},Ar]},
- {label,Entry}=Le|Is]}) ->
- %% The entry label gets maybe not correct here
- {function,F,Ar,Entry,
- [{label,L}||L<-Ls]++[{func_info,{atom,M},{atom,F},Ar},Le|Is]}.
-
-%%%
-%%% The validator follows.
-%%%
-%%% The purpose of the validator is find errors in the generated code
-%%% that may cause the emulator to crash or behave strangely.
-%%% We don't care about type errors in the user's code that will
-%%% cause a proper exception at run-time.
-%%%
-
-%%% Things currently not checked. XXX
-%%%
-%%% - That floating point registers are initialized before used.
-%%% - That fclearerror and fcheckerror are used properly.
-%%% - Heap allocation for floating point numbers.
-%%% - Heap allocation for binaries.
-%%% - That a catchtag or trytag is not overwritten by the wrong
-%%% type of instruction (such as move/2).
-%%% - Make sure that all catchtags and trytags have been removed
-%%% from the stack at return/tail call.
-%%% - Verify get_list instructions.
-%%%
-
-%% validate([Function]) -> [] | [Error]
-%% A list of functions with their code. The code is in the same
-%% format as used in the compiler and in .S files.
-validate([]) -> [];
-validate([{function,Name,Ar,Entry,Code}|Fs]) ->
- try validate_1(Code, Name, Ar, Entry) of
- _ -> validate(Fs)
- catch
- Error ->
- [Error|validate(Fs)];
- error:Error ->
- [validate_error(Error, Name, Ar)|validate(Fs)]
- end.
-
--ifdef(DEBUG).
-validate_error(Error, Name, Ar) ->
- exit(validate_error_1(Error, Name, Ar)).
--else.
-validate_error(Error, Name, Ar) ->
- validate_error_1(Error, Name, Ar).
--endif.
-validate_error_1(Error, Name, Ar) ->
- {{'_',Name,Ar},
- {internal_error,'_',{Error,erlang:get_stacktrace()}}}.
-
--record(st, %Emulation state
- {x=init_regs(0, term), %x register info.
- y=init_regs(0, initialized), %y register info.
- numy=none, %Number of y registers.
- h=0, %Available heap size.
- ct=[] %List of hot catch/try labels
- }).
-
--record(vst, %Validator state
- {current=none, %Current state
- branched=gb_trees:empty() %States at jumps
- }).
-
--ifdef(DEBUG).
-print_st(#st{x=Xs,y=Ys,numy=NumY,h=H,ct=Ct}) ->
- io:format(" #st{x=~p~n"
- " y=~p~n"
- " numy=~p,h=~p,ct=~w~n",
- [gb_trees:to_list(Xs),gb_trees:to_list(Ys),NumY,H,Ct]).
--endif.
-
-validate_1(Is, Name, Arity, Entry) ->
- validate_2(labels(Is), Name, Arity, Entry).
-
-validate_2({Ls1,[{func_info,{atom,Mod},{atom,Name},Arity}=_F|Is]},
- Name, Arity, Entry) ->
- lists:foreach(fun (_L) -> ?DBG_FORMAT(" ~p.~n", [_L]) end, Ls1),
- ?DBG_FORMAT(" ~p.~n", [_F]),
- validate_3(labels(Is), Name, Arity, Entry, Mod, Ls1);
-validate_2({Ls1,Is}, Name, Arity, _Entry) ->
- error({{'_',Name,Arity},{first(Is),length(Ls1),illegal_instruction}}).
-
-validate_3({Ls2,Is}, Name, Arity, Entry, Mod, Ls1) ->
- lists:foreach(fun (_L) -> ?DBG_FORMAT(" ~p.~n", [_L]) end, Ls2),
- Offset = 1 + length(Ls2),
- case lists:member(Entry, Ls2) of
- true ->
- St = init_state(Arity),
- Vst = #vst{current=St,
- branched=gb_trees_from_list([{L,St} || L <- Ls1])},
- valfun(Is, {Mod,Name,Arity}, Offset, Vst);
- false ->
- error({{Mod,Name,Arity},{first(Is),Offset,no_entry_label}})
- end.
-
-first([X|_]) -> X;
-first([]) -> [].
-
-labels(Is) ->
- labels_1(Is, []).
-
-labels_1([{label,L}|Is], R) ->
- labels_1(Is, [L|R]);
-labels_1(Is, R) ->
- {lists:reverse(R),Is}.
-
-init_state(Arity) ->
- Xs = init_regs(Arity, term),
- Ys = init_regs(0, initialized),
- #st{x=Xs,y=Ys,numy=none,h=0,ct=[]}.
-
-init_regs(0, _) ->
- gb_trees:empty();
-init_regs(N, Type) ->
- gb_trees_from_list([{R,Type} || R <- lists:seq(0, N-1)]).
-
-valfun([], _MFA, _Offset, Vst) -> Vst;
-valfun([I|Is], MFA, Offset, Vst) ->
- ?DBG_FORMAT(" ~p.\n", [I]),
- valfun(Is, MFA, Offset+1,
- try valfun_1(I, Vst)
- catch Error ->
- error({MFA,{I,Offset,Error}})
- end).
-
-%% Instructions that are allowed in dead code or when failing,
-%% that is while the state is undecided in some way.
-valfun_1({label,Lbl}, #vst{current=St0,branched=B}=Vst) ->
- St = merge_states(Lbl, St0, B),
- Vst#vst{current=St,branched=gb_trees:enter(Lbl, St, B)};
-valfun_1(_I, #vst{current=none}=Vst) ->
- %% Ignore instructions after erlang:error/1,2, which
- %% the original R10B compiler thought would return.
- ?DBG_FORMAT("Ignoring ~p\n", [_I]),
- Vst;
-valfun_1({badmatch,Src}, Vst) ->
- assert_term(Src, Vst),
- kill_state(Vst);
-valfun_1({case_end,Src}, Vst) ->
- assert_term(Src, Vst),
- kill_state(Vst);
-valfun_1(if_end, Vst) ->
- kill_state(Vst);
-valfun_1({try_case_end,Src}, Vst) ->
- assert_term(Src, Vst),
- kill_state(Vst);
-%% Instructions that can not cause exceptions
-valfun_1({move,Src,Dst}, Vst) ->
- Type = get_term_type(Src, Vst),
- set_type_reg(Type, Dst, Vst);
-valfun_1({fmove,Src,{fr,_}}, Vst) ->
- assert_type(float, Src, Vst);
-valfun_1({fmove,{fr,_},Dst}, Vst) ->
- set_type_reg({float,[]}, Dst, Vst);
-valfun_1({kill,{y,_}=Reg}, Vst) ->
- set_type_y(initialized, Reg, Vst);
-valfun_1({test_heap,Heap,Live}, Vst) ->
- test_heap(Heap, Live, Vst);
-valfun_1({bif,_Op,nofail,Src,Dst}, Vst) ->
- validate_src(Src, Vst),
- set_type_reg(term, Dst, Vst);
-%% Put instructions.
-valfun_1({put_list,A,B,Dst}, Vst0) ->
- assert_term(A, Vst0),
- assert_term(B, Vst0),
- Vst = eat_heap(2, Vst0),
- set_type_reg(cons, Dst, Vst);
-valfun_1({put_tuple,Sz,Dst}, Vst0) when is_integer(Sz) ->
- Vst = eat_heap(1, Vst0),
- set_type_reg({tuple,Sz}, Dst, Vst);
-valfun_1({put,Src}, Vst) ->
- assert_term(Src, Vst),
- eat_heap(1, Vst);
-valfun_1({put_string,Sz,_,Dst}, Vst0) when is_integer(Sz) ->
- Vst = eat_heap(2*Sz, Vst0),
- set_type_reg(cons, Dst, Vst);
-%% Allocate and deallocate, et.al
-valfun_1({allocate,Stk,Live}, Vst) ->
- allocate(false, Stk, 0, Live, Vst);
-valfun_1({allocate_heap,Stk,Heap,Live}, Vst) ->
- allocate(false, Stk, Heap, Live, Vst);
-valfun_1({allocate_zero,Stk,Live}, Vst) ->
- allocate(true, Stk, 0, Live, Vst);
-valfun_1({allocate_heap_zero,Stk,Heap,Live}, Vst) ->
- allocate(true, Stk, Heap, Live, Vst);
-valfun_1({init,{y,_}=Reg}, Vst) ->
- set_type_y(initialized, Reg, Vst);
-valfun_1({deallocate,StkSize}, #vst{current=#st{numy=StkSize,ct=[]}}=Vst) ->
- deallocate(Vst);
-valfun_1({deallocate,_}, #vst{current=#st{numy=NumY,ct=[]}}) ->
- error({allocated,NumY});
-valfun_1({deallocate,_}, #vst{current=#st{ct=Fails}}) ->
- error({catch_try_stack,Fails});
-%% Catch & try.
-valfun_1({'catch',Dst,{f,Fail}}, Vst0) when Fail /= none ->
- Vst = #vst{current=#st{ct=Fails}=St} =
- set_type_y({catchtag,Fail}, Dst, Vst0),
- Vst#vst{current=St#st{ct=[Fail|Fails]}};
-valfun_1({'try',Dst,{f,Fail}}, Vst0) ->
- Vst = #vst{current=#st{ct=Fails}=St} =
- set_type_y({trytag,Fail}, Dst, Vst0),
- Vst#vst{current=St#st{ct=[Fail|Fails]}};
-%% Do a postponed state branch if necessary and try next set of instructions
-valfun_1(I, #vst{current=#st{ct=[]}}=Vst) ->
- valfun_2(I, Vst);
-valfun_1(I, #vst{current=#st{ct=Fails}}=Vst0) ->
- %% Perform a postponed state branch
- Vst = #vst{current=St} = lists:foldl(fun branch_state/2, Vst0, Fails),
- valfun_2(I, Vst#vst{current=St#st{ct=[]}}).
-
-%% Instructions that can cause exceptions.
-valfun_2({apply,Live}, Vst) ->
- call(Live+2, Vst);
-valfun_2({apply_last,Live,_}, Vst) ->
- tail_call(Live+2, Vst);
-valfun_2({call_fun,Live}, Vst) ->
- call(Live, Vst);
-valfun_2({call,Live,_}, Vst) ->
- call(Live, Vst);
-valfun_2({call_ext,Live,Func}, Vst) ->
- call(Func, Live, Vst);
-valfun_2({call_only,Live,_}, Vst) ->
- tail_call(Live, Vst);
-valfun_2({call_ext_only,Live,_}, Vst) ->
- tail_call(Live, Vst);
-valfun_2({call_last,Live,_,_}, Vst) ->
- tail_call(Live, Vst);
-valfun_2({call_ext_last,Live,_,_}, Vst) ->
- tail_call(Live, Vst);
-valfun_2({make_fun,_,_,Live}, Vst) ->
- call(Live, Vst);
-valfun_2({make_fun2,_,_,_,Live}, Vst) ->
- call(Live, Vst);
-%% Floating point.
-valfun_2({fconv,Src,{fr,_}}, Vst) ->
- assert_term(Src, Vst);
-valfun_2({bif,fadd,_,[{fr,_},{fr,_}],{fr,_}}, Vst) ->
- Vst;
-valfun_2({bif,fdiv,_,[{fr,_},{fr,_}],{fr,_}}, Vst) ->
- Vst;
-valfun_2({bif,fmul,_,[{fr,_},{fr,_}],{fr,_}}, Vst) ->
- Vst;
-valfun_2({bif,fnegate,_,[{fr,_}],{fr,_}}, Vst) ->
- Vst;
-valfun_2({bif,fsub,_,[{fr,_},{fr,_}],{fr,_}}, Vst) ->
- Vst;
-valfun_2(fclearerror, Vst) ->
- Vst;
-valfun_2({fcheckerror,_}, Vst) ->
- Vst;
-%% Other BIFs
-valfun_2({bif,element,{f,Fail},[Pos,Tuple],Dst}, Vst0) ->
- TupleType0 = get_term_type(Tuple, Vst0),
- PosType = get_term_type(Pos, Vst0),
- Vst1 = branch_state(Fail, Vst0),
- TupleType = upgrade_type({tuple,[get_tuple_size(PosType)]}, TupleType0),
- Vst = set_type(TupleType, Tuple, Vst1),
- set_type_reg(term, Dst, Vst);
-valfun_2({bif,Op,{f,Fail},Src,Dst}, Vst0) ->
- validate_src(Src, Vst0),
- Vst = branch_state(Fail, Vst0),
- Type = bif_type(Op, Src, Vst),
- set_type_reg(Type, Dst, Vst);
-valfun_2(return, #vst{current=#st{numy=none}}=Vst) ->
- kill_state(Vst);
-valfun_2(return, #vst{current=#st{numy=NumY}}) ->
- error({stack_frame,NumY});
-valfun_2({jump,{f,_}}, #vst{current=none}=Vst) ->
- %% Must be an unreachable jump which was not optimized away.
- %% Do nothing.
- Vst;
-valfun_2({jump,{f,Lbl}}, Vst) ->
- kill_state(branch_state(Lbl, Vst));
-valfun_2({loop_rec,{f,Fail},Dst}, Vst0) ->
- Vst = branch_state(Fail, Vst0),
- set_type_reg(term, Dst, Vst);
-valfun_2(remove_message, Vst) ->
- Vst;
-valfun_2({wait,_}, Vst) ->
- kill_state(Vst);
-valfun_2({wait_timeout,_,Src}, Vst) ->
- assert_term(Src, Vst);
-valfun_2({loop_rec_end,_}, Vst) ->
- kill_state(Vst);
-valfun_2(timeout, #vst{current=St}=Vst) ->
- Vst#vst{current=St#st{x=init_regs(0, term)}};
-valfun_2(send, Vst) ->
- call(2, Vst);
-%% Catch & try.
-valfun_2({catch_end,Reg}, Vst0) ->
- case get_type(Reg, Vst0) of
- {catchtag,_} ->
- Vst = #vst{current=St} = set_type_reg(initialized, Reg, Vst0),
- Xs = gb_trees_from_list([{0,term}]),
- Vst#vst{current=St#st{x=Xs}};
- Type ->
- error({bad_type,Type})
- end;
-valfun_2({try_end,Reg}, Vst) ->
- case get_type(Reg, Vst) of
- {trytag,_} ->
- set_type_reg(initialized, Reg, Vst);
- Type ->
- error({bad_type,Type})
- end;
-valfun_2({try_case,Reg}, Vst0) ->
- case get_type(Reg, Vst0) of
- {trytag,_} ->
- Vst = #vst{current=St} = set_type_reg(initialized, Reg, Vst0),
- Xs = gb_trees_from_list([{0,{atom,[]}},{1,term},{2,term}]),
- Vst#vst{current=St#st{x=Xs}};
- Type ->
- error({bad_type,Type})
- end;
-valfun_2({set_tuple_element,Src,Tuple,I}, Vst) ->
- assert_term(Src, Vst),
- assert_type({tuple_element,I+1}, Tuple, Vst);
-%% Match instructions.
-valfun_2({select_val,Src,{f,Fail},{list,Choices}}, Vst) ->
- assert_term(Src, Vst),
- Lbls = [L || {f,L} <- Choices]++[Fail],
- kill_state(foldl(fun(L, S) -> branch_state(L, S) end, Vst, Lbls));
-valfun_2({select_tuple_arity,Tuple,{f,Fail},{list,Choices}}, Vst) ->
- assert_type(tuple, Tuple, Vst),
- kill_state(branch_arities(Choices, Tuple, branch_state(Fail, Vst)));
-valfun_2({get_list,Src,D1,D2}, Vst0) ->
- assert_term(Src, Vst0),
- Vst = set_type_reg(term, D1, Vst0),
- set_type_reg(term, D2, Vst);
-valfun_2({get_tuple_element,Src,I,Dst}, Vst) ->
- assert_type({tuple_element,I+1}, Src, Vst),
- set_type_reg(term, Dst, Vst);
-valfun_2({bs_restore,_}, Vst) ->
- Vst;
-valfun_2({bs_save,_}, Vst) ->
- Vst;
-valfun_2({bs_start_match,{f,Fail},Src}, Vst) ->
- assert_term(Src, Vst),
- branch_state(Fail, Vst);
-valfun_2({test,bs_skip_bits,{f,Fail},[Src,_,_]}, Vst) ->
- assert_term(Src, Vst),
- branch_state(Fail, Vst);
-valfun_2({test,_,{f,Fail},[_,_,_,Dst]}, Vst0) ->
- Vst = branch_state(Fail, Vst0),
- set_type_reg({integer,[]}, Dst, Vst);
-valfun_2({test,bs_test_tail,{f,Fail},_}, Vst) ->
- branch_state(Fail, Vst);
-%% Other test instructions.
-valfun_2({test,is_float,{f,Lbl},[Float]}, Vst0) ->
- assert_term(Float, Vst0),
- Vst = branch_state(Lbl, Vst0),
- set_type({float,[]}, Float, Vst);
-valfun_2({test,is_tuple,{f,Lbl},[Tuple]}, Vst0) ->
- assert_term(Tuple, Vst0),
- Vst = branch_state(Lbl, Vst0),
- set_type({tuple,[0]}, Tuple, Vst);
-valfun_2({test,test_arity,{f,Lbl},[Tuple,Sz]}, Vst0) when is_integer(Sz) ->
- assert_type(tuple, Tuple, Vst0),
- Vst = branch_state(Lbl, Vst0),
- set_type_reg({tuple,Sz}, Tuple, Vst);
-valfun_2({test,_Op,{f,Lbl},Src}, Vst) ->
- validate_src(Src, Vst),
- branch_state(Lbl, Vst);
-valfun_2({bs_add,{f,Fail},[A,B,_],Dst}, Vst0) ->
- assert_term(A, Vst0),
- assert_term(B, Vst0),
- Vst = branch_state(Fail, Vst0),
- set_type_reg({integer,[]}, Dst, Vst);
-valfun_2({bs_bits_to_bytes,{f,Fail},Src,Dst}, Vst0) ->
- assert_term(Src, Vst0),
- Vst = branch_state(Fail, Vst0),
- set_type_reg({integer,[]}, Dst, Vst);
-valfun_2({bs_init2,{f,Fail},_,Heap,_,_,Dst}, Vst0) ->
- Vst1 = heap_alloc(Heap, Vst0),
- Vst = branch_state(Fail, Vst1),
- set_type_reg(binary, Dst, Vst);
-valfun_2({bs_put_string,Sz,_}, Vst) when is_integer(Sz) ->
- Vst;
-valfun_2({bs_put_binary,{f,Fail},_,_,_,Src}, Vst0) ->
- assert_term(Src, Vst0),
- branch_state(Fail, Vst0);
-valfun_2({bs_put_float,{f,Fail},_,_,_,Src}, Vst0) ->
- assert_term(Src, Vst0),
- branch_state(Fail, Vst0);
-valfun_2({bs_put_integer,{f,Fail},_,_,_,Src}, Vst0) ->
- assert_term(Src, Vst0),
- branch_state(Fail, Vst0);
-%% Old bit syntax construction (before R10B).
-valfun_2({bs_init,_,_}, Vst) -> Vst;
-valfun_2({bs_need_buf,_}, Vst) -> Vst;
-valfun_2({bs_final,{f,Fail},Dst}, Vst0) ->
- Vst = branch_state(Fail, Vst0),
- set_type_reg(binary, Dst, Vst);
-%% Misc.
-valfun_2({'%live',Live}, Vst) ->
- verify_live(Live, Vst),
- Vst;
-valfun_2(_, _) ->
- error(unknown_instruction).
-
-kill_state(#vst{current=#st{ct=[]}}=Vst) ->
- Vst#vst{current=none};
-kill_state(#vst{current=#st{ct=Fails}}=Vst0) ->
- Vst = lists:foldl(fun branch_state/2, Vst0, Fails),
- Vst#vst{current=none}.
-
-%% A "plain" call.
-%% The stackframe must have a known size and be initialized.
-%% The instruction will return to the instruction following the call.
-call(Live, #vst{current=St}=Vst) ->
- verify_live(Live, Vst),
- verify_y_init(Vst),
- Xs = gb_trees_from_list([{0,term}]),
- Vst#vst{current=St#st{x=Xs}}.
-
-%% A "plain" call.
-%% The stackframe must have a known size and be initialized.
-%% The instruction will return to the instruction following the call.
-call(Name, Live, #vst{current=St}=Vst) ->
- verify_live(Live, Vst),
- case return_type(Name, Vst) of
- exception ->
- kill_state(Vst);
- Type ->
- verify_y_init(Vst),
- Xs = gb_trees_from_list([{0,Type}]),
- Vst#vst{current=St#st{x=Xs}}
- end.
-
-%% Tail call.
-%% The stackframe must have a known size and be initialized.
-%% Does not return to the instruction following the call.
-tail_call(Live, Vst) ->
- kill_state(call(Live, Vst)).
-
-allocate(Zero, Stk, Heap, Live, #vst{current=#st{numy=none}=St}=Vst) ->
- verify_live(Live, Vst),
- Ys = init_regs(case Zero of
- true -> Stk;
- false -> 0
- end, initialized),
- Vst#vst{current=St#st{y=Ys,numy=Stk,h=heap_alloc_1(Heap)}};
-allocate(_, _, _, _, #vst{current=#st{numy=Numy}}) ->
- error({existing_stack_frame,{size,Numy}}).
-
-deallocate(#vst{current=St}=Vst) ->
- Vst#vst{current=St#st{y=init_regs(0, initialized),numy=none}}.
-
-test_heap(Heap, Live, Vst) ->
- verify_live(Live, Vst),
- heap_alloc(Heap, Vst).
-
-heap_alloc(Heap, #vst{current=St}=Vst) ->
- Vst#vst{current=St#st{h=heap_alloc_1(Heap)}}.
-
-heap_alloc_1({alloc,Alloc}) ->
- {value,{_,Heap}} = lists:keysearch(words, 1, Alloc),
- Heap;
-heap_alloc_1(Heap) when is_integer(Heap) -> Heap.
-
-
-set_type(Type, {x,_}=Reg, Vst) -> set_type_reg(Type, Reg, Vst);
-set_type(Type, {y,_}=Reg, Vst) -> set_type_y(Type, Reg, Vst);
-set_type(_, _, #vst{}=Vst) -> Vst.
-
-set_type_reg(Type, {x,X}, #vst{current=#st{x=Xs}=St}=Vst)
- when 0 =< X, X < ?MAXREG ->
- Vst#vst{current=St#st{x=gb_trees:enter(X, Type, Xs)}};
-set_type_reg(Type, Reg, Vst) ->
- set_type_y(Type, Reg, Vst).
-
-set_type_y(Type, {y,Y}=Reg, #vst{current=#st{y=Ys,numy=NumY}=St}=Vst)
- when is_integer(Y), 0 =< Y, Y < ?MAXREG ->
- case {Y,NumY} of
- {_,none} ->
- error({no_stack_frame,Reg});
- {_,_} when Y > NumY ->
- error({y_reg_out_of_range,Reg,NumY});
- {_,_} ->
- Vst#vst{current=St#st{y=gb_trees:enter(Y, Type, Ys)}}
- end;
-set_type_y(Type, Reg, #vst{}) -> error({invalid_store,Reg,Type}).
-
-assert_term(Src, Vst) ->
- get_term_type(Src, Vst),
- Vst.
-
-%% The possible types.
-%%
-%% First non-term types:
-%%
-%% initialized Only for Y registers. Means that the Y register
-%% has been initialized with some valid term so that
-%% it is safe to pass to the garbage collector.
-%% NOT safe to use in any other way (will not crash the
-%% emulator, but clearly points to a bug in the compiler).
-%%
-%% {catchtag,Lbl} A special term used within a catch. Must only be used
-%% by the catch instructions; NOT safe to use in other
-%% instructions.
-%%
-%% {trytag,Lbl} A special term used within a try block. Must only be
-%% used by the catch instructions; NOT safe to use in other
-%% instructions.
-%%
-%% exception Can only be used as a type returned by return_type/2
-%% (which gives the type of the value returned by a BIF).
-%% Thus 'exception' is never stored as type descriptor
-%% for a register.
-%%
-%% Normal terms:
-%%
-%% term Any valid Erlang (but not of the special types above).
-%%
-%% bool The atom 'true' or the atom 'false'.
-%%
-%% cons Cons cell: [_|_]
-%%
-%% nil Empty list: []
-%%
-%% {tuple,[Sz]} Tuple. An element has been accessed using
-%% element/2 or setelement/3 so that it is known that
-%% the type is a tuple of size at least Sz.
-%%
-%% {tuple,Sz} Tuple. A test_arity instruction has been seen
-%% so that it is known that the size is exactly Sz.
-%%
-%% {atom,[]} Atom.
-%% {atom,Atom}
-%%
-%% {integer,[]} Integer.
-%% {integer,Integer}
-%%
-%% {float,[]} Float.
-%% {float,Float}
-%%
-%% number Integer or Float of unknown value
-%%
-
-assert_type(WantedType, Term, Vst) ->
- assert_type(WantedType, get_type(Term, Vst)),
- Vst.
-
-assert_type(float, {float,_}) -> ok;
-assert_type(tuple, {tuple,_}) -> ok;
-assert_type({tuple_element,I}, {tuple,[Sz]})
- when 1 =< I, I =< Sz ->
- ok;
-assert_type({tuple_element,I}, {tuple,Sz})
- when is_integer(Sz), 1 =< I, I =< Sz ->
- ok;
-assert_type(Needed, Actual) ->
- error({bad_type,{needed,Needed},{actual,Actual}}).
-
-%% upgrade_type/2 is used when linear code finds out more and
-%% more information about a type, so the type gets "narrower"
-%% or perhaps inconsistent. In the case of inconsistency
-%% we mostly widen the type to 'term' to make subsequent
-%% code fail if it assumes anything about the type.
-
-upgrade_type(Same, Same) -> Same;
-upgrade_type(term, OldT) -> OldT;
-upgrade_type(NewT, term) -> NewT;
-upgrade_type({Type,New}=NewT, {Type,Old}=OldT)
- when Type == atom; Type == integer; Type == float ->
- if New =:= Old -> OldT;
- New =:= [] -> OldT;
- Old =:= [] -> NewT;
- true -> term
- end;
-upgrade_type({Type,_}=NewT, number)
- when Type == integer; Type == float ->
- NewT;
-upgrade_type(number, {Type,_}=OldT)
- when Type == integer; Type == float ->
- OldT;
-upgrade_type(bool, {atom,A}) ->
- upgrade_bool(A);
-upgrade_type({atom,A}, bool) ->
- upgrade_bool(A);
-upgrade_type({tuple,[Sz]}, {tuple,[OldSz]})
- when is_integer(Sz) ->
- {tuple,[max(Sz, OldSz)]};
-upgrade_type({tuple,Sz}=T, {tuple,[_]})
- when is_integer(Sz) ->
- %% This also takes care of the user error when a tuple element
- %% is accesed outside the known exact tuple size; there is
- %% no more type information, just a runtime error which is not
- %% our problem.
- T;
-upgrade_type({tuple,[Sz]}, {tuple,_}=T)
- when is_integer(Sz) ->
- %% Same as the previous clause but mirrored.
- T;
-upgrade_type(_A, _B) ->
- %%io:format("upgrade_type: ~p ~p\n", [_A,_B]),
- term.
-
-upgrade_bool([]) -> bool;
-upgrade_bool(true) -> {atom,true};
-upgrade_bool(false) -> {atom,false};
-upgrade_bool(_) -> term.
-
-get_tuple_size({integer,[]}) -> 0;
-get_tuple_size({integer,Sz}) -> Sz;
-get_tuple_size(_) -> 0.
-
-validate_src(Ss, Vst) when is_list(Ss) ->
- foldl(fun(S, _) -> get_type(S, Vst) end, ok, Ss).
-
-get_term_type(Src, Vst) ->
- case get_type(Src, Vst) of
- initialized -> error({not_assigned,Src});
- exception -> error({exception,Src});
- {catchtag,_} -> error({catchtag,Src});
- {trytag,_} -> error({trytag,Src});
- Type -> Type
- end.
-
-get_type(nil=T, _) -> T;
-get_type({atom,A}=T, _) when is_atom(A) -> T;
-get_type({float,F}=T, _) when is_float(F) -> T;
-get_type({integer,I}=T, _) when is_integer(I) -> T;
-get_type({x,X}=Reg, #vst{current=#st{x=Xs}}) when is_integer(X) ->
- case gb_trees:lookup(X, Xs) of
- {value,Type} -> Type;
- none -> error({uninitialized_reg,Reg})
- end;
-get_type({y,Y}=Reg, #vst{current=#st{y=Ys}}) when is_integer(Y) ->
- case gb_trees:lookup(Y, Ys) of
- {value,initialized} -> error({unassigned_reg,Reg});
- {value,Type} -> Type;
- none -> error({uninitialized_reg,Reg})
- end;
-get_type(Src, _) -> error({bad_source,Src}).
-
-branch_arities([], _, #vst{}=Vst) -> Vst;
-branch_arities([Sz,{f,L}|T], Tuple, #vst{current=St}=Vst0)
- when is_integer(Sz) ->
- Vst1 = set_type_reg({tuple,Sz}, Tuple, Vst0),
- Vst = branch_state(L, Vst1),
- branch_arities(T, Tuple, Vst#vst{current=St}).
-
-branch_state(0, #vst{}=Vst) -> Vst;
-branch_state(L, #vst{current=St,branched=B}=Vst) ->
- Vst#vst{
- branched=case gb_trees:is_defined(L, B) of
- false ->
- gb_trees:insert(L, St#st{ct=[]}, B);
- true ->
- MergedSt = merge_states(L, St, B),
- gb_trees:update(L, MergedSt#st{ct=[]}, B)
- end}.
-
-%% merge_states/3 is used when there are more than one way to arrive
-%% at this point, and the type states for the different paths has
-%% to be merged. The type states are downgraded to the least common
-%% subset for the subsequent code.
-
-merge_states(0, St, _Branched) -> St;
-merge_states(L, St, Branched) ->
- case gb_trees:lookup(L, Branched) of
- none -> St;
- {value,OtherSt} when St == none -> OtherSt;
- {value,OtherSt} ->
- merge_states_1(St, OtherSt)
- end.
-
-merge_states_1(#st{x=Xs0,y=Ys0,numy=NumY0,h=H0}=St,
- #st{x=Xs1,y=Ys1,numy=NumY1,h=H1}) ->
- NumY = merge_stk(NumY0, NumY1),
- Xs = merge_regs(Xs0, Xs1),
- Ys = merge_regs(Ys0, Ys1),
- St#st{x=Xs,y=Ys,numy=NumY,h=min(H0, H1)}.
-
-merge_stk(S, S) -> S;
-merge_stk(_, _) -> undecided.
-
-merge_regs(Rs0, Rs1) ->
- Rs = merge_regs_1(gb_trees:to_list(Rs0), gb_trees:to_list(Rs1)),
- gb_trees_from_list(Rs).
-
-merge_regs_1([Same|Rs1], [Same|Rs2]) ->
- [Same|merge_regs_1(Rs1, Rs2)];
-merge_regs_1([{R1,_}|Rs1], [{R2,_}|_]=Rs2) when R1 < R2 ->
- merge_regs_1(Rs1, Rs2);
-merge_regs_1([{R1,_}|_]=Rs1, [{R2,_}|Rs2]) when R1 > R2 ->
- merge_regs_1(Rs1, Rs2);
-merge_regs_1([{R,Type1}|Rs1], [{R,Type2}|Rs2]) ->
- [{R,merge_types(Type1, Type2)}|merge_regs_1(Rs1, Rs2)];
-merge_regs_1([], []) -> [];
-merge_regs_1([], [_|_]) -> [];
-merge_regs_1([_|_], []) -> [].
-
-merge_types(T, T) -> T;
-merge_types(initialized=I, _) -> I;
-merge_types(_, initialized=I) -> I;
-merge_types({tuple,Same}=T, {tuple,Same}) -> T;
-merge_types({tuple,A}, {tuple,B}) ->
- {tuple,[min(tuple_sz(A), tuple_sz(B))]};
-merge_types({Type,A}, {Type,B})
- when Type == atom; Type == integer; Type == float ->
- if A =:= B -> {Type,A};
- true -> {Type,[]}
- end;
-merge_types({Type,_}, number)
- when Type == integer; Type == float ->
- number;
-merge_types(number, {Type,_})
- when Type == integer; Type == float ->
- number;
-merge_types(bool, {atom,A}) ->
- merge_bool(A);
-merge_types({atom,A}, bool) ->
- merge_bool(A);
-merge_types(_, _) -> term.
-
-tuple_sz([Sz]) -> Sz;
-tuple_sz(Sz) -> Sz.
-
-merge_bool([]) -> {atom,[]};
-merge_bool(true) -> bool;
-merge_bool(false) -> bool;
-merge_bool(_) -> {atom,[]}.
-
-verify_y_init(#vst{current=#st{numy=none}}) -> ok;
-verify_y_init(#vst{current=#st{numy=undecided}}) ->
- error(unknown_size_of_stackframe);
-verify_y_init(#vst{current=#st{y=Ys,numy=NumY}}) ->
- verify_y_init_1(NumY, Ys).
-
-verify_y_init_1(0, _) -> ok;
-verify_y_init_1(N, Ys) ->
- Y = N-1,
- case gb_trees:is_defined(Y, Ys) of
- false -> error({{y,Y},not_initialized});
- true -> verify_y_init_1(Y, Ys)
- end.
-
-verify_live(0, #vst{}) -> ok;
-verify_live(N, #vst{current=#st{x=Xs}}) ->
- verify_live_1(N, Xs).
-
-verify_live_1(0, _) -> ok;
-verify_live_1(N, Xs) ->
- X = N-1,
- case gb_trees:is_defined(X, Xs) of
- false -> error({{x,X},not_live});
- true -> verify_live_1(X, Xs)
- end.
-
-eat_heap(N, #vst{current=#st{h=Heap0}=St}=Vst) ->
- case Heap0-N of
- Neg when Neg < 0 ->
- error({heap_overflow,{left,Heap0},{wanted,N}});
- Heap ->
- Vst#vst{current=St#st{h=Heap}}
- end.
-
-bif_type('-', Src, Vst) ->
- arith_type(Src, Vst);
-bif_type('+', Src, Vst) ->
- arith_type(Src, Vst);
-bif_type('*', Src, Vst) ->
- arith_type(Src, Vst);
-bif_type(abs, [Num], Vst) ->
- case get_type(Num, Vst) of
- {float,_}=T -> T;
- {integer,_}=T -> T;
- _ -> number
- end;
-bif_type(float, _, _) -> {float,[]};
-bif_type('/', _, _) -> {float,[]};
-%% Integer operations.
-bif_type('div', [_,_], _) -> {integer,[]};
-bif_type('rem', [_,_], _) -> {integer,[]};
-bif_type(length, [_], _) -> {integer,[]};
-bif_type(size, [_], _) -> {integer,[]};
-bif_type(trunc, [_], _) -> {integer,[]};
-bif_type(round, [_], _) -> {integer,[]};
-bif_type('band', [_,_], _) -> {integer,[]};
-bif_type('bor', [_,_], _) -> {integer,[]};
-bif_type('bxor', [_,_], _) -> {integer,[]};
-bif_type('bnot', [_], _) -> {integer,[]};
-bif_type('bsl', [_,_], _) -> {integer,[]};
-bif_type('bsr', [_,_], _) -> {integer,[]};
-%% Booleans.
-bif_type('==', [_,_], _) -> bool;
-bif_type('/=', [_,_], _) -> bool;
-bif_type('=<', [_,_], _) -> bool;
-bif_type('<', [_,_], _) -> bool;
-bif_type('>=', [_,_], _) -> bool;
-bif_type('>', [_,_], _) -> bool;
-bif_type('=:=', [_,_], _) -> bool;
-bif_type('=/=', [_,_], _) -> bool;
-bif_type('not', [_], _) -> bool;
-bif_type('and', [_,_], _) -> bool;
-bif_type('or', [_,_], _) -> bool;
-bif_type('xor', [_,_], _) -> bool;
-bif_type(is_atom, [_], _) -> bool;
-bif_type(is_boolean, [_], _) -> bool;
-bif_type(is_binary, [_], _) -> bool;
-bif_type(is_constant, [_], _) -> bool;
-bif_type(is_float, [_], _) -> bool;
-bif_type(is_function, [_], _) -> bool;
-bif_type(is_integer, [_], _) -> bool;
-bif_type(is_list, [_], _) -> bool;
-bif_type(is_number, [_], _) -> bool;
-bif_type(is_pid, [_], _) -> bool;
-bif_type(is_port, [_], _) -> bool;
-bif_type(is_reference, [_], _) -> bool;
-bif_type(is_tuple, [_], _) -> bool;
-%% Misc.
-bif_type(node, [], _) -> {atom,[]};
-bif_type(node, [_], _) -> {atom,[]};
-bif_type(hd, [_], _) -> term;
-bif_type(tl, [_], _) -> term;
-bif_type(get, [_], _) -> term;
-bif_type(raise, [_,_], _) -> exception;
-bif_type(_, _, _) -> term.
-
-arith_type([A,B], Vst) ->
- case {get_type(A, Vst),get_type(B, Vst)} of
- {{float,_},_} -> {float,[]};
- {_,{float,_}} -> {float,[]};
- {_,_} -> number
- end;
-arith_type(_, _) -> number.
-
-return_type({extfunc,M,F,A}, Vst) ->
- return_type_1(M, F, A, Vst).
-
-return_type_1(erlang, setelement, 3, Vst) ->
- Tuple = {x,1},
- TupleType =
- case get_type(Tuple, Vst) of
- {tuple,_}=TT -> TT;
- _ -> {tuple,[0]}
- end,
- case get_type({x,0}, Vst) of
- {integer,[]} -> TupleType;
- {integer,I} -> upgrade_type({tuple,[I]}, TupleType);
- _ -> TupleType
- end;
-return_type_1(erlang, F, A, _) ->
- return_type_erl(F, A);
-return_type_1(math, F, A, _) ->
- return_type_math(F, A);
-return_type_1(_, _, _, _) -> term.
-
-return_type_erl(exit, 1) -> exception;
-return_type_erl(throw, 1) -> exception;
-return_type_erl(fault, 1) -> exception;
-return_type_erl(fault, 2) -> exception;
-return_type_erl(error, 1) -> exception;
-return_type_erl(error, 2) -> exception;
-return_type_erl(_, _) -> term.
-
-return_type_math(cos, 1) -> {float,[]};
-return_type_math(cosh, 1) -> {float,[]};
-return_type_math(sin, 1) -> {float,[]};
-return_type_math(sinh, 1) -> {float,[]};
-return_type_math(tan, 1) -> {float,[]};
-return_type_math(tanh, 1) -> {float,[]};
-return_type_math(acos, 1) -> {float,[]};
-return_type_math(acosh, 1) -> {float,[]};
-return_type_math(asin, 1) -> {float,[]};
-return_type_math(asinh, 1) -> {float,[]};
-return_type_math(atan, 1) -> {float,[]};
-return_type_math(atanh, 1) -> {float,[]};
-return_type_math(erf, 1) -> {float,[]};
-return_type_math(erfc, 1) -> {float,[]};
-return_type_math(exp, 1) -> {float,[]};
-return_type_math(log, 1) -> {float,[]};
-return_type_math(log10, 1) -> {float,[]};
-return_type_math(sqrt, 1) -> {float,[]};
-return_type_math(atan2, 2) -> {float,[]};
-return_type_math(pow, 2) -> {float,[]};
-return_type_math(pi, 0) -> {float,[]};
-return_type_math(_, _) -> term.
-
-min(A, B) when is_integer(A), is_integer(B), A < B -> A;
-min(A, B) when is_integer(A), is_integer(B) -> B.
-
-max(A, B) when is_integer(A), is_integer(B), A > B -> A;
-max(A, B) when is_integer(A), is_integer(B) -> B.
-
-gb_trees_from_list(L) -> gb_trees:from_orddict(orddict:from_list(L)).
-
--ifdef(DEBUG).
-error(Error) -> exit(Error).
--else.
-error(Error) -> throw(Error).
--endif.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl.erl
deleted file mode 100644
index be9e088276..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl.erl
+++ /dev/null
@@ -1,4169 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Richard Carlsson.
-%% Copyright (C) 1999-2002 Richard Carlsson.
-%% Portions created by Ericsson are Copyright 2001, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: cerl.erl,v 1.3 2010/03/04 13:54:20 maria Exp $
-
-%% =====================================================================
-%% @doc Core Erlang abstract syntax trees.
-%%
-%% This module defines an abstract data type for representing Core
-%% Erlang source code as syntax trees.
-%%
-%% A recommended starting point for the first-time user is the
-%% documentation of the function type/1
.
-%%
-%% NOTES:
-%%
-%% This module deals with the composition and decomposition of
-%% syntactic entities (as opposed to semantic ones); its
-%% purpose is to hide all direct references to the data structures
-%% used to represent these entities. With few exceptions, the
-%% functions in this module perform no semantic interpretation of
-%% their inputs, and in general, the user is assumed to pass
-%% type-correct arguments - if this is not done, the effects are not
-%% defined.
-%%
-%% The internal representations of abstract syntax trees are
-%% subject to change without notice, and should not be documented
-%% outside this module. Furthermore, we do not give any guarantees on
-%% how an abstract syntax tree may or may not be represented, with
-%% the following exceptions: no syntax tree is represented by a
-%% single atom, such as none
, by a list constructor
-%% [X | Y]
, or by the empty list []
. This
-%% can be relied on when writing functions that operate on syntax
-%% trees.
-%%
-%% @type cerl(). An abstract Core Erlang syntax tree.
-%%
-%% Every abstract syntax tree has a type, given by the
-%% function type/1
. In addition,
-%% each syntax tree has a list of user annotations (cf. get_ann/1
), which are included
-%% in the Core Erlang syntax.
-
--module(cerl).
-
--export([abstract/1, add_ann/2, alias_pat/1, alias_var/1,
- ann_abstract/2, ann_c_alias/3, ann_c_apply/3, ann_c_atom/2,
- ann_c_call/4, ann_c_case/3, ann_c_catch/2, ann_c_char/2,
- ann_c_clause/3, ann_c_clause/4, ann_c_cons/3, ann_c_float/2,
- ann_c_fname/3, ann_c_fun/3, ann_c_int/2, ann_c_let/4,
- ann_c_letrec/3, ann_c_module/4, ann_c_module/5, ann_c_nil/1,
- ann_c_cons_skel/3, ann_c_tuple_skel/2, ann_c_primop/3,
- ann_c_receive/2, ann_c_receive/4, ann_c_seq/3, ann_c_string/2,
- ann_c_try/6, ann_c_tuple/2, ann_c_values/2, ann_c_var/2,
- ann_make_data/3, ann_make_list/2, ann_make_list/3,
- ann_make_data_skel/3, ann_make_tree/3, apply_args/1,
- apply_arity/1, apply_op/1, atom_lit/1, atom_name/1, atom_val/1,
- c_alias/2, c_apply/2, c_atom/1, c_call/3, c_case/2, c_catch/1,
- c_char/1, c_clause/2, c_clause/3, c_cons/2, c_float/1,
- c_fname/2, c_fun/2, c_int/1, c_let/3, c_letrec/2, c_module/3,
- c_module/4, c_nil/0, c_cons_skel/2, c_tuple_skel/1, c_primop/2,
- c_receive/1, c_receive/3, c_seq/2, c_string/1, c_try/5,
- c_tuple/1, c_values/1, c_var/1, call_args/1, call_arity/1,
- call_module/1, call_name/1, case_arg/1, case_arity/1,
- case_clauses/1, catch_body/1, char_lit/1, char_val/1,
- clause_arity/1, clause_body/1, clause_guard/1, clause_pats/1,
- clause_vars/1, concrete/1, cons_hd/1, cons_tl/1, copy_ann/2,
- data_arity/1, data_es/1, data_type/1, float_lit/1, float_val/1,
- fname_arity/1, fname_id/1, fold_literal/1, from_records/1,
- fun_arity/1, fun_body/1, fun_vars/1, get_ann/1, int_lit/1,
- int_val/1, is_c_alias/1, is_c_apply/1, is_c_atom/1,
- is_c_call/1, is_c_case/1, is_c_catch/1, is_c_char/1,
- is_c_clause/1, is_c_cons/1, is_c_float/1, is_c_fname/1,
- is_c_fun/1, is_c_int/1, is_c_let/1, is_c_letrec/1, is_c_list/1,
- is_c_module/1, is_c_nil/1, is_c_primop/1, is_c_receive/1,
- is_c_seq/1, is_c_string/1, is_c_try/1, is_c_tuple/1,
- is_c_values/1, is_c_var/1, is_data/1, is_leaf/1, is_literal/1,
- is_literal_term/1, is_print_char/1, is_print_string/1,
- let_arg/1, let_arity/1, let_body/1, let_vars/1, letrec_body/1,
- letrec_defs/1, letrec_vars/1, list_elements/1, list_length/1,
- make_data/2, make_list/1, make_list/2, make_data_skel/2,
- make_tree/2, meta/1, module_attrs/1, module_defs/1,
- module_exports/1, module_name/1, module_vars/1,
- pat_list_vars/1, pat_vars/1, primop_args/1, primop_arity/1,
- primop_name/1, receive_action/1, receive_clauses/1,
- receive_timeout/1, seq_arg/1, seq_body/1, set_ann/2,
- string_lit/1, string_val/1, subtrees/1, to_records/1,
- try_arg/1, try_body/1, try_vars/1, try_evars/1, try_handler/1,
- tuple_arity/1, tuple_es/1, type/1, unfold_literal/1,
- update_c_alias/3, update_c_apply/3, update_c_call/4,
- update_c_case/3, update_c_catch/2, update_c_clause/4,
- update_c_cons/3, update_c_cons_skel/3, update_c_fname/2,
- update_c_fname/3, update_c_fun/3, update_c_let/4,
- update_c_letrec/3, update_c_module/5, update_c_primop/3,
- update_c_receive/4, update_c_seq/3, update_c_try/6,
- update_c_tuple/2, update_c_tuple_skel/2, update_c_values/2,
- update_c_var/2, update_data/3, update_list/2, update_list/3,
- update_data_skel/3, update_tree/2, update_tree/3,
- values_arity/1, values_es/1, var_name/1, c_binary/1,
- update_c_binary/2, ann_c_binary/2, is_c_binary/1,
- binary_segments/1, c_bitstr/3, c_bitstr/4, c_bitstr/5,
- update_c_bitstr/5, update_c_bitstr/6, ann_c_bitstr/5,
- ann_c_bitstr/6, is_c_bitstr/1, bitstr_val/1, bitstr_size/1,
- bitstr_bitsize/1, bitstr_unit/1, bitstr_type/1,
- bitstr_flags/1]).
-
--include("core_parse.hrl").
-
-
-%% =====================================================================
-%% Representation (general)
-%%
-%% All nodes are represented by tuples of arity 2 or (generally)
-%% greater, whose first element is an atom which uniquely identifies the
-%% type of the node, and whose second element is a (proper) list of
-%% annotation terms associated with the node - this is by default empty.
-%%
-%% For most node constructor functions, there are analogous functions
-%% named 'ann_...', taking one extra argument 'As' (always the first
-%% argument), specifying an annotation list at node creation time.
-%% Similarly, there are also functions named 'update_...', taking one
-%% extra argument 'Old', specifying a node from which all fields not
-%% explicitly given as arguments should be copied (generally, this is
-%% the annotation field only).
-%% =====================================================================
-
-%% This defines the general representation of constant literals:
-
--record(literal, {ann = [], val}).
-
-
-%% @spec type(Node::cerl()) -> atom()
-%%
-%% @doc Returns the type tag of Node
. Current node types
-%% are:
-%%
-%%
-%%
-%% alias |
-%% apply |
-%% binary |
-%% bitstr |
-%% call |
-%% case |
-%% catch |
-%%
-%% clause |
-%% cons |
-%% fun |
-%% let |
-%% letrec |
-%% literal |
-%% module |
-%%
-%% primop |
-%% receive |
-%% seq |
-%% try |
-%% tuple |
-%% values |
-%% var |
-%%
-%%
-%%
-%% Note: The name of the primary constructor function for a node
-%% type is always the name of the type itself, prefixed by
-%% "c_
"; recognizer predicates are correspondingly
-%% prefixed by "is_c_
". Furthermore, to simplify
-%% preservation of annotations (cf. get_ann/1
), there are
-%% analogous constructor functions prefixed by "ann_c_
"
-%% and "update_c_
", for setting the annotation list of
-%% the new node to either a specific value or to the annotations of an
-%% existing node, respectively.
-%%
-%% @see abstract/1
-%% @see c_alias/2
-%% @see c_apply/2
-%% @see c_binary/1
-%% @see c_bitstr/5
-%% @see c_call/3
-%% @see c_case/2
-%% @see c_catch/1
-%% @see c_clause/3
-%% @see c_cons/2
-%% @see c_fun/2
-%% @see c_let/3
-%% @see c_letrec/2
-%% @see c_module/3
-%% @see c_primop/2
-%% @see c_receive/1
-%% @see c_seq/2
-%% @see c_try/3
-%% @see c_tuple/1
-%% @see c_values/1
-%% @see c_var/1
-%% @see get_ann/1
-%% @see to_records/1
-%% @see from_records/1
-%% @see data_type/1
-%% @see subtrees/1
-%% @see meta/1
-
-type(Node) ->
- element(1, Node).
-
-
-%% @spec is_leaf(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is a leaf node,
-%% otherwise false
. The current leaf node types are
-%% literal
and var
.
-%%
-%% Note: all literals (cf. is_literal/1
) are leaf
-%% nodes, even if they represent structured (constant) values such as
-%% {foo, [bar, baz]}
. Also note that variables are leaf
-%% nodes but not literals.
-%%
-%% @see type/1
-%% @see is_literal/1
-
-is_leaf(Node) ->
- case type(Node) of
- literal -> true;
- var -> true;
- _ -> false
- end.
-
-
-%% @spec get_ann(cerl()) -> [term()]
-%%
-%% @doc Returns the list of user annotations associated with a syntax
-%% tree node. For a newly created node, this is the empty list. The
-%% annotations may be any terms.
-%%
-%% @see set_ann/2
-
-get_ann(Node) ->
- element(2, Node).
-
-
-%% @spec set_ann(Node::cerl(), Annotations::[term()]) -> cerl()
-%%
-%% @doc Sets the list of user annotations of Node
to
-%% Annotations
.
-%%
-%% @see get_ann/1
-%% @see add_ann/2
-%% @see copy_ann/2
-
-set_ann(Node, List) ->
- setelement(2, Node, List).
-
-
-%% @spec add_ann(Annotations::[term()], Node::cerl()) -> cerl()
-%%
-%% @doc Appends Annotations
to the list of user
-%% annotations of Node
.
-%%
-%% Note: this is equivalent to set_ann(Node, Annotations ++
-%% get_ann(Node))
, but potentially more efficient.
-%%
-%% @see get_ann/1
-%% @see set_ann/2
-
-add_ann(Terms, Node) ->
- set_ann(Node, Terms ++ get_ann(Node)).
-
-
-%% @spec copy_ann(Source::cerl(), Target::cerl()) -> cerl()
-%%
-%% @doc Copies the list of user annotations from Source
-%% to Target
.
-%%
-%% Note: this is equivalent to set_ann(Target,
-%% get_ann(Source))
, but potentially more efficient.
-%%
-%% @see get_ann/1
-%% @see set_ann/2
-
-copy_ann(Source, Target) ->
- set_ann(Target, get_ann(Source)).
-
-
-%% @spec abstract(Term::term()) -> cerl()
-%%
-%% @doc Creates a syntax tree corresponding to an Erlang term.
-%% Term
must be a literal term, i.e., one that can be
-%% represented as a source code literal. Thus, it may not contain a
-%% process identifier, port, reference, binary or function value as a
-%% subterm.
-%%
-%% Note: This is a constant time operation.
-%%
-%% @see ann_abstract/2
-%% @see concrete/1
-%% @see is_literal/1
-%% @see is_literal_term/1
-
-abstract(T) ->
- #literal{val = T}.
-
-
-%% @spec ann_abstract(Annotations::[term()], Term::term()) -> cerl()
-%% @see abstract/1
-
-ann_abstract(As, T) ->
- #literal{val = T, ann = As}.
-
-
-%% @spec is_literal_term(Term::term()) -> boolean()
-%%
-%% @doc Returns true
if Term
can be
-%% represented as a literal, otherwise false
. This
-%% function takes time proportional to the size of Term
.
-%%
-%% @see abstract/1
-
-is_literal_term(T) when integer(T) -> true;
-is_literal_term(T) when float(T) -> true;
-is_literal_term(T) when atom(T) -> true;
-is_literal_term([]) -> true;
-is_literal_term([H | T]) ->
- case is_literal_term(H) of
- true ->
- is_literal_term(T);
- false ->
- false
- end;
-is_literal_term(T) when tuple(T) ->
- is_literal_term_list(tuple_to_list(T));
-is_literal_term(_) ->
- false.
-
-is_literal_term_list([T | Ts]) ->
- case is_literal_term(T) of
- true ->
- is_literal_term_list(Ts);
- false ->
- false
- end;
-is_literal_term_list([]) ->
- true.
-
-
-%% @spec concrete(Node::cerl()) -> term()
-%%
-%% @doc Returns the Erlang term represented by a syntax tree. An
-%% exception is thrown if Node
does not represent a
-%% literal term.
-%%
-%% Note: This is a constant time operation.
-%%
-%% @see abstract/1
-%% @see is_literal/1
-
-%% Because the normal tuple and list constructor operations always
-%% return a literal if the arguments are literals, 'concrete' and
-%% 'is_literal' never need to traverse the structure.
-
-concrete(#literal{val = V}) ->
- V.
-
-
-%% @spec is_literal(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
represents a
-%% literal term, otherwise false
. This function returns
-%% true
if and only if the value of
-%% concrete(Node)
is defined.
-%%
-%% Note: This is a constant time operation.
-%%
-%% @see abstract/1
-%% @see concrete/1
-%% @see fold_literal/1
-
-is_literal(#literal{}) ->
- true;
-is_literal(_) ->
- false.
-
-
-%% @spec fold_literal(Node::cerl()) -> cerl()
-%%
-%% @doc Assures that literals have a compact representation. This is
-%% occasionally useful if c_cons_skel/2
,
-%% c_tuple_skel/1
or unfold_literal/1
were
-%% used in the construction of Node
, and you want to revert
-%% to the normal "folded" representation of literals. If
-%% Node
represents a tuple or list constructor, its
-%% elements are rewritten recursively, and the node is reconstructed
-%% using c_cons/2
or c_tuple/1
, respectively;
-%% otherwise, Node
is not changed.
-%%
-%% @see is_literal/1
-%% @see c_cons_skel/2
-%% @see c_tuple_skel/1
-%% @see c_cons/2
-%% @see c_tuple/1
-%% @see unfold_literal/1
-
-fold_literal(Node) ->
- case type(Node) of
- tuple ->
- update_c_tuple(Node, fold_literal_list(tuple_es(Node)));
- cons ->
- update_c_cons(Node, fold_literal(cons_hd(Node)),
- fold_literal(cons_tl(Node)));
- _ ->
- Node
- end.
-
-fold_literal_list([E | Es]) ->
- [fold_literal(E) | fold_literal_list(Es)];
-fold_literal_list([]) ->
- [].
-
-
-%% @spec unfold_literal(Node::cerl()) -> cerl()
-%%
-%% @doc Assures that literals have a fully expanded representation. If
-%% Node
represents a literal tuple or list constructor, its
-%% elements are rewritten recursively, and the node is reconstructed
-%% using c_cons_skel/2
or c_tuple_skel/1
,
-%% respectively; otherwise, Node
is not changed. The {@link
-%% fold_literal/1} can be used to revert to the normal compact
-%% representation.
-%%
-%% @see is_literal/1
-%% @see c_cons_skel/2
-%% @see c_tuple_skel/1
-%% @see c_cons/2
-%% @see c_tuple/1
-%% @see fold_literal/1
-
-unfold_literal(Node) ->
- case type(Node) of
- literal ->
- copy_ann(Node, unfold_concrete(concrete(Node)));
- _ ->
- Node
- end.
-
-unfold_concrete(Val) ->
- case Val of
- _ when tuple(Val) ->
- c_tuple_skel(unfold_concrete_list(tuple_to_list(Val)));
- [H|T] ->
- c_cons_skel(unfold_concrete(H), unfold_concrete(T));
- _ ->
- abstract(Val)
- end.
-
-unfold_concrete_list([E | Es]) ->
- [unfold_concrete(E) | unfold_concrete_list(Es)];
-unfold_concrete_list([]) ->
- [].
-
-
-%% ---------------------------------------------------------------------
-
--record(module, {ann = [], name, exports, attrs, defs}).
-
-
-%% @spec c_module(Name::cerl(), Exports, Definitions) -> cerl()
-%%
-%% Exports = [cerl()]
-%% Definitions = [{cerl(), cerl()}]
-%%
-%% @equiv c_module(Name, Exports, [], Definitions)
-
-c_module(Name, Exports, Es) ->
- #module{name = Name, exports = Exports, attrs = [], defs = Es}.
-
-
-%% @spec c_module(Name::cerl(), Exports, Attributes, Definitions) ->
-%% cerl()
-%%
-%% Exports = [cerl()]
-%% Attributes = [{cerl(), cerl()}]
-%% Definitions = [{cerl(), cerl()}]
-%%
-%% @doc Creates an abstract module definition. The result represents
-%%
-%% module Name [E1, ..., Ek]
-%% attributes [K1 = T1, ...,
-%% Km = Tm]
-%% V1 = F1
-%% ...
-%% Vn = Fn
-%% end
-%%
-%% if Exports
= [E1, ..., Ek]
,
-%% Attributes
= [{K1, T1}, ..., {Km, Tm}]
,
-%% and Definitions
= [{V1, F1}, ..., {Vn,
-%% Fn}]
.
-%%
-%% Name
and all the Ki
must be atom
-%% literals, and all the Ti
must be constant literals. All
-%% the Vi
and Ei
must have type
-%% var
and represent function names. All the
-%% Fi
must have type 'fun'
.
-%%
-%% @see c_module/3
-%% @see module_name/1
-%% @see module_exports/1
-%% @see module_attrs/1
-%% @see module_defs/1
-%% @see module_vars/1
-%% @see ann_c_module/4
-%% @see ann_c_module/5
-%% @see update_c_module/5
-%% @see c_atom/1
-%% @see c_var/1
-%% @see c_fun/2
-%% @see is_literal/1
-
-c_module(Name, Exports, Attrs, Es) ->
- #module{name = Name, exports = Exports, attrs = Attrs, defs = Es}.
-
-
-%% @spec ann_c_module(As::[term()], Name::cerl(), Exports,
-%% Definitions) -> cerl()
-%%
-%% Exports = [cerl()]
-%% Definitions = [{cerl(), cerl()}]
-%%
-%% @see c_module/3
-%% @see ann_c_module/5
-
-ann_c_module(As, Name, Exports, Es) ->
- #module{name = Name, exports = Exports, attrs = [], defs = Es,
- ann = As}.
-
-
-%% @spec ann_c_module(As::[term()], Name::cerl(), Exports,
-%% Attributes, Definitions) -> cerl()
-%%
-%% Exports = [cerl()]
-%% Attributes = [{cerl(), cerl()}]
-%% Definitions = [{cerl(), cerl()}]
-%%
-%% @see c_module/4
-%% @see ann_c_module/4
-
-ann_c_module(As, Name, Exports, Attrs, Es) ->
- #module{name = Name, exports = Exports, attrs = Attrs, defs = Es,
- ann = As}.
-
-
-%% @spec update_c_module(Old::cerl(), Name::cerl(), Exports,
-%% Attributes, Definitions) -> cerl()
-%%
-%% Exports = [cerl()]
-%% Attributes = [{cerl(), cerl()}]
-%% Definitions = [{cerl(), cerl()}]
-%%
-%% @see c_module/4
-
-update_c_module(Node, Name, Exports, Attrs, Es) ->
- #module{name = Name, exports = Exports, attrs = Attrs, defs = Es,
- ann = get_ann(Node)}.
-
-
-%% @spec is_c_module(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% module definition, otherwise false
.
-%%
-%% @see type/1
-
-is_c_module(#module{}) ->
- true;
-is_c_module(_) ->
- false.
-
-
-%% @spec module_name(Node::cerl()) -> cerl()
-%%
-%% @doc Returns the name subtree of an abstract module definition.
-%%
-%% @see c_module/4
-
-module_name(Node) ->
- Node#module.name.
-
-
-%% @spec module_exports(Node::cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of exports subtrees of an abstract module
-%% definition.
-%%
-%% @see c_module/4
-
-module_exports(Node) ->
- Node#module.exports.
-
-
-%% @spec module_attrs(Node::cerl()) -> [{cerl(), cerl()}]
-%%
-%% @doc Returns the list of pairs of attribute key/value subtrees of
-%% an abstract module definition.
-%%
-%% @see c_module/4
-
-module_attrs(Node) ->
- Node#module.attrs.
-
-
-%% @spec module_defs(Node::cerl()) -> [{cerl(), cerl()}]
-%%
-%% @doc Returns the list of function definitions of an abstract module
-%% definition.
-%%
-%% @see c_module/4
-
-module_defs(Node) ->
- Node#module.defs.
-
-
-%% @spec module_vars(Node::cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of left-hand side function variable subtrees
-%% of an abstract module definition.
-%%
-%% @see c_module/4
-
-module_vars(Node) ->
- [F || {F, _} <- module_defs(Node)].
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_int(Value::integer()) -> cerl()
-%%
-%%
-%% @doc Creates an abstract integer literal. The lexical
-%% representation is the canonical decimal numeral of
-%% Value
.
-%%
-%% @see ann_c_int/2
-%% @see is_c_int/1
-%% @see int_val/1
-%% @see int_lit/1
-%% @see c_char/1
-
-c_int(Value) ->
- #literal{val = Value}.
-
-
-%% @spec ann_c_int(As::[term()], Value::integer()) -> cerl()
-%% @see c_int/1
-
-ann_c_int(As, Value) ->
- #literal{val = Value, ann = As}.
-
-
-%% @spec is_c_int(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
represents an
-%% integer literal, otherwise false
.
-%% @see c_int/1
-
-is_c_int(#literal{val = V}) when integer(V) ->
- true;
-is_c_int(_) ->
- false.
-
-
-%% @spec int_val(cerl()) -> integer()
-%%
-%% @doc Returns the value represented by an integer literal node.
-%% @see c_int/1
-
-int_val(Node) ->
- Node#literal.val.
-
-
-%% @spec int_lit(cerl()) -> string()
-%%
-%% @doc Returns the numeral string represented by an integer literal
-%% node.
-%% @see c_int/1
-
-int_lit(Node) ->
- integer_to_list(int_val(Node)).
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_float(Value::float()) -> cerl()
-%%
-%% @doc Creates an abstract floating-point literal. The lexical
-%% representation is the decimal floating-point numeral of
-%% Value
.
-%%
-%% @see ann_c_float/2
-%% @see is_c_float/1
-%% @see float_val/1
-%% @see float_lit/1
-
-%% Note that not all floating-point numerals can be represented with
-%% full precision.
-
-c_float(Value) ->
- #literal{val = Value}.
-
-
-%% @spec ann_c_float(As::[term()], Value::float()) -> cerl()
-%% @see c_float/1
-
-ann_c_float(As, Value) ->
- #literal{val = Value, ann = As}.
-
-
-%% @spec is_c_float(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
represents a
-%% floating-point literal, otherwise false
.
-%% @see c_float/1
-
-is_c_float(#literal{val = V}) when float(V) ->
- true;
-is_c_float(_) ->
- false.
-
-
-%% @spec float_val(cerl()) -> float()
-%%
-%% @doc Returns the value represented by a floating-point literal
-%% node.
-%% @see c_float/1
-
-float_val(Node) ->
- Node#literal.val.
-
-
-%% @spec float_lit(cerl()) -> string()
-%%
-%% @doc Returns the numeral string represented by a floating-point
-%% literal node.
-%% @see c_float/1
-
-float_lit(Node) ->
- float_to_list(float_val(Node)).
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_atom(Name) -> cerl()
-%% Name = atom() | string()
-%%
-%% @doc Creates an abstract atom literal. The print name of the atom
-%% is the character sequence represented by Name
.
-%%
-%% Note: passing a string as argument to this function causes a
-%% corresponding atom to be created for the internal representation.
-%%
-%% @see ann_c_atom/2
-%% @see is_c_atom/1
-%% @see atom_val/1
-%% @see atom_name/1
-%% @see atom_lit/1
-
-c_atom(Name) when atom(Name) ->
- #literal{val = Name};
-c_atom(Name) ->
- #literal{val = list_to_atom(Name)}.
-
-
-%% @spec ann_c_atom(As::[term()], Name) -> cerl()
-%% Name = atom() | string()
-%% @see c_atom/1
-
-ann_c_atom(As, Name) when atom(Name) ->
- #literal{val = Name, ann = As};
-ann_c_atom(As, Name) ->
- #literal{val = list_to_atom(Name), ann = As}.
-
-
-%% @spec is_c_atom(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
represents an
-%% atom literal, otherwise false
.
-%%
-%% @see c_atom/1
-
-is_c_atom(#literal{val = V}) when atom(V) ->
- true;
-is_c_atom(_) ->
- false.
-
-%% @spec atom_val(cerl())-> atom()
-%%
-%% @doc Returns the value represented by an abstract atom.
-%%
-%% @see c_atom/1
-
-atom_val(Node) ->
- Node#literal.val.
-
-
-%% @spec atom_name(cerl()) -> string()
-%%
-%% @doc Returns the printname of an abstract atom.
-%%
-%% @see c_atom/1
-
-atom_name(Node) ->
- atom_to_list(atom_val(Node)).
-
-
-%% @spec atom_lit(cerl()) -> string()
-%%
-%% @doc Returns the literal string represented by an abstract
-%% atom. This always includes surrounding single-quote characters.
-%%
-%% Note that an abstract atom may have several literal
-%% representations, and that the representation yielded by this
-%% function is not fixed; e.g.,
-%% atom_lit(c_atom("a\012b"))
could yield the string
-%% "\'a\\nb\'"
.
-%%
-%% @see c_atom/1
-
-%% TODO: replace the use of the unofficial 'write_string/2'.
-
-atom_lit(Node) ->
- io_lib:write_string(atom_name(Node), $'). %' stupid Emacs.
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_char(Value) -> cerl()
-%%
-%% Value = char() | integer()
-%%
-%% @doc Creates an abstract character literal. If the local
-%% implementation of Erlang defines char()
as a subset of
-%% integer()
, this function is equivalent to
-%% c_int/1
. Otherwise, if the given value is an integer,
-%% it will be converted to the character with the corresponding
-%% code. The lexical representation of a character is
-%% "$Char
", where Char
is a single
-%% printing character or an escape sequence.
-%%
-%% @see c_int/1
-%% @see c_string/1
-%% @see ann_c_char/2
-%% @see is_c_char/1
-%% @see char_val/1
-%% @see char_lit/1
-%% @see is_print_char/1
-
-c_char(Value) when integer(Value), Value >= 0 ->
- #literal{val = Value}.
-
-
-%% @spec ann_c_char(As::[term()], Value::char()) -> cerl()
-%% @see c_char/1
-
-ann_c_char(As, Value) ->
- #literal{val = Value, ann = As}.
-
-
-%% @spec is_c_char(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
may represent a
-%% character literal, otherwise false
.
-%%
-%% If the local implementation of Erlang defines
-%% char()
as a subset of integer()
, then
-%% is_c_int(Node)
will also yield
-%% true
.
-%%
-%% @see c_char/1
-%% @see is_print_char/1
-
-is_c_char(#literal{val = V}) when integer(V), V >= 0 ->
- is_char_value(V);
-is_c_char(_) ->
- false.
-
-
-%% @spec is_print_char(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
may represent a
-%% "printing" character, otherwise false
. (Cf.
-%% is_c_char/1
.) A "printing" character has either a
-%% given graphical representation, or a "named" escape sequence such
-%% as "\n
". Currently, only ISO 8859-1 (Latin-1)
-%% character values are recognized.
-%%
-%% @see c_char/1
-%% @see is_c_char/1
-
-is_print_char(#literal{val = V}) when integer(V), V >= 0 ->
- is_print_char_value(V);
-is_print_char(_) ->
- false.
-
-
-%% @spec char_val(cerl()) -> char()
-%%
-%% @doc Returns the value represented by an abstract character literal.
-%%
-%% @see c_char/1
-
-char_val(Node) ->
- Node#literal.val.
-
-
-%% @spec char_lit(cerl()) -> string()
-%%
-%% @doc Returns the literal string represented by an abstract
-%% character. This includes a leading $
-%% character. Currently, all characters that are not in the set of ISO
-%% 8859-1 (Latin-1) "printing" characters will be escaped.
-%%
-%% @see c_char/1
-
-char_lit(Node) ->
- io_lib:write_char(char_val(Node)).
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_string(Value::string()) -> cerl()
-%%
-%% @doc Creates an abstract string literal. Equivalent to creating an
-%% abstract list of the corresponding character literals
-%% (cf. is_c_string/1
), but is typically more
-%% efficient. The lexical representation of a string is
-%% ""Chars"
", where Chars
is a
-%% sequence of printing characters or spaces.
-%%
-%% @see c_char/1
-%% @see ann_c_string/2
-%% @see is_c_string/1
-%% @see string_val/1
-%% @see string_lit/1
-%% @see is_print_string/1
-
-c_string(Value) ->
- #literal{val = Value}.
-
-
-%% @spec ann_c_string(As::[term()], Value::string()) -> cerl()
-%% @see c_string/1
-
-ann_c_string(As, Value) ->
- #literal{val = Value, ann = As}.
-
-
-%% @spec is_c_string(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
may represent a
-%% string literal, otherwise false
. Strings are defined
-%% as lists of characters; see is_c_char/1
for details.
-%%
-%% @see c_string/1
-%% @see is_c_char/1
-%% @see is_print_string/1
-
-is_c_string(#literal{val = V}) ->
- is_char_list(V);
-is_c_string(_) ->
- false.
-
-
-%% @spec is_print_string(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
may represent a
-%% string literal containing only "printing" characters, otherwise
-%% false
. See is_c_string/1
and
-%% is_print_char/1
for details. Currently, only ISO
-%% 8859-1 (Latin-1) character values are recognized.
-%%
-%% @see c_string/1
-%% @see is_c_string/1
-%% @see is_print_char/1
-
-is_print_string(#literal{val = V}) ->
- is_print_char_list(V);
-is_print_string(_) ->
- false.
-
-
-%% @spec string_val(cerl()) -> string()
-%%
-%% @doc Returns the value represented by an abstract string literal.
-%%
-%% @see c_string/1
-
-string_val(Node) ->
- Node#literal.val.
-
-
-%% @spec string_lit(cerl()) -> string()
-%%
-%% @doc Returns the literal string represented by an abstract string.
-%% This includes surrounding double-quote characters
-%% "..."
. Currently, characters that are not in the set
-%% of ISO 8859-1 (Latin-1) "printing" characters will be escaped,
-%% except for spaces.
-%%
-%% @see c_string/1
-
-string_lit(Node) ->
- io_lib:write_string(string_val(Node)).
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_nil() -> cerl()
-%%
-%% @doc Creates an abstract empty list. The result represents
-%% "[]
". The empty list is traditionally called "nil".
-%%
-%% @see ann_c_nil/1
-%% @see is_c_list/1
-%% @see c_cons/2
-
-c_nil() ->
- #literal{val = []}.
-
-
-%% @spec ann_c_nil(As::[term()]) -> cerl()
-%% @see c_nil/0
-
-ann_c_nil(As) ->
- #literal{val = [], ann = As}.
-
-
-%% @spec is_c_nil(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% empty list, otherwise false
.
-
-is_c_nil(#literal{val = []}) ->
- true;
-is_c_nil(_) ->
- false.
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_cons(Head::cerl(), Tail::cerl()) -> cerl()
-%%
-%% @doc Creates an abstract list constructor. The result represents
-%% "[Head | Tail]
". Note that if both
-%% Head
and Tail
have type
-%% literal
, then the result will also have type
-%% literal
, and annotations on Head
and
-%% Tail
are lost.
-%%
-%% Recall that in Erlang, the tail element of a list constructor is
-%% not necessarily a list.
-%%
-%% @see ann_c_cons/3
-%% @see update_c_cons/3
-%% @see c_cons_skel/2
-%% @see is_c_cons/1
-%% @see cons_hd/1
-%% @see cons_tl/1
-%% @see is_c_list/1
-%% @see c_nil/0
-%% @see list_elements/1
-%% @see list_length/1
-%% @see make_list/2
-
--record(cons, {ann = [], hd, tl}).
-
-%% *Always* collapse literals.
-
-c_cons(#literal{val = Head}, #literal{val = Tail}) ->
- #literal{val = [Head | Tail]};
-c_cons(Head, Tail) ->
- #cons{hd = Head, tl = Tail}.
-
-
-%% @spec ann_c_cons(As::[term()], Head::cerl(), Tail::cerl()) -> cerl()
-%% @see c_cons/2
-
-ann_c_cons(As, #literal{val = Head}, #literal{val = Tail}) ->
- #literal{val = [Head | Tail], ann = As};
-ann_c_cons(As, Head, Tail) ->
- #cons{hd = Head, tl = Tail, ann = As}.
-
-
-%% @spec update_c_cons(Old::cerl(), Head::cerl(), Tail::cerl()) ->
-%% cerl()
-%% @see c_cons/2
-
-update_c_cons(Node, #literal{val = Head}, #literal{val = Tail}) ->
- #literal{val = [Head | Tail], ann = get_ann(Node)};
-update_c_cons(Node, Head, Tail) ->
- #cons{hd = Head, tl = Tail, ann = get_ann(Node)}.
-
-
-%% @spec c_cons_skel(Head::cerl(), Tail::cerl()) -> cerl()
-%%
-%% @doc Creates an abstract list constructor skeleton. Does not fold
-%% constant literals, i.e., the result always has type
-%% cons
, representing "[Head |
-%% Tail]
".
-%%
-%% This function is occasionally useful when it is necessary to have
-%% annotations on the subnodes of a list constructor node, even when the
-%% subnodes are constant literals. Note however that
-%% is_literal/1
will yield false
and
-%% concrete/1
will fail if passed the result from this
-%% function.
-%%
-%% fold_literal/1
can be used to revert a node to the
-%% normal-form representation.
-%%
-%% @see ann_c_cons_skel/3
-%% @see update_c_cons_skel/3
-%% @see c_cons/2
-%% @see is_c_cons/1
-%% @see is_c_list/1
-%% @see c_nil/0
-%% @see is_literal/1
-%% @see fold_literal/1
-%% @see concrete/1
-
-%% *Never* collapse literals.
-
-c_cons_skel(Head, Tail) ->
- #cons{hd = Head, tl = Tail}.
-
-
-%% @spec ann_c_cons_skel(As::[term()], Head::cerl(), Tail::cerl()) ->
-%% cerl()
-%% @see c_cons_skel/2
-
-ann_c_cons_skel(As, Head, Tail) ->
- #cons{hd = Head, tl = Tail, ann = As}.
-
-
-%% @spec update_c_cons_skel(Old::cerl(), Head::cerl(), Tail::cerl()) ->
-%% cerl()
-%% @see c_cons_skel/2
-
-update_c_cons_skel(Node, Head, Tail) ->
- #cons{hd = Head, tl = Tail, ann = get_ann(Node)}.
-
-
-%% @spec is_c_cons(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% list constructor, otherwise false
.
-
-is_c_cons(#cons{}) ->
- true;
-is_c_cons(#literal{val = [_ | _]}) ->
- true;
-is_c_cons(_) ->
- false.
-
-
-%% @spec cons_hd(cerl()) -> cerl()
-%%
-%% @doc Returns the head subtree of an abstract list constructor.
-%%
-%% @see c_cons/2
-
-cons_hd(#cons{hd = Head}) ->
- Head;
-cons_hd(#literal{val = [Head | _]}) ->
- #literal{val = Head}.
-
-
-%% @spec cons_tl(cerl()) -> cerl()
-%%
-%% @doc Returns the tail subtree of an abstract list constructor.
-%%
-%% Recall that the tail does not necessarily represent a proper
-%% list.
-%%
-%% @see c_cons/2
-
-cons_tl(#cons{tl = Tail}) ->
- Tail;
-cons_tl(#literal{val = [_ | Tail]}) ->
- #literal{val = Tail}.
-
-
-%% @spec is_c_list(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
represents a
-%% proper list, otherwise false
. A proper list is either
-%% the empty list []
, or a cons cell [Head |
-%% Tail]
, where recursively Tail
is a
-%% proper list.
-%%
-%% Note: Because Node
is a syntax tree, the actual
-%% run-time values corresponding to its subtrees may often be partially
-%% or completely unknown. Thus, if Node
represents e.g.
-%% "[... | Ns]
" (where Ns
is a variable), then
-%% the function will return false
, because it is not known
-%% whether Ns
will be bound to a list at run-time. If
-%% Node
instead represents e.g. "[1, 2, 3]
" or
-%% "[A | []]
", then the function will return
-%% true
.
-%%
-%% @see c_cons/2
-%% @see c_nil/0
-%% @see list_elements/1
-%% @see list_length/1
-
-is_c_list(#cons{tl = Tail}) ->
- is_c_list(Tail);
-is_c_list(#literal{val = V}) ->
- is_proper_list(V);
-is_c_list(_) ->
- false.
-
-is_proper_list([_ | Tail]) ->
- is_proper_list(Tail);
-is_proper_list([]) ->
- true;
-is_proper_list(_) ->
- false.
-
-%% @spec list_elements(cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of element subtrees of an abstract list.
-%% Node
must represent a proper list. E.g., if
-%% Node
represents "[X1, X2 |
-%% [X3, X4 | []]
", then
-%% list_elements(Node)
yields the list [X1, X2, X3,
-%% X4]
.
-%%
-%% @see c_cons/2
-%% @see c_nil/1
-%% @see is_c_list/1
-%% @see list_length/1
-%% @see make_list/2
-
-list_elements(#cons{hd = Head, tl = Tail}) ->
- [Head | list_elements(Tail)];
-list_elements(#literal{val = V}) ->
- abstract_list(V).
-
-abstract_list([X | Xs]) ->
- [abstract(X) | abstract_list(Xs)];
-abstract_list([]) ->
- [].
-
-
-%% @spec list_length(Node::cerl()) -> integer()
-%%
-%% @doc Returns the number of element subtrees of an abstract list.
-%% Node
must represent a proper list. E.g., if
-%% Node
represents "[X1 | [X2, X3 | [X4, X5,
-%% X6]]]
", then list_length(Node)
returns the
-%% integer 6.
-%%
-%% Note: this is equivalent to
-%% length(list_elements(Node))
, but potentially more
-%% efficient.
-%%
-%% @see c_cons/2
-%% @see c_nil/1
-%% @see is_c_list/1
-%% @see list_elements/1
-
-list_length(L) ->
- list_length(L, 0).
-
-list_length(#cons{tl = Tail}, A) ->
- list_length(Tail, A + 1);
-list_length(#literal{val = V}, A) ->
- A + length(V).
-
-
-%% @spec make_list(List) -> Node
-%% @equiv make_list(List, none)
-
-make_list(List) ->
- ann_make_list([], List).
-
-
-%% @spec make_list(List::[cerl()], Tail) -> cerl()
-%%
-%% Tail = cerl() | none
-%%
-%% @doc Creates an abstract list from the elements in List
-%% and the optional Tail
. If Tail
is
-%% none
, the result will represent a nil-terminated list,
-%% otherwise it represents "[... | Tail]
".
-%%
-%% @see c_cons/2
-%% @see c_nil/0
-%% @see ann_make_list/3
-%% @see update_list/3
-%% @see list_elements/1
-
-make_list(List, Tail) ->
- ann_make_list([], List, Tail).
-
-
-%% @spec update_list(Old::cerl(), List::[cerl()]) -> cerl()
-%% @equiv update_list(Old, List, none)
-
-update_list(Node, List) ->
- ann_make_list(get_ann(Node), List).
-
-
-%% @spec update_list(Old::cerl(), List::[cerl()], Tail) -> cerl()
-%%
-%% Tail = cerl() | none
-%%
-%% @see make_list/2
-%% @see update_list/2
-
-update_list(Node, List, Tail) ->
- ann_make_list(get_ann(Node), List, Tail).
-
-
-%% @spec ann_make_list(As::[term()], List::[cerl()]) -> cerl()
-%% @equiv ann_make_list(As, List, none)
-
-ann_make_list(As, List) ->
- ann_make_list(As, List, none).
-
-
-%% @spec ann_make_list(As::[term()], List::[cerl()], Tail) -> cerl()
-%%
-%% Tail = cerl() | none
-%%
-%% @see make_list/2
-%% @see ann_make_list/2
-
-ann_make_list(As, [H | T], Tail) ->
- ann_c_cons(As, H, make_list(T, Tail)); % `c_cons' folds literals
-ann_make_list(As, [], none) ->
- ann_c_nil(As);
-ann_make_list(_, [], Node) ->
- Node.
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_tuple(Elements::[cerl()]) -> cerl()
-%%
-%% @doc Creates an abstract tuple. If Elements
is
-%% [E1, ..., En]
, the result represents
-%% "{E1, ..., En}
". Note that if all
-%% nodes in Elements
have type literal
, or if
-%% Elements
is empty, then the result will also have type
-%% literal
and annotations on nodes in
-%% Elements
are lost.
-%%
-%% Recall that Erlang has distinct 1-tuples, i.e., {X}
-%% is always distinct from X
itself.
-%%
-%% @see ann_c_tuple/2
-%% @see update_c_tuple/2
-%% @see is_c_tuple/1
-%% @see tuple_es/1
-%% @see tuple_arity/1
-%% @see c_tuple_skel/1
-
--record(tuple, {ann = [], es}).
-
-%% *Always* collapse literals.
-
-c_tuple(Es) ->
- case is_lit_list(Es) of
- false ->
- #tuple{es = Es};
- true ->
- #literal{val = list_to_tuple(lit_list_vals(Es))}
- end.
-
-
-%% @spec ann_c_tuple(As::[term()], Elements::[cerl()]) -> cerl()
-%% @see c_tuple/1
-
-ann_c_tuple(As, Es) ->
- case is_lit_list(Es) of
- false ->
- #tuple{es = Es, ann = As};
- true ->
- #literal{val = list_to_tuple(lit_list_vals(Es)), ann = As}
- end.
-
-
-%% @spec update_c_tuple(Old::cerl(), Elements::[cerl()]) -> cerl()
-%% @see c_tuple/1
-
-update_c_tuple(Node, Es) ->
- case is_lit_list(Es) of
- false ->
- #tuple{es = Es, ann = get_ann(Node)};
- true ->
- #literal{val = list_to_tuple(lit_list_vals(Es)),
- ann = get_ann(Node)}
- end.
-
-
-%% @spec c_tuple_skel(Elements::[cerl()]) -> cerl()
-%%
-%% @doc Creates an abstract tuple skeleton. Does not fold constant
-%% literals, i.e., the result always has type tuple
,
-%% representing "{E1, ..., En}
", if
-%% Elements
is [E1, ..., En]
.
-%%
-%% This function is occasionally useful when it is necessary to have
-%% annotations on the subnodes of a tuple node, even when all the
-%% subnodes are constant literals. Note however that
-%% is_literal/1
will yield false
and
-%% concrete/1
will fail if passed the result from this
-%% function.
-%%
-%% fold_literal/1
can be used to revert a node to the
-%% normal-form representation.
-%%
-%% @see ann_c_tuple_skel/2
-%% @see update_c_tuple_skel/2
-%% @see c_tuple/1
-%% @see tuple_es/1
-%% @see is_c_tuple/1
-%% @see is_literal/1
-%% @see fold_literal/1
-%% @see concrete/1
-
-%% *Never* collapse literals.
-
-c_tuple_skel(Es) ->
- #tuple{es = Es}.
-
-
-%% @spec ann_c_tuple_skel(As::[term()], Elements::[cerl()]) -> cerl()
-%% @see c_tuple_skel/1
-
-ann_c_tuple_skel(As, Es) ->
- #tuple{es = Es, ann = As}.
-
-
-%% @spec update_c_tuple_skel(Old::cerl(), Elements::[cerl()]) -> cerl()
-%% @see c_tuple_skel/1
-
-update_c_tuple_skel(Old, Es) ->
- #tuple{es = Es, ann = get_ann(Old)}.
-
-
-%% @spec is_c_tuple(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% tuple, otherwise false
.
-%%
-%% @see c_tuple/1
-
-is_c_tuple(#tuple{}) ->
- true;
-is_c_tuple(#literal{val = V}) when tuple(V) ->
- true;
-is_c_tuple(_) ->
- false.
-
-
-%% @spec tuple_es(cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of element subtrees of an abstract tuple.
-%%
-%% @see c_tuple/1
-
-tuple_es(#tuple{es = Es}) ->
- Es;
-tuple_es(#literal{val = V}) ->
- make_lit_list(tuple_to_list(V)).
-
-
-%% @spec tuple_arity(Node::cerl()) -> integer()
-%%
-%% @doc Returns the number of element subtrees of an abstract tuple.
-%%
-%% Note: this is equivalent to length(tuple_es(Node))
,
-%% but potentially more efficient.
-%%
-%% @see tuple_es/1
-%% @see c_tuple/1
-
-tuple_arity(#tuple{es = Es}) ->
- length(Es);
-tuple_arity(#literal{val = V}) when tuple(V) ->
- size(V).
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_var(Name::var_name()) -> cerl()
-%%
-%% var_name() = integer() | atom() | {atom(), integer()}
-%%
-%% @doc Creates an abstract variable. A variable is identified by its
-%% name, given by the Name
parameter.
-%%
-%% If a name is given by a single atom, it should either be a
-%% "simple" atom which does not need to be single-quoted in Erlang, or
-%% otherwise its print name should correspond to a proper Erlang
-%% variable, i.e., begin with an uppercase character or an
-%% underscore. Names on the form {A, N}
represent
-%% function name variables "A/N
"; these
-%% are special variables which may be bound only in the function
-%% definitions of a module or a letrec
. They may not be
-%% bound in let
expressions and cannot occur in clause
-%% patterns. The atom A
in a function name may be any
-%% atom; the integer N
must be nonnegative. The functions
-%% c_fname/2
etc. are utilities for handling function
-%% name variables.
-%%
-%% When printing variable names, they must have the form of proper
-%% Core Erlang variables and function names. E.g., a name represented
-%% by an integer such as 42
could be formatted as
-%% "_42
", an atom 'Xxx'
simply as
-%% "Xxx
", and an atom foo
as
-%% "_foo
". However, one must assure that any two valid
-%% distinct names are never mapped to the same strings. Tuples such
-%% as {foo, 2}
representing function names can simply by
-%% formatted as "'foo'/2
", with no risk of conflicts.
-%%
-%% @see ann_c_var/2
-%% @see update_c_var/2
-%% @see is_c_var/1
-%% @see var_name/1
-%% @see c_fname/2
-%% @see c_module/4
-%% @see c_letrec/2
-
--record(var, {ann = [], name}).
-
-c_var(Name) ->
- #var{name = Name}.
-
-
-%% @spec ann_c_var(As::[term()], Name::var_name()) -> cerl()
-%%
-%% @see c_var/1
-
-ann_c_var(As, Name) ->
- #var{name = Name, ann = As}.
-
-%% @spec update_c_var(Old::cerl(), Name::var_name()) -> cerl()
-%%
-%% @see c_var/1
-
-update_c_var(Node, Name) ->
- #var{name = Name, ann = get_ann(Node)}.
-
-
-%% @spec is_c_var(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% variable, otherwise false
.
-%%
-%% @see c_var/1
-
-is_c_var(#var{}) ->
- true;
-is_c_var(_) ->
- false.
-
-
-%% @spec c_fname(Name::atom(), Arity::integer()) -> cerl()
-%% @equiv c_var({Name, Arity})
-%% @see fname_id/1
-%% @see fname_arity/1
-%% @see is_c_fname/1
-%% @see ann_c_fname/3
-%% @see update_c_fname/3
-
-c_fname(Atom, Arity) ->
- c_var({Atom, Arity}).
-
-
-%% @spec ann_c_fname(As::[term()], Name::atom(), Arity::integer()) ->
-%% cerl()
-%% @equiv ann_c_var(As, {Atom, Arity})
-%% @see c_fname/2
-
-ann_c_fname(As, Atom, Arity) ->
- ann_c_var(As, {Atom, Arity}).
-
-
-%% @spec update_c_fname(Old::cerl(), Name::atom()) -> cerl()
-%% @doc Like update_c_fname/3
, but takes the arity from
-%% Node
.
-%% @see update_c_fname/3
-%% @see c_fname/2
-
-update_c_fname(#var{name = {_, Arity}, ann = As}, Atom) ->
- #var{name = {Atom, Arity}, ann = As}.
-
-
-%% @spec update_c_fname(Old::cerl(), Name::atom(), Arity::integer()) ->
-%% cerl()
-%% @equiv update_c_var(Old, {Atom, Arity})
-%% @see update_c_fname/2
-%% @see c_fname/2
-
-update_c_fname(Node, Atom, Arity) ->
- update_c_var(Node, {Atom, Arity}).
-
-
-%% @spec is_c_fname(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% function name variable, otherwise false
.
-%%
-%% @see c_fname/2
-%% @see c_var/1
-%% @see c_var_name/1
-
-is_c_fname(#var{name = {A, N}}) when atom(A), integer(N), N >= 0 ->
- true;
-is_c_fname(_) ->
- false.
-
-
-%% @spec var_name(cerl()) -> var_name()
-%%
-%% @doc Returns the name of an abstract variable.
-%%
-%% @see c_var/1
-
-var_name(Node) ->
- Node#var.name.
-
-
-%% @spec fname_id(cerl()) -> atom()
-%%
-%% @doc Returns the identifier part of an abstract function name
-%% variable.
-%%
-%% @see fname_arity/1
-%% @see c_fname/2
-
-fname_id(#var{name={A,_}}) ->
- A.
-
-
-%% @spec fname_arity(cerl()) -> integer()
-%%
-%% @doc Returns the arity part of an abstract function name variable.
-%%
-%% @see fname_id/1
-%% @see c_fname/2
-
-fname_arity(#var{name={_,N}}) ->
- N.
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_values(Elements::[cerl()]) -> cerl()
-%%
-%% @doc Creates an abstract value list. If Elements
is
-%% [E1, ..., En]
, the result represents
-%% "<E1, ..., En>
".
-%%
-%% @see ann_c_values/2
-%% @see update_c_values/2
-%% @see is_c_values/1
-%% @see values_es/1
-%% @see values_arity/1
-
--record(values, {ann = [], es}).
-
-c_values(Es) ->
- #values{es = Es}.
-
-
-%% @spec ann_c_values(As::[term()], Elements::[cerl()]) -> cerl()
-%% @see c_values/1
-
-ann_c_values(As, Es) ->
- #values{es = Es, ann = As}.
-
-
-%% @spec update_c_values(Old::cerl(), Elements::[cerl()]) -> cerl()
-%% @see c_values/1
-
-update_c_values(Node, Es) ->
- #values{es = Es, ann = get_ann(Node)}.
-
-
-%% @spec is_c_values(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% value list; otherwise false
.
-%%
-%% @see c_values/1
-
-is_c_values(#values{}) ->
- true;
-is_c_values(_) ->
- false.
-
-
-%% @spec values_es(cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of element subtrees of an abstract value
-%% list.
-%%
-%% @see c_values/1
-%% @see values_arity/1
-
-values_es(Node) ->
- Node#values.es.
-
-
-%% @spec values_arity(Node::cerl()) -> integer()
-%%
-%% @doc Returns the number of element subtrees of an abstract value
-%% list.
-%%
-%% Note: This is equivalent to
-%% length(values_es(Node))
, but potentially more
-%% efficient.
-%%
-%% @see c_values/1
-%% @see values_es/1
-
-values_arity(Node) ->
- length(values_es(Node)).
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_binary(Segments::[cerl()]) -> cerl()
-%%
-%% @doc Creates an abstract binary-template. A binary object is a
-%% sequence of 8-bit bytes. It is specified by zero or more bit-string
-%% template segments of arbitrary lengths (in number of bits),
-%% such that the sum of the lengths is evenly divisible by 8. If
-%% Segments
is [S1, ..., Sn]
, the result
-%% represents "#{S1, ..., Sn}#
". All the
-%% Si
must have type bitstr
.
-%%
-%% @see ann_c_binary/2
-%% @see update_c_binary/2
-%% @see is_c_binary/1
-%% @see binary_segments/1
-%% @see c_bitstr/5
-
--record(binary, {ann = [], segments}).
-
-c_binary(Segments) ->
- #binary{segments = Segments}.
-
-
-%% @spec ann_c_binary(As::[term()], Segments::[cerl()]) -> cerl()
-%% @see c_binary/1
-
-ann_c_binary(As, Segments) ->
- #binary{segments = Segments, ann = As}.
-
-
-%% @spec update_c_binary(Old::cerl(), Segments::[cerl()]) -> cerl()
-%% @see c_binary/1
-
-update_c_binary(Node, Segments) ->
- #binary{segments = Segments, ann = get_ann(Node)}.
-
-
-%% @spec is_c_binary(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% binary-template; otherwise false
.
-%%
-%% @see c_binary/1
-
-is_c_binary(#binary{}) ->
- true;
-is_c_binary(_) ->
- false.
-
-
-%% @spec binary_segments(cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of segment subtrees of an abstract
-%% binary-template.
-%%
-%% @see c_binary/1
-%% @see c_bitstr/5
-
-binary_segments(Node) ->
- Node#binary.segments.
-
-
-%% @spec c_bitstr(Value::cerl(), Size::cerl(), Unit::cerl(),
-%% Type::cerl(), Flags::cerl()) -> cerl()
-%%
-%% @doc Creates an abstract bit-string template. These can only occur as
-%% components of an abstract binary-template (see {@link c_binary/1}).
-%% The result represents "#<Value>(Size,
-%% Unit, Type, Flags)
", where
-%% Unit
must represent a positive integer constant,
-%% Type
must represent a constant atom (one of
-%% 'integer'
, 'float'
, or
-%% 'binary'
), and Flags
must represent a
-%% constant list "[F1, ..., Fn]"
where
-%% all the Fi
are atoms.
-%%
-%% @see c_binary/1
-%% @see ann_c_bitstr/6
-%% @see update_c_bitstr/6
-%% @see is_c_bitstr/1
-%% @see bitstr_val/1
-%% @see bitstr_size/1
-%% @see bitstr_unit/1
-%% @see bitstr_type/1
-%% @see bitstr_flags/1
-
--record(bitstr, {ann = [], val, size, unit, type, flags}).
-
-c_bitstr(Val, Size, Unit, Type, Flags) ->
- #bitstr{val = Val, size = Size, unit = Unit, type = Type,
- flags = Flags}.
-
-
-%% @spec c_bitstr(Value::cerl(), Size::cerl(), Type::cerl(),
-%% Flags::cerl()) -> cerl()
-%% @equiv c_bitstr(Value, Size, abstract(1), Type, Flags)
-
-c_bitstr(Val, Size, Type, Flags) ->
- c_bitstr(Val, Size, abstract(1), Type, Flags).
-
-
-%% @spec c_bitstr(Value::cerl(), Type::cerl(),
-%% Flags::cerl()) -> cerl()
-%% @equiv c_bitstr(Value, abstract(all), abstract(1), Type, Flags)
-
-c_bitstr(Val, Type, Flags) ->
- c_bitstr(Val, abstract(all), abstract(1), Type, Flags).
-
-
-%% @spec ann_c_bitstr(As::[term()], Value::cerl(), Size::cerl(),
-%% Unit::cerl(), Type::cerl(), Flags::cerl()) -> cerl()
-%% @see c_bitstr/5
-%% @see ann_c_bitstr/5
-
-ann_c_bitstr(As, Val, Size, Unit, Type, Flags) ->
- #bitstr{val = Val, size = Size, unit = Unit, type = Type,
- flags = Flags, ann = As}.
-
-%% @spec ann_c_bitstr(As::[term()], Value::cerl(), Size::cerl(),
-%% Type::cerl(), Flags::cerl()) -> cerl()
-%% @equiv ann_c_bitstr(As, Value, Size, abstract(1), Type, Flags)
-
-ann_c_bitstr(As, Value, Size, Type, Flags) ->
- ann_c_bitstr(As, Value, Size, abstract(1), Type, Flags).
-
-
-%% @spec update_c_bitstr(Old::cerl(), Value::cerl(), Size::cerl(),
-%% Unit::cerl(), Type::cerl(), Flags::cerl()) -> cerl()
-%% @see c_bitstr/5
-%% @see update_c_bitstr/5
-
-update_c_bitstr(Node, Val, Size, Unit, Type, Flags) ->
- #bitstr{val = Val, size = Size, unit = Unit, type = Type,
- flags = Flags, ann = get_ann(Node)}.
-
-
-%% @spec update_c_bitstr(Old::cerl(), Value::cerl(), Size::cerl(),
-%% Type::cerl(), Flags::cerl()) -> cerl()
-%% @equiv update_c_bitstr(Node, Value, Size, abstract(1), Type, Flags)
-
-update_c_bitstr(Node, Value, Size, Type, Flags) ->
- update_c_bitstr(Node, Value, Size, abstract(1), Type, Flags).
-
-%% @spec is_c_bitstr(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% bit-string template; otherwise false
.
-%%
-%% @see c_bitstr/5
-
-is_c_bitstr(#bitstr{}) ->
- true;
-is_c_bitstr(_) ->
- false.
-
-
-%% @spec bitstr_val(cerl()) -> cerl()
-%%
-%% @doc Returns the value subtree of an abstract bit-string template.
-%%
-%% @see c_bitstr/5
-
-bitstr_val(Node) ->
- Node#bitstr.val.
-
-
-%% @spec bitstr_size(cerl()) -> cerl()
-%%
-%% @doc Returns the size subtree of an abstract bit-string template.
-%%
-%% @see c_bitstr/5
-
-bitstr_size(Node) ->
- Node#bitstr.size.
-
-
-%% @spec bitstr_bitsize(cerl()) -> integer() | any | all
-%%
-%% @doc Returns the total size in bits of an abstract bit-string
-%% template. If the size field is an integer literal, the result is the
-%% product of the size and unit values; if the size field is the atom
-%% literal all
, the atom all
is returned; in
-%% all other cases, the atom any
is returned.
-%%
-%% @see c_bitstr/5
-
-bitstr_bitsize(Node) ->
- Size = Node#bitstr.size,
- case is_literal(Size) of
- true ->
- case concrete(Size) of
- all ->
- all;
- S when integer(S) ->
- S*concrete(Node#bitstr.unit);
- true ->
- any
- end;
- false ->
- any
- end.
-
-
-%% @spec bitstr_unit(cerl()) -> cerl()
-%%
-%% @doc Returns the unit subtree of an abstract bit-string template.
-%%
-%% @see c_bitstr/5
-
-bitstr_unit(Node) ->
- Node#bitstr.unit.
-
-
-%% @spec bitstr_type(cerl()) -> cerl()
-%%
-%% @doc Returns the type subtree of an abstract bit-string template.
-%%
-%% @see c_bitstr/5
-
-bitstr_type(Node) ->
- Node#bitstr.type.
-
-
-%% @spec bitstr_flags(cerl()) -> cerl()
-%%
-%% @doc Returns the flags subtree of an abstract bit-string template.
-%%
-%% @see c_bitstr/5
-
-bitstr_flags(Node) ->
- Node#bitstr.flags.
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_fun(Variables::[cerl()], Body::cerl()) -> cerl()
-%%
-%% @doc Creates an abstract fun-expression. If Variables
-%% is [V1, ..., Vn]
, the result represents "fun
-%% (V1, ..., Vn) -> Body
". All the
-%% Vi
must have type var
.
-%%
-%% @see ann_c_fun/3
-%% @see update_c_fun/3
-%% @see is_c_fun/1
-%% @see fun_vars/1
-%% @see fun_body/1
-%% @see fun_arity/1
-
--record('fun', {ann = [], vars, body}).
-
-c_fun(Variables, Body) ->
- #'fun'{vars = Variables, body = Body}.
-
-
-%% @spec ann_c_fun(As::[term()], Variables::[cerl()], Body::cerl()) ->
-%% cerl()
-%% @see c_fun/2
-
-ann_c_fun(As, Variables, Body) ->
- #'fun'{vars = Variables, body = Body, ann = As}.
-
-
-%% @spec update_c_fun(Old::cerl(), Variables::[cerl()],
-%% Body::cerl()) -> cerl()
-%% @see c_fun/2
-
-update_c_fun(Node, Variables, Body) ->
- #'fun'{vars = Variables, body = Body, ann = get_ann(Node)}.
-
-
-%% @spec is_c_fun(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% fun-expression, otherwise false
.
-%%
-%% @see c_fun/2
-
-is_c_fun(#'fun'{}) ->
- true; % Now this is fun!
-is_c_fun(_) ->
- false.
-
-
-%% @spec fun_vars(cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of parameter subtrees of an abstract
-%% fun-expression.
-%%
-%% @see c_fun/2
-%% @see fun_arity/1
-
-fun_vars(Node) ->
- Node#'fun'.vars.
-
-
-%% @spec fun_body(cerl()) -> cerl()
-%%
-%% @doc Returns the body subtree of an abstract fun-expression.
-%%
-%% @see c_fun/2
-
-fun_body(Node) ->
- Node#'fun'.body.
-
-
-%% @spec fun_arity(Node::cerl()) -> integer()
-%%
-%% @doc Returns the number of parameter subtrees of an abstract
-%% fun-expression.
-%%
-%% Note: this is equivalent to length(fun_vars(Node))
,
-%% but potentially more efficient.
-%%
-%% @see c_fun/2
-%% @see fun_vars/1
-
-fun_arity(Node) ->
- length(fun_vars(Node)).
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_seq(Argument::cerl(), Body::cerl()) -> cerl()
-%%
-%% @doc Creates an abstract sequencing expression. The result
-%% represents "do Argument Body
".
-%%
-%% @see ann_c_seq/3
-%% @see update_c_seq/3
-%% @see is_c_seq/1
-%% @see seq_arg/1
-%% @see seq_body/1
-
--record(seq, {ann = [], arg, body}).
-
-c_seq(Argument, Body) ->
- #seq{arg = Argument, body = Body}.
-
-
-%% @spec ann_c_seq(As::[term()], Argument::cerl(), Body::cerl()) ->
-%% cerl()
-%% @see c_seq/2
-
-ann_c_seq(As, Argument, Body) ->
- #seq{arg = Argument, body = Body, ann = As}.
-
-
-%% @spec update_c_seq(Old::cerl(), Argument::cerl(), Body::cerl()) ->
-%% cerl()
-%% @see c_seq/2
-
-update_c_seq(Node, Argument, Body) ->
- #seq{arg = Argument, body = Body, ann = get_ann(Node)}.
-
-
-%% @spec is_c_seq(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% sequencing expression, otherwise false
.
-%%
-%% @see c_seq/2
-
-is_c_seq(#seq{}) ->
- true;
-is_c_seq(_) ->
- false.
-
-
-%% @spec seq_arg(cerl()) -> cerl()
-%%
-%% @doc Returns the argument subtree of an abstract sequencing
-%% expression.
-%%
-%% @see c_seq/2
-
-seq_arg(Node) ->
- Node#seq.arg.
-
-
-%% @spec seq_body(cerl()) -> cerl()
-%%
-%% @doc Returns the body subtree of an abstract sequencing expression.
-%%
-%% @see c_seq/2
-
-seq_body(Node) ->
- Node#seq.body.
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_let(Variables::[cerl()], Argument::cerl(), Body::cerl()) ->
-%% cerl()
-%%
-%% @doc Creates an abstract let-expression. If Variables
-%% is [V1, ..., Vn]
, the result represents "let
-%% <V1, ..., Vn> = Argument in
-%% Body
". All the Vi
must have type
-%% var
.
-%%
-%% @see ann_c_let/4
-%% @see update_c_let/4
-%% @see is_c_let/1
-%% @see let_vars/1
-%% @see let_arg/1
-%% @see let_body/1
-%% @see let_arity/1
-
--record('let', {ann = [], vars, arg, body}).
-
-c_let(Variables, Argument, Body) ->
- #'let'{vars = Variables, arg = Argument, body = Body}.
-
-
-%% ann_c_let(As, Variables, Argument, Body) -> Node
-%% @see c_let/3
-
-ann_c_let(As, Variables, Argument, Body) ->
- #'let'{vars = Variables, arg = Argument, body = Body, ann = As}.
-
-
-%% update_c_let(Old, Variables, Argument, Body) -> Node
-%% @see c_let/3
-
-update_c_let(Node, Variables, Argument, Body) ->
- #'let'{vars = Variables, arg = Argument, body = Body,
- ann = get_ann(Node)}.
-
-
-%% @spec is_c_let(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% let-expression, otherwise false
.
-%%
-%% @see c_let/3
-
-is_c_let(#'let'{}) ->
- true;
-is_c_let(_) ->
- false.
-
-
-%% @spec let_vars(cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of left-hand side variables of an abstract
-%% let-expression.
-%%
-%% @see c_let/3
-%% @see let_arity/1
-
-let_vars(Node) ->
- Node#'let'.vars.
-
-
-%% @spec let_arg(cerl()) -> cerl()
-%%
-%% @doc Returns the argument subtree of an abstract let-expression.
-%%
-%% @see c_let/3
-
-let_arg(Node) ->
- Node#'let'.arg.
-
-
-%% @spec let_body(cerl()) -> cerl()
-%%
-%% @doc Returns the body subtree of an abstract let-expression.
-%%
-%% @see c_let/3
-
-let_body(Node) ->
- Node#'let'.body.
-
-
-%% @spec let_arity(Node::cerl()) -> integer()
-%%
-%% @doc Returns the number of left-hand side variables of an abstract
-%% let-expression.
-%%
-%% Note: this is equivalent to length(let_vars(Node))
,
-%% but potentially more efficient.
-%%
-%% @see c_let/3
-%% @see let_vars/1
-
-let_arity(Node) ->
- length(let_vars(Node)).
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_letrec(Definitions::[{cerl(), cerl()}], Body::cerl()) ->
-%% cerl()
-%%
-%% @doc Creates an abstract letrec-expression. If
-%% Definitions
is [{V1, F1}, ..., {Vn, Fn}]
,
-%% the result represents "letrec V1 = F1
-%% ... Vn = Fn in Body
. All the
-%% Vi
must have type var
and represent
-%% function names. All the Fi
must have type
-%% 'fun'
.
-%%
-%% @see ann_c_letrec/3
-%% @see update_c_letrec/3
-%% @see is_c_letrec/1
-%% @see letrec_defs/1
-%% @see letrec_body/1
-%% @see letrec_vars/1
-
--record(letrec, {ann = [], defs, body}).
-
-c_letrec(Defs, Body) ->
- #letrec{defs = Defs, body = Body}.
-
-
-%% @spec ann_c_letrec(As::[term()], Definitions::[{cerl(), cerl()}],
-%% Body::cerl()) -> cerl()
-%% @see c_letrec/2
-
-ann_c_letrec(As, Defs, Body) ->
- #letrec{defs = Defs, body = Body, ann = As}.
-
-
-%% @spec update_c_letrec(Old::cerl(),
-%% Definitions::[{cerl(), cerl()}],
-%% Body::cerl()) -> cerl()
-%% @see c_letrec/2
-
-update_c_letrec(Node, Defs, Body) ->
- #letrec{defs = Defs, body = Body, ann = get_ann(Node)}.
-
-
-%% @spec is_c_letrec(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% letrec-expression, otherwise false
.
-%%
-%% @see c_letrec/2
-
-is_c_letrec(#letrec{}) ->
- true;
-is_c_letrec(_) ->
- false.
-
-
-%% @spec letrec_defs(Node::cerl()) -> [{cerl(), cerl()}]
-%%
-%% @doc Returns the list of definitions of an abstract
-%% letrec-expression. If Node
represents "letrec
-%% V1 = F1 ... Vn = Fn in
-%% Body
", the returned value is [{V1, F1}, ...,
-%% {Vn, Fn}]
.
-%%
-%% @see c_letrec/2
-
-letrec_defs(Node) ->
- Node#letrec.defs.
-
-
-%% @spec letrec_body(cerl()) -> cerl()
-%%
-%% @doc Returns the body subtree of an abstract letrec-expression.
-%%
-%% @see c_letrec/2
-
-letrec_body(Node) ->
- Node#letrec.body.
-
-
-%% @spec letrec_vars(cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of left-hand side function variable subtrees
-%% of a letrec-expression. If Node
represents
-%% "letrec V1 = F1 ... Vn =
-%% Fn in Body
", the returned value is
-%% [V1, ..., Vn]
.
-%%
-%% @see c_letrec/2
-
-letrec_vars(Node) ->
- [F || {F, _} <- letrec_defs(Node)].
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_case(Argument::cerl(), Clauses::[cerl()]) -> cerl()
-%%
-%% @doc Creates an abstract case-expression. If Clauses
-%% is [C1, ..., Cn]
, the result represents "case
-%% Argument of C1 ... Cn
-%% end
". Clauses
must not be empty.
-%%
-%% @see ann_c_case/3
-%% @see update_c_case/3
-%% @see is_c_case/1
-%% @see c_clause/3
-%% @see case_arg/1
-%% @see case_clauses/1
-%% @see case_arity/1
-
--record('case', {ann = [], arg, clauses}).
-
-c_case(Expr, Clauses) ->
- #'case'{arg = Expr, clauses = Clauses}.
-
-
-%% @spec ann_c_case(As::[term()], Argument::cerl(),
-%% Clauses::[cerl()]) -> cerl()
-%% @see c_case/2
-
-ann_c_case(As, Expr, Clauses) ->
- #'case'{arg = Expr, clauses = Clauses, ann = As}.
-
-
-%% @spec update_c_case(Old::cerl(), Argument::cerl(),
-%% Clauses::[cerl()]) -> cerl()
-%% @see c_case/2
-
-update_c_case(Node, Expr, Clauses) ->
- #'case'{arg = Expr, clauses = Clauses, ann = get_ann(Node)}.
-
-
-%% is_c_case(Node) -> boolean()
-%%
-%% Node = cerl()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% case-expression; otherwise false
.
-%%
-%% @see c_case/2
-
-is_c_case(#'case'{}) ->
- true;
-is_c_case(_) ->
- false.
-
-
-%% @spec case_arg(cerl()) -> cerl()
-%%
-%% @doc Returns the argument subtree of an abstract case-expression.
-%%
-%% @see c_case/2
-
-case_arg(Node) ->
- Node#'case'.arg.
-
-
-%% @spec case_clauses(cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of clause subtrees of an abstract
-%% case-expression.
-%%
-%% @see c_case/2
-%% @see case_arity/1
-
-case_clauses(Node) ->
- Node#'case'.clauses.
-
-
-%% @spec case_arity(Node::cerl()) -> integer()
-%%
-%% @doc Equivalent to
-%% clause_arity(hd(case_clauses(Node)))
, but potentially
-%% more efficient.
-%%
-%% @see c_case/2
-%% @see case_clauses/1
-%% @see clause_arity/1
-
-case_arity(Node) ->
- clause_arity(hd(case_clauses(Node))).
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_clause(Patterns::[cerl()], Body::cerl()) -> cerl()
-%% @equiv c_clause(Patterns, c_atom(true), Body)
-%% @see c_atom/1
-
-c_clause(Patterns, Body) ->
- c_clause(Patterns, c_atom(true), Body).
-
-
-%% @spec c_clause(Patterns::[cerl()], Guard::cerl(), Body::cerl()) ->
-%% cerl()
-%%
-%% @doc Creates an an abstract clause. If Patterns
is
-%% [P1, ..., Pn]
, the result represents
-%% "<P1, ..., Pn> when Guard ->
-%% Body
".
-%%
-%% @see c_clause/2
-%% @see ann_c_clause/4
-%% @see update_c_clause/4
-%% @see is_c_clause/1
-%% @see c_case/2
-%% @see c_receive/3
-%% @see clause_pats/1
-%% @see clause_guard/1
-%% @see clause_body/1
-%% @see clause_arity/1
-%% @see clause_vars/1
-
--record(clause, {ann = [], pats, guard, body}).
-
-c_clause(Patterns, Guard, Body) ->
- #clause{pats = Patterns, guard = Guard, body = Body}.
-
-
-%% @spec ann_c_clause(As::[term()], Patterns::[cerl()],
-%% Body::cerl()) -> cerl()
-%% @equiv ann_c_clause(As, Patterns, c_atom(true), Body)
-%% @see c_clause/3
-ann_c_clause(As, Patterns, Body) ->
- ann_c_clause(As, Patterns, c_atom(true), Body).
-
-
-%% @spec ann_c_clause(As::[term()], Patterns::[cerl()], Guard::cerl(),
-%% Body::cerl()) -> cerl()
-%% @see ann_c_clause/3
-%% @see c_clause/3
-
-ann_c_clause(As, Patterns, Guard, Body) ->
- #clause{pats = Patterns, guard = Guard, body = Body, ann = As}.
-
-
-%% @spec update_c_clause(Old::cerl(), Patterns::[cerl()],
-%% Guard::cerl(), Body::cerl()) -> cerl()
-%% @see c_clause/3
-
-update_c_clause(Node, Patterns, Guard, Body) ->
- #clause{pats = Patterns, guard = Guard, body = Body,
- ann = get_ann(Node)}.
-
-
-%% @spec is_c_clause(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% clause, otherwise false
.
-%%
-%% @see c_clause/3
-
-is_c_clause(#clause{}) ->
- true;
-is_c_clause(_) ->
- false.
-
-
-%% @spec clause_pats(cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of pattern subtrees of an abstract clause.
-%%
-%% @see c_clause/3
-%% @see clause_arity/1
-
-clause_pats(Node) ->
- Node#clause.pats.
-
-
-%% @spec clause_guard(cerl()) -> cerl()
-%%
-%% @doc Returns the guard subtree of an abstract clause.
-%%
-%% @see c_clause/3
-
-clause_guard(Node) ->
- Node#clause.guard.
-
-
-%% @spec clause_body(cerl()) -> cerl()
-%%
-%% @doc Returns the body subtree of an abstract clause.
-%%
-%% @see c_clause/3
-
-clause_body(Node) ->
- Node#clause.body.
-
-
-%% @spec clause_arity(Node::cerl()) -> integer()
-%%
-%% @doc Returns the number of pattern subtrees of an abstract clause.
-%%
-%% Note: this is equivalent to
-%% length(clause_pats(Node))
, but potentially more
-%% efficient.
-%%
-%% @see c_clause/3
-%% @see clause_pats/1
-
-clause_arity(Node) ->
- length(clause_pats(Node)).
-
-
-%% @spec clause_vars(cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of all abstract variables in the patterns of
-%% an abstract clause. The order of listing is not defined.
-%%
-%% @see c_clause/3
-%% @see pat_list_vars/1
-
-clause_vars(Clause) ->
- pat_list_vars(clause_pats(Clause)).
-
-
-%% @spec pat_vars(Pattern::cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of all abstract variables in a pattern. An
-%% exception is thrown if Node
does not represent a
-%% well-formed Core Erlang clause pattern. The order of listing is not
-%% defined.
-%%
-%% @see pat_list_vars/1
-%% @see clause_vars/1
-
-pat_vars(Node) ->
- pat_vars(Node, []).
-
-pat_vars(Node, Vs) ->
- case type(Node) of
- var ->
- [Node | Vs];
- literal ->
- Vs;
- cons ->
- pat_vars(cons_hd(Node), pat_vars(cons_tl(Node), Vs));
- tuple ->
- pat_list_vars(tuple_es(Node), Vs);
- binary ->
- pat_list_vars(binary_segments(Node), Vs);
- bitstr ->
- pat_vars(bitstr_val(Node), Vs);
- alias ->
- pat_vars(alias_pat(Node), [alias_var(Node) | Vs])
- end.
-
-
-%% @spec pat_list_vars(Patterns::[cerl()]) -> [cerl()]
-%%
-%% @doc Returns the list of all abstract variables in the given
-%% patterns. An exception is thrown if some element in
-%% Patterns
does not represent a well-formed Core Erlang
-%% clause pattern. The order of listing is not defined.
-%%
-%% @see pat_vars/1
-%% @see clause_vars/1
-
-pat_list_vars(Ps) ->
- pat_list_vars(Ps, []).
-
-pat_list_vars([P | Ps], Vs) ->
- pat_list_vars(Ps, pat_vars(P, Vs));
-pat_list_vars([], Vs) ->
- Vs.
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_alias(Variable::cerl(), Pattern::cerl()) -> cerl()
-%%
-%% @doc Creates an abstract pattern alias. The result represents
-%% "Variable = Pattern
".
-%%
-%% @see ann_c_alias/3
-%% @see update_c_alias/3
-%% @see is_c_alias/1
-%% @see alias_var/1
-%% @see alias_pat/1
-%% @see c_clause/3
-
--record(alias, {ann = [], var, pat}).
-
-c_alias(Var, Pattern) ->
- #alias{var = Var, pat = Pattern}.
-
-
-%% @spec ann_c_alias(As::[term()], Variable::cerl(),
-%% Pattern::cerl()) -> cerl()
-%% @see c_alias/2
-
-ann_c_alias(As, Var, Pattern) ->
- #alias{var = Var, pat = Pattern, ann = As}.
-
-
-%% @spec update_c_alias(Old::cerl(), Variable::cerl(),
-%% Pattern::cerl()) -> cerl()
-%% @see c_alias/2
-
-update_c_alias(Node, Var, Pattern) ->
- #alias{var = Var, pat = Pattern, ann = get_ann(Node)}.
-
-
-%% @spec is_c_alias(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% pattern alias, otherwise false
.
-%%
-%% @see c_alias/2
-
-is_c_alias(#alias{}) ->
- true;
-is_c_alias(_) ->
- false.
-
-
-%% @spec alias_var(cerl()) -> cerl()
-%%
-%% @doc Returns the variable subtree of an abstract pattern alias.
-%%
-%% @see c_alias/2
-
-alias_var(Node) ->
- Node#alias.var.
-
-
-%% @spec alias_pat(cerl()) -> cerl()
-%%
-%% @doc Returns the pattern subtree of an abstract pattern alias.
-%%
-%% @see c_alias/2
-
-alias_pat(Node) ->
- Node#alias.pat.
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_receive(Clauses::[cerl()]) -> cerl()
-%% @equiv c_receive(Clauses, c_atom(infinity), c_atom(true))
-%% @see c_atom/1
-
-c_receive(Clauses) ->
- c_receive(Clauses, c_atom(infinity), c_atom(true)).
-
-
-%% @spec c_receive(Clauses::[cerl()], Timeout::cerl(),
-%% Action::cerl()) -> cerl()
-%%
-%% @doc Creates an abstract receive-expression. If
-%% Clauses
is [C1, ..., Cn]
, the result
-%% represents "receive C1 ... Cn after
-%% Timeout -> Action end
".
-%%
-%% @see c_receive/1
-%% @see ann_c_receive/4
-%% @see update_c_receive/4
-%% @see is_c_receive/1
-%% @see receive_clauses/1
-%% @see receive_timeout/1
-%% @see receive_action/1
-
--record('receive', {ann = [], clauses, timeout, action}).
-
-c_receive(Clauses, Timeout, Action) ->
- #'receive'{clauses = Clauses, timeout = Timeout, action = Action}.
-
-
-%% @spec ann_c_receive(As::[term()], Clauses::[cerl()]) -> cerl()
-%% @equiv ann_c_receive(As, Clauses, c_atom(infinity), c_atom(true))
-%% @see c_receive/3
-%% @see c_atom/1
-
-ann_c_receive(As, Clauses) ->
- ann_c_receive(As, Clauses, c_atom(infinity), c_atom(true)).
-
-
-%% @spec ann_c_receive(As::[term()], Clauses::[cerl()],
-%% Timeout::cerl(), Action::cerl()) -> cerl()
-%% @see ann_c_receive/2
-%% @see c_receive/3
-
-ann_c_receive(As, Clauses, Timeout, Action) ->
- #'receive'{clauses = Clauses, timeout = Timeout, action = Action,
- ann = As}.
-
-
-%% @spec update_c_receive(Old::cerl(), Clauses::[cerl()],
-%% Timeout::cerl(), Action::cerl()) -> cerl()
-%% @see c_receive/3
-
-update_c_receive(Node, Clauses, Timeout, Action) ->
- #'receive'{clauses = Clauses, timeout = Timeout, action = Action,
- ann = get_ann(Node)}.
-
-
-%% @spec is_c_receive(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% receive-expression, otherwise false
.
-%%
-%% @see c_receive/3
-
-is_c_receive(#'receive'{}) ->
- true;
-is_c_receive(_) ->
- false.
-
-
-%% @spec receive_clauses(cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of clause subtrees of an abstract
-%% receive-expression.
-%%
-%% @see c_receive/3
-
-receive_clauses(Node) ->
- Node#'receive'.clauses.
-
-
-%% @spec receive_timeout(cerl()) -> cerl()
-%%
-%% @doc Returns the timeout subtree of an abstract receive-expression.
-%%
-%% @see c_receive/3
-
-receive_timeout(Node) ->
- Node#'receive'.timeout.
-
-
-%% @spec receive_action(cerl()) -> cerl()
-%%
-%% @doc Returns the action subtree of an abstract receive-expression.
-%%
-%% @see c_receive/3
-
-receive_action(Node) ->
- Node#'receive'.action.
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_apply(Operator::cerl(), Arguments::[cerl()]) -> cerl()
-%%
-%% @doc Creates an abstract function application. If
-%% Arguments
is [A1, ..., An]
, the result
-%% represents "apply Operator(A1, ...,
-%% An)
".
-%%
-%% @see ann_c_apply/3
-%% @see update_c_apply/3
-%% @see is_c_apply/1
-%% @see apply_op/1
-%% @see apply_args/1
-%% @see apply_arity/1
-%% @see c_call/3
-%% @see c_primop/2
-
--record(apply, {ann = [], op, args}).
-
-c_apply(Operator, Arguments) ->
- #apply{op = Operator, args = Arguments}.
-
-
-%% @spec ann_c_apply(As::[term()], Operator::cerl(),
-%% Arguments::[cerl()]) -> cerl()
-%% @see c_apply/2
-
-ann_c_apply(As, Operator, Arguments) ->
- #apply{op = Operator, args = Arguments, ann = As}.
-
-
-%% @spec update_c_apply(Old::cerl(), Operator::cerl(),
-%% Arguments::[cerl()]) -> cerl()
-%% @see c_apply/2
-
-update_c_apply(Node, Operator, Arguments) ->
- #apply{op = Operator, args = Arguments, ann = get_ann(Node)}.
-
-
-%% @spec is_c_apply(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% function application, otherwise false
.
-%%
-%% @see c_apply/2
-
-is_c_apply(#apply{}) ->
- true;
-is_c_apply(_) ->
- false.
-
-
-%% @spec apply_op(cerl()) -> cerl()
-%%
-%% @doc Returns the operator subtree of an abstract function
-%% application.
-%%
-%% @see c_apply/2
-
-apply_op(Node) ->
- Node#apply.op.
-
-
-%% @spec apply_args(cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of argument subtrees of an abstract function
-%% application.
-%%
-%% @see c_apply/2
-%% @see apply_arity/1
-
-apply_args(Node) ->
- Node#apply.args.
-
-
-%% @spec apply_arity(Node::cerl()) -> integer()
-%%
-%% @doc Returns the number of argument subtrees of an abstract
-%% function application.
-%%
-%% Note: this is equivalent to
-%% length(apply_args(Node))
, but potentially more
-%% efficient.
-%%
-%% @see c_apply/2
-%% @see apply_args/1
-
-apply_arity(Node) ->
- length(apply_args(Node)).
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_call(Module::cerl(), Name::cerl(), Arguments::[cerl()]) ->
-%% cerl()
-%%
-%% @doc Creates an abstract inter-module call. If
-%% Arguments
is [A1, ..., An]
, the result
-%% represents "call Module:Name(A1,
-%% ..., An)
".
-%%
-%% @see ann_c_call/4
-%% @see update_c_call/4
-%% @see is_c_call/1
-%% @see call_module/1
-%% @see call_name/1
-%% @see call_args/1
-%% @see call_arity/1
-%% @see c_apply/2
-%% @see c_primop/2
-
--record(call, {ann = [], module, name, args}).
-
-c_call(Module, Name, Arguments) ->
- #call{module = Module, name = Name, args = Arguments}.
-
-
-%% @spec ann_c_call(As::[term()], Module::cerl(), Name::cerl(),
-%% Arguments::[cerl()]) -> cerl()
-%% @see c_call/3
-
-ann_c_call(As, Module, Name, Arguments) ->
- #call{module = Module, name = Name, args = Arguments, ann = As}.
-
-
-%% @spec update_c_call(Old::cerl(), Module::cerl(), Name::cerl(),
-%% Arguments::[cerl()]) -> cerl()
-%% @see c_call/3
-
-update_c_call(Node, Module, Name, Arguments) ->
- #call{module = Module, name = Name, args = Arguments,
- ann = get_ann(Node)}.
-
-
-%% @spec is_c_call(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% inter-module call expression; otherwise false
.
-%%
-%% @see c_call/3
-
-is_c_call(#call{}) ->
- true;
-is_c_call(_) ->
- false.
-
-
-%% @spec call_module(cerl()) -> cerl()
-%%
-%% @doc Returns the module subtree of an abstract inter-module call.
-%%
-%% @see c_call/3
-
-call_module(Node) ->
- Node#call.module.
-
-
-%% @spec call_name(cerl()) -> cerl()
-%%
-%% @doc Returns the name subtree of an abstract inter-module call.
-%%
-%% @see c_call/3
-
-call_name(Node) ->
- Node#call.name.
-
-
-%% @spec call_args(cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of argument subtrees of an abstract
-%% inter-module call.
-%%
-%% @see c_call/3
-%% @see call_arity/1
-
-call_args(Node) ->
- Node#call.args.
-
-
-%% @spec call_arity(Node::cerl()) -> integer()
-%%
-%% @doc Returns the number of argument subtrees of an abstract
-%% inter-module call.
-%%
-%% Note: this is equivalent to
-%% length(call_args(Node))
, but potentially more
-%% efficient.
-%%
-%% @see c_call/3
-%% @see call_args/1
-
-call_arity(Node) ->
- length(call_args(Node)).
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_primop(Name::cerl(), Arguments::[cerl()]) -> cerl()
-%%
-%% @doc Creates an abstract primitive operation call. If
-%% Arguments
is [A1, ..., An]
, the result
-%% represents "primop Name(A1, ...,
-%% An)
". Name
must be an atom literal.
-%%
-%% @see ann_c_primop/3
-%% @see update_c_primop/3
-%% @see is_c_primop/1
-%% @see primop_name/1
-%% @see primop_args/1
-%% @see primop_arity/1
-%% @see c_apply/2
-%% @see c_call/3
-
--record(primop, {ann = [], name, args}).
-
-c_primop(Name, Arguments) ->
- #primop{name = Name, args = Arguments}.
-
-
-%% @spec ann_c_primop(As::[term()], Name::cerl(),
-%% Arguments::[cerl()]) -> cerl()
-%% @see c_primop/2
-
-ann_c_primop(As, Name, Arguments) ->
- #primop{name = Name, args = Arguments, ann = As}.
-
-
-%% @spec update_c_primop(Old::cerl(), Name::cerl(),
-%% Arguments::[cerl()]) -> cerl()
-%% @see c_primop/2
-
-update_c_primop(Node, Name, Arguments) ->
- #primop{name = Name, args = Arguments, ann = get_ann(Node)}.
-
-
-%% @spec is_c_primop(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% primitive operation call, otherwise false
.
-%%
-%% @see c_primop/2
-
-is_c_primop(#primop{}) ->
- true;
-is_c_primop(_) ->
- false.
-
-
-%% @spec primop_name(cerl()) -> cerl()
-%%
-%% @doc Returns the name subtree of an abstract primitive operation
-%% call.
-%%
-%% @see c_primop/2
-
-primop_name(Node) ->
- Node#primop.name.
-
-
-%% @spec primop_args(cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of argument subtrees of an abstract primitive
-%% operation call.
-%%
-%% @see c_primop/2
-%% @see primop_arity/1
-
-primop_args(Node) ->
- Node#primop.args.
-
-
-%% @spec primop_arity(Node::cerl()) -> integer()
-%%
-%% @doc Returns the number of argument subtrees of an abstract
-%% primitive operation call.
-%%
-%% Note: this is equivalent to
-%% length(primop_args(Node))
, but potentially more
-%% efficient.
-%%
-%% @see c_primop/2
-%% @see primop_args/1
-
-primop_arity(Node) ->
- length(primop_args(Node)).
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_try(Argument::cerl(), Variables::[cerl()], Body::cerl(),
-%% ExceptionVars::[cerl()], Handler::cerl()) -> cerl()
-%%
-%% @doc Creates an abstract try-expression. If Variables
is
-%% [V1, ..., Vn]
and ExceptionVars
is
-%% [X1, ..., Xm]
, the result represents "try
-%% Argument of <V1, ..., Vn> ->
-%% Body catch <X1, ..., Xm> ->
-%% Handler
". All the Vi
and Xi
-%% must have type var
.
-%%
-%% @see ann_c_try/6
-%% @see update_c_try/6
-%% @see is_c_try/1
-%% @see try_arg/1
-%% @see try_vars/1
-%% @see try_body/1
-%% @see c_catch/1
-
--record('try', {ann = [], arg, vars, body, evars, handler}).
-
-c_try(Expr, Vs, Body, Evs, Handler) ->
- #'try'{arg = Expr, vars = Vs, body = Body,
- evars = Evs, handler = Handler}.
-
-
-%% @spec ann_c_try(As::[term()], Expression::cerl(),
-%% Variables::[cerl()], Body::cerl(),
-%% EVars::[cerl()], EBody::[cerl()]) -> cerl()
-%% @see c_try/3
-
-ann_c_try(As, Expr, Vs, Body, Evs, Handler) ->
- #'try'{arg = Expr, vars = Vs, body = Body,
- evars = Evs, handler = Handler, ann = As}.
-
-
-%% @spec update_c_try(Old::cerl(), Expression::cerl(),
-%% Variables::[cerl()], Body::cerl(),
-%% EVars::[cerl()], EBody::[cerl()]) -> cerl()
-%% @see c_try/3
-
-update_c_try(Node, Expr, Vs, Body, Evs, Handler) ->
- #'try'{arg = Expr, vars = Vs, body = Body,
- evars = Evs, handler = Handler, ann = get_ann(Node)}.
-
-
-%% @spec is_c_try(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% try-expression, otherwise false
.
-%%
-%% @see c_try/3
-
-is_c_try(#'try'{}) ->
- true;
-is_c_try(_) ->
- false.
-
-
-%% @spec try_arg(cerl()) -> cerl()
-%%
-%% @doc Returns the expression subtree of an abstract try-expression.
-%%
-%% @see c_try/3
-
-try_arg(Node) ->
- Node#'try'.arg.
-
-
-%% @spec try_vars(cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of success variable subtrees of an abstract
-%% try-expression.
-%%
-%% @see c_try/3
-
-try_vars(Node) ->
- Node#'try'.vars.
-
-
-%% @spec try_body(cerl()) -> cerl()
-%%
-%% @doc Returns the success body subtree of an abstract try-expression.
-%%
-%% @see c_try/3
-
-try_body(Node) ->
- Node#'try'.body.
-
-
-%% @spec try_evars(cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of exception variable subtrees of an abstract
-%% try-expression.
-%%
-%% @see c_try/3
-
-try_evars(Node) ->
- Node#'try'.evars.
-
-
-%% @spec try_handler(cerl()) -> cerl()
-%%
-%% @doc Returns the exception body subtree of an abstract
-%% try-expression.
-%%
-%% @see c_try/3
-
-try_handler(Node) ->
- Node#'try'.handler.
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec c_catch(Body::cerl()) -> cerl()
-%%
-%% @doc Creates an abstract catch-expression. The result represents
-%% "catch Body
".
-%%
-%% Note: catch-expressions can be rewritten as try-expressions, and
-%% will eventually be removed from Core Erlang.
-%%
-%% @see ann_c_catch/2
-%% @see update_c_catch/2
-%% @see is_c_catch/1
-%% @see catch_body/1
-%% @see c_try/3
-
--record('catch', {ann = [], body}).
-
-c_catch(Body) ->
- #'catch'{body = Body}.
-
-
-%% @spec ann_c_catch(As::[term()], Body::cerl()) -> cerl()
-%% @see c_catch/1
-
-ann_c_catch(As, Body) ->
- #'catch'{body = Body, ann = As}.
-
-
-%% @spec update_c_catch(Old::cerl(), Body::cerl()) -> cerl()
-%% @see c_catch/1
-
-update_c_catch(Node, Body) ->
- #'catch'{body = Body, ann = get_ann(Node)}.
-
-
-%% @spec is_c_catch(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
is an abstract
-%% catch-expression, otherwise false
.
-%%
-%% @see c_catch/1
-
-is_c_catch(#'catch'{}) ->
- true;
-is_c_catch(_) ->
- false.
-
-
-%% @spec catch_body(Node::cerl()) -> cerl()
-%%
-%% @doc Returns the body subtree of an abstract catch-expression.
-%%
-%% @see c_catch/1
-
-catch_body(Node) ->
- Node#'catch'.body.
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec to_records(Tree::cerl()) -> record(record_types())
-%%
-%% @doc Translates an abstract syntax tree to a corresponding explicit
-%% record representation. The records are defined in the file
-%% "cerl.hrl
".
-%%
-%% Note: Compound constant literals are always unfolded in the
-%% record representation.
-%%
-%% @see type/1
-%% @see from_records/1
-
-to_records(Node) ->
- A = get_ann(Node),
- case type(Node) of
- literal ->
- lit_to_records(concrete(Node), A);
- binary ->
- #c_binary{anno = A,
- segments =
- list_to_records(binary_segments(Node))};
- bitstr ->
- #c_bitstr{anno = A,
- val = to_records(bitstr_val(Node)),
- size = to_records(bitstr_size(Node)),
- unit = to_records(bitstr_unit(Node)),
- type = to_records(bitstr_type(Node)),
- flags = to_records(bitstr_flags(Node))};
- cons ->
- #c_cons{anno = A,
- hd = to_records(cons_hd(Node)),
- tl = to_records(cons_tl(Node))};
- tuple ->
- #c_tuple{anno = A,
- es = list_to_records(tuple_es(Node))};
- var ->
- case is_c_fname(Node) of
- true ->
- #c_fname{anno = A,
- id = fname_id(Node),
- arity = fname_arity(Node)};
- false ->
- #c_var{anno = A, name = var_name(Node)}
- end;
- values ->
- #c_values{anno = A,
- es = list_to_records(values_es(Node))};
- 'fun' ->
- #c_fun{anno = A,
- vars = list_to_records(fun_vars(Node)),
- body = to_records(fun_body(Node))};
- seq ->
- #c_seq{anno = A,
- arg = to_records(seq_arg(Node)),
- body = to_records(seq_body(Node))};
- 'let' ->
- #c_let{anno = A,
- vars = list_to_records(let_vars(Node)),
- arg = to_records(let_arg(Node)),
- body = to_records(let_body(Node))};
- letrec ->
- #c_letrec{anno = A,
- defs = [#c_def{name = to_records(N),
- val = to_records(F)}
- || {N, F} <- letrec_defs(Node)],
- body = to_records(letrec_body(Node))};
- 'case' ->
- #c_case{anno = A,
- arg = to_records(case_arg(Node)),
- clauses =
- list_to_records(case_clauses(Node))};
- clause ->
- #c_clause{anno = A,
- pats = list_to_records(clause_pats(Node)),
- guard = to_records(clause_guard(Node)),
- body = to_records(clause_body(Node))};
- alias ->
- #c_alias{anno = A,
- var = to_records(alias_var(Node)),
- pat = to_records(alias_pat(Node))};
- 'receive' ->
- #c_receive{anno = A,
- clauses =
- list_to_records(receive_clauses(Node)),
- timeout =
- to_records(receive_timeout(Node)),
- action =
- to_records(receive_action(Node))};
- apply ->
- #c_apply{anno = A,
- op = to_records(apply_op(Node)),
- args = list_to_records(apply_args(Node))};
- call ->
- #c_call{anno = A,
- module = to_records(call_module(Node)),
- name = to_records(call_name(Node)),
- args = list_to_records(call_args(Node))};
- primop ->
- #c_primop{anno = A,
- name = to_records(primop_name(Node)),
- args = list_to_records(primop_args(Node))};
- 'try' ->
- #c_try{anno = A,
- arg = to_records(try_arg(Node)),
- vars = list_to_records(try_vars(Node)),
- body = to_records(try_body(Node)),
- evars = list_to_records(try_evars(Node)),
- handler = to_records(try_handler(Node))};
- 'catch' ->
- #c_catch{anno = A,
- body = to_records(catch_body(Node))};
- module ->
- #c_module{anno = A,
- name = to_records(module_name(Node)),
- exports = list_to_records(
- module_exports(Node)),
- attrs = [#c_def{name = to_records(K),
- val = to_records(V)}
- || {K, V} <- module_attrs(Node)],
- defs = [#c_def{name = to_records(N),
- val = to_records(F)}
- || {N, F} <- module_defs(Node)]}
- end.
-
-list_to_records([T | Ts]) ->
- [to_records(T) | list_to_records(Ts)];
-list_to_records([]) ->
- [].
-
-lit_to_records(V, A) when integer(V) ->
- #c_int{anno = A, val = V};
-lit_to_records(V, A) when float(V) ->
- #c_float{anno = A, val = V};
-lit_to_records(V, A) when atom(V) ->
- #c_atom{anno = A, val = V};
-lit_to_records([H | T] = V, A) ->
- case is_print_char_list(V) of
- true ->
- #c_string{anno = A, val = V};
- false ->
- #c_cons{anno = A,
- hd = lit_to_records(H, []),
- tl = lit_to_records(T, [])}
- end;
-lit_to_records([], A) ->
- #c_nil{anno = A};
-lit_to_records(V, A) when tuple(V) ->
- #c_tuple{anno = A, es = lit_list_to_records(tuple_to_list(V))}.
-
-lit_list_to_records([T | Ts]) ->
- [lit_to_records(T, []) | lit_list_to_records(Ts)];
-lit_list_to_records([]) ->
- [].
-
-
-%% @spec from_records(Tree::record(record_types())) -> cerl()
-%%
-%% record_types() = c_alias | c_apply | c_call | c_case | c_catch |
-%% c_clause | c_cons | c_def| c_fun | c_let |
-%% c_letrec |c_lit | c_module | c_primop |
-%% c_receive | c_seq | c_try | c_tuple |
-%% c_values | c_var
-%%
-%% @doc Translates an explicit record representation to a
-%% corresponding abstract syntax tree. The records are defined in the
-%% file "cerl.hrl
".
-%%
-%% Note: Compound constant literals are folded, discarding
-%% annotations on subtrees. There are no c_def
nodes in
-%% the abstract representation; annotations on c_def
-%% records are discarded.
-%%
-%% @see type/1
-%% @see to_records/1
-
-from_records(#c_int{val = V, anno = As}) ->
- ann_c_int(As, V);
-from_records(#c_float{val = V, anno = As}) ->
- ann_c_float(As, V);
-from_records(#c_atom{val = V, anno = As}) ->
- ann_c_atom(As, V);
-from_records(#c_char{val = V, anno = As}) ->
- ann_c_char(As, V);
-from_records(#c_string{val = V, anno = As}) ->
- ann_c_string(As, V);
-from_records(#c_nil{anno = As}) ->
- ann_c_nil(As);
-from_records(#c_binary{segments = Ss, anno = As}) ->
- ann_c_binary(As, from_records_list(Ss));
-from_records(#c_bitstr{val = V, size = S, unit = U, type = T,
- flags = Fs, anno = As}) ->
- ann_c_bitstr(As, from_records(V), from_records(S), from_records(U),
- from_records(T), from_records(Fs));
-from_records(#c_cons{hd = H, tl = T, anno = As}) ->
- ann_c_cons(As, from_records(H), from_records(T));
-from_records(#c_tuple{es = Es, anno = As}) ->
- ann_c_tuple(As, from_records_list(Es));
-from_records(#c_var{name = Name, anno = As}) ->
- ann_c_var(As, Name);
-from_records(#c_fname{id = Id, arity = Arity, anno = As}) ->
- ann_c_fname(As, Id, Arity);
-from_records(#c_values{es = Es, anno = As}) ->
- ann_c_values(As, from_records_list(Es));
-from_records(#c_fun{vars = Vs, body = B, anno = As}) ->
- ann_c_fun(As, from_records_list(Vs), from_records(B));
-from_records(#c_seq{arg = A, body = B, anno = As}) ->
- ann_c_seq(As, from_records(A), from_records(B));
-from_records(#c_let{vars = Vs, arg = A, body = B, anno = As}) ->
- ann_c_let(As, from_records_list(Vs), from_records(A),
- from_records(B));
-from_records(#c_letrec{defs = Fs, body = B, anno = As}) ->
- ann_c_letrec(As, [{from_records(N), from_records(F)}
- || #c_def{name = N, val = F} <- Fs],
- from_records(B));
-from_records(#c_case{arg = A, clauses = Cs, anno = As}) ->
- ann_c_case(As, from_records(A), from_records_list(Cs));
-from_records(#c_clause{pats = Ps, guard = G, body = B, anno = As}) ->
- ann_c_clause(As, from_records_list(Ps), from_records(G),
- from_records(B));
-from_records(#c_alias{var = V, pat = P, anno = As}) ->
- ann_c_alias(As, from_records(V), from_records(P));
-from_records(#c_receive{clauses = Cs, timeout = T, action = A,
- anno = As}) ->
- ann_c_receive(As, from_records_list(Cs), from_records(T),
- from_records(A));
-from_records(#c_apply{op = Op, args = Es, anno = As}) ->
- ann_c_apply(As, from_records(Op), from_records_list(Es));
-from_records(#c_call{module = M, name = N, args = Es, anno = As}) ->
- ann_c_call(As, from_records(M), from_records(N),
- from_records_list(Es));
-from_records(#c_primop{name = N, args = Es, anno = As}) ->
- ann_c_primop(As, from_records(N), from_records_list(Es));
-from_records(#c_try{arg = E, vars = Vs, body = B,
- evars = Evs, handler = H, anno = As}) ->
- ann_c_try(As, from_records(E), from_records_list(Vs),
- from_records(B), from_records_list(Evs), from_records(H));
-from_records(#c_catch{body = B, anno = As}) ->
- ann_c_catch(As, from_records(B));
-from_records(#c_module{name = N, exports = Es, attrs = Ds, defs = Fs,
- anno = As}) ->
- ann_c_module(As, from_records(N),
- from_records_list(Es),
- [{from_records(K), from_records(V)}
- || #c_def{name = K, val = V} <- Ds],
- [{from_records(V), from_records(F)}
- || #c_def{name = V, val = F} <- Fs]).
-
-from_records_list([T | Ts]) ->
- [from_records(T) | from_records_list(Ts)];
-from_records_list([]) ->
- [].
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec is_data(Node::cerl()) -> boolean()
-%%
-%% @doc Returns true
if Node
represents a
-%% data constructor, otherwise false
. Data constructors
-%% are cons cells, tuples, and atomic literals.
-%%
-%% @see data_type/1
-%% @see data_es/1
-%% @see data_arity/1
-
-is_data(#literal{}) ->
- true;
-is_data(#cons{}) ->
- true;
-is_data(#tuple{}) ->
- true;
-is_data(_) ->
- false.
-
-
-%% @spec data_type(Node::cerl()) -> dtype()
-%%
-%% dtype() = cons | tuple | {'atomic', Value}
-%% Value = integer() | float() | atom() | []
-%%
-%% @doc Returns a type descriptor for a data constructor
-%% node. (Cf. is_data/1
.) This is mainly useful for
-%% comparing types and for constructing new nodes of the same type
-%% (cf. make_data/2
). If Node
represents an
-%% integer, floating-point number, atom or empty list, the result is
-%% {'atomic', Value}
, where Value
is the value
-%% of concrete(Node)
, otherwise the result is either
-%% cons
or tuple
.
-%%
-%% Type descriptors can be compared for equality or order (in the
-%% Erlang term order), but remember that floating-point values should
-%% in general never be tested for equality.
-%%
-%% @see is_data/1
-%% @see make_data/2
-%% @see type/1
-%% @see concrete/1
-
-data_type(#literal{val = V}) ->
- case V of
- [_ | _] ->
- cons;
- _ when tuple(V) ->
- tuple;
- _ ->
- {'atomic', V}
- end;
-data_type(#cons{}) ->
- cons;
-data_type(#tuple{}) ->
- tuple.
-
-
-%% @spec data_es(Node::cerl()) -> [cerl()]
-%%
-%% @doc Returns the list of subtrees of a data constructor node. If
-%% the arity of the constructor is zero, the result is the empty list.
-%%
-%% Note: if data_type(Node)
is cons
, the
-%% number of subtrees is exactly two. If data_type(Node)
-%% is {'atomic', Value}
, the number of subtrees is
-%% zero.
-%%
-%% @see is_data/1
-%% @see data_type/1
-%% @see data_arity/1
-%% @see make_data/2
-
-data_es(#literal{val = V}) ->
- case V of
- [Head | Tail] ->
- [#literal{val = Head}, #literal{val = Tail}];
- _ when tuple(V) ->
- make_lit_list(tuple_to_list(V));
- _ ->
- []
- end;
-data_es(#cons{hd = H, tl = T}) ->
- [H, T];
-data_es(#tuple{es = Es}) ->
- Es.
-
-
-%% @spec data_arity(Node::cerl()) -> integer()
-%%
-%% @doc Returns the number of subtrees of a data constructor
-%% node. This is equivalent to length(data_es(Node))
, but
-%% potentially more efficient.
-%%
-%% @see is_data/1
-%% @see data_es/1
-
-data_arity(#literal{val = V}) ->
- case V of
- [_ | _] ->
- 2;
- _ when tuple(V) ->
- size(V);
- _ ->
- 0
- end;
-data_arity(#cons{}) ->
- 2;
-data_arity(#tuple{es = Es}) ->
- length(Es).
-
-
-%% @spec make_data(Type::dtype(), Elements::[cerl()]) -> cerl()
-%%
-%% @doc Creates a data constructor node with the specified type and
-%% subtrees. (Cf. data_type/1
.) An exception is thrown
-%% if the length of Elements
is invalid for the given
-%% Type
; see data_es/1
for arity constraints
-%% on constructor types.
-%%
-%% @see data_type/1
-%% @see data_es/1
-%% @see ann_make_data/3
-%% @see update_data/3
-%% @see make_data_skel/2
-
-make_data(CType, Es) ->
- ann_make_data([], CType, Es).
-
-
-%% @spec ann_make_data(As::[term()], Type::dtype(),
-%% Elements::[cerl()]) -> cerl()
-%% @see make_data/2
-
-ann_make_data(As, {'atomic', V}, []) -> #literal{val = V, ann = As};
-ann_make_data(As, cons, [H, T]) -> ann_c_cons(As, H, T);
-ann_make_data(As, tuple, Es) -> ann_c_tuple(As, Es).
-
-
-%% @spec update_data(Old::cerl(), Type::dtype(),
-%% Elements::[cerl()]) -> cerl()
-%% @see make_data/2
-
-update_data(Node, CType, Es) ->
- ann_make_data(get_ann(Node), CType, Es).
-
-
-%% @spec make_data_skel(Type::dtype(), Elements::[cerl()]) -> cerl()
-%%
-%% @doc Like make_data/2
, but analogous to
-%% c_tuple_skel/1
and c_cons_skel/2
.
-%%
-%% @see ann_make_data_skel/3
-%% @see update_data_skel/3
-%% @see make_data/2
-%% @see c_tuple_skel/1
-%% @see c_cons_skel/2
-
-make_data_skel(CType, Es) ->
- ann_make_data_skel([], CType, Es).
-
-
-%% @spec ann_make_data_skel(As::[term()], Type::dtype(),
-%% Elements::[cerl()]) -> cerl()
-%% @see make_data_skel/2
-
-ann_make_data_skel(As, {'atomic', V}, []) -> #literal{val = V, ann = As};
-ann_make_data_skel(As, cons, [H, T]) -> ann_c_cons_skel(As, H, T);
-ann_make_data_skel(As, tuple, Es) -> ann_c_tuple_skel(As, Es).
-
-
-%% @spec update_data_skel(Old::cerl(), Type::dtype(),
-%% Elements::[cerl()]) -> cerl()
-%% @see make_data_skel/2
-
-update_data_skel(Node, CType, Es) ->
- ann_make_data_skel(get_ann(Node), CType, Es).
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec subtrees(Node::cerl()) -> [[cerl()]]
-%%
-%% @doc Returns the grouped list of all subtrees of a node. If
-%% Node
is a leaf node (cf. is_leaf/1
), this
-%% is the empty list, otherwise the result is always a nonempty list,
-%% containing the lists of subtrees of Node
, in
-%% left-to-right order as they occur in the printed program text, and
-%% grouped by category. Often, each group contains only a single
-%% subtree.
-%%
-%% Depending on the type of Node
, the size of some
-%% groups may be variable (e.g., the group consisting of all the
-%% elements of a tuple), while others always contain the same number
-%% of elements - usually exactly one (e.g., the group containing the
-%% argument expression of a case-expression). Note, however, that the
-%% exact structure of the returned list (for a given node type) should
-%% in general not be depended upon, since it might be subject to
-%% change without notice.
-%%
-%% The function subtrees/1
and the constructor functions
-%% make_tree/2
and update_tree/2
can be a
-%% great help if one wants to traverse a syntax tree, visiting all its
-%% subtrees, but treat nodes of the tree in a uniform way in most or all
-%% cases. Using these functions makes this simple, and also assures that
-%% your code is not overly sensitive to extensions of the syntax tree
-%% data type, because any node types not explicitly handled by your code
-%% can be left to a default case.
-%%
-%% For example:
-%%
-%% postorder(F, Tree) ->
-%% F(case subtrees(Tree) of
-%% [] -> Tree;
-%% List -> update_tree(Tree,
-%% [[postorder(F, Subtree)
-%% || Subtree <- Group]
-%% || Group <- List])
-%% end).
-%%
-%% maps the function F
on Tree
and all its
-%% subtrees, doing a post-order traversal of the syntax tree. (Note
-%% the use of update_tree/2
to preserve annotations.) For
-%% a simple function like:
-%%
-%% f(Node) ->
-%% case type(Node) of
-%% atom -> atom("a_" ++ atom_name(Node));
-%% _ -> Node
-%% end.
-%%
-%% the call postorder(fun f/1, Tree)
will yield a new
-%% representation of Tree
in which all atom names have
-%% been extended with the prefix "a_", but nothing else (including
-%% annotations) has been changed.
-%%
-%% @see is_leaf/1
-%% @see make_tree/2
-%% @see update_tree/2
-
-subtrees(T) ->
- case is_leaf(T) of
- true ->
- [];
- false ->
- case type(T) of
- values ->
- [values_es(T)];
- binary ->
- [binary_segments(T)];
- bitstr ->
- [[bitstr_val(T)], [bitstr_size(T)],
- [bitstr_unit(T)], [bitstr_type(T)],
- [bitstr_flags(T)]];
- cons ->
- [[cons_hd(T)], [cons_tl(T)]];
- tuple ->
- [tuple_es(T)];
- 'let' ->
- [let_vars(T), [let_arg(T)], [let_body(T)]];
- seq ->
- [[seq_arg(T)], [seq_body(T)]];
- apply ->
- [[apply_op(T)], apply_args(T)];
- call ->
- [[call_module(T)], [call_name(T)],
- call_args(T)];
- primop ->
- [[primop_name(T)], primop_args(T)];
- 'case' ->
- [[case_arg(T)], case_clauses(T)];
- clause ->
- [clause_pats(T), [clause_guard(T)],
- [clause_body(T)]];
- alias ->
- [[alias_var(T)], [alias_pat(T)]];
- 'fun' ->
- [fun_vars(T), [fun_body(T)]];
- 'receive' ->
- [receive_clauses(T), [receive_timeout(T)],
- [receive_action(T)]];
- 'try' ->
- [[try_arg(T)], try_vars(T), [try_body(T)],
- try_evars(T), [try_handler(T)]];
- 'catch' ->
- [[catch_body(T)]];
- letrec ->
- Es = unfold_tuples(letrec_defs(T)),
- [Es, [letrec_body(T)]];
- module ->
- As = unfold_tuples(module_attrs(T)),
- Es = unfold_tuples(module_defs(T)),
- [[module_name(T)], module_exports(T), As, Es]
- end
- end.
-
-
-%% @spec update_tree(Old::cerl(), Groups::[[cerl()]]) -> cerl()
-%%
-%% @doc Creates a syntax tree with the given subtrees, and the same
-%% type and annotations as the Old
node. This is
-%% equivalent to ann_make_tree(get_ann(Node), type(Node),
-%% Groups)
, but potentially more efficient.
-%%
-%% @see update_tree/3
-%% @see ann_make_tree/3
-%% @see get_ann/1
-%% @see type/1
-
-update_tree(Node, Gs) ->
- ann_make_tree(get_ann(Node), type(Node), Gs).
-
-
-%% @spec update_tree(Old::cerl(), Type::atom(), Groups::[[cerl()]]) ->
-%% cerl()
-%%
-%% @doc Creates a syntax tree with the given type and subtrees, and
-%% the same annotations as the Old
node. This is
-%% equivalent to ann_make_tree(get_ann(Node), Type,
-%% Groups)
, but potentially more efficient.
-%%
-%% @see update_tree/2
-%% @see ann_make_tree/3
-%% @see get_ann/1
-
-update_tree(Node, Type, Gs) ->
- ann_make_tree(get_ann(Node), Type, Gs).
-
-
-%% @spec make_tree(Type::atom(), Groups::[[cerl()]]) -> cerl()
-%%
-%% @doc Creates a syntax tree with the given type and subtrees.
-%% Type
must be a node type name
-%% (cf. type/1
) that does not denote a leaf node type
-%% (cf. is_leaf/1
). Groups
must be a
-%% nonempty list of groups of syntax trees, representing the
-%% subtrees of a node of the given type, in left-to-right order as
-%% they would occur in the printed program text, grouped by category
-%% as done by subtrees/1
.
-%%
-%% The result of ann_make_tree(get_ann(Node), type(Node),
-%% subtrees(Node))
(cf. update_tree/2
) represents
-%% the same source code text as the original Node
,
-%% assuming that subtrees(Node)
yields a nonempty
-%% list. However, it does not necessarily have the exact same data
-%% representation as Node
.
-%%
-%% @see ann_make_tree/3
-%% @see type/1
-%% @see is_leaf/1
-%% @see subtrees/1
-%% @see update_tree/2
-
-make_tree(Type, Gs) ->
- ann_make_tree([], Type, Gs).
-
-
-%% @spec ann_make_tree(As::[term()], Type::atom(),
-%% Groups::[[cerl()]]) -> cerl()
-%%
-%% @doc Creates a syntax tree with the given annotations, type and
-%% subtrees. See make_tree/2
for details.
-%%
-%% @see make_tree/2
-
-ann_make_tree(As, values, [Es]) -> ann_c_values(As, Es);
-ann_make_tree(As, binary, [Ss]) -> ann_c_binary(As, Ss);
-ann_make_tree(As, bitstr, [[V],[S],[U],[T],[Fs]]) ->
- ann_c_bitstr(As, V, S, U, T, Fs);
-ann_make_tree(As, cons, [[H], [T]]) -> ann_c_cons(As, H, T);
-ann_make_tree(As, tuple, [Es]) -> ann_c_tuple(As, Es);
-ann_make_tree(As, 'let', [Vs, [A], [B]]) -> ann_c_let(As, Vs, A, B);
-ann_make_tree(As, seq, [[A], [B]]) -> ann_c_seq(As, A, B);
-ann_make_tree(As, apply, [[Op], Es]) -> ann_c_apply(As, Op, Es);
-ann_make_tree(As, call, [[M], [N], Es]) -> ann_c_call(As, M, N, Es);
-ann_make_tree(As, primop, [[N], Es]) -> ann_c_primop(As, N, Es);
-ann_make_tree(As, 'case', [[A], Cs]) -> ann_c_case(As, A, Cs);
-ann_make_tree(As, clause, [Ps, [G], [B]]) -> ann_c_clause(As, Ps, G, B);
-ann_make_tree(As, alias, [[V], [P]]) -> ann_c_alias(As, V, P);
-ann_make_tree(As, 'fun', [Vs, [B]]) -> ann_c_fun(As, Vs, B);
-ann_make_tree(As, 'receive', [Cs, [T], [A]]) ->
- ann_c_receive(As, Cs, T, A);
-ann_make_tree(As, 'try', [[E], Vs, [B], Evs, [H]]) ->
- ann_c_try(As, E, Vs, B, Evs, H);
-ann_make_tree(As, 'catch', [[B]]) -> ann_c_catch(As, B);
-ann_make_tree(As, letrec, [Es, [B]]) ->
- ann_c_letrec(As, fold_tuples(Es), B);
-ann_make_tree(As, module, [[N], Xs, Es, Ds]) ->
- ann_c_module(As, N, Xs, fold_tuples(Es), fold_tuples(Ds)).
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec meta(Tree::cerl()) -> cerl()
-%%
-%% @doc Creates a meta-representation of a syntax tree. The result
-%% represents an Erlang expression "MetaTree
"
-%% which, if evaluated, will yield a new syntax tree representing the
-%% same source code text as Tree
(although the actual
-%% data representation may be different). The expression represented
-%% by MetaTree
is implementation independent
-%% with regard to the data structures used by the abstract syntax tree
-%% implementation.
-%%
-%% Any node in Tree
whose node type is
-%% var
(cf. type/1
), and whose list of
-%% annotations (cf. get_ann/1
) contains the atom
-%% meta_var
, will remain unchanged in the resulting tree,
-%% except that exactly one occurrence of meta_var
is
-%% removed from its annotation list.
-%%
-%% The main use of the function meta/1
is to transform
-%% a data structure Tree
, which represents a piece of
-%% program code, into a form that is representation independent
-%% when printed. E.g., suppose Tree
represents a
-%% variable named "V". Then (assuming a function print/1
-%% for printing syntax trees), evaluating
-%% print(abstract(Tree))
- simply using
-%% abstract/1
to map the actual data structure onto a
-%% syntax tree representation - would output a string that might look
-%% something like "{var, ..., 'V'}
", which is obviously
-%% dependent on the implementation of the abstract syntax trees. This
-%% could e.g. be useful for caching a syntax tree in a file. However,
-%% in some situations like in a program generator generator (with two
-%% "generator"), it may be unacceptable. Using
-%% print(meta(Tree))
instead would output a
-%% representation independent syntax tree generating
-%% expression; in the above case, something like
-%% "cerl:c_var('V')
".
-%%
-%% The implementation tries to generate compact code with respect
-%% to literals and lists.
-%%
-%% @see abstract/1
-%% @see type/1
-%% @see get_ann/1
-
-meta(Node) ->
- %% First of all we check for metavariables:
- case type(Node) of
- var ->
- case lists:member(meta_var, get_ann(Node)) of
- false ->
- meta_0(var, Node);
- true ->
- %% A meta-variable: remove the first found
- %% 'meta_var' annotation, but otherwise leave
- %% the node unchanged.
- set_ann(Node, lists:delete(meta_var, get_ann(Node)))
- end;
- Type ->
- meta_0(Type, Node)
- end.
-
-meta_0(Type, Node) ->
- case get_ann(Node) of
- [] ->
- meta_1(Type, Node);
- As ->
- meta_call(set_ann, [meta_1(Type, Node), abstract(As)])
- end.
-
-meta_1(literal, Node) ->
- %% We handle atomic literals separately, to get a bit
- %% more compact code. For the rest, we use 'abstract'.
- case concrete(Node) of
- V when atom(V) ->
- meta_call(c_atom, [Node]);
- V when integer(V) ->
- meta_call(c_int, [Node]);
- V when float(V) ->
- meta_call(c_float, [Node]);
- [] ->
- meta_call(c_nil, []);
- _ ->
- meta_call(abstract, [Node])
- end;
-meta_1(var, Node) ->
- %% A normal variable or function name.
- meta_call(c_var, [abstract(var_name(Node))]);
-meta_1(values, Node) ->
- meta_call(c_values,
- [make_list(meta_list(values_es(Node)))]);
-meta_1(binary, Node) ->
- meta_call(c_binary,
- [make_list(meta_list(binary_segments(Node)))]);
-meta_1(bitstr, Node) ->
- meta_call(c_bitstr,
- [meta(bitstr_val(Node)),
- meta(bitstr_size(Node)),
- meta(bitstr_unit(Node)),
- meta(bitstr_type(Node)),
- meta(bitstr_flags(Node))]);
-meta_1(cons, Node) ->
- %% The list is split up if some sublist has annotatations. If
- %% we get exactly one element, we generate a 'c_cons' call
- %% instead of 'make_list' to reconstruct the node.
- case split_list(Node) of
- {[H], none} ->
- meta_call(c_cons, [meta(H), meta(c_nil())]);
- {[H], Node1} ->
- meta_call(c_cons, [meta(H), meta(Node1)]);
- {L, none} ->
- meta_call(make_list, [make_list(meta_list(L))]);
- {L, Node1} ->
- meta_call(make_list,
- [make_list(meta_list(L)), meta(Node1)])
- end;
-meta_1(tuple, Node) ->
- meta_call(c_tuple,
- [make_list(meta_list(tuple_es(Node)))]);
-meta_1('let', Node) ->
- meta_call(c_let,
- [make_list(meta_list(let_vars(Node))),
- meta(let_arg(Node)), meta(let_body(Node))]);
-meta_1(seq, Node) ->
- meta_call(c_seq,
- [meta(seq_arg(Node)), meta(seq_body(Node))]);
-meta_1(apply, Node) ->
- meta_call(c_apply,
- [meta(apply_op(Node)),
- make_list(meta_list(apply_args(Node)))]);
-meta_1(call, Node) ->
- meta_call(c_call,
- [meta(call_module(Node)), meta(call_name(Node)),
- make_list(meta_list(call_args(Node)))]);
-meta_1(primop, Node) ->
- meta_call(c_primop,
- [meta(primop_name(Node)),
- make_list(meta_list(primop_args(Node)))]);
-meta_1('case', Node) ->
- meta_call(c_case,
- [meta(case_arg(Node)),
- make_list(meta_list(case_clauses(Node)))]);
-meta_1(clause, Node) ->
- meta_call(c_clause,
- [make_list(meta_list(clause_pats(Node))),
- meta(clause_guard(Node)),
- meta(clause_body(Node))]);
-meta_1(alias, Node) ->
- meta_call(c_alias,
- [meta(alias_var(Node)), meta(alias_pat(Node))]);
-meta_1('fun', Node) ->
- meta_call(c_fun,
- [make_list(meta_list(fun_vars(Node))),
- meta(fun_body(Node))]);
-meta_1('receive', Node) ->
- meta_call(c_receive,
- [make_list(meta_list(receive_clauses(Node))),
- meta(receive_timeout(Node)),
- meta(receive_action(Node))]);
-meta_1('try', Node) ->
- meta_call(c_try,
- [meta(try_arg(Node)),
- make_list(meta_list(try_vars(Node))),
- meta(try_body(Node)),
- make_list(meta_list(try_evars(Node))),
- meta(try_handler(Node))]);
-meta_1('catch', Node) ->
- meta_call(c_catch, [meta(catch_body(Node))]);
-meta_1(letrec, Node) ->
- meta_call(c_letrec,
- [make_list([c_tuple([meta(N), meta(F)])
- || {N, F} <- letrec_defs(Node)]),
- meta(letrec_body(Node))]);
-meta_1(module, Node) ->
- meta_call(c_module,
- [meta(module_name(Node)),
- make_list(meta_list(module_exports(Node))),
- make_list([c_tuple([meta(A), meta(V)])
- || {A, V} <- module_attrs(Node)]),
- make_list([c_tuple([meta(N), meta(F)])
- || {N, F} <- module_defs(Node)])]).
-
-meta_call(F, As) ->
- c_call(c_atom(?MODULE), c_atom(F), As).
-
-meta_list([T | Ts]) ->
- [meta(T) | meta_list(Ts)];
-meta_list([]) ->
- [].
-
-split_list(Node) ->
- split_list(set_ann(Node, []), []).
-
-split_list(Node, L) ->
- A = get_ann(Node),
- case type(Node) of
- cons when A == [] ->
- split_list(cons_tl(Node), [cons_hd(Node) | L]);
- nil when A == [] ->
- {lists:reverse(L), none};
- _ ->
- {lists:reverse(L), Node}
- end.
-
-
-%% ---------------------------------------------------------------------
-
-%% General utilities
-
-is_lit_list([#literal{} | Es]) ->
- is_lit_list(Es);
-is_lit_list([_ | _]) ->
- false;
-is_lit_list([]) ->
- true.
-
-lit_list_vals([#literal{val = V} | Es]) ->
- [V | lit_list_vals(Es)];
-lit_list_vals([]) ->
- [].
-
-make_lit_list([V | Vs]) ->
- [#literal{val = V} | make_lit_list(Vs)];
-make_lit_list([]) ->
- [].
-
-%% The following tests are the same as done by 'io_lib:char_list' and
-%% 'io_lib:printable_list', respectively, but for a single character.
-
-is_char_value(V) when V >= $\000, V =< $\377 -> true;
-is_char_value(_) -> false.
-
-is_print_char_value(V) when V >= $\040, V =< $\176 -> true;
-is_print_char_value(V) when V >= $\240, V =< $\377 -> true;
-is_print_char_value(V) when V =:= $\b -> true;
-is_print_char_value(V) when V =:= $\d -> true;
-is_print_char_value(V) when V =:= $\e -> true;
-is_print_char_value(V) when V =:= $\f -> true;
-is_print_char_value(V) when V =:= $\n -> true;
-is_print_char_value(V) when V =:= $\r -> true;
-is_print_char_value(V) when V =:= $\s -> true;
-is_print_char_value(V) when V =:= $\t -> true;
-is_print_char_value(V) when V =:= $\v -> true;
-is_print_char_value(V) when V =:= $\" -> true;
-is_print_char_value(V) when V =:= $\' -> true;
-is_print_char_value(V) when V =:= $\\ -> true;
-is_print_char_value(_) -> false.
-
-is_char_list([V | Vs]) when integer(V) ->
- case is_char_value(V) of
- true ->
- is_char_list(Vs);
- false ->
- false
- end;
-is_char_list([]) ->
- true;
-is_char_list(_) ->
- false.
-
-is_print_char_list([V | Vs]) when integer(V) ->
- case is_print_char_value(V) of
- true ->
- is_print_char_list(Vs);
- false ->
- false
- end;
-is_print_char_list([]) ->
- true;
-is_print_char_list(_) ->
- false.
-
-unfold_tuples([{X, Y} | Ps]) ->
- [X, Y | unfold_tuples(Ps)];
-unfold_tuples([]) ->
- [].
-
-fold_tuples([X, Y | Es]) ->
- [{X, Y} | fold_tuples(Es)];
-fold_tuples([]) ->
- [].
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_clauses.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_clauses.erl
deleted file mode 100644
index f207178f13..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_clauses.erl
+++ /dev/null
@@ -1,409 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Richard Carlsson.
-%% Copyright (C) 1999-2002 Richard Carlsson.
-%% Portions created by Ericsson are Copyright 2001, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: cerl_clauses.erl,v 1.2 2009/09/17 09:46:19 kostis Exp $
-
-%% @doc Utility functions for Core Erlang case/receive clauses.
-%%
-%% Syntax trees are defined in the module cerl
.
-%%
-%% @type cerl() = cerl:cerl()
-
--module(cerl_clauses).
-
--export([any_catchall/1, eval_guard/1, is_catchall/1, match/2,
- match_list/2, reduce/1, reduce/2]).
-
--import(cerl, [alias_pat/1, alias_var/1, data_arity/1, data_es/1,
- data_type/1, clause_guard/1, clause_pats/1, concrete/1,
- is_data/1, is_c_var/1, let_body/1, letrec_body/1,
- seq_body/1, try_arg/1, type/1, values_es/1]).
-
--import(lists, [reverse/1]).
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec is_catchall(Clause::cerl()) -> boolean()
-%%
-%% @doc Returns true
if an abstract clause is a
-%% catch-all, otherwise false
. A clause is a catch-all if
-%% all its patterns are variables, and its guard expression always
-%% evaluates to true
; cf. eval_guard/1
.
-%%
-%% Note: Clause
must have type
-%% clause
.
-%%
-%% @see eval_guard/1
-%% @see any_catchall/1
-
-is_catchall(C) ->
- case all_vars(clause_pats(C)) of
- true ->
- case eval_guard(clause_guard(C)) of
- {value, true} ->
- true;
- _ ->
- false
- end;
- false ->
- false
- end.
-
-all_vars([C | Cs]) ->
- case is_c_var(C) of
- true ->
- all_vars(Cs);
- false ->
- false
- end;
-all_vars([]) ->
- true.
-
-
-%% @spec any_catchall(Clauses::[cerl()]) -> boolean()
-%%
-%% @doc Returns true
if any of the abstract clauses in
-%% the list is a catch-all, otherwise false
. See
-%% is_catchall/1
for details.
-%%
-%% Note: each node in Clauses
must have type
-%% clause
.
-%%
-%% @see is_catchall/1
-
-any_catchall([C | Cs]) ->
- case is_catchall(C) of
- true ->
- true;
- false ->
- any_catchall(Cs)
- end;
-any_catchall([]) ->
- false.
-
-
-%% @spec eval_guard(Expr::cerl()) -> none | {value, term()}
-%%
-%% @doc Tries to reduce a guard expression to a single constant value,
-%% if possible. The returned value is {value, Term}
if the
-%% guard expression Expr
always yields the constant value
-%% Term
, and is otherwise none
.
-%%
-%% Note that although guard expressions should only yield boolean
-%% values, this function does not guarantee that Term
is
-%% either true
or false
. Also note that only
-%% simple constructs like let-expressions are examined recursively;
-%% general constant folding is not performed.
-%%
-%% @see is_catchall/1
-
-%% This function could possibly be improved further, but constant
-%% folding should in general be performed elsewhere.
-
-eval_guard(E) ->
- case type(E) of
- literal ->
- {value, concrete(E)};
- values ->
- case values_es(E) of
- [E1] ->
- eval_guard(E1);
- _ ->
- none
- end;
- 'try' ->
- eval_guard(try_arg(E));
- seq ->
- eval_guard(seq_body(E));
- 'let' ->
- eval_guard(let_body(E));
- 'letrec' ->
- eval_guard(letrec_body(E));
- _ ->
- none
- end.
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec reduce(Clauses) -> {true, {Clauses, Bindings}}
-%% | {false, Clauses}
-%%
-%% @equiv reduce(Cs, [])
-
-reduce(Cs) ->
- reduce(Cs, []).
-
-%% @spec reduce(Clauses::[Clause], Exprs::[Expr]) ->
-%% {true, {Clause, Bindings}}
-%% | {false, [Clause]}
-%%
-%% Clause = cerl()
-%% Expr = any | cerl()
-%% Bindings = [{cerl(), cerl()}]
-%%
-%% @doc Selects a single clause, if possible, or otherwise reduces the
-%% list of selectable clauses. The input is a list Clauses
-%% of abstract clauses (i.e., syntax trees of type clause
),
-%% and a list of switch expressions Exprs
. The function
-%% tries to uniquely select a single clause or discard unselectable
-%% clauses, with respect to the switch expressions. All abstract clauses
-%% in the list must have the same number of patterns. If
-%% Exprs
is not the empty list, it must have the same
-%% length as the number of patterns in each clause; see
-%% match_list/2
for details.
-%%
-%% A clause can only be selected if its guard expression always
-%% yields the atom true
, and a clause whose guard
-%% expression always yields the atom false
can never be
-%% selected. Other guard expressions are considered to have unknown
-%% value; cf. eval_guard/1
.
-%%
-%% If a particular clause can be selected, the function returns
-%% {true, {Clause, Bindings}}
, where Clause
is
-%% the selected clause and Bindings
is a list of pairs
-%% {Var, SubExpr}
associating the variables occurring in
-%% the patterns of Clause
with the corresponding
-%% subexpressions in Exprs
. The list of bindings is given
-%% in innermost-first order; see the match/2
function for
-%% details.
-%%
-%% If no clause could be definitely selected, the function returns
-%% {false, NewClauses}
, where NewClauses
is
-%% the list of entries in Clauses
that remain after
-%% eliminating unselectable clauses, preserving the relative order.
-%%
-%% @see eval_guard/1
-%% @see match/2
-%% @see match_list/2
-
-reduce(Cs, Es) ->
- reduce(Cs, Es, []).
-
-reduce([C | Cs], Es, Cs1) ->
- Ps = clause_pats(C),
- case match_list(Ps, Es) of
- none ->
- %% Here, we know that the current clause cannot possibly be
- %% selected, so we drop it and visit the rest.
- reduce(Cs, Es, Cs1);
- {false, _} ->
- %% We are not sure if this clause might be selected, so we
- %% save it and visit the rest.
- reduce(Cs, Es, [C | Cs1]);
- {true, Bs} ->
- case eval_guard(clause_guard(C)) of
- {value, true} when Cs1 == [] ->
- %% We have a definite match - we return the residual
- %% expression and signal that a selection has been
- %% made. All other clauses are dropped.
- {true, {C, Bs}};
- {value, true} ->
- %% Unless one of the previous clauses is selected,
- %% this clause will definitely be, so we can drop
- %% the rest.
- {false, reverse([C | Cs1])};
- {value, false} ->
- %% This clause can never be selected, since its
- %% guard is never 'true', so we drop it.
- reduce(Cs, Es, Cs1);
- _ ->
- %% We are not sure if this clause might be selected
- %% (or might even cause a crash), so we save it and
- %% visit the rest.
- reduce(Cs, Es, [C | Cs1])
- end
- end;
-reduce([], _, Cs) ->
- %% All clauses visited, without a complete match. Signal "not
- %% reduced" and return the saved clauses, in the correct order.
- {false, reverse(Cs)}.
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec match(Pattern::cerl(), Expr) ->
-%% none | {true, Bindings} | {false, Bindings}
-%%
-%% Expr = any | cerl()
-%% Bindings = [{cerl(), Expr}]
-%%
-%% @doc Matches a pattern against an expression. The returned value is
-%% none
if a match is impossible, {true,
-%% Bindings}
if Pattern
definitely matches
-%% Expr
, and {false, Bindings}
if a match is
-%% not definite, but cannot be excluded. Bindings
is then
-%% a list of pairs {Var, SubExpr}
, associating each
-%% variable in the pattern with either the corresponding subexpression
-%% of Expr
, or with the atom any
if no
-%% matching subexpression exists. (Recall that variables may not be
-%% repeated in a Core Erlang pattern.) The list of bindings is given
-%% in innermost-first order; this should only be of interest if
-%% Pattern
contains one or more alias patterns. If the
-%% returned value is {true, []}
, it implies that the
-%% pattern and the expression are syntactically identical.
-%%
-%% Instead of a syntax tree, the atom any
can be
-%% passed for Expr
(or, more generally, be used for any
-%% subtree of Expr
, in as much the abstract syntax tree
-%% implementation allows it); this means that it cannot be decided
-%% whether the pattern will match or not, and the corresponding
-%% variable bindings will all map to any
. The typical use
-%% is for producing bindings for receive
clauses.
-%%
-%% Note: Binary-syntax patterns are never structurally matched
-%% against binary-syntax expressions by this function.
-%%
-%% Examples:
-%%
-%% - Matching a pattern "
{X, Y}
" against the
-%% expression "{foo, f(Z)}
" yields {true,
-%% Bindings}
where Bindings
associates
-%% "X
" with the subtree "foo
" and
-%% "Y
" with the subtree "f(Z)
".
-%%
-%% - Matching pattern "
{X, {bar, Y}}
" against
-%% expression "{foo, f(Z)}
" yields {false,
-%% Bindings}
where Bindings
associates
-%% "X
" with the subtree "foo
" and
-%% "Y
" with any
(because it is not known
-%% if "{foo, Y}
" might match the run-time value of
-%% "f(Z)
" or not).
-%%
-%% - Matching pattern "
{foo, bar}
" against expression
-%% "{foo, f()}
" yields {false, []}
,
-%% telling us that there might be a match, but we cannot deduce any
-%% bindings.
-%%
-%% - Matching
{foo, X = {bar, Y}}
against expression
-%% "{foo, {bar, baz}}
" yields {true,
-%% Bindings}
where Bindings
associates
-%% "Y
" with "baz
", and "X
"
-%% with "{bar, baz}
".
-%%
-%% - Matching a pattern "
{X, Y}
" against
-%% any
yields {false, Bindings}
where
-%% Bindings
associates both "X
" and
-%% "Y
" with any
.
-%%
-
-match(P, E) ->
- match(P, E, []).
-
-match(P, E, Bs) ->
- case type(P) of
- var ->
- %% Variables always match, since they cannot have repeated
- %% occurrences in a pattern.
- {true, [{P, E} | Bs]};
- alias ->
- %% All variables in P1 will be listed before the alias
- %% variable in the result.
- match(alias_pat(P), E, [{alias_var(P), E} | Bs]);
- binary ->
- %% The most we can do is to say "definitely no match" if a
- %% binary pattern is matched against non-binary data.
- if E == any ->
- {false, Bs};
- true ->
- case is_data(E) of
- true ->
- none;
- false ->
- {false, Bs}
- end
- end;
- _ ->
- match_1(P, E, Bs)
- end.
-
-match_1(P, E, Bs) ->
- case is_data(P) of
- true when E == any ->
- %% If we don't know the structure of the value of E at this
- %% point, we just match the subpatterns against 'any', and
- %% make sure the result is a "maybe".
- Ps = data_es(P),
- Es = lists:duplicate(length(Ps), any),
- case match_list(Ps, Es, Bs) of
- {_, Bs1} ->
- {false, Bs1};
- none ->
- none
- end;
- true ->
- %% Test if the expression represents a constructor
- case is_data(E) of
- true ->
- T1 = {data_type(E), data_arity(E)},
- T2 = {data_type(P), data_arity(P)},
- %% Note that we must test for exact equality.
- if T1 =:= T2 ->
- match_list(data_es(P), data_es(E), Bs);
- true ->
- none
- end;
- false ->
- %% We don't know the run-time structure of E, and P
- %% is not a variable or an alias pattern, so we
- %% match against 'any' instead.
- match_1(P, any, Bs)
- end;
- false ->
- %% Strange pattern - give up, but don't say "no match".
- {false, Bs}
- end.
-
-
-%% @spec match_list(Patterns::[cerl()], Exprs::[Expr]) ->
-%% none | {true, Bindings} | {false, Bindings}
-%%
-%% Expr = any | cerl()
-%% Bindings = [{cerl(), cerl()}]
-%%
-%% @doc Like match/2
, but matching a sequence of patterns
-%% against a sequence of expressions. Passing an empty list for
-%% Exprs
is equivalent to passing a list of
-%% any
atoms of the same length as Patterns
.
-%%
-%% @see match/2
-
-match_list([], []) ->
- {true, []}; % no patterns always match
-match_list(Ps, []) ->
- match_list(Ps, lists:duplicate(length(Ps), any), []);
-match_list(Ps, Es) ->
- match_list(Ps, Es, []).
-
-match_list([P | Ps], [E | Es], Bs) ->
- case match(P, E, Bs) of
- {true, Bs1} ->
- match_list(Ps, Es, Bs1);
- {false, Bs1} ->
- %% Make sure "maybe" is preserved
- case match_list(Ps, Es, Bs1) of
- {_, Bs2} ->
- {false, Bs2};
- none ->
- none
- end;
- none ->
- none
- end;
-match_list([], [], Bs) ->
- {true, Bs}.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_inline.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_inline.erl
deleted file mode 100644
index e040904a19..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_inline.erl
+++ /dev/null
@@ -1,2762 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Richard Carlsson.
-%% Copyright (C) 1999-2002 Richard Carlsson.
-%% Portions created by Ericsson are Copyright 2001, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: cerl_inline.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
-%%
-%% Core Erlang inliner.
-
-%% =====================================================================
-%%
-%% This is an implementation of the algorithm by Waddell and Dybvig
-%% ("Fast and Effective Procedure Inlining", International Static
-%% Analysis Symposium 1997), adapted to the Core Erlang language.
-%%
-%% Instead of always renaming variables and function variables, this
-%% implementation uses the "no-shadowing strategy" of Peyton Jones and
-%% Marlow ("Secrets of the Glasgow Haskell Compiler Inliner", 1999).
-%%
-%% =====================================================================
-
-%% TODO: inline single-source-reference operands without size limit.
-
--module(cerl_inline).
-
--export([core_transform/2, transform/1, transform/2]).
-
--import(cerl, [abstract/1, alias_pat/1, alias_var/1, apply_args/1,
- apply_op/1, atom_name/1, atom_val/1, bitstr_val/1,
- bitstr_size/1, bitstr_unit/1, bitstr_type/1,
- bitstr_flags/1, binary_segments/1, update_c_alias/3,
- update_c_apply/3, update_c_binary/2, update_c_bitstr/6,
- update_c_call/4, update_c_case/3, update_c_catch/2,
- update_c_clause/4, c_fun/2, c_int/1, c_let/3,
- update_c_let/4, update_c_letrec/3, update_c_module/5,
- update_c_primop/3, update_c_receive/4, update_c_seq/3,
- c_seq/2, update_c_try/6, c_tuple/1, update_c_values/2,
- c_values/1, c_var/1, call_args/1, call_module/1,
- call_name/1, case_arity/1, case_arg/1, case_clauses/1,
- catch_body/1, clause_body/1, clause_guard/1,
- clause_pats/1, clause_vars/1, concrete/1, cons_hd/1,
- cons_tl/1, data_arity/1, data_es/1, data_type/1,
- fun_body/1, fun_vars/1, get_ann/1, int_val/1,
- is_c_atom/1, is_c_cons/1, is_c_fun/1, is_c_int/1,
- is_c_list/1, is_c_seq/1, is_c_tuple/1, is_c_var/1,
- is_data/1, is_literal/1, is_literal_term/1, let_arg/1,
- let_body/1, let_vars/1, letrec_body/1, letrec_defs/1,
- list_length/1, list_elements/1, update_data/3,
- make_list/1, make_data_skel/2, module_attrs/1,
- module_defs/1, module_exports/1, module_name/1,
- primop_args/1, primop_name/1, receive_action/1,
- receive_clauses/1, receive_timeout/1, seq_arg/1,
- seq_body/1, set_ann/2, try_arg/1, try_body/1, try_vars/1,
- try_evars/1, try_handler/1, tuple_es/1, tuple_arity/1,
- type/1, values_es/1, var_name/1]).
-
--import(lists, [foldl/3, foldr/3, mapfoldl/3, reverse/1]).
-
-%%
-%% Constants
-%%
-
-debug_runtime() -> false.
-debug_counters() -> false.
-
-%% Normal execution times for inlining are between 0.1 and 0.3 seconds
-%% (on the author's current equipment). The default effort limit of 150
-%% is high enough that most normal programs never hit the limit even
-%% once, and for difficult programs, it generally keeps the execution
-%% times below 2-5 seconds. Using an effort counter of 1000 will thus
-%% have no further effect on most programs, but some programs may take
-%% as much as 10 seconds or more. Effort counts larger than 2500 have
-%% never been observed even on very ill-conditioned programs.
-%%
-%% Size limits between 6 and 18 tend to actually shrink the code,
-%% because of the simplifications made possible by inlining. A limit of
-%% 16 seems to be optimal for this purpose, often shrinking the
-%% executable code by up to 10%. Size limits between 18 and 30 generally
-%% give the same code size as if no inlining was done (i.e., code
-%% duplication balances out the simplifications at these levels). A size
-%% limit between 1 and 5 tends to inline small functions and propagate
-%% constants, but does not cause much simplifications do be done, so the
-%% net effect will be a slight increase in code size. For size limits
-%% above 30, the executable code size tends to increase with about 10%
-%% per 100 units, with some variations depending on the sizes of
-%% functions in the source code.
-%%
-%% Typically, about 90% of the maximum speedup achievable is already
-%% reached using a size limit of 30, and 98% is reached at limits around
-%% 100-150; there is rarely any point in letting the code size increase
-%% by more than 10-15%. If too large functions are inlined, cache
-%% effects will slow the program down.
-
-default_effort() -> 150.
-default_size() -> 24.
-
-%% Base costs/weights for different kinds of expressions. If these are
-%% modified, the size limits above may have to be adjusted.
-
-weight(var) -> 0; % We count no cost for variable accesses.
-weight(values) -> 0; % Value aggregates have no cost in themselves.
-weight(literal) -> 1; % We assume efficient handling of constants.
-weight(data) -> 1; % Base cost; add 1 per element.
-weight(element) -> 1; % Cost of storing/fetching an element.
-weight(argument) -> 1; % Cost of passing a function argument.
-weight('fun') -> 6; % Base cost + average number of free vars.
-weight('let') -> 0; % Count no cost for let-bindings.
-weight(letrec) -> 0; % Like a let-binding.
-weight('case') -> 0; % Case switches have no base cost.
-weight(clause) -> 1; % Count one jump at the end of each clause body.
-weight('receive') -> 9; % Initialization/cleanup cost.
-weight('try') -> 1; % Assume efficient implementation.
-weight('catch') -> 1; % See `try'.
-weight(apply) -> 3; % Average base cost: call/return.
-weight(call) -> 3; % Assume remote-calls as efficient as `apply'.
-weight(primop) -> 2; % Assume more efficient than `apply'.
-weight(binary) -> 4; % Initialisation base cost.
-weight(bitstr) -> 3; % Coding/decoding a value; like a primop.
-weight(module) -> 1. % Like a letrec with a constant body
-
-%% These "reference" structures are used for variables and function
-%% variables. They keep track of the variable name, any bound operand,
-%% and the associated store location.
-
--record(ref, {name, opnd, loc}).
-
-%% Operand structures contain the operand expression, the renaming and
-%% environment, the state location, and the effort counter at the call
-%% site (cf. `visit').
-
--record(opnd, {expr, ren, env, loc, effort}).
-
-%% Since expressions are only visited in `effect' context when they are
-%% not bound to a referenced variable, only expressions visited in
-%% 'value' context are cached.
-
--record(cache, {expr, size}).
-
-%% The context flags for an application structure are kept separate from
-%% the structure itself. Note that the original algorithm had exactly
-%% one operand in each application context structure, while we can have
-%% several, or none.
-
--record(app, {opnds, ctxt, loc}).
-
-
-%%
-%% Interface functions
-%%
-
-%% Use compile option `{core_transform, inline}' to insert this as a
-%% compilation pass.
-
-core_transform(Code, Opts) ->
- cerl:to_records(transform(cerl:from_records(Code), Opts)).
-
-transform(Tree) ->
- transform(Tree, []).
-
-transform(Tree, Opts) ->
- main(Tree, value, Opts).
-
-main(Tree, Ctxt, Opts) ->
- %% We spawn a new process to do the work, so we don't have to worry
- %% about cluttering the process dictionary with debugging info, or
- %% proper deallocation of ets-tables.
- Opts1 = Opts ++ [{inline_size, default_size()},
- {inline_effort, default_effort()}],
- Reply = self(),
- Pid = spawn_link(fun () -> start(Reply, Tree, Ctxt, Opts1) end),
- receive
- {Pid1, Tree1} when Pid1 == Pid ->
- Tree1
- end.
-
-start(Reply, Tree, Ctxt, Opts) ->
- init_debug(),
- case debug_runtime() of
- true ->
- put(inline_start_time,
- element(1, erlang:statistics(runtime)));
- _ ->
- ok
- end,
- Size = max(1, proplists:get_value(inline_size, Opts)),
- Effort = max(1, proplists:get_value(inline_effort, Opts)),
- case proplists:get_bool(verbose, Opts) of
- true ->
- io:fwrite("Inlining: inline_size=~w inline_effort=~w\n",
- [Size, Effort]);
- false ->
- ok
- end,
-
- %% Note that the counters of the new state are passive.
- S = st__new(Effort, Size),
-
-%%% Initialization is not needed at present. Note that the code in
-%%% `inline_init' is not up-to-date with this module.
-%%% {Tree1, S1} = inline_init:init(Tree, S),
-%%% {Tree2, _S2} = i(Tree1, Ctxt, S1),
- {Tree2, _S2} = i(Tree, Ctxt, S),
- report_debug(),
- Reply ! {self(), Tree2}.
-
-init_debug() ->
- case debug_counters() of
- true ->
- put(counter_effort_triggers, 0),
- put(counter_effort_max, 0),
- put(counter_size_triggers, 0),
- put(counter_size_max, 0);
- _ ->
- ok
- end.
-
-report_debug() ->
- case debug_runtime() of
- true ->
- {Time, _} = erlang:statistics(runtime),
- report("Total run time for inlining: ~.2.0f s.\n",
- [(Time - get(inline_start_time))/1000]);
- _ ->
- ok
- end,
- case debug_counters() of
- true ->
- counter_stats();
- _ ->
- ok
- end.
-
-counter_stats() ->
- T1 = get(counter_effort_triggers),
- T2 = get(counter_size_triggers),
- E = get(counter_effort_max),
- S = get(counter_size_max),
- M1 = io_lib:fwrite("\tNumber of triggered "
- "effort counters: ~p.\n", [T1]),
- M2 = io_lib:fwrite("\tNumber of triggered "
- "size counters: ~p.\n", [T2]),
- M3 = io_lib:fwrite("\tLargest active effort counter: ~p.\n",
- [E]),
- M4 = io_lib:fwrite("\tLargest active size counter: ~p.\n",
- [S]),
- report("Counter statistics:\n~s", [[M1, M2, M3, M4]]).
-
-
-%% =====================================================================
-%% The main inlining function
-%%
-%% i(E :: coreErlang(),
-%% Ctxt :: value | effect | #app{}
-%% Ren :: renaming(),
-%% Env :: environment(),
-%% S :: state())
-%% -> {E', S'}
-%%
-%% Note: It is expected that the input source code ('E') does not
-%% contain free variables. If it does, there is a risk of accidental
-%% name capture, in case a generated "new" variable name happens to be
-%% the same as the name of a variable that is free further below in the
-%% tree; the algorithm only consults the current environment to check if
-%% a name already exists.
-%%
-%% The renaming maps names of source-code variable and function
-%% variables to new names as necessary to avoid clashes, according to
-%% the "no-shadowing" strategy. The environment maps *residual-code*
-%% variables and function variables to operands and global information.
-%% Separating the renaming from the environment, and using the
-%% residual-code variables instead of the source-code variables as its
-%% domain, improves the behaviour of the algorithm when code needs to be
-%% traversed more than once.
-%%
-%% Note that there is no such thing as a `test' context for expressions
-%% in (Core) Erlang (see `i_case' below for details).
-
-i(E, Ctxt, S) ->
- i(E, Ctxt, ren__identity(), env__empty(), S).
-
-i(E, Ctxt, Ren, Env, S0) ->
- %% Count one unit of effort on each pass.
- S = count_effort(1, S0),
- case is_data(E) of
- true ->
- i_data(E, Ctxt, Ren, Env, S);
- false ->
- case type(E) of
- var ->
- i_var(E, Ctxt, Ren, Env, S);
- values ->
- i_values(E, Ctxt, Ren, Env, S);
- 'fun' ->
- i_fun(E, Ctxt, Ren, Env, S);
- seq ->
- i_seq(E, Ctxt, Ren, Env, S);
- 'let' ->
- i_let(E, Ctxt, Ren, Env, S);
- letrec ->
- i_letrec(E, Ctxt, Ren, Env, S);
- 'case' ->
- i_case(E, Ctxt, Ren, Env, S);
- 'receive' ->
- i_receive(E, Ctxt, Ren, Env, S);
- apply ->
- i_apply(E, Ctxt, Ren, Env, S);
- call ->
- i_call(E, Ctxt, Ren, Env, S);
- primop ->
- i_primop(E, Ren, Env, S);
- 'try' ->
- i_try(E, Ctxt, Ren, Env, S);
- 'catch' ->
- i_catch(E, Ctxt, Ren, Env, S);
- binary ->
- i_binary(E, Ren, Env, S);
- module ->
- i_module(E, Ctxt, Ren, Env, S)
- end
- end.
-
-i_data(E, Ctxt, Ren, Env, S) ->
- case is_literal(E) of
- true ->
- %% This is the `(const c)' case of the original algorithm:
- %% literal terms which (regardless of size) do not need to
- %% be constructed dynamically at runtime - boldly assuming
- %% that the compiler/runtime system can handle this.
- case Ctxt of
- effect ->
- %% Reduce useless constants to a simple value.
- {void(), count_size(weight(literal), S)};
- _ ->
- %% (In Erlang, we cannot set all non-`false'
- %% constants to `true' in a `test' context, like we
- %% could do in Lisp or C, so the above is the only
- %% special case to be handled here.)
- {E, count_size(weight(literal), S)}
- end;
- false ->
- %% Data constructors are like to calls to safe built-in
- %% functions, for which we can "decide to inline"
- %% immediately; there is no need to create operand
- %% structures. In `effect' context, we can simply make a
- %% sequence of the argument expressions, also visited in
- %% `effect' context. In all other cases, the arguments are
- %% visited for value.
- case Ctxt of
- effect ->
- %% Note that this will count the sizes of the
- %% subexpressions, even though some or all of them
- %% might be discarded by the sequencing afterwards.
- {Es1, S1} = mapfoldl(fun (E, S) ->
- i(E, effect, Ren, Env,
- S)
- end,
- S, data_es(E)),
- E1 = foldl(fun (E1, E2) -> make_seq(E1, E2) end,
- void(), Es1),
- {E1, S1};
- _ ->
- {Es1, S1} = mapfoldl(fun (E, S) ->
- i(E, value, Ren, Env,
- S)
- end,
- S, data_es(E)),
- %% The total size/cost is the base cost for a data
- %% constructor plus the cost for storing each
- %% element.
- N = weight(data) + length(Es1) * weight(element),
- S2 = count_size(N, S1),
- {update_data(E, data_type(E), Es1), S2}
- end
- end.
-
-%% This is the `(ref x)' (variable use) case of the original algorithm.
-%% Note that binding occurrences are always handled in the respective
-%% cases of the binding constructs.
-
-i_var(E, Ctxt, Ren, Env, S) ->
- case Ctxt of
- effect ->
- %% Reduce useless variable references to a simple constant.
- %% This also avoids useless visiting of bound operands.
- {void(), count_size(weight(literal), S)};
- _ ->
- Name = var_name(E),
- case env__lookup(ren__map(Name, Ren), Env) of
- {ok, R} ->
- case R#ref.opnd of
- undefined ->
- %% The variable is not associated with an
- %% argument expression; just residualize it.
- residualize_var(R, S);
- Opnd ->
- i_var_1(R, Opnd, Ctxt, Env, S)
- end;
- error ->
- %% The variable is unbound. (It has not been
- %% accidentally captured, however, or it would have
- %% been in the environment.) We leave it as it is,
- %% without any warning.
- {E, count_size(weight(var), S)}
- end
- end.
-
-%% This first visits the bound operand and then does copy propagation.
-%% Note that we must first set the "inner-pending" flag, and clear the
-%% flag afterwards.
-
-i_var_1(R, Opnd, Ctxt, Env, S) ->
- %% If the operand is already "inner-pending", it is residualised.
- %% (In Lisp/C, if the variable might be assigned to, it should also
- %% be residualised.)
- L = Opnd#opnd.loc,
- case st__test_inner_pending(L, S) of
- true ->
- residualize_var(R, S);
- false ->
- S1 = st__mark_inner_pending(L, S),
- case catch {ok, visit(Opnd, S1)} of
- {ok, {E, S2}} ->
- %% Note that we pass the current environment and
- %% context to `copy', but not the current renaming.
- S3 = st__clear_inner_pending(L, S2),
- copy(R, Opnd, E, Ctxt, Env, S3);
- {'EXIT', X} ->
- exit(X);
- X ->
- %% If we use destructive update for the
- %% `inner-pending' flag, we must make sure to clear
- %% it also if we make a nonlocal return.
- st__clear_inner_pending(Opnd#opnd.loc, S1),
- throw(X)
- end
- end.
-
-%% A multiple-value aggregate `'. This is very much like a
-%% tuple data constructor `{e1, ..., en}'; cf. `i_data' for details.
-
-i_values(E, Ctxt, Ren, Env, S) ->
- case values_es(E) of
- [E1] ->
- %% Single-value aggregates can be dropped; they are simply
- %% notation.
- i(E1, Ctxt, Ren, Env, S);
- Es ->
- %% In `effect' context, we can simply make a sequence of the
- %% argument expressions, also visited in `effect' context.
- %% In all other cases, the arguments are visited for value.
- case Ctxt of
- effect ->
- {Es1, S1} =
- mapfoldl(fun (E, S) ->
- i(E, effect, Ren, Env, S)
- end,
- S, Es),
- E1 = foldl(fun (E1, E2) ->
- make_seq(E1, E2)
- end,
- void(), Es1),
- {E1, S1}; % drop annotations on E
- _ ->
- {Es1, S1} = mapfoldl(fun (E, S) ->
- i(E, value, Ren, Env,
- S)
- end,
- S, Es),
- %% Aggregating values does not write them to memory,
- %% so we count no extra cost per element.
- S2 = count_size(weight(values), S1),
- {update_c_values(E, Es1), S2}
- end
- end.
-
-%% A let-expression `let = e0 in e1' is semantically
-%% equivalent to a case-expression `case e0 of when 'true'
-%% -> e1 end'. As a special case, `let = e0 in e1' is also
-%% equivalent to `apply fun (v) -> e0 (e1)'. However, for efficiency,
-%% and in order to allow the handling of `case' clauses to introduce new
-%% let-expressions without entering an infinite rewrite loop, we handle
-%% these directly.
-
-%%% %% Rewriting a `let' to an equivalent expression.
-%%% i_let(E, Ctxt, Ren, Env, S) ->
-%%% case let_vars(E) of
-%%% [V] ->
-%%% E1 = update_c_apply(E, c_fun([V], let_body(E)), [let_arg(E)]),
-%%% i(E1, Ctxt, Ren, Env, S);
-%%% Vs ->
-%%% C = c_clause(Vs, abstract(true), let_body(E)),
-%%% E1 = update_c_case(E, let_arg(E), [C]),
-%%% i(E1, Ctxt, Ren, Env, S)
-%%% end.
-
-i_let(E, Ctxt, Ren, Env, S) ->
- case let_vars(E) of
- [V] ->
- i_let_1(V, E, Ctxt, Ren, Env, S);
- Vs ->
- %% Visit the argument expression in `value' context, to
- %% simplify it as far as possible.
- {A, S1} = i(let_arg(E), value, Ren, Env, S),
- case get_components(length(Vs), result(A)) of
- {true, As} ->
- %% Note that only the components of the result of
- %% `A' are passed on; any effects are hoisted.
- {E1, S2} = i_let_2(Vs, As, E, Ctxt, Ren, Env, S1),
- {hoist_effects(A, E1), S2};
- false ->
- %% We cannot do anything with this `let', since the
- %% variables cannot be matched against the argument
- %% components. Just visit the variables for renaming
- %% and visit the body for value (cf. `i_fun').
- {_, Ren1, Env1, S2} = bind_locals(Vs, Ren, Env, S1),
- Vs1 = i_params(Vs, Ren1, Env1),
- %% The body is always visited for value here.
- {B, S3} = i(let_body(E), value, Ren1, Env1, S2),
- S4 = count_size(weight('let'), S3),
- {update_c_let(E, Vs1, A, B), S4}
- end
- end.
-
-%% Single-variable `let' binding.
-
-i_let_1(V, E, Ctxt, Ren, Env, S) ->
- %% Make an operand structure for the argument expression, create a
- %% local binding from the parameter to the operand structure, and
- %% visit the body. Finally create necessary bindings and/or set
- %% flags.
- {Opnd, S1} = make_opnd(let_arg(E), Ren, Env, S),
- {[R], Ren1, Env1, S2} = bind_locals([V], [Opnd], Ren, Env, S1),
- {E1, S3} = i(let_body(E), Ctxt, Ren1, Env1, S2),
- i_let_3([R], [Opnd], E1, S3).
-
-%% Multi-variable `let' binding.
-
-i_let_2(Vs, As, E, Ctxt, Ren, Env, S) ->
- %% Make operand structures for the argument components. Note that
- %% since the argument has already been visited at this point, we use
- %% the identity renaming for the operands.
- {Opnds, S1} = mapfoldl(fun (E, S) ->
- make_opnd(E, ren__identity(), Env, S)
- end,
- S, As),
- %% Create local bindings from the parameters to their respective
- %% operand structures, and visit the body.
- {Rs, Ren1, Env1, S2} = bind_locals(Vs, Opnds, Ren, Env, S1),
- {E1, S3} = i(let_body(E), Ctxt, Ren1, Env1, S2),
- i_let_3(Rs, Opnds, E1, S3).
-
-i_let_3(Rs, Opnds, E, S) ->
- %% Create necessary bindings and/or set flags.
- {E1, S1} = make_let_bindings(Rs, E, S),
-
- %% We must also create evaluation for effect, for any unused
- %% operands, as after an application expression.
- residualize_operands(Opnds, E1, S1).
-
-%% A sequence `do e1 e2', written `(seq e1 e2)' in the original
-%% algorithm, where `e1' is evaluated for effect only (since its value
-%% is not used), and `e2' yields the final value. Note that we use
-%% `make_seq' to recompose the sequence after visiting the parts.
-
-i_seq(E, Ctxt, Ren, Env, S) ->
- {E1, S1} = i(seq_arg(E), effect, Ren, Env, S),
- {E2, S2} = i(seq_body(E), Ctxt, Ren, Env, S1),
- %% A sequence has no cost in itself.
- {make_seq(E1, E2), S2}.
-
-
-%% The `case' switch of Core Erlang is rather different from the boolean
-%% `(if e1 e2 e3)' case of the original algorithm, but the central idea
-%% is the same: if, given the simplified switch expression (which is
-%% visited in `value' context - a boolean `test' context would not be
-%% generally useful), there is a clause which could definitely be
-%% selected, such that no clause before it can possibly be selected,
-%% then we can eliminate all other clauses. (And even if this is not the
-%% case, some clauses can often be eliminated.) Furthermore, if a clause
-%% can be selected, we can replace the case-expression (including the
-%% switch expression) with the body of the clause and a set of zero or
-%% more let-bindings of subexpressions of the switch expression. (In the
-%% simplest case, the switch expression is evaluated only for effect.)
-
-i_case(E, Ctxt, Ren, Env, S) ->
- %% First visit the switch expression in `value' context, to simplify
- %% it as far as possible. Note that only the result part is passed
- %% on to the clause matching below; any effects are hoisted.
- {A, S1} = i(case_arg(E), value, Ren, Env, S),
- A1 = result(A),
-
- %% Propagating an application context into the branches could cause
- %% the arguments of the application to be evaluated *after* the
- %% switch expression, but *before* the body of the selected clause.
- %% Such interleaving is not allowed in general, and it does not seem
- %% worthwile to make a more powerful transformation here. Therefore,
- %% the clause bodies are conservatively visited for value if the
- %% context is `application'.
- Ctxt1 = safe_context(Ctxt),
- {E1, S2} = case get_components(case_arity(E), A1) of
- {true, As} ->
- i_case_1(As, E, Ctxt1, Ren, Env, S1);
- false ->
- i_case_1([], E, Ctxt1, Ren, Env, S1)
- end,
- {hoist_effects(A, E1), S2}.
-
-i_case_1(As, E, Ctxt, Ren, Env, S) ->
- case i_clauses(As, case_clauses(E), Ctxt, Ren, Env, S) of
- {false, {As1, Vs, Env1, Cs}, S1} ->
- %% We still have a list of clauses. Sanity check:
- if Cs == [] ->
- report_warning("empty list of clauses "
- "in residual program!.\n");
- true ->
- ok
- end,
- {A, S2} = i(c_values(As1), value, ren__identity(), Env1,
- S1),
- {E1, S3} = i_case_2(Cs, A, E, S2),
- i_case_3(Vs, Env1, E1, S3);
- {true, {_, Vs, Env1, [C]}, S1} ->
- %% A single clause was selected; we just take the body.
- i_case_3(Vs, Env1, clause_body(C), S1)
- end.
-
-%% Check if all clause bodies are actually equivalent expressions that
-%% do not depent on pattern variables (this sometimes occurs as a
-%% consequence of inlining, e.g., all branches might yield 'true'), and
-%% if so, replace the `case' with a sequence, first evaluating the
-%% clause selection for effect, then evaluating one of the clause bodies
-%% for its value. (Unless the switch contains a catch-all clause, the
-%% clause selection must be evaluated for effect, since there is no
-%% guarantee that any of the clauses will actually match. Assuming that
-%% some clause always matches could make an undefined program produce a
-%% value.) This makes the final size less than what was accounted for
-%% when visiting the clauses, but currently we don't try to adjust for
-%% this.
-
-i_case_2(Cs, A, E, S) ->
- case equivalent_clauses(Cs) of
- false ->
- %% Count the base sizes for the remaining clauses; pattern
- %% and guard sizes are already counted.
- N = weight('case') + weight(clause) * length(Cs),
- S1 = count_size(N, S),
- {update_c_case(E, A, Cs), S1};
- true ->
- case cerl_clauses:any_catchall(Cs) of
- true ->
- %% We know that some clause must be selected, so we
- %% can drop all the testing as well.
- E1 = make_seq(A, clause_body(hd(Cs))),
- {E1, S};
- false ->
- %% The clause selection must be performed for
- %% effect.
- E1 = update_c_case(E, A,
- set_clause_bodies(Cs, void())),
- {make_seq(E1, clause_body(hd(Cs))), S}
- end
- end.
-
-i_case_3(Vs, Env, E, S) ->
- %% For the variables bound to the switch expression subexpressions,
- %% make let bindings or create evaluation for effect.
- Rs = [env__get(var_name(V), Env) || V <- Vs],
- {E1, S1} = make_let_bindings(Rs, E, S),
- Opnds = [R#ref.opnd || R <- Rs],
- residualize_operands(Opnds, E1, S1).
-
-%% This function takes a sequence of switch expressions `Es' (which can
-%% be the empty list if these are unknown) and a list `Cs' of clauses,
-%% and returns `{Match, {As, Vs, Env1, Cs1}, S1}' where `As' is a list
-%% of residual switch expressions, `Vs' the list of variables used in
-%% the templates, `Env1' the environment for the templates, and `Cs1'
-%% the list of residual clauses. `Match' is `true' if some clause could
-%% be shown to definitely match (in this case, `Cs1' contains exactly
-%% one element), and `false' otherwise. `S1' is the new state. The given
-%% `Ctxt' is the context to be used for visiting the body of clauses.
-%%
-%% Visiting a clause basically amounts to extending the environment for
-%% all variables in the pattern, as for a `fun' (cf. `i_fun'),
-%% propagating match information if possible, and visiting the guard and
-%% body in the new environment.
-%%
-%% To make it cheaper to do handle a set of clauses, and to avoid
-%% unnecessarily exceeding the size limit, we avoid visiting the bodies
-%% of clauses which are subsequently removed, by dividing the visiting
-%% of a clause into two stages: first construct the environment(s) and
-%% visit the pattern (for renaming) and the guard (for value), then
-%% reduce the switch as much as possible, and lastly visit the body.
-
-i_clauses(Cs, Ctxt, Ren, Env, S) ->
- i_clauses([], Cs, Ctxt, Ren, Env, S).
-
-i_clauses(Es, Cs, Ctxt, Ren, Env, S) ->
- %% Create templates for the switch expressions.
- {Ts, {Vs, Env0}} = mapfoldl(fun (E, {Vs, Env}) ->
- {T, Vs1, Env1} =
- make_template(E, Env),
- {T, {Vs1 ++ Vs, Env1}}
- end,
- {[], Env}, Es),
-
- %% Make operand structures for the switch subexpression templates
- %% (found in `Env0') and add proper ref-structure bindings to the
- %% environment. Since the subexpressions in general can be
- %% interdependent (Vs is in reverse-dependency order), the
- %% environment (and renaming) must be created incrementally. Note
- %% that since the switch expressions have been visited already, the
- %% identity renaming is used for the operands.
- Vs1 = lists:reverse(Vs),
- {Ren1, Env1, S1} =
- foldl(fun (V, {Ren, Env, S}) ->
- E = env__get(var_name(V), Env0),
- {Opnd, S_1} = make_opnd(E, ren__identity(), Env,
- S),
- {_, Ren1, Env1, S_2} = bind_locals([V], [Opnd],
- Ren, Env, S_1),
- {Ren1, Env1, S_2}
- end,
- {Ren, Env, S}, Vs1),
-
- %% First we visit the head of each individual clause, renaming
- %% pattern variables, inserting let-bindings in the guard and body,
- %% and visiting the guard. The information used for visiting the
- %% clause body will be prefixed to the clause annotations.
- {Cs1, S2} = mapfoldl(fun (C, S) ->
- i_clause_head(C, Ts, Ren1, Env1, S)
- end,
- S1, Cs),
-
- %% Now that the clause guards have been reduced as far as possible,
- %% we can attempt to reduce the clauses.
- As = [hd(get_ann(T)) || T <- Ts],
- case cerl_clauses:reduce(Cs1, Ts) of
- {false, Cs2} ->
- %% We still have one or more clauses (with associated
- %% extended environments). Their bodies have not yet been
- %% visited, so we do that (in the respective safe
- %% environments, adding the sizes of the visited heads to
- %% the current size counter) and return the final list of
- %% clauses.
- {Cs3, S3} = mapfoldl(
- fun (C, S) ->
- i_clause_body(C, Ctxt, S)
- end,
- S2, Cs2),
- {false, {As, Vs1, Env1, Cs3}, S3};
- {true, {C, _}} ->
- %% A clause C could be selected (the bindings have already
- %% been added to the guard/body). Note that since the clause
- %% head will probably be discarded, its size is not counted.
- {C1, Ren2, Env2, _} = get_clause_extras(C),
- {B, S3} = i(clause_body(C), Ctxt, Ren2, Env2, S2),
- C2 = update_c_clause(C1, clause_pats(C1), clause_guard(C1), B),
- {true, {As, Vs1, Env1, [C2]}, S3}
- end.
-
-%% This visits the head of a clause, renames pattern variables, inserts
-%% let-bindings in the guard and body, and does inlining on the guard
-%% expression. Returns a list of pairs `{NewClause, Data}', where `Data'
-%% is `{Renaming, Environment, Size}' used for visiting the body of the
-%% new clause.
-
-i_clause_head(C, Ts, Ren, Env, S) ->
- %% Match the templates against the (non-renamed) patterns to get the
- %% available information about matching subexpressions. We don't
- %% care at this point whether an exact match/nomatch is detected.
- Ps = clause_pats(C),
- Bs = case cerl_clauses:match_list(Ps, Ts) of
- {_, Bs1} -> Bs1;
- none -> []
- end,
-
- %% The patterns must be visited for renaming; cf. `i_pattern'. We
- %% use a passive size counter for visiting the patterns and the
- %% guard (cf. `visit'), because we do not know at this stage whether
- %% the clause will be kept or not; the final value of the counter is
- %% included in the returned value below.
- {_, Ren1, Env1, S1} = bind_locals(clause_vars(C), Ren, Env, S),
- S2 = new_passive_size(get_size_limit(S1), S1),
- {Ps1, S3} = mapfoldl(fun (P, S) ->
- i_pattern(P, Ren1, Env1, Ren, Env, S)
- end,
- S2, Ps),
-
- %% Rewrite guard and body and visit the guard for value. Discard the
- %% latter size count if the guard turns out to be a constant.
- G = add_match_bindings(Bs, clause_guard(C)),
- B = add_match_bindings(Bs, clause_body(C)),
- {G1, S4} = i(G, value, Ren1, Env1, S3),
- S5 = case is_literal(G1) of
- true ->
- revert_size(S3, S4);
- false ->
- S4
- end,
-
- %% Revert to the size counter we had on entry to this function. The
- %% environment and renaming, together with the size of the clause
- %% head, are prefixed to the annotations for later use.
- Size = get_size_value(S5),
- C1 = update_c_clause(C, Ps1, G1, B),
- {set_clause_extras(C1, Ren1, Env1, Size), revert_size(S, S5)}.
-
-add_match_bindings(Bs, E) ->
- %% Don't waste time if the variables definitely cannot be used.
- %% (Most guards are simply `true'.)
- case is_literal(E) of
- true ->
- E;
- false ->
- Vs = [V || {V, E} <- Bs, E /= any],
- Es = [hd(get_ann(E)) || {_V, E} <- Bs, E /= any],
- c_let(Vs, c_values(Es), E)
- end.
-
-i_clause_body(C0, Ctxt, S) ->
- {C, Ren, Env, Size} = get_clause_extras(C0),
- S1 = count_size(Size, S),
- {B, S2} = i(clause_body(C), Ctxt, Ren, Env, S1),
- C1 = update_c_clause(C, clause_pats(C), clause_guard(C), B),
- {C1, S2}.
-
-get_clause_extras(C) ->
- [{Ren, Env, Size} | As] = get_ann(C),
- {set_ann(C, As), Ren, Env, Size}.
-
-set_clause_extras(C, Ren, Env, Size) ->
- As = [{Ren, Env, Size} | get_ann(C)],
- set_ann(C, As).
-
-%% This is the `(lambda x e)' case of the original algorithm. A
-%% `fun' is like a lambda expression, but with a varying number of
-%% parameters; possibly zero.
-
-i_fun(E, Ctxt, Ren, Env, S) ->
- case Ctxt of
- effect ->
- %% Reduce useless `fun' expressions to a simple constant;
- %% visiting the body would be a waste of time, and could
- %% needlessly mark variables as referenced.
- {void(), count_size(weight(literal), S)};
- value ->
- %% Note that the variables are visited as patterns.
- Vs = fun_vars(E),
- {_, Ren1, Env1, S1} = bind_locals(Vs, Ren, Env, S),
- Vs1 = i_params(Vs, Ren1, Env1),
-
- %% The body is always visited for value.
- {B, S2} = i(fun_body(E), value, Ren1, Env1, S1),
-
- %% We don't bother to include the exact number of free
- %% variables in the cost for creating a fun-value.
- S3 = count_size(weight('fun'), S2),
-
- %% Inlining might have duplicated code, so we must remove
- %% any 'id'-annotations from the original fun-expression.
- %% (This forces a later stage to invent new id:s.) This is
- %% necessary as long as fun:s may still need to be
- %% identified the old way. Function variables that are not
- %% in application context also have such annotations, but
- %% the inlining will currently lose all annotations on
- %% variable references (I think), so that's not a problem.
- {set_ann(c_fun(Vs1, B), kill_id_anns(get_ann(E))), S3};
- #app{} ->
- %% An application of a fun-expression (in the source code)
- %% is handled by going directly to `inline'; this is never
- %% residualised, and we don't set up new counters here. Note
- %% that inlining of copy-propagated fun-expressions is done
- %% in `copy'; not here.
- inline(E, Ctxt, Ren, Env, S)
- end.
-
-%% A `letrec' requires a circular environment, but is otherwise like a
-%% `let', i.e. like a direct lambda application. Note that only
-%% fun-expressions (lambda abstractions) may occur in the right-hand
-%% side of each definition.
-
-i_letrec(E, Ctxt, Ren, Env, S) ->
- %% Note that we pass an empty list for the auto-referenced
- %% (exported) functions here.
- {Es, B, _, S1} = i_letrec(letrec_defs(E), letrec_body(E), [], Ctxt,
- Ren, Env, S),
-
- %% If no bindings remain, only the body is returned.
- case Es of
- [] ->
- {B, S1}; % drop annotations on E
- _ ->
- S2 = count_size(weight(letrec), S1),
- {update_c_letrec(E, Es, B), S2}
- end.
-
-%% The major part of this is shared by letrec-expressions and module
-%% definitions alike.
-
-i_letrec(Es, B, Xs, Ctxt, Ren, Env, S) ->
- %% First, we create operands with dummy renamings and environments,
- %% and with fresh store locations for cached expressions and operand
- %% info.
- {Opnds, S1} = mapfoldl(fun ({_, E}, S) ->
- make_opnd(E, undefined, undefined, S)
- end,
- S, Es),
-
- %% Then we make recursive bindings for the definitions.
- {Rs, Ren1, Env1, S2} = bind_recursive([F || {F, _} <- Es],
- Opnds, Ren, Env, S1),
-
- %% For the function variables listed in Xs (none for a
- %% letrec-expression), we must make sure that the corresponding
- %% operand expressions are visited and that the definitions are
- %% marked as referenced; we also need to return the possibly renamed
- %% function variables.
- {Xs1, S3} =
- mapfoldl(
- fun (X, S) ->
- Name = ren__map(var_name(X), Ren1),
- case env__lookup(Name, Env1) of
- {ok, R} ->
- S_1 = i_letrec_export(R, S),
- {ref_to_var(R), S_1};
- error ->
- %% We just skip any exports that are not
- %% actually defined here, and generate a
- %% warning message.
- {N, A} = var_name(X),
- report_warning("export `~w'/~w "
- "not defined.\n", [N, A]),
- {X, S}
- end
- end,
- S2, Xs),
-
- %% At last, we can then visit the body.
- {B1, S4} = i(B, Ctxt, Ren1, Env1, S3),
-
- %% Finally, we create new letrec-bindings for any and all
- %% residualised definitions. All referenced functions should have
- %% been visited; the call to `visit' below is expected to retreive a
- %% cached expression.
- Rs1 = keep_referenced(Rs, S4),
- {Es1, S5} = mapfoldl(fun (R, S) ->
- {E_1, S_1} = visit(R#ref.opnd, S),
- {{ref_to_var(R), E_1}, S_1}
- end,
- S4, Rs1),
- {Es1, B1, Xs1, S5}.
-
-%% This visits the operand for a function definition exported by a
-%% `letrec' (which is really a `module' module definition, since normal
-%% letrecs have no export declarations). Only the updated state is
-%% returned. We must handle the "inner-pending" flag when doing this;
-%% cf. `i_var'.
-
-i_letrec_export(R, S) ->
- Opnd = R#ref.opnd,
- S1 = st__mark_inner_pending(Opnd#opnd.loc, S),
- {_, S2} = visit(Opnd, S1),
- {_, S3} = residualize_var(R, st__clear_inner_pending(Opnd#opnd.loc,
- S2)),
- S3.
-
-%% This is the `(call e1 e2)' case of the original algorithm. The only
-%% difference is that we must handle multiple (or no) operand
-%% expressions.
-
-i_apply(E, Ctxt, Ren, Env, S) ->
- {Opnds, S1} = mapfoldl(fun (E, S) ->
- make_opnd(E, Ren, Env, S)
- end,
- S, apply_args(E)),
-
- %% Allocate a new app-context location and set up an application
- %% context structure containing the surrounding context.
- {L, S2} = st__new_app_loc(S1),
- Ctxt1 = #app{opnds = Opnds, ctxt = Ctxt, loc = L},
-
- %% Visit the operator expression in the new call context.
- {E1, S3} = i(apply_op(E), Ctxt1, Ren, Env, S2),
-
- %% Check the "inlined" flag to find out what to do next. (The store
- %% location could be recycled after the flag has been tested, but
- %% there is no real advantage to that, because in practice, only
- %% 4-5% of all created store locations will ever be reused, while
- %% there will be a noticable overhead for managing the free list.)
- case st__get_app_inlined(L, S3) of
- true ->
- %% The application was inlined, so we have the final
- %% expression in `E1'. We just have to handle any operands
- %% that need to be residualized for effect only (i.e., those
- %% the values of which are not used).
- residualize_operands(Opnds, E1, S3);
- false ->
- %% Otherwise, `E1' is the residual operator expression. We
- %% make sure all operands are visited, and rebuild the
- %% application.
- {Es, S4} = mapfoldl(fun (Opnd, S) ->
- visit_and_count_size(Opnd, S)
- end,
- S3, Opnds),
- N = apply_size(length(Es)),
- {update_c_apply(E, E1, Es), count_size(N, S4)}
- end.
-
-apply_size(A) ->
- weight(apply) + weight(argument) * A.
-
-%% Since it is not the task of this transformation to handle
-%% cross-module inlining, all inter-module calls are handled by visiting
-%% the components (the module and function name, and the arguments of
-%% the call) for value. In `effect' context, if the function itself is
-%% known to be completely effect free, the call can be discarded and the
-%% arguments evaluated for effect. Otherwise, if all the visited
-%% arguments are to constants, and the function is known to be safe to
-%% execute at compile time, then we try to evaluate the call. If
-%% evaluation completes normally, the call is replaced by the result;
-%% otherwise the call is residualised.
-
-i_call(E, Ctxt, Ren, Env, S) ->
- {M, S1} = i(call_module(E), value, Ren, Env, S),
- {F, S2} = i(call_name(E), value, Ren, Env, S1),
- As = call_args(E),
- Arity = length(As),
-
- %% Check if the name of the called function is static. If so,
- %% discard the size counts performed above, since the values will
- %% not cause any runtime cost.
- Static = is_c_atom(M) and is_c_atom(F),
- S3 = case Static of
- true ->
- revert_size(S, S2);
- false ->
- S2
- end,
- case Ctxt of
- effect when Static == true ->
- case is_safe_call(atom_val(M), atom_val(F), Arity) of
- true ->
- %% The result will not be used, and the call is
- %% effect free, so we create a multiple-value
- %% aggregate containing the (not yet visited)
- %% arguments and process that instead.
- i(c_values(As), effect, Ren, Env, S3);
- false ->
- %% We are not allowed to simply discard the call,
- %% but we can try to evaluate it.
- i_call_1(Static, M, F, Arity, As, E, Ctxt, Ren, Env,
- S3)
- end;
- _ ->
- i_call_1(Static, M, F, Arity, As, E, Ctxt, Ren, Env, S3)
- end.
-
-i_call_1(Static, M, F, Arity, As, E, Ctxt, Ren, Env, S) ->
- %% Visit the arguments for value.
- {As1, S1} = mapfoldl(fun (X, A) -> i(X, value, Ren, Env, A) end,
- S, As),
- case Static of
- true ->
- case erl_bifs:is_pure(atom_val(M), atom_val(F), Arity) of
- true ->
- %% It is allowed to evaluate this at compile time.
- case all_static(As1) of
- true ->
- i_call_3(M, F, As1, E, Ctxt, Env, S1);
- false ->
- %% See if the call can be rewritten instead.
- i_call_4(M, F, As1, E, Ctxt, Env, S1)
- end;
- false ->
- i_call_2(M, F, As1, E, S1)
- end;
- false ->
- i_call_2(M, F, As1, E, S1)
- end.
-
-%% Residualise the call.
-
-i_call_2(M, F, As, E, S) ->
- N = weight(call) + weight(argument) * length(As),
- {update_c_call(E, M, F, As), count_size(N, S)}.
-
-%% Attempt to evaluate the call to yield a literal; if that fails, try
-%% to rewrite the expression.
-
-i_call_3(M, F, As, E, Ctxt, Env, S) ->
- %% Note that we extract the results of argument expessions here; the
- %% expressions could still be sequences with side effects.
- Vs = [concrete(result(A)) || A <- As],
- case catch {ok, apply(atom_val(M), atom_val(F), Vs)} of
- {ok, V} ->
- %% Evaluation completed normally - try to turn the result
- %% back into a syntax tree (representing a literal).
- case is_literal_term(V) of
- true ->
- %% Make a sequence of the arguments (as a
- %% multiple-value aggregate) and the final value.
- S1 = count_size(weight(values), S),
- S2 = count_size(weight(literal), S1),
- {make_seq(c_values(As), abstract(V)), S2};
- false ->
- %% The result could not be represented as a literal.
- i_call_4(M, F, As, E, Ctxt, Env, S)
- end;
- _ ->
- %% The evaluation attempt did not complete normally.
- i_call_4(M, F, As, E, Ctxt, Env, S)
- end.
-
-%% Rewrite the expression, if possible, otherwise residualise it.
-
-i_call_4(M, F, As, E, Ctxt, Env, S) ->
- case reduce_bif_call(atom_val(M), atom_val(F), As, Env) of
- false ->
- %% Nothing more to be done - residualise the call.
- i_call_2(M, F, As, E, S);
- {true, E1} ->
- %% We revisit the result, because the rewriting might have
- %% opened possibilities for further inlining. Since the
- %% parts have already been visited once, we use the identity
- %% renaming here.
- i(E1, Ctxt, ren__identity(), Env, S)
- end.
-
-%% For now, we assume that primops cannot be evaluated at compile time,
-%% probably being too special. Also, we have no knowledge about their
-%% side effects.
-
-i_primop(E, Ren, Env, S) ->
- %% Visit the arguments for value.
- {As, S1} = mapfoldl(fun (E, S) ->
- i(E, value, Ren, Env, S)
- end,
- S, primop_args(E)),
- N = weight(primop) + weight(argument) * length(As),
- {update_c_primop(E, primop_name(E), As), count_size(N, S1)}.
-
-%% This is like having an expression with an extra fun-expression
-%% attached for "exceptional cases"; actually, there are exactly two
-%% parameter variables for the body, but they are easiest handled as if
-%% their number might vary, just as for a `fun'.
-
-i_try(E, Ctxt, Ren, Env, S) ->
- %% The argument expression is evaluated in `value' context, and the
- %% surrounding context is propagated into both branches. We do not
- %% try to recognize cases when the protected expression will
- %% actually raise an exception. Note that the variables are visited
- %% as patterns.
- {A, S1} = i(try_arg(E), value, Ren, Env, S),
- Vs = try_vars(E),
- {_, Ren1, Env1, S2} = bind_locals(Vs, Ren, Env, S1),
- Vs1 = i_params(Vs, Ren1, Env1),
- {B, S3} = i(try_body(E), Ctxt, Ren1, Env1, S2),
- case is_safe(A) of
- true ->
- %% The `try' wrapper can be dropped in this case. Since the
- %% expressions have been visited already, the identity
- %% renaming is used when we revisit the new let-expression.
- i(c_let(Vs1, A, B), Ctxt, ren__identity(), Env, S3);
- false ->
- Evs = try_evars(E),
- {_, Ren2, Env2, S4} = bind_locals(Evs, Ren, Env, S3),
- Evs1 = i_params(Evs, Ren2, Env2),
- {H, S5} = i(try_handler(E), Ctxt, Ren2, Env2, S4),
- S6 = count_size(weight('try'), S5),
- {update_c_try(E, A, Vs1, B, Evs1, H), S6}
- end.
-
-%% A special case of try-expressions:
-
-i_catch(E, Ctxt, Ren, Env, S) ->
- %% We cannot propagate application contexts into the catch.
- {E1, S1} = i(catch_body(E), safe_context(Ctxt), Ren, Env, S),
- case is_safe(E1) of
- true ->
- %% The `catch' wrapper can be dropped in this case.
- {E1, S1};
- false ->
- S2 = count_size(weight('catch'), S1),
- {update_c_catch(E, E1), S2}
- end.
-
-%% A receive-expression is very much like a case-expression, with the
-%% difference that we do not have access to a switch expression, since
-%% the value being switched on is taken from the mailbox. The fact that
-%% the receive-expression may iterate over an arbitrary number of
-%% messages is not of interest to us. All we can do here is to visit its
-%% subexpressions, and possibly eliminate definitely unselectable
-%% clauses.
-
-i_receive(E, Ctxt, Ren, Env, S) ->
- %% We first visit the expiry expression (for value) and the expiry
- %% body (in the surrounding context).
- {T, S1} = i(receive_timeout(E), value, Ren, Env, S),
- {B, S2} = i(receive_action(E), Ctxt, Ren, Env, S1),
-
- %% Then we visit the clauses. Note that application contexts may not
- %% in general be propagated into the branches (and the expiry body),
- %% because the execution of the `receive' may remove a message from
- %% the mailbox as a side effect; the situation is thus analogous to
- %% that in a `case' expression.
- Ctxt1 = safe_context(Ctxt),
- case i_clauses(receive_clauses(E), Ctxt1, Ren, Env, S2) of
- {false, {[], _, _, Cs}, S3} ->
- %% We still have a list of clauses. If the list is empty,
- %% and the expiry expression is the integer zero, the
- %% expression reduces to the expiry body.
- if Cs == [] ->
- case is_c_int(T) andalso (int_val(T) == 0) of
- true ->
- {B, S3};
- false ->
- i_receive_1(E, Cs, T, B, S3)
- end;
- true ->
- i_receive_1(E, Cs, T, B, S3)
- end;
- {true, {_, _, _, Cs}, S3} ->
- %% Cs is a single clause that will always be matched (if a
- %% message exists), but we must keep the `receive' statement
- %% in order to fetch the message from the mailbox.
- i_receive_1(E, Cs, T, B, S3)
- end.
-
-i_receive_1(E, Cs, T, B, S) ->
- %% Here, we just add the base sizes for the receive-expression
- %% itself and for each remaining clause; cf. `case'.
- N = weight('receive') + weight(clause) * length(Cs),
- {update_c_receive(E, Cs, T, B), count_size(N, S)}.
-
-%% A module definition is like a `letrec', with some add-ons (export and
-%% attribute declarations) but without an explicit body. Actually, the
-%% exporting of function names has the same effect as if there was a
-%% body consisting of the list of references to the exported functions.
-%% Thus, the exported functions are exactly those which can be
-%% referenced from outside the module.
-
-i_module(E, Ctxt, Ren, Env, S) ->
- %% Cf. `i_letrec'. Note that we pass a dummy constant value for the
- %% "body" parameter.
- {Es, _, Xs1, S1} = i_letrec(module_defs(E), void(),
- module_exports(E), Ctxt, Ren, Env, S),
- %% Sanity check:
- case Es of
- [] ->
- report_warning("no function definitions remaining "
- "in module `~s'.\n",
- [atom_name(module_name(E))]);
- _ ->
- ok
- end,
- E1 = update_c_module(E, module_name(E), Xs1, module_attrs(E), Es),
- {E1, count_size(weight(module), S1)}.
-
-%% Binary-syntax expressions are too complicated to do anything
-%% interesting with here - that is beyond the scope of this program;
-%% also, their construction could have side effects, so even in effect
-%% context we can't remove them. (We don't bother to identify cases of
-%% "safe" unused binaries which could be removed.)
-
-i_binary(E, Ren, Env, S) ->
- %% Visit the segments for value.
- {Es, S1} = mapfoldl(fun (E, S) ->
- i_bitstr(E, Ren, Env, S)
- end,
- S, binary_segments(E)),
- S2 = count_size(weight(binary), S1),
- {update_c_binary(E, Es), S2}.
-
-i_bitstr(E, Ren, Env, S) ->
- %% It is not necessary to visit the Unit, Type and Flags fields,
- %% since these are always literals.
- {Val, S1} = i(bitstr_val(E), value, Ren, Env, S),
- {Size, S2} = i(bitstr_size(E), value, Ren, Env, S1),
- Unit = bitstr_unit(E),
- Type = bitstr_type(E),
- Flags = bitstr_flags(E),
- S3 = count_size(weight(bitstr), S2),
- {update_c_bitstr(E, Val, Size, Unit, Type, Flags), S3}.
-
-%% This is a simplified version of `i_pattern', for lists of parameter
-%% variables only. It does not modify the state.
-
-i_params([V | Vs], Ren, Env) ->
- Name = ren__map(var_name(V), Ren),
- case env__lookup(Name, Env) of
- {ok, R} ->
- [ref_to_var(R) | i_params(Vs, Ren, Env)];
- error ->
- report_internal_error("variable `~w' not bound "
- "in pattern.\n", [Name]),
- exit(error)
- end;
-i_params([], _, _) ->
- [].
-
-%% For ordinary patterns, we just visit to rename variables and count
-%% the size/cost. All occurring binding instances of variables should
-%% already have been added to the renaming and environment; however, to
-%% handle the size expressions of binary-syntax patterns, we must pass
-%% the renaming and environment of the containing expression
-
-i_pattern(E, Ren, Env, Ren0, Env0, S) ->
- case type(E) of
- var ->
- %% Count no size.
- Name = ren__map(var_name(E), Ren),
- case env__lookup(Name, Env) of
- {ok, R} ->
- {ref_to_var(R), S};
- error ->
- report_internal_error("variable `~w' not bound "
- "in pattern.\n", [Name]),
- exit(error)
- end;
- alias ->
- %% Count no size.
- V = alias_var(E),
- Name = ren__map(var_name(V), Ren),
- case env__lookup(Name, Env) of
- {ok, R} ->
- %% Visit the subpattern and recompose.
- V1 = ref_to_var(R),
- {P, S1} = i_pattern(alias_pat(E), Ren, Env, Ren0,
- Env0, S),
- {update_c_alias(E, V1, P), S1};
- error ->
- report_internal_error("variable `~w' not bound "
- "in pattern.\n", [Name]),
- exit(error)
- end;
- binary ->
- {Es, S1} = mapfoldl(fun (E, S) ->
- i_bitstr_pattern(E, Ren, Env,
- Ren0, Env0, S)
- end,
- S, binary_segments(E)),
- S2 = count_size(weight(binary), S1),
- {update_c_binary(E, Es), S2};
- _ ->
- case is_literal(E) of
- true ->
- {E, count_size(weight(literal), S)};
- false ->
- {Es1, S1} = mapfoldl(fun (E, S) ->
- i_pattern(E, Ren, Env,
- Ren0, Env0,
- S)
- end,
- S, data_es(E)),
- %% We assume that in general, the elements of the
- %% constructor will all be fetched.
- N = weight(data) + length(Es1) * weight(element),
- S2 = count_size(N, S1),
- {update_data(E, data_type(E), Es1), S2}
- end
- end.
-
-i_bitstr_pattern(E, Ren, Env, Ren0, Env0, S) ->
- %% It is not necessary to visit the Unit, Type and Flags fields,
- %% since these are always literals. The Value field is a limited
- %% pattern - either a literal or an unbound variable. The Size field
- %% is a limited expression - either a literal or a variable bound in
- %% the environment of the containing expression.
- {Val, S1} = i_pattern(bitstr_val(E), Ren, Env, Ren0, Env0, S),
- {Size, S2} = i(bitstr_size(E), value, Ren0, Env0, S1),
- Unit = bitstr_unit(E),
- Type = bitstr_type(E),
- Flags = bitstr_flags(E),
- S3 = count_size(weight(bitstr), S2),
- {update_c_bitstr(E, Val, Size, Unit, Type, Flags), S3}.
-
-
-%% ---------------------------------------------------------------------
-%% Other central inlining functions
-
-%% It is assumed here that `E' is a fun-expression and the context is an
-%% app-structure. If the inlining might be aborted for some reason, a
-%% corresponding catch should have been set up before entering `inline'.
-%%
-%% Note: if the inlined body is a lambda abstraction, and the
-%% surrounding context of the app-context is also an app-context, the
-%% `inlined' flag of the outermost context will be set before that of
-%% the inner context is set. E.g.: `let F = fun (X) -> fun (Y) -> E in
-%% apply apply F(A)(B)' will propagate the body of F, which is a lambda
-%% abstraction, into the outer application context, which will be
-%% inlined to produce expression `E', and the flag of the outer context
-%% will be set. Upon return, the flag of the inner context will also be
-%% set. However, the flags are then tested in innermost-first order.
-%% Thus, if some inlining attempt is aborted, the `inlined' flags of any
-%% nested app-contexts must be cleared.
-%%
-%% This implementation does nothing to handle inlining of calls to
-%% recursive functions in a smart way. This means that as long as the
-%% size and effort counters do not prevent it, the function body will be
-%% inlined (i.e., the first iteration will be unrolled), and the
-%% recursive calls will be residualized.
-
-inline(E, #app{opnds = Opnds, ctxt = Ctxt, loc = L}, Ren, Env, S) ->
- %% Check that the arities match:
- Vs = fun_vars(E),
- if length(Opnds) /= length(Vs) ->
- report_error("function called with wrong number "
- "of arguments!\n"),
- %% TODO: should really just residualise the call...
- exit(error);
- true ->
- ok
- end,
- %% Create local bindings for the parameters to their respective
- %% operand structures from the app-structure, and visit the body in
- %% the context saved in the structure.
- {Rs, Ren1, Env1, S1} = bind_locals(Vs, Opnds, Ren, Env, S),
- {E1, S2} = i(fun_body(E), Ctxt, Ren1, Env1, S1),
-
- %% Create necessary bindings and/or set flags.
- {E2, S3} = make_let_bindings(Rs, E1, S2),
-
- %% Lastly, flag the application as inlined, since the inlining
- %% attempt was not aborted before we reached this point.
- {E2, st__set_app_inlined(L, S3)}.
-
-%% For the (possibly renamed) argument variables to an inlined call,
-%% either create `let' bindings for them, if they are still referenced
-%% in the residual expression (in C/Lisp, also if they are assigned to),
-%% or otherwise (if they are not referenced or assigned) mark them for
-%% evaluation for side effects.
-
-make_let_bindings([R | Rs], E, S) ->
- {E1, S1} = make_let_bindings(Rs, E, S),
- make_let_binding(R, E1, S1);
-make_let_bindings([], E, S) ->
- {E, S}.
-
-make_let_binding(R, E, S) ->
- %% The `referenced' flag is conservatively computed. We therefore
- %% first check some simple cases where parameter R is definitely not
- %% referenced in the resulting body E.
- case is_literal(E) of
- true ->
- %% A constant contains no variable references.
- make_let_binding_1(R, E, S);
- false ->
- case is_c_var(E) of
- true ->
- case var_name(E) =:= R#ref.name of
- true ->
- %% The body is simply the parameter variable
- %% itself. Visit the operand for value and
- %% substitute the result for the body.
- visit_and_count_size(R#ref.opnd, S);
- false ->
- %% Not the same variable, so the parameter
- %% is not referenced at all.
- make_let_binding_1(R, E, S)
- end;
- false ->
- %% Proceed to check the `referenced' flag.
- case st__get_var_referenced(R#ref.loc, S) of
- true ->
- %% The parameter is probably referenced in
- %% the residual code (although it might not
- %% be). Visit the operand for value and
- %% create a let-binding.
- {E1, S1} = visit_and_count_size(R#ref.opnd,
- S),
- S2 = count_size(weight('let'), S1),
- {c_let([ref_to_var(R)], E1, E), S2};
- false ->
- %% The parameter is definitely not
- %% referenced.
- make_let_binding_1(R, E, S)
- end
- end
- end.
-
-%% This marks the operand for evaluation for effect.
-
-make_let_binding_1(R, E, S) ->
- Opnd = R#ref.opnd,
- {E, st__set_opnd_effect(Opnd#opnd.loc, S)}.
-
-%% Here, `R' is the ref-structure which is the target of the copy
-%% propagation, and `Opnd' is a visited operand structure, to be
-%% propagated through `R' if possible - if not, `R' is residualised.
-%% `Opnd' is normally the operand that `R' is bound to, and `E' is the
-%% result of visiting `Opnd' for value; we pass this as an argument so
-%% we don't have to fetch it multiple times (because we don't have
-%% constant time access).
-%%
-%% We also pass the environment of the site of the variable reference,
-%% for use when inlining a propagated fun-expression. In the original
-%% algorithm by Waddell, the environment used for inlining such cases is
-%% the identity mapping, because the fun-expression body has already
-%% been visited for value, and their algorithm combines renaming of
-%% source-code variables with the looking up of information about
-%% residual-code variables. We, however, need to check the environment
-%% of the call site when creating new non-shadowed variables, but we
-%% must avoid repeated renaming. We therefore separate the renaming and
-%% the environment (as in the renaming algorithm of Peyton-Jones and
-%% Marlow). This also makes our implementation more general, compared to
-%% the original algorithm, because we do not give up on propagating
-%% variables that were free in the fun-body.
-%%
-%% Example:
-%%
-%% let F = fun (X) -> {'foo', X} in
-%% let G = fun (H) -> apply H(F) % F is free in the fun G
-%% in apply G(fun (F) -> apply F(42))
-%% =>
-%% let F = fun (X) -> {'foo', X} in
-%% apply (fun (H) -> apply H(F))(fun (F) -> apply F(42))
-%% =>
-%% let F = fun (X) -> {'foo', X} in
-%% apply (fun (F) -> apply F(42))(F)
-%% =>
-%% let F = fun (X) -> {'foo', X} in
-%% apply F(42)
-%% =>
-%% apply (fun (X) -> {'foo', X})(2)
-%% =>
-%% {'foo', 42}
-%%
-%% The original algorithm would give up at stage 4, because F was free
-%% in the propagated fun-expression. Our version inlines this example
-%% completely.
-
-copy(R, Opnd, E, Ctxt, Env, S) ->
- case is_c_var(E) of
- true ->
- %% The operand reduces to another variable - get its
- %% ref-structure and attempt to propagate further.
- copy_var(env__get(var_name(E), Opnd#opnd.env), Ctxt, Env,
- S);
- false ->
- %% Apart from variables and functional values (the latter
- %% are handled by `copy_1' below), only constant literals
- %% are copyable in general; other things, including e.g.
- %% tuples `{foo, X}', could cause duplication of work, and
- %% are not copy propagated.
- case is_literal(E) of
- true ->
- {E, count_size(weight(literal), S)};
- false ->
- copy_1(R, Opnd, E, Ctxt, Env, S)
- end
- end.
-
-copy_var(R, Ctxt, Env, S) ->
- %% (In Lisp or C, if this other variable might be assigned to, we
- %% should residualize the "parent" instead, so we don't bypass any
- %% destructive updates.)
- case R#ref.opnd of
- undefined ->
- %% This variable is not bound to an expression, so just
- %% residualize it.
- residualize_var(R, S);
- Opnd ->
- %% Note that because operands are always visited before
- %% copied, all copyable operand expressions will be
- %% propagated through any number of bindings. If `R' was
- %% bound to a constant literal, we would never have reached
- %% this point.
- case st__lookup_opnd_cache(Opnd#opnd.loc, S) of
- error ->
- %% The result for this operand is not yet ready
- %% (which should mean that it is a recursive
- %% reference). Thus, we must residualise the
- %% variable.
- residualize_var(R, S);
- {ok, #cache{expr = E1}} ->
- %% The result for the operand is ready, so we can
- %% proceed to propagate it.
- copy_1(R, Opnd, E1, Ctxt, Env, S)
- end
- end.
-
-copy_1(R, Opnd, E, Ctxt, Env, S) ->
- %% Fun-expression (lambdas) are a bit special; they are copyable,
- %% but should preferably not be duplicated, so they should not be
- %% copy propagated except into application contexts, where they can
- %% be inlined.
- case is_c_fun(E) of
- true ->
- case Ctxt of
- #app{} ->
- %% First test if the operand is "outer-pending"; if
- %% so, don't inline.
- case st__test_outer_pending(Opnd#opnd.loc, S) of
- false ->
- copy_inline(R, Opnd, E, Ctxt, Env, S);
- true ->
- %% Cyclic reference forced inlining to stop
- %% (avoiding infinite unfolding).
- residualize_var(R, S)
- end;
- _ ->
- residualize_var(R, S)
- end;
- false ->
- %% We have no other cases to handle here
- residualize_var(R, S)
- end.
-
-%% This inlines a function value that was propagated to an application
-%% context. The inlining is done with an identity renaming (since the
-%% expression is already visited) but in the environment of the call
-%% site (which is OK because of the no-shadowing strategy for renaming,
-%% and because the domain of our environments are the residual-program
-%% variables instead of the source-program variables). Note that we must
-%% first set the "outer-pending" flag, and clear it afterwards.
-
-copy_inline(R, Opnd, E, Ctxt, Env, S) ->
- S1 = st__mark_outer_pending(Opnd#opnd.loc, S),
- case catch {ok, copy_inline_1(R, E, Ctxt, Env, S1)} of
- {ok, {E1, S2}} ->
- {E1, st__clear_outer_pending(Opnd#opnd.loc, S2)};
- {'EXIT', X} ->
- exit(X);
- X ->
- %% If we use destructive update for the `outer-pending'
- %% flag, we must make sure to clear it upon a nonlocal
- %% return.
- st__clear_outer_pending(Opnd#opnd.loc, S1),
- throw(X)
- end.
-
-%% If the current effort counter was passive, we use a new active effort
-%% counter with the inherited limit for this particular inlining.
-
-copy_inline_1(R, E, Ctxt, Env, S) ->
- case effort_is_active(S) of
- true ->
- copy_inline_2(R, E, Ctxt, Env, S);
- false ->
- S1 = new_active_effort(get_effort_limit(S), S),
- case catch {ok, copy_inline_2(R, E, Ctxt, Env, S1)} of
- {ok, {E1, S2}} ->
- %% Revert to the old effort counter.
- {E1, revert_effort(S, S2)};
- {counter_exceeded, effort, _} ->
- %% Aborted this inlining attempt because too much
- %% effort was spent. Residualize the variable and
- %% revert to the previous state.
- residualize_var(R, S);
- {'EXIT', X} ->
- exit(X);
- X ->
- throw(X)
- end
- end.
-
-%% Regardless of whether the current size counter is active or not, we
-%% use a new active size counter for each inlining. If the current
-%% counter was passive, the new counter gets the inherited size limit;
-%% if it was active, the size limit of the new counter will be equal to
-%% the remaining budget of the current counter (which itself is not
-%% affected by the inlining). This distributes the size budget more
-%% evenly over "inlinings within inlinings", so that the whole size
-%% budget is not spent on the first few call sites (in an inlined
-%% function body) forcing the remaining call sites to be residualised.
-
-copy_inline_2(R, E, Ctxt, Env, S) ->
- Limit = case size_is_active(S) of
- true ->
- get_size_limit(S) - get_size_value(S);
- false ->
- get_size_limit(S)
- end,
- %% Add the cost of the application to the new size limit, so we
- %% always inline functions that are small enough, even if `Limit' is
- %% close to zero at this point. (This is an extension to the
- %% original algorithm.)
- S1 = new_active_size(Limit + apply_size(length(Ctxt#app.opnds)), S),
- case catch {ok, inline(E, Ctxt, ren__identity(), Env, S1)} of
- {ok, {E1, S2}} ->
- %% Revert to the old size counter.
- {E1, revert_size(S, S2)};
- {counter_exceeded, size, S2} ->
- %% Aborted this inlining attempt because it got too big.
- %% Residualize the variable and revert to the old size
- %% counter. (It is important that we do not also revert the
- %% effort counter here. Because the effort and size counters
- %% are always set up together, we know that the effort
- %% counter returned in S2 is the same that was passed to
- %% `inline'.)
- S3 = revert_size(S, S2),
- %% If we use destructive update for the `inlined' flag, we
- %% must make sure to clear the flags of any nested
- %% app-contexts upon aborting; see `inline' for details.
- reset_nested_apps(Ctxt, S3), % for effect
- residualize_var(R, S3);
- {'EXIT', X} ->
- exit(X);
- X ->
- throw(X)
- end.
-
-reset_nested_apps(#app{ctxt = Ctxt, loc = L}, S) ->
- reset_nested_apps(Ctxt, st__clear_app_inlined(L, S));
-reset_nested_apps(_, S) ->
- S.
-
-
-%% ---------------------------------------------------------------------
-%% Support functions
-
-new_var(Env) ->
- Name = env__new_vname(Env),
- c_var(Name).
-
-residualize_var(R, S) ->
- S1 = count_size(weight(var), S),
- {ref_to_var(R), st__set_var_referenced(R#ref.loc, S1)}.
-
-%% This function returns the value-producing subexpression of any
-%% expression. (Except for sequencing expressions, this is the
-%% expression itself.)
-
-result(E) ->
- case is_c_seq(E) of
- true ->
- %% Also see `make_seq', which is used in all places to build
- %% sequences so that they are always nested in the first
- %% position.
- seq_body(E);
- false ->
- E
- end.
-
-%% This function rewrites E to `do A1 E' if A is `do A1 A2', and
-%% otherwise returns E unchanged.
-
-hoist_effects(A, E) ->
- case type(A) of
- seq -> make_seq(seq_arg(A), E);
- _ -> E
- end.
-
-%% This "build sequencing expression" operation assures that sequences
-%% are always nested in the first position, which makes it easy to find
-%% the actual value-producing expression of a sequence (cf. `result').
-
-make_seq(E1, E2) ->
- case is_safe(E1) of
- true ->
- %% The first expression can safely be dropped.
- E2;
- false ->
- %% If `E1' is a sequence whose final expression has no side
- %% effects, then we can lose *that* expression when we
- %% compose the new sequence, since its value will not be
- %% used.
- E3 = case is_c_seq(E1) of
- true ->
- case is_safe(seq_body(E1)) of
- true ->
- %% Drop the final expression.
- seq_arg(E1);
- false ->
- E1
- end;
- false ->
- E1
- end,
- case is_c_seq(E2) of
- true ->
- %% `E2' is a sequence (E2' E2''), so we must
- %% rearrange the nesting to ((E1, E2') E2''), to
- %% preserve the invariant. Annotations on `E2' are
- %% lost.
- c_seq(c_seq(E3, seq_arg(E2)), seq_body(E2));
- false ->
- c_seq(E3, E2)
- end
- end.
-
-%% Currently, safe expressions include variables, lambda expressions,
-%% constructors with safe subexpressions (this includes atoms, integers,
-%% empty lists, etc.), seq-, let- and letrec-expressions with safe
-%% subexpressions, try- and catch-expressions with safe subexpressions
-%% and calls to safe functions with safe argument subexpressions.
-%% Binaries seem too tricky to be considered.
-
-is_safe(E) ->
- case is_data(E) of
- true ->
- is_safe_list(data_es(E));
- false ->
- case type(E) of
- var ->
- true;
- 'fun' ->
- true;
- values ->
- is_safe_list(values_es(E));
- 'seq' ->
- case is_safe(seq_arg(E)) of
- true ->
- is_safe(seq_body(E));
- false ->
- false
- end;
- 'let' ->
- case is_safe(let_arg(E)) of
- true ->
- is_safe(let_body(E));
- false ->
- false
- end;
- letrec ->
- is_safe(letrec_body(E));
- 'try' ->
- %% If the argument expression is not safe, it could
- %% be modifying the state; thus, even if the body is
- %% safe, the try-expression as a whole would not be.
- %% If the argument is safe, the handler is not used.
- case is_safe(try_arg(E)) of
- true ->
- is_safe(try_body(E));
- false ->
- false
- end;
- 'catch' ->
- is_safe(catch_body(E));
- call ->
- M = call_module(E),
- F = call_name(E),
- case is_c_atom(M) and is_c_atom(F) of
- true ->
- As = call_args(E),
- case is_safe_list(As) of
- true ->
- is_safe_call(atom_val(M),
- atom_val(F),
- length(As));
- false ->
- false
- end;
- false ->
- false
- end;
- _ ->
- false
- end
- end.
-
-is_safe_list([E | Es]) ->
- case is_safe(E) of
- true ->
- is_safe_list(Es);
- false ->
- false
- end;
-is_safe_list([]) ->
- true.
-
-is_safe_call(M, F, A) ->
- erl_bifs:is_safe(M, F, A).
-
-%% When setting up local variables, we only create new names if we have
-%% to, according to the "no-shadowing" strategy.
-
-make_locals(Vs, Ren, Env) ->
- make_locals(Vs, [], Ren, Env).
-
-make_locals([V | Vs], As, Ren, Env) ->
- Name = var_name(V),
- case env__is_defined(Name, Env) of
- false ->
- %% The variable need not be renamed. Just make sure that the
- %% renaming will map it to itself.
- Name1 = Name,
- Ren1 = ren__add_identity(Name, Ren);
- true ->
- %% The variable must be renamed to maintain the no-shadowing
- %% invariant. Do the right thing for function variables.
- Name1 = case Name of
- {A, N} ->
- env__new_fname(A, N, Env);
- _ ->
- env__new_vname(Env)
- end,
- Ren1 = ren__add(Name, Name1, Ren)
- end,
- %% This temporary binding is added for correct new-key generation.
- Env1 = env__bind(Name1, dummy, Env),
- make_locals(Vs, [Name1 | As], Ren1, Env1);
-make_locals([], As, Ren, Env) ->
- {reverse(As), Ren, Env}.
-
-%% This adds let-bindings for the source code variables in `Es' to the
-%% environment `Env'.
-%%
-%% Note that we always assign a new state location for the
-%% residual-program variable, since we cannot know when a location for a
-%% particular variable in the source code can be reused.
-
-bind_locals(Vs, Ren, Env, S) ->
- Opnds = lists:duplicate(length(Vs), undefined),
- bind_locals(Vs, Opnds, Ren, Env, S).
-
-bind_locals(Vs, Opnds, Ren, Env, S) ->
- {Ns, Ren1, Env1} = make_locals(Vs, Ren, Env),
- {Rs, Env2, S1} = bind_locals_1(Ns, Opnds, [], Env1, S),
- {Rs, Ren1, Env2, S1}.
-
-%% Note that the `Vs' are currently not used for anything except the
-%% number of variables. If we were maintaining "source-referenced"
-%% flags, then the flag in the new variable should be initialized to the
-%% current value of the (residual-) referenced-flag of the "parent".
-
-bind_locals_1([N | Ns], [Opnd | Opnds], Rs, Env, S) ->
- {R, S1} = new_ref(N, Opnd, S),
- Env1 = env__bind(N, R, Env),
- bind_locals_1(Ns, Opnds, [R | Rs], Env1, S1);
-bind_locals_1([], [], Rs, Env, S) ->
- {lists:reverse(Rs), Env, S}.
-
-new_refs(Ns, Opnds, S) ->
- new_refs(Ns, Opnds, [], S).
-
-new_refs([N | Ns], [Opnd | Opnds], Rs, S) ->
- {R, S1} = new_ref(N, Opnd, S),
- new_refs(Ns, Opnds, [R | Rs], S1);
-new_refs([], [], Rs, S) ->
- {lists:reverse(Rs), S}.
-
-new_ref(N, Opnd, S) ->
- {L, S1} = st__new_ref_loc(S),
- {#ref{name = N, opnd = Opnd, loc = L}, S1}.
-
-%% This adds recursive bindings for the source code variables in `Es' to
-%% the environment `Env'. Note that recursive binding of a set of
-%% variables is an atomic operation on the environment - they cannot be
-%% added one at a time.
-
-bind_recursive(Vs, Opnds, Ren, Env, S) ->
- {Ns, Ren1, Env1} = make_locals(Vs, Ren, Env),
- {Rs, S1} = new_refs(Ns, Opnds, S),
-
- %% When this fun-expression is evaluated, it updates the operand
- %% structure in the ref-structure to contain the recursively defined
- %% environment and the correct renaming.
- Fun = fun (R, Env) ->
- Opnd = R#ref.opnd,
- R#ref{opnd = Opnd#opnd{ren = Ren1, env = Env}}
- end,
- {Rs, Ren1, env__bind_recursive(Ns, Rs, Fun, Env1), S1}.
-
-safe_context(Ctxt) ->
- case Ctxt of
- #app{} ->
- value;
- _ ->
- Ctxt
- end.
-
-%% Note that the name of a variable encodes its type: a "plain" variable
-%% or a function variable. The latter kind also contains an arity number
-%% which should be preserved upon renaming.
-
-ref_to_var(#ref{name = Name}) ->
- %% If we were maintaining "source-referenced" flags, the annotation
- %% `add_ann([#source_ref{loc = L}], E)' should also be done here, to
- %% make the algorithm reapplicable. This is however not necessary
- %% since there are no destructive variable assignments in Erlang.
- c_var(Name).
-
-%% Including the effort counter of the call site assures that the cost
-%% of processing an operand via `visit' is charged to the correct
-%% counter. In particular, if the effort counter of the call site was
-%% passive, the operands will also be processed with a passive counter.
-
-make_opnd(E, Ren, Env, S) ->
- {L, S1} = st__new_opnd_loc(S),
- C = st__get_effort(S1),
- Opnd = #opnd{expr = E, ren = Ren, env = Env, loc = L, effort = C},
- {Opnd, S1}.
-
-keep_referenced(Rs, S) ->
- [R || R <- Rs, st__get_var_referenced(R#ref.loc, S)].
-
-residualize_operands(Opnds, E, S) ->
- foldr(fun (Opnd, {E, S}) -> residualize_operand(Opnd, E, S) end,
- {E, S}, Opnds).
-
-%% This is the only case where an operand expression can be visited in
-%% `effect' context instead of `value' context.
-
-residualize_operand(Opnd, E, S) ->
- case st__get_opnd_effect(Opnd#opnd.loc, S) of
- true ->
- %% The operand has not been visited, so we do that now, but
- %% in `effect' context. (Waddell's algoritm does some stuff
- %% here to account specially for the operand size, which
- %% appears unnecessary.)
- {E1, S1} = i(Opnd#opnd.expr, effect, Opnd#opnd.ren,
- Opnd#opnd.env, S),
- {make_seq(E1, E), S1};
- false ->
- {E, S}
- end.
-
-%% The `visit' function always visits the operand expression in `value'
-%% context (`residualize_operand' visits an unreferenced operand
-%% expression in `effect' context when necessary). A new passive size
-%% counter is used for visiting the operand, the final value of which is
-%% then cached along with the resulting expression.
-%%
-%% Note that the effort counter of the call site, included in the
-%% operand structure, is not a shared object. Thus, the effort budget is
-%% actually reused over all occurrences of the operands of a single
-%% application. This does not appear to be a problem; just a
-%% modification of the algorithm.
-
-visit(Opnd, S) ->
- {C, S1} = visit_1(Opnd, S),
- {C#cache.expr, S1}.
-
-visit_and_count_size(Opnd, S) ->
- {C, S1} = visit_1(Opnd, S),
- {C#cache.expr, count_size(C#cache.size, S1)}.
-
-visit_1(Opnd, S) ->
- case st__lookup_opnd_cache(Opnd#opnd.loc, S) of
- error ->
- %% Use a new, passive, size counter for visiting operands,
- %% and use the effort counter of the context of the operand.
- %% It turns out that if the latter is active, it must be the
- %% same object as the one currently used, and if it is
- %% passive, it does not matter if it is the same object as
- %% any other counter.
- Effort = Opnd#opnd.effort,
- Active = counter__is_active(Effort),
- S1 = case Active of
- true ->
- S; % don't change effort counter
- false ->
- st__set_effort(Effort, S)
- end,
- S2 = new_passive_size(get_size_limit(S1), S1),
-
- %% Visit the expression and cache the result, along with the
- %% final value of the size counter.
- {E, S3} = i(Opnd#opnd.expr, value, Opnd#opnd.ren,
- Opnd#opnd.env, S2),
- Size = get_size_value(S3),
- C = #cache{expr = E, size = Size},
- S4 = revert_size(S, st__set_opnd_cache(Opnd#opnd.loc, C,
- S3)),
- case Active of
- true ->
- {C, S4}; % keep using the same effort counter
- false ->
- {C, revert_effort(S, S4)}
- end;
- {ok, C} ->
- {C, S}
- end.
-
-%% Create a pattern matching template for an expression. A template
-%% contains only data constructors (including atomic ones) and
-%% variables, and compound literals are not folded into a single node.
-%% Each node in the template is annotated with the variable which holds
-%% the corresponding subexpression; these are new, unique variables not
-%% existing in the given `Env'. Returns `{Template, Variables, NewEnv}',
-%% where `Variables' is the list of all variables corresponding to nodes
-%% in the template *listed in reverse dependency order*, and `NewEnv' is
-%% `Env' augmented with mappings from the variable names to
-%% subexpressions of `E' (not #ref{} structures!) rewritten so that no
-%% computations are duplicated. `Variables' is guaranteed to be nonempty
-%% - at least the root node will always be bound to a new variable.
-
-make_template(E, Env) ->
- make_template(E, [], Env).
-
-make_template(E, Vs0, Env0) ->
- case is_data(E) of
- true ->
- {Ts, {Vs1, Env1}} = mapfoldl(
- fun (E, {Vs0, Env0}) ->
- {T, Vs1, Env1} =
- make_template(E, Vs0,
- Env0),
- {T, {Vs1, Env1}}
- end,
- {Vs0, Env0}, data_es(E)),
- T = make_data_skel(data_type(E), Ts),
- E1 = update_data(E, data_type(E),
- [hd(get_ann(T)) || T <- Ts]),
- V = new_var(Env1),
- Env2 = env__bind(var_name(V), E1, Env1),
- {set_ann(T, [V]), [V | Vs1], Env2};
- false ->
- case type(E) of
- seq ->
- %% For a sequencing, we can rebind the variable used
- %% for the body, and pass on the template as it is.
- {T, Vs1, Env1} = make_template(seq_body(E), Vs0,
- Env0),
- V = var_name(hd(get_ann(T))),
- E1 = update_c_seq(E, seq_arg(E), env__get(V, Env1)),
- Env2 = env__bind(V, E1, Env1),
- {T, Vs1, Env2};
- _ ->
- V = new_var(Env0),
- Env1 = env__bind(var_name(V), E, Env0),
- {set_ann(V, [V]), [V | Vs0], Env1}
- end
- end.
-
-%% Two clauses are equivalent if their bodies are equivalent expressions
-%% given that the respective pattern variables are local.
-
-equivalent_clauses([]) ->
- true;
-equivalent_clauses([C | Cs]) ->
- Env = cerl_trees:variables(c_values(clause_pats(C))),
- equivalent_clauses_1(clause_body(C), Cs, Env).
-
-equivalent_clauses_1(E, [C | Cs], Env) ->
- Env1 = cerl_trees:variables(c_values(clause_pats(C))),
- case equivalent(E, clause_body(C), ordsets:union(Env, Env1)) of
- true ->
- equivalent_clauses_1(E, Cs, Env);
- false ->
- false
- end;
-equivalent_clauses_1(_, [], _Env) ->
- true.
-
-%% Two expressions are equivalent if and only if they yield the same
-%% value and has the same side effects in the same order. Currently, we
-%% only accept equality between constructors (constants) and nonlocal
-%% variables, since this should cover most cases of interest. If a
-%% variable is locally bound in one expression, it cannot be equivalent
-%% to one with the same name in the other expression, so we need not
-%% keep track of two environments.
-
-equivalent(E1, E2, Env) ->
- case is_data(E1) of
- true ->
- case is_data(E2) of
- true ->
- T1 = {data_type(E1), data_arity(E1)},
- T2 = {data_type(E2), data_arity(E2)},
- %% Note that we must test for exact equality.
- if T1 =:= T2 ->
- equivalent_lists(data_es(E1), data_es(E2),
- Env);
- true ->
- false
- end;
- false ->
- false
- end;
- false ->
- case type(E1) of
- var ->
- case is_c_var(E2) of
- true ->
- N1 = var_name(E1),
- N2 = var_name(E2),
- if N1 =:= N2 ->
- not ordsets:is_element(N1, Env);
- true ->
- false
- end;
- false ->
- false
- end;
- _ ->
- %% Other constructs are not being considered.
- false
- end
- end.
-
-equivalent_lists([E1 | Es1], [E2 | Es2], Env) ->
- equivalent(E1, E2, Env) and equivalent_lists(Es1, Es2, Env);
-equivalent_lists([], [], _) ->
- true;
-equivalent_lists(_, _, _) ->
- false.
-
-%% Return `false' or `{true, EffectExpr, ValueExpr}'. The environment is
-%% passed for new-variable generation.
-
-reduce_bif_call(M, F, As, Env) ->
- reduce_bif_call_1(M, F, length(As), As, Env).
-
-reduce_bif_call_1(erlang, element, 2, [X, Y], _Env) ->
- case is_c_int(X) and is_c_tuple(Y) of
- true ->
- %% We are free to change the relative evaluation order of
- %% the elements, so lifting out a particular element is OK.
- T = list_to_tuple(tuple_es(Y)),
- N = int_val(X),
- if integer(N), N > 0, N =< size(T) ->
- E = element(N, T),
- Es = tuple_to_list(setelement(N, T, void())),
- {true, make_seq(c_tuple(Es), E)};
- true ->
- false
- end;
- false ->
- false
- end;
-reduce_bif_call_1(erlang, hd, 1, [X], _Env) ->
- case is_c_cons(X) of
- true ->
- %% Cf. `element/2' above.
- {true, make_seq(cons_tl(X), cons_hd(X))};
- false ->
- false
- end;
-reduce_bif_call_1(erlang, length, 1, [X], _Env) ->
- case is_c_list(X) of
- true ->
- %% Cf. `erlang:size/1' below.
- {true, make_seq(X, c_int(list_length(X)))};
- false ->
- false
- end;
-reduce_bif_call_1(erlang, list_to_tuple, 1, [X], _Env) ->
- case is_c_list(X) of
- true ->
- %% This does not actually preserve all the evaluation order
- %% constraints of the list, but I don't imagine that it will
- %% be a problem.
- {true, c_tuple(list_elements(X))};
- false ->
- false
- end;
-reduce_bif_call_1(erlang, setelement, 3, [X, Y, Z], Env) ->
- case is_c_int(X) and is_c_tuple(Y) of
- true ->
- %% Here, unless `Z' is a simple expression, we must bind it
- %% to a new variable, because in that case, `Z' must be
- %% evaluated before any part of `Y'.
- T = list_to_tuple(tuple_es(Y)),
- N = int_val(X),
- if integer(N), N > 0, N =< size(T) ->
- E = element(N, T),
- case is_simple(Z) of
- true ->
- Es = tuple_to_list(setelement(N, T, Z)),
- {true, make_seq(E, c_tuple(Es))};
- false ->
- V = new_var(Env),
- Es = tuple_to_list(setelement(N, T, V)),
- E1 = make_seq(E, c_tuple(Es)),
- {true, c_let([V], Z, E1)}
- end;
- true ->
- false
- end;
- false ->
- false
- end;
-reduce_bif_call_1(erlang, size, 1, [X], _Env) ->
- case is_c_tuple(X) of
- true ->
- %% Just evaluate the tuple for effect and use the size (the
- %% arity) as the result.
- {true, make_seq(X, c_int(tuple_arity(X)))};
- false ->
- false
- end;
-reduce_bif_call_1(erlang, tl, 1, [X], _Env) ->
- case is_c_cons(X) of
- true ->
- %% Cf. `element/2' above.
- {true, make_seq(cons_hd(X), cons_tl(X))};
- false ->
- false
- end;
-reduce_bif_call_1(erlang, tuple_to_list, 1, [X], _Env) ->
- case is_c_tuple(X) of
- true ->
- %% This actually introduces slightly stronger constraints on
- %% the evaluation order of the subexpressions.
- {true, make_list(tuple_es(X))};
- false ->
- false
- end;
-reduce_bif_call_1(_M, _F, _A, _As, _Env) ->
- false.
-
-effort_is_active(S) ->
- counter__is_active(st__get_effort(S)).
-
-size_is_active(S) ->
- counter__is_active(st__get_size(S)).
-
-get_effort_limit(S) ->
- counter__limit(st__get_effort(S)).
-
-new_active_effort(Limit, S) ->
- st__set_effort(counter__new_active(Limit), S).
-
-revert_effort(S1, S2) ->
- st__set_effort(st__get_effort(S1), S2).
-
-new_active_size(Limit, S) ->
- st__set_size(counter__new_active(Limit), S).
-
-new_passive_size(Limit, S) ->
- st__set_size(counter__new_passive(Limit), S).
-
-revert_size(S1, S2) ->
- st__set_size(st__get_size(S1), S2).
-
-count_effort(N, S) ->
- C = st__get_effort(S),
- C1 = counter__add(N, C, effort, S),
- case debug_counters() of
- true ->
- case counter__is_active(C1) of
- true ->
- V = counter__value(C1),
- case V > get(counter_effort_max) of
- true ->
- put(counter_effort_max, V);
- false ->
- ok
- end;
- false ->
- ok
- end;
- _ ->
- ok
- end,
- st__set_effort(C1, S).
-
-count_size(N, S) ->
- C = st__get_size(S),
- C1 = counter__add(N, C, size, S),
- case debug_counters() of
- true ->
- case counter__is_active(C1) of
- true ->
- V = counter__value(C1),
- case V > get(counter_size_max) of
- true ->
- put(counter_size_max, V);
- false ->
- ok
- end;
- false ->
- ok
- end;
- _ ->
- ok
- end,
- st__set_size(C1, S).
-
-get_size_value(S) ->
- counter__value(st__get_size(S)).
-
-get_size_limit(S) ->
- counter__limit(st__get_size(S)).
-
-kill_id_anns([{'id',_} | As]) ->
- kill_id_anns(As);
-kill_id_anns([A | As]) ->
- [A | kill_id_anns(As)];
-kill_id_anns([]) ->
- [].
-
-
-%% =====================================================================
-%% General utilities
-
-max(X, Y) when X > Y -> X;
-max(_, Y) -> Y.
-
-%% The atom `ok', is widely used in Erlang for "void" values.
-
-void() -> abstract(ok).
-
-is_simple(E) ->
- case type(E) of
- literal -> true;
- var -> true;
- 'fun' -> true;
- _ -> false
- end.
-
-get_components(N, E) ->
- case type(E) of
- values ->
- Es = values_es(E),
- if length(Es) == N ->
- {true, Es};
- true ->
- false
- end;
- _ when N == 1 ->
- {true, [E]};
- _ ->
- false
- end.
-
-all_static([E | Es]) ->
- case is_literal(result(E)) of
- true ->
- all_static(Es);
- false ->
- false
- end;
-all_static([]) ->
- true.
-
-set_clause_bodies([C | Cs], B) ->
- [update_c_clause(C, clause_pats(C), clause_guard(C), B)
- | set_clause_bodies(Cs, B)];
-set_clause_bodies([], _) ->
- [].
-
-filename([C | T]) when integer(C), C > 0, C =< 255 ->
- [C | filename(T)];
-filename([H|T]) ->
- filename(H) ++ filename(T);
-filename([]) ->
- [];
-filename(N) when atom(N) ->
- atom_to_list(N);
-filename(N) ->
- report_error("bad filename: `~P'.", [N, 25]),
- exit(error).
-
-
-%% =====================================================================
-%% Abstract datatype: renaming()
-
-ren__identity() ->
- dict:new().
-
-ren__add(X, Y, Ren) ->
- dict:store(X, Y, Ren).
-
-ren__map(X, Ren) ->
- case dict:find(X, Ren) of
- {ok, Y} ->
- Y;
- error ->
- X
- end.
-
-ren__add_identity(X, Ren) ->
- dict:erase(X, Ren).
-
-
-%% =====================================================================
-%% Abstract datatype: environment()
-
-env__empty() ->
- rec_env:empty().
-
-env__bind(Key, Val, Env) ->
- rec_env:bind(Key, Val, Env).
-
-%% `Es' should have type `[{Key, Val}]', and `Fun' should have type
-%% `(Val, Env) -> T', mapping a value together with the recursive
-%% environment itself to some term `T' to be returned when the entry is
-%% looked up.
-
-env__bind_recursive(Ks, Vs, F, Env) ->
- rec_env:bind_recursive(Ks, Vs, F, Env).
-
-env__lookup(Key, Env) ->
- rec_env:lookup(Key, Env).
-
-env__get(Key, Env) ->
- rec_env:get(Key, Env).
-
-env__is_defined(Key, Env) ->
- rec_env:is_defined(Key, Env).
-
-env__new_vname(Env) ->
- rec_env:new_key(Env).
-
-env__new_fname(A, N, Env) ->
- rec_env:new_key(fun (X) ->
- S = integer_to_list(X),
- {list_to_atom(atom_to_list(A) ++ "_" ++ S),
- N}
- end, Env).
-
-
-%% =====================================================================
-%% Abstract datatype: state()
-
--record(state, {free, % next free location
- size, % size counter
- effort, % effort counter
- cache, % operand expression cache
- var_flags, % flags for variables (#ref-structures)
- opnd_flags, % flags for operands
- app_flags}). % flags for #app-structures
-
-%% Note that we do not have a `var_assigned' flag, since there is no
-%% destructive assignment in Erlang. In the original algorithm, the
-%% "residual-referenced"-flags of the previous inlining pass (or
-%% initialization pass) are used as the "source-referenced"-flags for
-%% the subsequent pass. The latter may then be used as a safe
-%% approximation whenever we need to base a decision on whether or not a
-%% particular variable or function variable could be referenced in the
-%% program being generated, and computation of the new
-%% "residual-referenced" flag for that variable is not yet finished. In
-%% the present algorithm, this can only happen in the presence of
-%% variable assignments, which do not exist in Erlang. Therefore, we do
-%% not keep "source-referenced" flags for residual-code references in
-%% our implementation.
-%%
-%% The "inner-pending" flag tells us whether we are already in the
-%% process of visiting a particular operand, and the "outer-pending"
-%% flag whether we are in the process of inlining a propagated
-%% functional value. The "pending flags" are really counters limiting
-%% the number of times an operand may be inlined recursively, causing
-%% loop unrolling; however, unrolling more than one iteration does not
-%% work offhand in the present implementation. (TODO: find out why.)
-%% Note that the initial value must be greater than zero in order for
-%% any inlining at all to be done.
-
-%% Flags are stored in ETS-tables, one table for each class. The second
-%% element in each stored tuple is the key (the "label").
-
--record(var_flags, {lab, referenced = false}).
--record(opnd_flags, {lab, inner_pending = 1, outer_pending = 1,
- effect = false}).
--record(app_flags, {lab, inlined = false}).
-
-st__new(Effort, Size) ->
- #state{free = 0,
- size = counter__new_passive(Size),
- effort = counter__new_passive(Effort),
- cache = dict:new(),
- var_flags = ets:new(var, [set, private, {keypos, 2}]),
- opnd_flags = ets:new(opnd, [set, private, {keypos, 2}]),
- app_flags = ets:new(app, [set, private, {keypos, 2}])}.
-
-st__new_loc(S) ->
- N = S#state.free,
- {N, S#state{free = N + 1}}.
-
-st__get_effort(S) ->
- S#state.effort.
-
-st__set_effort(C, S) ->
- S#state{effort = C}.
-
-st__get_size(S) ->
- S#state.size.
-
-st__set_size(C, S) ->
- S#state{size = C}.
-
-st__set_var_referenced(L, S) ->
- T = S#state.var_flags,
- [F] = ets:lookup(T, L),
- ets:insert(T, F#var_flags{referenced = true}),
- S.
-
-st__get_var_referenced(L, S) ->
- ets:lookup_element(S#state.var_flags, L, #var_flags.referenced).
-
-st__lookup_opnd_cache(L, S) ->
- dict:find(L, S#state.cache).
-
-%% Note that setting the cache should only be done once.
-
-st__set_opnd_cache(L, C, S) ->
- S#state{cache = dict:store(L, C, S#state.cache)}.
-
-st__set_opnd_effect(L, S) ->
- T = S#state.opnd_flags,
- [F] = ets:lookup(T, L),
- ets:insert(T, F#opnd_flags{effect = true}),
- S.
-
-st__get_opnd_effect(L, S) ->
- ets:lookup_element(S#state.opnd_flags, L, #opnd_flags.effect).
-
-st__set_app_inlined(L, S) ->
- T = S#state.app_flags,
- [F] = ets:lookup(T, L),
- ets:insert(T, F#app_flags{inlined = true}),
- S.
-
-st__clear_app_inlined(L, S) ->
- T = S#state.app_flags,
- [F] = ets:lookup(T, L),
- ets:insert(T, F#app_flags{inlined = false}),
- S.
-
-st__get_app_inlined(L, S) ->
- ets:lookup_element(S#state.app_flags, L, #app_flags.inlined).
-
-%% The pending-flags are initialized by `st__new_opnd_loc' below.
-
-st__test_inner_pending(L, S) ->
- T = S#state.opnd_flags,
- P = ets:lookup_element(T, L, #opnd_flags.inner_pending),
- P =< 0.
-
-st__mark_inner_pending(L, S) ->
- ets:update_counter(S#state.opnd_flags, L,
- {#opnd_flags.inner_pending, -1}),
- S.
-
-st__clear_inner_pending(L, S) ->
- ets:update_counter(S#state.opnd_flags, L,
- {#opnd_flags.inner_pending, 1}),
- S.
-
-st__test_outer_pending(L, S) ->
- T = S#state.opnd_flags,
- P = ets:lookup_element(T, L, #opnd_flags.outer_pending),
- P =< 0.
-
-st__mark_outer_pending(L, S) ->
- ets:update_counter(S#state.opnd_flags, L,
- {#opnd_flags.outer_pending, -1}),
- S.
-
-st__clear_outer_pending(L, S) ->
- ets:update_counter(S#state.opnd_flags, L,
- {#opnd_flags.outer_pending, 1}),
- S.
-
-st__new_app_loc(S) ->
- V = {L, _S1} = st__new_loc(S),
- ets:insert(S#state.app_flags, #app_flags{lab = L}),
- V.
-
-st__new_ref_loc(S) ->
- V = {L, _S1} = st__new_loc(S),
- ets:insert(S#state.var_flags, #var_flags{lab = L}),
- V.
-
-st__new_opnd_loc(S) ->
- V = {L, _S1} = st__new_loc(S),
- ets:insert(S#state.opnd_flags, #opnd_flags{lab = L}),
- V.
-
-
-%% =====================================================================
-%% Abstract datatype: counter()
-%%
-%% `counter__add' throws `{counter_exceeded, Type, Data}' if the
-%% resulting counter value would exceed the limit for the counter in
-%% question (`Type' and `Data' are given by the user).
-
--record(counter, {active, value, limit}).
-
-counter__new_passive(Limit) when Limit > 0 ->
- {0, Limit}.
-
-counter__new_active(Limit) when Limit > 0 ->
- {Limit, Limit}.
-
-%% Active counters have values > 0 internally; passive counters start at
-%% zero. The 'limit' field is only accessed by the 'counter__limit'
-%% function.
-
-counter__is_active({C, _}) ->
- C > 0.
-
-counter__limit({_, L}) ->
- L.
-
-counter__value({N, L}) ->
- if N > 0 ->
- L - N;
- true ->
- -N
- end.
-
-counter__add(N, {V, L}, Type, Data) ->
- N1 = V - N,
- if V > 0, N1 =< 0 ->
- case debug_counters() of
- true ->
- case Type of
- effort ->
- put(counter_effort_triggers,
- get(counter_effort_triggers) + 1);
- size ->
- put(counter_size_triggers,
- get(counter_size_triggers) + 1)
- end;
- _ ->
- ok
- end,
- throw({counter_exceeded, Type, Data});
- true ->
- {N1, L}
- end.
-
-
-%% =====================================================================
-%% Reporting
-
-% report_internal_error(S) ->
-% report_internal_error(S, []).
-
-report_internal_error(S, Vs) ->
- report_error("internal error: " ++ S, Vs).
-
-report_error(D) ->
- report_error(D, []).
-
-report_error({F, L, D}, Vs) ->
- report({F, L, {error, D}}, Vs);
-report_error(D, Vs) ->
- report({error, D}, Vs).
-
-report_warning(D) ->
- report_warning(D, []).
-
-report_warning({F, L, D}, Vs) ->
- report({F, L, {warning, D}}, Vs);
-report_warning(D, Vs) ->
- report({warning, D}, Vs).
-
-report(D, Vs) ->
- io:put_chars(format(D, Vs)).
-
-format({error, D}, Vs) ->
- ["error: ", format(D, Vs)];
-format({warning, D}, Vs) ->
- ["warning: ", format(D, Vs)];
-format({"", L, D}, Vs) when integer(L), L > 0 ->
- [io_lib:fwrite("~w: ", [L]), format(D, Vs)];
-format({"", _L, D}, Vs) ->
- format(D, Vs);
-format({F, L, D}, Vs) when integer(L), L > 0 ->
- [io_lib:fwrite("~s:~w: ", [filename(F), L]), format(D, Vs)];
-format({F, _L, D}, Vs) ->
- [io_lib:fwrite("~s: ", [filename(F)]), format(D, Vs)];
-format(S, Vs) when list(S) ->
- [io_lib:fwrite(S, Vs), $\n].
-
-
-%% =====================================================================
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_trees.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_trees.erl
deleted file mode 100644
index 50384a6ff8..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_trees.erl
+++ /dev/null
@@ -1,801 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Richard Carlsson.
-%% Copyright (C) 1999-2002 Richard Carlsson.
-%% Portions created by Ericsson are Copyright 2001, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: cerl_trees.erl,v 1.2 2010/06/07 06:32:39 kostis Exp $
-
-%% @doc Basic functions on Core Erlang abstract syntax trees.
-%%
-%% Syntax trees are defined in the module cerl
.
-%%
-%% @type cerl() = cerl:cerl()
-
--module(cerl_trees).
-
--export([depth/1, fold/3, free_variables/1, label/1, label/2, map/2,
- mapfold/3, size/1, variables/1]).
-
--import(cerl, [alias_pat/1, alias_var/1, ann_c_alias/3, ann_c_apply/3,
- ann_c_binary/2, ann_c_bitstr/6, ann_c_call/4,
- ann_c_case/3, ann_c_catch/2, ann_c_clause/4,
- ann_c_cons_skel/3, ann_c_fun/3, ann_c_let/4,
- ann_c_letrec/3, ann_c_module/5, ann_c_primop/3,
- ann_c_receive/4, ann_c_seq/3, ann_c_try/6,
- ann_c_tuple_skel/2, ann_c_values/2, apply_args/1,
- apply_op/1, binary_segments/1, bitstr_val/1,
- bitstr_size/1, bitstr_unit/1, bitstr_type/1,
- bitstr_flags/1, call_args/1, call_module/1, call_name/1,
- case_arg/1, case_clauses/1, catch_body/1, clause_body/1,
- clause_guard/1, clause_pats/1, clause_vars/1, concrete/1,
- cons_hd/1, cons_tl/1, fun_body/1, fun_vars/1, get_ann/1,
- let_arg/1, let_body/1, let_vars/1, letrec_body/1,
- letrec_defs/1, letrec_vars/1, module_attrs/1,
- module_defs/1, module_exports/1, module_name/1,
- module_vars/1, primop_args/1, primop_name/1,
- receive_action/1, receive_clauses/1, receive_timeout/1,
- seq_arg/1, seq_body/1, set_ann/2, subtrees/1, try_arg/1,
- try_body/1, try_vars/1, try_evars/1, try_handler/1,
- tuple_es/1, type/1, update_c_alias/3, update_c_apply/3,
- update_c_binary/2, update_c_bitstr/6, update_c_call/4,
- update_c_case/3, update_c_catch/2, update_c_clause/4,
- update_c_cons/3, update_c_cons_skel/3, update_c_fun/3,
- update_c_let/4, update_c_letrec/3, update_c_module/5,
- update_c_primop/3, update_c_receive/4, update_c_seq/3,
- update_c_try/6, update_c_tuple/2, update_c_tuple_skel/2,
- update_c_values/2, values_es/1, var_name/1]).
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec depth(Tree::cerl) -> integer()
-%%
-%% @doc Returns the length of the longest path in the tree. A leaf
-%% node has depth zero, the tree representing "{foo,
-%% bar}
" has depth one, etc.
-
-depth(T) ->
- case subtrees(T) of
- [] ->
- 0;
- Gs ->
- 1 + lists:foldl(fun (G, A) -> erlang:max(depth_1(G), A) end, 0, Gs)
- end.
-
-depth_1(Ts) ->
- lists:foldl(fun (T, A) -> erlang:max(depth(T), A) end, 0, Ts).
-
-%% max(X, Y) when X > Y -> X;
-%% max(_, Y) -> Y.
-
-
-%% @spec size(Tree::cerl()) -> integer()
-%%
-%% @doc Returns the number of nodes in Tree
.
-
-size(T) ->
- fold(fun (_, S) -> S + 1 end, 0, T).
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec map(Function, Tree::cerl()) -> cerl()
-%%
-%% Function = (cerl()) -> cerl()
-%%
-%% @doc Maps a function onto the nodes of a tree. This replaces each
-%% node in the tree by the result of applying the given function on
-%% the original node, bottom-up.
-%%
-%% @see mapfold/3
-
-map(F, T) ->
- F(map_1(F, T)).
-
-map_1(F, T) ->
- case type(T) of
- literal ->
- case concrete(T) of
- [_ | _] ->
- update_c_cons(T, map(F, cons_hd(T)),
- map(F, cons_tl(T)));
- V when tuple_size(V) > 0 ->
- update_c_tuple(T, map_list(F, tuple_es(T)));
- _ ->
- T
- end;
- var ->
- T;
- values ->
- update_c_values(T, map_list(F, values_es(T)));
- cons ->
- update_c_cons_skel(T, map(F, cons_hd(T)),
- map(F, cons_tl(T)));
- tuple ->
- update_c_tuple_skel(T, map_list(F, tuple_es(T)));
- 'let' ->
- update_c_let(T, map_list(F, let_vars(T)),
- map(F, let_arg(T)),
- map(F, let_body(T)));
- seq ->
- update_c_seq(T, map(F, seq_arg(T)),
- map(F, seq_body(T)));
- apply ->
- update_c_apply(T, map(F, apply_op(T)),
- map_list(F, apply_args(T)));
- call ->
- update_c_call(T, map(F, call_module(T)),
- map(F, call_name(T)),
- map_list(F, call_args(T)));
- primop ->
- update_c_primop(T, map(F, primop_name(T)),
- map_list(F, primop_args(T)));
- 'case' ->
- update_c_case(T, map(F, case_arg(T)),
- map_list(F, case_clauses(T)));
- clause ->
- update_c_clause(T, map_list(F, clause_pats(T)),
- map(F, clause_guard(T)),
- map(F, clause_body(T)));
- alias ->
- update_c_alias(T, map(F, alias_var(T)),
- map(F, alias_pat(T)));
- 'fun' ->
- update_c_fun(T, map_list(F, fun_vars(T)),
- map(F, fun_body(T)));
- 'receive' ->
- update_c_receive(T, map_list(F, receive_clauses(T)),
- map(F, receive_timeout(T)),
- map(F, receive_action(T)));
- 'try' ->
- update_c_try(T, map(F, try_arg(T)),
- map_list(F, try_vars(T)),
- map(F, try_body(T)),
- map_list(F, try_evars(T)),
- map(F, try_handler(T)));
- 'catch' ->
- update_c_catch(T, map(F, catch_body(T)));
- binary ->
- update_c_binary(T, map_list(F, binary_segments(T)));
- bitstr ->
- update_c_bitstr(T, map(F, bitstr_val(T)),
- map(F, bitstr_size(T)),
- map(F, bitstr_unit(T)),
- map(F, bitstr_type(T)),
- map(F, bitstr_flags(T)));
- letrec ->
- update_c_letrec(T, map_pairs(F, letrec_defs(T)),
- map(F, letrec_body(T)));
- module ->
- update_c_module(T, map(F, module_name(T)),
- map_list(F, module_exports(T)),
- map_pairs(F, module_attrs(T)),
- map_pairs(F, module_defs(T)))
- end.
-
-map_list(F, [T | Ts]) ->
- [map(F, T) | map_list(F, Ts)];
-map_list(_, []) ->
- [].
-
-map_pairs(F, [{T1, T2} | Ps]) ->
- [{map(F, T1), map(F, T2)} | map_pairs(F, Ps)];
-map_pairs(_, []) ->
- [].
-
-
-%% @spec fold(Function, Unit::term(), Tree::cerl()) -> term()
-%%
-%% Function = (cerl(), term()) -> term()
-%%
-%% @doc Does a fold operation over the nodes of the tree. The result
-%% is the value of Function(X1, Function(X2, ... Function(Xn,
-%% Unit) ... ))
, where X1, ..., Xn
are the nodes
-%% of Tree
in a post-order traversal.
-%%
-%% @see mapfold/3
-
-fold(F, S, T) ->
- F(T, fold_1(F, S, T)).
-
-fold_1(F, S, T) ->
- case type(T) of
- literal ->
- case concrete(T) of
- [_ | _] ->
- fold(F, fold(F, S, cons_hd(T)), cons_tl(T));
- V when tuple_size(V) > 0 ->
- fold_list(F, S, tuple_es(T));
- _ ->
- S
- end;
- var ->
- S;
- values ->
- fold_list(F, S, values_es(T));
- cons ->
- fold(F, fold(F, S, cons_hd(T)), cons_tl(T));
- tuple ->
- fold_list(F, S, tuple_es(T));
- 'let' ->
- fold(F, fold(F, fold_list(F, S, let_vars(T)),
- let_arg(T)),
- let_body(T));
- seq ->
- fold(F, fold(F, S, seq_arg(T)), seq_body(T));
- apply ->
- fold_list(F, fold(F, S, apply_op(T)), apply_args(T));
- call ->
- fold_list(F, fold(F, fold(F, S, call_module(T)),
- call_name(T)),
- call_args(T));
- primop ->
- fold_list(F, fold(F, S, primop_name(T)), primop_args(T));
- 'case' ->
- fold_list(F, fold(F, S, case_arg(T)), case_clauses(T));
- clause ->
- fold(F, fold(F, fold_list(F, S, clause_pats(T)),
- clause_guard(T)),
- clause_body(T));
- alias ->
- fold(F, fold(F, S, alias_var(T)), alias_pat(T));
- 'fun' ->
- fold(F, fold_list(F, S, fun_vars(T)), fun_body(T));
- 'receive' ->
- fold(F, fold(F, fold_list(F, S, receive_clauses(T)),
- receive_timeout(T)),
- receive_action(T));
- 'try' ->
- fold(F, fold_list(F, fold(F, fold_list(F, fold(F, S, try_arg(T)),
- try_vars(T)),
- try_body(T)),
- try_evars(T)),
- try_handler(T));
- 'catch' ->
- fold(F, S, catch_body(T));
- binary ->
- fold_list(F, S, binary_segments(T));
- bitstr ->
- fold(F,
- fold(F,
- fold(F,
- fold(F,
- fold(F, S, bitstr_val(T)),
- bitstr_size(T)),
- bitstr_unit(T)),
- bitstr_type(T)),
- bitstr_flags(T));
- letrec ->
- fold(F, fold_pairs(F, S, letrec_defs(T)), letrec_body(T));
- module ->
- fold_pairs(F,
- fold_pairs(F,
- fold_list(F,
- fold(F, S, module_name(T)),
- module_exports(T)),
- module_attrs(T)),
- module_defs(T))
- end.
-
-fold_list(F, S, [T | Ts]) ->
- fold_list(F, fold(F, S, T), Ts);
-fold_list(_, S, []) ->
- S.
-
-fold_pairs(F, S, [{T1, T2} | Ps]) ->
- fold_pairs(F, fold(F, fold(F, S, T1), T2), Ps);
-fold_pairs(_, S, []) ->
- S.
-
-
-%% @spec mapfold(Function, Initial::term(), Tree::cerl()) ->
-%% {cerl(), term()}
-%%
-%% Function = (cerl(), term()) -> {cerl(), term()}
-%%
-%% @doc Does a combined map/fold operation on the nodes of the
-%% tree. This is similar to map/2
, but also propagates a
-%% value from each application of Function
to the next,
-%% starting with the given value Initial
, while doing a
-%% post-order traversal of the tree, much like fold/3
.
-%%
-%% @see map/2
-%% @see fold/3
-
-mapfold(F, S0, T) ->
- case type(T) of
- literal ->
- case concrete(T) of
- [_ | _] ->
- {T1, S1} = mapfold(F, S0, cons_hd(T)),
- {T2, S2} = mapfold(F, S1, cons_tl(T)),
- F(update_c_cons(T, T1, T2), S2);
- V when tuple_size(V) > 0 ->
- {Ts, S1} = mapfold_list(F, S0, tuple_es(T)),
- F(update_c_tuple(T, Ts), S1);
- _ ->
- F(T, S0)
- end;
- var ->
- F(T, S0);
- values ->
- {Ts, S1} = mapfold_list(F, S0, values_es(T)),
- F(update_c_values(T, Ts), S1);
- cons ->
- {T1, S1} = mapfold(F, S0, cons_hd(T)),
- {T2, S2} = mapfold(F, S1, cons_tl(T)),
- F(update_c_cons_skel(T, T1, T2), S2);
- tuple ->
- {Ts, S1} = mapfold_list(F, S0, tuple_es(T)),
- F(update_c_tuple_skel(T, Ts), S1);
- 'let' ->
- {Vs, S1} = mapfold_list(F, S0, let_vars(T)),
- {A, S2} = mapfold(F, S1, let_arg(T)),
- {B, S3} = mapfold(F, S2, let_body(T)),
- F(update_c_let(T, Vs, A, B), S3);
- seq ->
- {A, S1} = mapfold(F, S0, seq_arg(T)),
- {B, S2} = mapfold(F, S1, seq_body(T)),
- F(update_c_seq(T, A, B), S2);
- apply ->
- {E, S1} = mapfold(F, S0, apply_op(T)),
- {As, S2} = mapfold_list(F, S1, apply_args(T)),
- F(update_c_apply(T, E, As), S2);
- call ->
- {M, S1} = mapfold(F, S0, call_module(T)),
- {N, S2} = mapfold(F, S1, call_name(T)),
- {As, S3} = mapfold_list(F, S2, call_args(T)),
- F(update_c_call(T, M, N, As), S3);
- primop ->
- {N, S1} = mapfold(F, S0, primop_name(T)),
- {As, S2} = mapfold_list(F, S1, primop_args(T)),
- F(update_c_primop(T, N, As), S2);
- 'case' ->
- {A, S1} = mapfold(F, S0, case_arg(T)),
- {Cs, S2} = mapfold_list(F, S1, case_clauses(T)),
- F(update_c_case(T, A, Cs), S2);
- clause ->
- {Ps, S1} = mapfold_list(F, S0, clause_pats(T)),
- {G, S2} = mapfold(F, S1, clause_guard(T)),
- {B, S3} = mapfold(F, S2, clause_body(T)),
- F(update_c_clause(T, Ps, G, B), S3);
- alias ->
- {V, S1} = mapfold(F, S0, alias_var(T)),
- {P, S2} = mapfold(F, S1, alias_pat(T)),
- F(update_c_alias(T, V, P), S2);
- 'fun' ->
- {Vs, S1} = mapfold_list(F, S0, fun_vars(T)),
- {B, S2} = mapfold(F, S1, fun_body(T)),
- F(update_c_fun(T, Vs, B), S2);
- 'receive' ->
- {Cs, S1} = mapfold_list(F, S0, receive_clauses(T)),
- {E, S2} = mapfold(F, S1, receive_timeout(T)),
- {A, S3} = mapfold(F, S2, receive_action(T)),
- F(update_c_receive(T, Cs, E, A), S3);
- 'try' ->
- {E, S1} = mapfold(F, S0, try_arg(T)),
- {Vs, S2} = mapfold_list(F, S1, try_vars(T)),
- {B, S3} = mapfold(F, S2, try_body(T)),
- {Evs, S4} = mapfold_list(F, S3, try_evars(T)),
- {H, S5} = mapfold(F, S4, try_handler(T)),
- F(update_c_try(T, E, Vs, B, Evs, H), S5);
- 'catch' ->
- {B, S1} = mapfold(F, S0, catch_body(T)),
- F(update_c_catch(T, B), S1);
- binary ->
- {Ds, S1} = mapfold_list(F, S0, binary_segments(T)),
- F(update_c_binary(T, Ds), S1);
- bitstr ->
- {Val, S1} = mapfold(F, S0, bitstr_val(T)),
- {Size, S2} = mapfold(F, S1, bitstr_size(T)),
- {Unit, S3} = mapfold(F, S2, bitstr_unit(T)),
- {Type, S4} = mapfold(F, S3, bitstr_type(T)),
- {Flags, S5} = mapfold(F, S4, bitstr_flags(T)),
- F(update_c_bitstr(T, Val, Size, Unit, Type, Flags), S5);
- letrec ->
- {Ds, S1} = mapfold_pairs(F, S0, letrec_defs(T)),
- {B, S2} = mapfold(F, S1, letrec_body(T)),
- F(update_c_letrec(T, Ds, B), S2);
- module ->
- {N, S1} = mapfold(F, S0, module_name(T)),
- {Es, S2} = mapfold_list(F, S1, module_exports(T)),
- {As, S3} = mapfold_pairs(F, S2, module_attrs(T)),
- {Ds, S4} = mapfold_pairs(F, S3, module_defs(T)),
- F(update_c_module(T, N, Es, As, Ds), S4)
- end.
-
-mapfold_list(F, S0, [T | Ts]) ->
- {T1, S1} = mapfold(F, S0, T),
- {Ts1, S2} = mapfold_list(F, S1, Ts),
- {[T1 | Ts1], S2};
-mapfold_list(_, S, []) ->
- {[], S}.
-
-mapfold_pairs(F, S0, [{T1, T2} | Ps]) ->
- {T3, S1} = mapfold(F, S0, T1),
- {T4, S2} = mapfold(F, S1, T2),
- {Ps1, S3} = mapfold_pairs(F, S2, Ps),
- {[{T3, T4} | Ps1], S3};
-mapfold_pairs(_, S, []) ->
- {[], S}.
-
-
-%% ---------------------------------------------------------------------
-
-%% @spec variables(Tree::cerl()) -> [var_name()]
-%%
-%% var_name() = integer() | atom() | {atom(), integer()}
-%%
-%% @doc Returns an ordered-set list of the names of all variables in
-%% the syntax tree. (This includes function name variables.) An
-%% exception is thrown if Tree
does not represent a
-%% well-formed Core Erlang syntax tree.
-%%
-%% @see free_variables/1
-
-variables(T) ->
- variables(T, false).
-
-
-%% @spec free_variables(Tree::cerl()) -> [var_name()]
-%%
-%% @doc Like variables/1
, but only includes variables
-%% that are free in the tree.
-%%
-%% @see variables/1
-
-free_variables(T) ->
- variables(T, true).
-
-
-%% This is not exported
-
-variables(T, S) ->
- case type(T) of
- literal ->
- [];
- var ->
- [var_name(T)];
- values ->
- vars_in_list(values_es(T), S);
- cons ->
- ordsets:union(variables(cons_hd(T), S),
- variables(cons_tl(T), S));
- tuple ->
- vars_in_list(tuple_es(T), S);
- 'let' ->
- Vs = variables(let_body(T), S),
- Vs1 = var_list_names(let_vars(T)),
- Vs2 = case S of
- true ->
- ordsets:subtract(Vs, Vs1);
- false ->
- ordsets:union(Vs, Vs1)
- end,
- ordsets:union(variables(let_arg(T), S), Vs2);
- seq ->
- ordsets:union(variables(seq_arg(T), S),
- variables(seq_body(T), S));
- apply ->
- ordsets:union(
- variables(apply_op(T), S),
- vars_in_list(apply_args(T), S));
- call ->
- ordsets:union(variables(call_module(T), S),
- ordsets:union(
- variables(call_name(T), S),
- vars_in_list(call_args(T), S)));
- primop ->
- vars_in_list(primop_args(T), S);
- 'case' ->
- ordsets:union(variables(case_arg(T), S),
- vars_in_list(case_clauses(T), S));
- clause ->
- Vs = ordsets:union(variables(clause_guard(T), S),
- variables(clause_body(T), S)),
- Vs1 = vars_in_list(clause_pats(T), S),
- case S of
- true ->
- ordsets:subtract(Vs, Vs1);
- false ->
- ordsets:union(Vs, Vs1)
- end;
- alias ->
- ordsets:add_element(var_name(alias_var(T)),
- variables(alias_pat(T)));
- 'fun' ->
- Vs = variables(fun_body(T), S),
- Vs1 = var_list_names(fun_vars(T)),
- case S of
- true ->
- ordsets:subtract(Vs, Vs1);
- false ->
- ordsets:union(Vs, Vs1)
- end;
- 'receive' ->
- ordsets:union(
- vars_in_list(receive_clauses(T), S),
- ordsets:union(variables(receive_timeout(T), S),
- variables(receive_action(T), S)));
- 'try' ->
- Vs = variables(try_body(T), S),
- Vs1 = var_list_names(try_vars(T)),
- Vs2 = case S of
- true ->
- ordsets:subtract(Vs, Vs1);
- false ->
- ordsets:union(Vs, Vs1)
- end,
- Vs3 = variables(try_handler(T), S),
- Vs4 = var_list_names(try_evars(T)),
- Vs5 = case S of
- true ->
- ordsets:subtract(Vs3, Vs4);
- false ->
- ordsets:union(Vs3, Vs4)
- end,
- ordsets:union(variables(try_arg(T), S),
- ordsets:union(Vs2, Vs5));
- 'catch' ->
- variables(catch_body(T), S);
- binary ->
- vars_in_list(binary_segments(T), S);
- bitstr ->
- ordsets:union(variables(bitstr_val(T), S),
- variables(bitstr_size(T), S));
- letrec ->
- Vs = vars_in_defs(letrec_defs(T), S),
- Vs1 = ordsets:union(variables(letrec_body(T), S), Vs),
- Vs2 = var_list_names(letrec_vars(T)),
- case S of
- true ->
- ordsets:subtract(Vs1, Vs2);
- false ->
- ordsets:union(Vs1, Vs2)
- end;
- module ->
- Vs = vars_in_defs(module_defs(T), S),
- Vs1 = ordsets:union(vars_in_list(module_exports(T), S), Vs),
- Vs2 = var_list_names(module_vars(T)),
- case S of
- true ->
- ordsets:subtract(Vs1, Vs2);
- false ->
- ordsets:union(Vs1, Vs2)
- end
- end.
-
-vars_in_list(Ts, S) ->
- vars_in_list(Ts, S, []).
-
-vars_in_list([T | Ts], S, A) ->
- vars_in_list(Ts, S, ordsets:union(variables(T, S), A));
-vars_in_list([], _, A) ->
- A.
-
-%% Note that this function only visits the right-hand side of function
-%% definitions.
-
-vars_in_defs(Ds, S) ->
- vars_in_defs(Ds, S, []).
-
-vars_in_defs([{_, F} | Ds], S, A) ->
- vars_in_defs(Ds, S, ordsets:union(variables(F, S), A));
-vars_in_defs([], _, A) ->
- A.
-
-%% This amounts to insertion sort. Since the lists are generally short,
-%% it is hardly worthwhile to use an asymptotically better sort.
-
-var_list_names(Vs) ->
- var_list_names(Vs, []).
-
-var_list_names([V | Vs], A) ->
- var_list_names(Vs, ordsets:add_element(var_name(V), A));
-var_list_names([], A) ->
- A.
-
-
-%% ---------------------------------------------------------------------
-
-%% label(Tree::cerl()) -> {cerl(), integer()}
-%%
-%% @equiv label(Tree, 0)
-
-label(T) ->
- label(T, 0).
-
-%% @spec label(Tree::cerl(), N::integer()) -> {cerl(), integer()}
-%%
-%% @doc Labels each expression in the tree. A term {label,
-%% L}
is prefixed to the annotation list of each expression node,
-%% where L is a unique number for every node, except for variables (and
-%% function name variables) which get the same label if they represent
-%% the same variable. Constant literal nodes are not labeled.
-%%
-%% The returned value is a tuple {NewTree, Max}
, where
-%% NewTree
is the labeled tree and Max
is 1
-%% plus the largest label value used. All previous annotation terms on
-%% the form {label, X}
are deleted.
-%%
-%% The values of L used in the tree is a dense range from
-%% N
to Max - 1
, where N =< Max
-%% =< N + size(Tree)
. Note that it is possible that no
-%% labels are used at all, i.e., N = Max
.
-%%
-%% Note: All instances of free variables will be given distinct
-%% labels.
-%%
-%% @see label/1
-%% @see size/1
-
-label(T, N) ->
- label(T, N, dict:new()).
-
-label(T, N, Env) ->
- case type(T) of
- literal ->
- %% Constant literals are not labeled.
- {T, N};
- var ->
- case dict:find(var_name(T), Env) of
- {ok, L} ->
- {As, _} = label_ann(T, L),
- N1 = N;
- error ->
- {As, N1} = label_ann(T, N)
- end,
- {set_ann(T, As), N1};
- values ->
- {Ts, N1} = label_list(values_es(T), N, Env),
- {As, N2} = label_ann(T, N1),
- {ann_c_values(As, Ts), N2};
- cons ->
- {T1, N1} = label(cons_hd(T), N, Env),
- {T2, N2} = label(cons_tl(T), N1, Env),
- {As, N3} = label_ann(T, N2),
- {ann_c_cons_skel(As, T1, T2), N3};
- tuple ->
- {Ts, N1} = label_list(tuple_es(T), N, Env),
- {As, N2} = label_ann(T, N1),
- {ann_c_tuple_skel(As, Ts), N2};
- 'let' ->
- {A, N1} = label(let_arg(T), N, Env),
- {Vs, N2, Env1} = label_vars(let_vars(T), N1, Env),
- {B, N3} = label(let_body(T), N2, Env1),
- {As, N4} = label_ann(T, N3),
- {ann_c_let(As, Vs, A, B), N4};
- seq ->
- {A, N1} = label(seq_arg(T), N, Env),
- {B, N2} = label(seq_body(T), N1, Env),
- {As, N3} = label_ann(T, N2),
- {ann_c_seq(As, A, B), N3};
- apply ->
- {E, N1} = label(apply_op(T), N, Env),
- {Es, N2} = label_list(apply_args(T), N1, Env),
- {As, N3} = label_ann(T, N2),
- {ann_c_apply(As, E, Es), N3};
- call ->
- {M, N1} = label(call_module(T), N, Env),
- {F, N2} = label(call_name(T), N1, Env),
- {Es, N3} = label_list(call_args(T), N2, Env),
- {As, N4} = label_ann(T, N3),
- {ann_c_call(As, M, F, Es), N4};
- primop ->
- {F, N1} = label(primop_name(T), N, Env),
- {Es, N2} = label_list(primop_args(T), N1, Env),
- {As, N3} = label_ann(T, N2),
- {ann_c_primop(As, F, Es), N3};
- 'case' ->
- {A, N1} = label(case_arg(T), N, Env),
- {Cs, N2} = label_list(case_clauses(T), N1, Env),
- {As, N3} = label_ann(T, N2),
- {ann_c_case(As, A, Cs), N3};
- clause ->
- {_, N1, Env1} = label_vars(clause_vars(T), N, Env),
- {Ps, N2} = label_list(clause_pats(T), N1, Env1),
- {G, N3} = label(clause_guard(T), N2, Env1),
- {B, N4} = label(clause_body(T), N3, Env1),
- {As, N5} = label_ann(T, N4),
- {ann_c_clause(As, Ps, G, B), N5};
- alias ->
- {V, N1} = label(alias_var(T), N, Env),
- {P, N2} = label(alias_pat(T), N1, Env),
- {As, N3} = label_ann(T, N2),
- {ann_c_alias(As, V, P), N3};
- 'fun' ->
- {Vs, N1, Env1} = label_vars(fun_vars(T), N, Env),
- {B, N2} = label(fun_body(T), N1, Env1),
- {As, N3} = label_ann(T, N2),
- {ann_c_fun(As, Vs, B), N3};
- 'receive' ->
- {Cs, N1} = label_list(receive_clauses(T), N, Env),
- {E, N2} = label(receive_timeout(T), N1, Env),
- {A, N3} = label(receive_action(T), N2, Env),
- {As, N4} = label_ann(T, N3),
- {ann_c_receive(As, Cs, E, A), N4};
- 'try' ->
- {E, N1} = label(try_arg(T), N, Env),
- {Vs, N2, Env1} = label_vars(try_vars(T), N1, Env),
- {B, N3} = label(try_body(T), N2, Env1),
- {Evs, N4, Env2} = label_vars(try_evars(T), N3, Env),
- {H, N5} = label(try_handler(T), N4, Env2),
- {As, N6} = label_ann(T, N5),
- {ann_c_try(As, E, Vs, B, Evs, H), N6};
- 'catch' ->
- {B, N1} = label(catch_body(T), N, Env),
- {As, N2} = label_ann(T, N1),
- {ann_c_catch(As, B), N2};
- binary ->
- {Ds, N1} = label_list(binary_segments(T), N, Env),
- {As, N2} = label_ann(T, N1),
- {ann_c_binary(As, Ds), N2};
- bitstr ->
- {Val, N1} = label(bitstr_val(T), N, Env),
- {Size, N2} = label(bitstr_size(T), N1, Env),
- {Unit, N3} = label(bitstr_unit(T), N2, Env),
- {Type, N4} = label(bitstr_type(T), N3, Env),
- {Flags, N5} = label(bitstr_flags(T), N4, Env),
- {As, N6} = label_ann(T, N5),
- {ann_c_bitstr(As, Val, Size, Unit, Type, Flags), N6};
- letrec ->
- {_, N1, Env1} = label_vars(letrec_vars(T), N, Env),
- {Ds, N2} = label_defs(letrec_defs(T), N1, Env1),
- {B, N3} = label(letrec_body(T), N2, Env1),
- {As, N4} = label_ann(T, N3),
- {ann_c_letrec(As, Ds, B), N4};
- module ->
- %% The module name is not labeled.
- {_, N1, Env1} = label_vars(module_vars(T), N, Env),
- {Ts, N2} = label_defs(module_attrs(T), N1, Env1),
- {Ds, N3} = label_defs(module_defs(T), N2, Env1),
- {Es, N4} = label_list(module_exports(T), N3, Env1),
- {As, N5} = label_ann(T, N4),
- {ann_c_module(As, module_name(T), Es, Ts, Ds), N5}
- end.
-
-label_list([T | Ts], N, Env) ->
- {T1, N1} = label(T, N, Env),
- {Ts1, N2} = label_list(Ts, N1, Env),
- {[T1 | Ts1], N2};
-label_list([], N, _Env) ->
- {[], N}.
-
-label_vars([T | Ts], N, Env) ->
- Env1 = dict:store(var_name(T), N, Env),
- {As, N1} = label_ann(T, N),
- T1 = set_ann(T, As),
- {Ts1, N2, Env2} = label_vars(Ts, N1, Env1),
- {[T1 | Ts1], N2, Env2};
-label_vars([], N, Env) ->
- {[], N, Env}.
-
-label_defs([{F, T} | Ds], N, Env) ->
- {F1, N1} = label(F, N, Env),
- {T1, N2} = label(T, N1, Env),
- {Ds1, N3} = label_defs(Ds, N2, Env),
- {[{F1, T1} | Ds1], N3};
-label_defs([], N, _Env) ->
- {[], N}.
-
-label_ann(T, N) ->
- {[{label, N} | filter_labels(get_ann(T))], N + 1}.
-
-filter_labels([{label, _} | As]) ->
- filter_labels(As);
-filter_labels([A | As]) ->
- [A | filter_labels(As)];
-filter_labels([]) ->
- [].
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/compile.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/compile.erl
deleted file mode 100644
index 4542bf9eb9..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/compile.erl
+++ /dev/null
@@ -1,1109 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: compile.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
-%%
-%% Purpose: Run the Erlang compiler.
-
--module(compile).
--include("erl_compile.hrl").
--include("core_parse.hrl").
-
-%% High-level interface.
--export([file/1,file/2,format_error/1,iofile/1]).
--export([forms/1,forms/2]).
--export([output_generated/1]).
--export([options/0]).
-
-%% Erlc interface.
--export([compile/3,compile_beam/3,compile_asm/3,compile_core/3]).
-
-
--import(lists, [member/2,reverse/1,keysearch/3,last/1,
- map/2,flatmap/2,foreach/2,foldr/3,any/2,filter/2]).
-
-%% file(FileName)
-%% file(FileName, Options)
-%% Compile the module in file FileName.
-
--define(DEFAULT_OPTIONS, [verbose,report_errors,report_warnings]).
-
--define(pass(P), {P,fun P/1}).
-
-file(File) -> file(File, ?DEFAULT_OPTIONS).
-
-file(File, Opts) when list(Opts) ->
- do_compile({file,File}, Opts++env_default_opts());
-file(File, Opt) ->
- file(File, [Opt|?DEFAULT_OPTIONS]).
-
-forms(File) -> forms(File, ?DEFAULT_OPTIONS).
-
-forms(Forms, Opts) when list(Opts) ->
- do_compile({forms,Forms}, [binary|Opts++env_default_opts()]);
-forms(Forms, Opts) when atom(Opts) ->
- forms(Forms, [Opts|?DEFAULT_OPTIONS]).
-
-env_default_opts() ->
- Key = "ERL_COMPILER_OPTIONS",
- case os:getenv(Key) of
- false -> [];
- Str when list(Str) ->
- case erl_scan:string(Str) of
- {ok,Tokens,_} ->
- case erl_parse:parse_term(Tokens ++ [{dot, 1}]) of
- {ok,List} when list(List) -> List;
- {ok,Term} -> [Term];
- {error,_Reason} ->
- io:format("Ignoring bad term in ~s\n", [Key]),
- []
- end;
- {error, {_,_,_Reason}, _} ->
- io:format("Ignoring bad term in ~s\n", [Key]),
- []
- end
- end.
-
-do_compile(Input, Opts0) ->
- Opts = expand_opts(Opts0),
- Self = self(),
- Serv = spawn_link(fun() -> internal(Self, Input, Opts) end),
- receive
- {Serv,Rep} -> Rep
- end.
-
-%% Given a list of compilation options, returns true if compile:file/2
-%% would have generated a Beam file, false otherwise (if only a binary or a
-%% listing file would have been generated).
-
-output_generated(Opts) ->
- any(fun ({save_binary,_F}) -> true;
- (_Other) -> false
- end, passes(file, expand_opts(Opts))).
-
-expand_opts(Opts) ->
- foldr(fun expand_opt/2, [], Opts).
-
-expand_opt(basic_validation, Os) ->
- [no_code_generation,to_pp,binary|Os];
-expand_opt(strong_validation, Os) ->
- [no_code_generation,to_kernel,binary|Os];
-expand_opt(report, Os) ->
- [report_errors,report_warnings|Os];
-expand_opt(return, Os) ->
- [return_errors,return_warnings|Os];
-expand_opt(r7, Os) ->
- [no_float_opt,no_new_funs,no_new_binaries,no_new_apply|Os];
-expand_opt(O, Os) -> [O|Os].
-
-filter_opts(Opts0) ->
- %% Native code generation is not supported if no_new_funs is given.
- case member(no_new_funs, Opts0) of
- false -> Opts0;
- true -> Opts0 -- [native]
- end.
-
-%% format_error(ErrorDescriptor) -> string()
-
-format_error(no_native_support) ->
- "this system is not configured for native-code compilation.";
-format_error({native, E}) ->
- io_lib:fwrite("native-code compilation failed with reason: ~P.",
- [E, 25]);
-format_error({native_crash, E}) ->
- io_lib:fwrite("native-code compilation crashed with reason: ~P.",
- [E, 25]);
-format_error({open,E}) ->
- io_lib:format("open error '~s'", [file:format_error(E)]);
-format_error({epp,E}) ->
- epp:format_error(E);
-format_error(write_error) ->
- "error writing file";
-format_error({rename,S}) ->
- io_lib:format("error renaming ~s", [S]);
-format_error({parse_transform,M,R}) ->
- io_lib:format("error in parse transform '~s': ~p", [M, R]);
-format_error({core_transform,M,R}) ->
- io_lib:format("error in core transform '~s': ~p", [M, R]);
-format_error({crash,Pass,Reason}) ->
- io_lib:format("internal error in ~p;\ncrash reason: ~p", [Pass,Reason]);
-format_error({bad_return,Pass,Reason}) ->
- io_lib:format("internal error in ~p;\nbad return value: ~p", [Pass,Reason]).
-
-%% The compile state record.
--record(compile, {filename="",
- dir="",
- base="",
- ifile="",
- ofile="",
- module=[],
- code=[],
- core_code=[],
- abstract_code=[], %Abstract code for debugger.
- options=[],
- errors=[],
- warnings=[]}).
-
-internal(Master, Input, Opts) ->
- Master ! {self(),
- case catch internal(Input, Opts) of
- {'EXIT', Reason} ->
- {error, Reason};
- Other ->
- Other
- end}.
-
-internal({forms,Forms}, Opts) ->
- Ps = passes(forms, Opts),
- internal_comp(Ps, "", "", #compile{code=Forms,options=Opts});
-internal({file,File}, Opts) ->
- Ps = passes(file, Opts),
- Compile = #compile{options=Opts},
- case member(from_core, Opts) of
- true -> internal_comp(Ps, File, ".core", Compile);
- false ->
- case member(from_beam, Opts) of
- true ->
- internal_comp(Ps, File, ".beam", Compile);
- false ->
- case member(from_asm, Opts) orelse member(asm, Opts) of
- true ->
- internal_comp(Ps, File, ".S", Compile);
- false ->
- internal_comp(Ps, File, ".erl", Compile)
- end
- end
- end.
-
-internal_comp(Passes, File, Suffix, St0) ->
- Dir = filename:dirname(File),
- Base = filename:basename(File, Suffix),
- St1 = St0#compile{filename=File, dir=Dir, base=Base,
- ifile=erlfile(Dir, Base, Suffix),
- ofile=objfile(Base, St0)},
- Run = case member(time, St1#compile.options) of
- true ->
- io:format("Compiling ~p\n", [File]),
- fun run_tc/2;
- false -> fun({_Name,Fun}, St) -> catch Fun(St) end
- end,
- case fold_comp(Passes, Run, St1) of
- {ok,St2} -> comp_ret_ok(St2);
- {error,St2} -> comp_ret_err(St2)
- end.
-
-fold_comp([{Name,Test,Pass}|Ps], Run, St) ->
- case Test(St) of
- false -> %Pass is not needed.
- fold_comp(Ps, Run, St);
- true -> %Run pass in the usual way.
- fold_comp([{Name,Pass}|Ps], Run, St)
- end;
-fold_comp([{Name,Pass}|Ps], Run, St0) ->
- case Run({Name,Pass}, St0) of
- {ok,St1} -> fold_comp(Ps, Run, St1);
- {error,St1} -> {error,St1};
- {'EXIT',Reason} ->
- Es = [{St0#compile.ifile,[{none,?MODULE,{crash,Name,Reason}}]}],
- {error,St0#compile{errors=St0#compile.errors ++ Es}};
- Other ->
- Es = [{St0#compile.ifile,[{none,?MODULE,{bad_return,Name,Other}}]}],
- {error,St0#compile{errors=St0#compile.errors ++ Es}}
- end;
-fold_comp([], _Run, St) -> {ok,St}.
-
-os_process_size() ->
- case os:type() of
- {unix, sunos} ->
- Size = os:cmd("ps -o vsz -p " ++ os:getpid() ++ " | tail -1"),
- list_to_integer(lib:nonl(Size));
- _ ->
- 0
- end.
-
-run_tc({Name,Fun}, St) ->
- Before0 = statistics(runtime),
- Val = (catch Fun(St)),
- After0 = statistics(runtime),
- {Before_c, _} = Before0,
- {After_c, _} = After0,
- io:format(" ~-30s: ~10.3f s (~w k)\n",
- [Name, (After_c-Before_c) / 1000, os_process_size()]),
- Val.
-
-comp_ret_ok(#compile{code=Code,warnings=Warn,module=Mod,options=Opts}=St) ->
- report_warnings(St),
- Ret1 = case member(binary, Opts) andalso not member(no_code_generation, Opts) of
- true -> [Code];
- false -> []
- end,
- Ret2 = case member(return_warnings, Opts) of
- true -> Ret1 ++ [Warn];
- false -> Ret1
- end,
- list_to_tuple([ok,Mod|Ret2]).
-
-comp_ret_err(St) ->
- report_errors(St),
- report_warnings(St),
- case member(return_errors, St#compile.options) of
- true -> {error,St#compile.errors,St#compile.warnings};
- false -> error
- end.
-
-%% passes(form|file, [Option]) -> [{Name,PassFun}]
-%% Figure out which passes that need to be run.
-
-passes(forms, Opts) ->
- select_passes(standard_passes(), Opts);
-passes(file, Opts) ->
- case member(from_beam, Opts) of
- true ->
- Ps = [?pass(read_beam_file)|binary_passes()],
- select_passes(Ps, Opts);
- false ->
- Ps = case member(from_asm, Opts) orelse member(asm, Opts) of
- true ->
- [?pass(beam_consult_asm)|asm_passes()];
- false ->
- case member(from_core, Opts) of
- true ->
- [?pass(parse_core)|core_passes()];
- false ->
- [?pass(parse_module)|standard_passes()]
- end
- end,
- Fs = select_passes(Ps, Opts),
-
- %% If the last pass saves the resulting binary to a file,
- %% insert a first pass to remove the file.
- case last(Fs) of
- {save_binary,_Fun} -> [?pass(remove_file)|Fs];
- _Other -> Fs
- end
- end.
-
-%% select_passes([Command], Opts) -> [{Name,Function}]
-%% Interpret the lists of commands to return a pure list of passes.
-%%
-%% Command can be one of:
-%%
-%% {pass,Mod} Will be expanded to a call to the external
-%% function Mod:module(Code, Options). This
-%% function must transform the code and return
-%% {ok,NewCode} or {error,Term}.
-%% Example: {pass,beam_codegen}
-%%
-%% {Name,Fun} Name is an atom giving the name of the pass.
-%% Fun is an 'fun' taking one argument: a compile record.
-%% The fun should return {ok,NewCompileRecord} or
-%% {error,NewCompileRecord}.
-%% Note: ?pass(Name) is equvivalent to {Name,fun Name/1}.
-%% Example: ?pass(parse_module)
-%%
-%% {Name,Test,Fun} Like {Name,Fun} above, but the pass will be run
-%% (and listed by the `time' option) only if Test(St)
-%% returns true.
-%%
-%% {src_listing,Ext} Produces an Erlang source listing with the
-%% the file extension Ext. (Ext should not contain
-%% a period.) No more passes will be run.
-%%
-%% {listing,Ext} Produce an listing of the terms in the internal
-%% representation. The extension of the listing
-%% file will be Ext. (Ext should not contain
-%% a period.) No more passes will be run.
-%%
-%% {done,Ext} End compilation at this point. Produce a listing
-%% as with {listing,Ext}, unless 'binary' is
-%% specified, in which case the current
-%% representation of the code is returned without
-%% creating an output file.
-%%
-%% {iff,Flag,Cmd} If the given Flag is given in the option list,
-%% Cmd will be interpreted as a command.
-%% Otherwise, Cmd will be ignored.
-%% Example: {iff,dcg,{listing,"codegen}}
-%%
-%% {unless,Flag,Cmd} If the given Flag is NOT given in the option list,
-%% Cmd will be interpreted as a command.
-%% Otherwise, Cmd will be ignored.
-%% Example: {unless,no_kernopt,{pass,sys_kernopt}}
-%%
-
-select_passes([{pass,Mod}|Ps], Opts) ->
- F = fun(St) ->
- case catch Mod:module(St#compile.code, St#compile.options) of
- {ok,Code} ->
- {ok,St#compile{code=Code}};
- {error,Es} ->
- {error,St#compile{errors=St#compile.errors ++ Es}}
- end
- end,
- [{Mod,F}|select_passes(Ps, Opts)];
-select_passes([{src_listing,Ext}|_], _Opts) ->
- [{listing,fun (St) -> src_listing(Ext, St) end}];
-select_passes([{listing,Ext}|_], _Opts) ->
- [{listing,fun (St) -> listing(Ext, St) end}];
-select_passes([{done,Ext}|_], Opts) ->
- select_passes([{unless,binary,{listing,Ext}}], Opts);
-select_passes([{iff,Flag,Pass}|Ps], Opts) ->
- select_cond(Flag, true, Pass, Ps, Opts);
-select_passes([{unless,Flag,Pass}|Ps], Opts) ->
- select_cond(Flag, false, Pass, Ps, Opts);
-select_passes([{_,Fun}=P|Ps], Opts) when is_function(Fun) ->
- [P|select_passes(Ps, Opts)];
-select_passes([{_,Test,Fun}=P|Ps], Opts) when is_function(Test),
- is_function(Fun) ->
- [P|select_passes(Ps, Opts)];
-select_passes([], _Opts) ->
- [];
-select_passes([List|Ps], Opts) when is_list(List) ->
- case select_passes(List, Opts) of
- [] -> select_passes(Ps, Opts);
- Nested ->
- case last(Nested) of
- {listing,_Fun} -> Nested;
- _Other -> Nested ++ select_passes(Ps, Opts)
- end
- end.
-
-select_cond(Flag, ShouldBe, Pass, Ps, Opts) ->
- ShouldNotBe = not ShouldBe,
- case member(Flag, Opts) of
- ShouldBe -> select_passes([Pass|Ps], Opts);
- ShouldNotBe -> select_passes(Ps, Opts)
- end.
-
-%% The standard passes (almost) always run.
-
-standard_passes() ->
- [?pass(transform_module),
- {iff,'dpp',{listing,"pp"}},
- ?pass(lint_module),
- {iff,'P',{src_listing,"P"}},
- {iff,'to_pp',{done,"P"}},
-
- {iff,'dabstr',{listing,"abstr"}},
- {iff,debug_info,?pass(save_abstract_code)},
-
- ?pass(expand_module),
- {iff,'dexp',{listing,"expand"}},
- {iff,'E',{src_listing,"E"}},
- {iff,'to_exp',{done,"E"}},
-
- %% Conversion to Core Erlang.
- ?pass(core_module),
- {iff,'dcore',{listing,"core"}},
- {iff,'to_core0',{done,"core"}}
- | core_passes()].
-
-core_passes() ->
- %% Optimization and transforms of Core Erlang code.
- [{unless,no_copt,
- [{core_old_inliner,fun test_old_inliner/1,fun core_old_inliner/1},
- ?pass(core_fold_module),
- {core_inline_module,fun test_core_inliner/1,fun core_inline_module/1},
- {core_fold_after_inline,fun test_core_inliner/1,fun core_fold_module/1},
- ?pass(core_transforms)]},
- {iff,dcopt,{listing,"copt"}},
- {iff,'to_core',{done,"core"}}
- | kernel_passes()].
-
-kernel_passes() ->
- %% Destructive setelement/3 optimization and core lint.
- [?pass(core_dsetel_module),
- {iff,clint,?pass(core_lint_module)},
- {iff,core,?pass(save_core_code)},
-
- %% Kernel Erlang and code generation.
- ?pass(kernel_module),
- {iff,dkern,{listing,"kernel"}},
- {iff,'to_kernel',{done,"kernel"}},
- {pass,v3_life},
- {iff,dlife,{listing,"life"}},
- {pass,v3_codegen},
- {iff,dcg,{listing,"codegen"}}
- | asm_passes()].
-
-asm_passes() ->
- %% Assembly level optimisations.
- [{unless,no_postopt,
- [{pass,beam_block},
- {iff,dblk,{listing,"block"}},
- {unless,no_bopt,{pass,beam_bool}},
- {iff,dbool,{listing,"bool"}},
- {unless,no_topt,{pass,beam_type}},
- {iff,dtype,{listing,"type"}},
- {pass,beam_dead}, %Must always run since it splits blocks.
- {iff,ddead,{listing,"dead"}},
- {unless,no_jopt,{pass,beam_jump}},
- {iff,djmp,{listing,"jump"}},
- {pass,beam_clean},
- {iff,dclean,{listing,"clean"}},
- {pass,beam_flatten}]},
-
- %% If post optimizations are turned off, we still coalesce
- %% adjacent labels and remove unused labels to keep the
- %% HiPE compiler happy.
- {iff,no_postopt,
- [?pass(beam_unused_labels),
- {pass,beam_clean}]},
-
- {iff,dopt,{listing,"optimize"}},
- {iff,'S',{listing,"S"}},
- {iff,'to_asm',{done,"S"}},
-
- {pass,beam_validator},
- ?pass(beam_asm)
- | binary_passes()].
-
-binary_passes() ->
- [{native_compile,fun test_native/1,fun native_compile/1},
- {unless,binary,?pass(save_binary)}].
-
-%%%
-%%% Compiler passes.
-%%%
-
-%% Remove the target file so we don't have an old one if the compilation fail.
-remove_file(St) ->
- file:delete(St#compile.ofile),
- {ok,St}.
-
--record(asm_module, {module,
- exports,
- labels,
- functions=[],
- cfun,
- code,
- attributes=[]}).
-
-preprocess_asm_forms(Forms) ->
- R = #asm_module{},
- R1 = collect_asm(Forms, R),
- {R1#asm_module.module,
- {R1#asm_module.module,
- R1#asm_module.exports,
- R1#asm_module.attributes,
- R1#asm_module.functions,
- R1#asm_module.labels}}.
-
-collect_asm([], R) ->
- case R#asm_module.cfun of
- undefined ->
- R;
- {A,B,C} ->
- R#asm_module{functions=R#asm_module.functions++
- [{function,A,B,C,R#asm_module.code}]}
- end;
-collect_asm([{module,M} | Rest], R) ->
- collect_asm(Rest, R#asm_module{module=M});
-collect_asm([{exports,M} | Rest], R) ->
- collect_asm(Rest, R#asm_module{exports=M});
-collect_asm([{labels,M} | Rest], R) ->
- collect_asm(Rest, R#asm_module{labels=M});
-collect_asm([{function,A,B,C} | Rest], R) ->
- R1 = case R#asm_module.cfun of
- undefined ->
- R;
- {A0,B0,C0} ->
- R#asm_module{functions=R#asm_module.functions++
- [{function,A0,B0,C0,R#asm_module.code}]}
- end,
- collect_asm(Rest, R1#asm_module{cfun={A,B,C}, code=[]});
-collect_asm([{attributes, Attr} | Rest], R) ->
- collect_asm(Rest, R#asm_module{attributes=Attr});
-collect_asm([X | Rest], R) ->
- collect_asm(Rest, R#asm_module{code=R#asm_module.code++[X]}).
-
-beam_consult_asm(St) ->
- case file:consult(St#compile.ifile) of
- {ok, Forms0} ->
- {Module, Forms} = preprocess_asm_forms(Forms0),
- {ok,St#compile{module=Module, code=Forms}};
- {error,E} ->
- Es = [{St#compile.ifile,[{none,?MODULE,{open,E}}]}],
- {error,St#compile{errors=St#compile.errors ++ Es}}
- end.
-
-read_beam_file(St) ->
- case file:read_file(St#compile.ifile) of
- {ok,Beam} ->
- Infile = St#compile.ifile,
- case is_too_old(Infile) of
- true ->
- {ok,St#compile{module=none,code=none}};
- false ->
- Mod0 = filename:rootname(filename:basename(Infile)),
- Mod = list_to_atom(Mod0),
- {ok,St#compile{module=Mod,code=Beam,ofile=Infile}}
- end;
- {error,E} ->
- Es = [{St#compile.ifile,[{none,?MODULE,{open,E}}]}],
- {error,St#compile{errors=St#compile.errors ++ Es}}
- end.
-
-is_too_old(BeamFile) ->
- case beam_lib:chunks(BeamFile, ["CInf"]) of
- {ok,{_,[{"CInf",Term0}]}} ->
- Term = binary_to_term(Term0),
- Opts = proplists:get_value(options, Term, []),
- lists:member(no_new_funs, Opts);
- _ -> false
- end.
-
-parse_module(St) ->
- Opts = St#compile.options,
- Cwd = ".",
- IncludePath = [Cwd, St#compile.dir|inc_paths(Opts)],
- Tab = ets:new(compiler__tab, [protected,named_table]),
- ets:insert(Tab, {compiler_options,Opts}),
- R = epp:parse_file(St#compile.ifile, IncludePath, pre_defs(Opts)),
- ets:delete(Tab),
- case R of
- {ok,Forms} ->
- {ok,St#compile{code=Forms}};
- {error,E} ->
- Es = [{St#compile.ifile,[{none,?MODULE,{epp,E}}]}],
- {error,St#compile{errors=St#compile.errors ++ Es}}
- end.
-
-parse_core(St) ->
- case file:read_file(St#compile.ifile) of
- {ok,Bin} ->
- case core_scan:string(binary_to_list(Bin)) of
- {ok,Toks,_} ->
- case core_parse:parse(Toks) of
- {ok,Mod} ->
- Name = (Mod#c_module.name)#c_atom.val,
- {ok,St#compile{module=Name,code=Mod}};
- {error,E} ->
- Es = [{St#compile.ifile,[E]}],
- {error,St#compile{errors=St#compile.errors ++ Es}}
- end;
- {error,E,_} ->
- Es = [{St#compile.ifile,[E]}],
- {error,St#compile{errors=St#compile.errors ++ Es}}
- end;
- {error,E} ->
- Es = [{St#compile.ifile,[{none,compile,{open,E}}]}],
- {error,St#compile{errors=St#compile.errors ++ Es}}
- end.
-
-compile_options([{attribute,_L,compile,C}|Fs]) when is_list(C) ->
- C ++ compile_options(Fs);
-compile_options([{attribute,_L,compile,C}|Fs]) ->
- [C|compile_options(Fs)];
-compile_options([_F|Fs]) -> compile_options(Fs);
-compile_options([]) -> [].
-
-transforms(Os) -> [ M || {parse_transform,M} <- Os ].
-
-transform_module(St) ->
- %% Extract compile options from code into options field.
- Ts = transforms(St#compile.options ++ compile_options(St#compile.code)),
- foldl_transform(St, Ts).
-
-foldl_transform(St, [T|Ts]) ->
- Name = "transform " ++ atom_to_list(T),
- Fun = fun(S) -> T:parse_transform(S#compile.code, S#compile.options) end,
- Run = case member(time, St#compile.options) of
- true -> fun run_tc/2;
- false -> fun({_Name,F}, S) -> catch F(S) end
- end,
- case Run({Name, Fun}, St) of
- {error,Es,Ws} ->
- {error,St#compile{warnings=St#compile.warnings ++ Ws,
- errors=St#compile.errors ++ Es}};
- {'EXIT',R} ->
- Es = [{St#compile.ifile,[{none,compile,{parse_transform,T,R}}]}],
- {error,St#compile{errors=St#compile.errors ++ Es}};
- Forms ->
- foldl_transform(St#compile{code=Forms}, Ts)
- end;
-foldl_transform(St, []) -> {ok,St}.
-
-get_core_transforms(Opts) -> [M || {core_transform,M} <- Opts].
-
-core_transforms(St) ->
- %% The options field holds the complete list of options at this
-
- Ts = get_core_transforms(St#compile.options),
- foldl_core_transforms(St, Ts).
-
-foldl_core_transforms(St, [T|Ts]) ->
- Name = "core transform " ++ atom_to_list(T),
- Fun = fun(S) -> T:core_transform(S#compile.code, S#compile.options) end,
- Run = case member(time, St#compile.options) of
- true -> fun run_tc/2;
- false -> fun({_Name,F}, S) -> catch F(S) end
- end,
- case Run({Name, Fun}, St) of
- {'EXIT',R} ->
- Es = [{St#compile.ifile,[{none,compile,{core_transform,T,R}}]}],
- {error,St#compile{errors=St#compile.errors ++ Es}};
- Forms ->
- foldl_core_transforms(St#compile{code=Forms}, Ts)
- end;
-foldl_core_transforms(St, []) -> {ok,St}.
-
-%%% Fetches the module name from a list of forms. The module attribute must
-%%% be present.
-get_module([{attribute,_,module,{M,_As}} | _]) -> M;
-get_module([{attribute,_,module,M} | _]) -> M;
-get_module([_ | Rest]) ->
- get_module(Rest).
-
-%%% A #compile state is returned, where St.base has been filled in
-%%% with the module name from Forms, as a string, in case it wasn't
-%%% set in St (i.e., it was "").
-add_default_base(St, Forms) ->
- F = St#compile.filename,
- case F of
- "" ->
- M = get_module(Forms),
- St#compile{base = atom_to_list(M)};
- _ ->
- St
- end.
-
-lint_module(St) ->
- case erl_lint:module(St#compile.code,
- St#compile.ifile, St#compile.options) of
- {ok,Ws} ->
- %% Insert name of module as base name, if needed. This is
- %% for compile:forms to work with listing files.
- St1 = add_default_base(St, St#compile.code),
- {ok,St1#compile{warnings=St1#compile.warnings ++ Ws}};
- {error,Es,Ws} ->
- {error,St#compile{warnings=St#compile.warnings ++ Ws,
- errors=St#compile.errors ++ Es}}
- end.
-
-core_lint_module(St) ->
- case core_lint:module(St#compile.code, St#compile.options) of
- {ok,Ws} ->
- {ok,St#compile{warnings=St#compile.warnings ++ Ws}};
- {error,Es,Ws} ->
- {error,St#compile{warnings=St#compile.warnings ++ Ws,
- errors=St#compile.errors ++ Es}}
- end.
-
-%% expand_module(State) -> State'
-%% Do the common preprocessing of the input forms.
-
-expand_module(#compile{code=Code,options=Opts0}=St0) ->
- {Mod,Exp,Forms,Opts1} = sys_pre_expand:module(Code, Opts0),
- Opts2 = expand_opts(Opts1),
- Opts = filter_opts(Opts2),
- {ok,St0#compile{module=Mod,options=Opts,code={Mod,Exp,Forms}}}.
-
-core_module(#compile{code=Code0,options=Opts,ifile=File}=St) ->
- {ok,Code,Ws} = v3_core:module(Code0, Opts),
- {ok,St#compile{code=Code,warnings=St#compile.warnings ++ [{File,Ws}]}}.
-
-core_fold_module(#compile{code=Code0,options=Opts,ifile=File}=St) ->
- {ok,Code,Ws} = sys_core_fold:module(Code0, Opts),
- {ok,St#compile{code=Code,warnings=St#compile.warnings ++ [{File,Ws}]}}.
-
-test_old_inliner(#compile{options=Opts}) ->
- %% The point of this test is to avoid loading the old inliner
- %% if we know that it will not be used.
- case any(fun(no_inline) -> true;
- (_) -> false
- end, Opts) of
- true -> false;
- false ->
- any(fun({inline,_}) -> true;
- (_) -> false
- end, Opts)
- end.
-
-test_core_inliner(#compile{options=Opts}) ->
- case any(fun(no_inline) -> true;
- (_) -> false
- end, Opts) of
- true -> false;
- false ->
- any(fun(inline) -> true;
- (_) -> false
- end, Opts)
- end.
-
-core_old_inliner(#compile{code=Code0,options=Opts}=St) ->
- case catch sys_core_inline:module(Code0, Opts) of
- {ok,Code} ->
- {ok,St#compile{code=Code}};
- {error,Es} ->
- {error,St#compile{errors=St#compile.errors ++ Es}}
- end.
-
-core_inline_module(#compile{code=Code0,options=Opts}=St) ->
- Code = cerl_inline:core_transform(Code0, Opts),
- {ok,St#compile{code=Code}}.
-
-core_dsetel_module(#compile{code=Code0,options=Opts}=St) ->
- {ok,Code} = sys_core_dsetel:module(Code0, Opts),
- {ok,St#compile{code=Code}}.
-
-kernel_module(#compile{code=Code0,options=Opts,ifile=File}=St) ->
- {ok,Code,Ws} = v3_kernel:module(Code0, Opts),
- {ok,St#compile{code=Code,warnings=St#compile.warnings ++ [{File,Ws}]}}.
-
-save_abstract_code(St) ->
- {ok,St#compile{abstract_code=abstract_code(St)}}.
-
-abstract_code(#compile{code=Code}) ->
- Abstr = {raw_abstract_v1,Code},
- case catch erlang:term_to_binary(Abstr, [compressed]) of
- {'EXIT',_} -> term_to_binary(Abstr);
- Other -> Other
- end.
-
-save_core_code(St) ->
- {ok,St#compile{core_code=cerl:from_records(St#compile.code)}}.
-
-beam_unused_labels(#compile{code=Code0}=St) ->
- Code = beam_jump:module_labels(Code0),
- {ok,St#compile{code=Code}}.
-
-beam_asm(#compile{ifile=File,code=Code0,abstract_code=Abst,options=Opts0}=St) ->
- Source = filename:absname(File),
- Opts = filter(fun is_informative_option/1, Opts0),
- case beam_asm:module(Code0, Abst, Source, Opts) of
- {ok,Code} -> {ok,St#compile{code=Code,abstract_code=[]}};
- {error,Es} -> {error,St#compile{errors=St#compile.errors ++ Es}}
- end.
-
-test_native(#compile{options=Opts}) ->
- %% This test must be made late, because the r7 or no_new_funs options
- %% will turn off the native option.
- member(native, Opts).
-
-native_compile(#compile{code=none}=St) -> {ok,St};
-native_compile(St) ->
- case erlang:system_info(hipe_architecture) of
- undefined ->
- Ws = [{St#compile.ifile,[{none,compile,no_native_support}]}],
- {ok,St#compile{warnings=St#compile.warnings ++ Ws}};
- _ ->
- native_compile_1(St)
- end.
-
-native_compile_1(St) ->
- Opts0 = [no_new_binaries|St#compile.options],
- IgnoreErrors = member(ignore_native_errors, Opts0),
- Opts = case keysearch(hipe, 1, Opts0) of
- {value,{hipe,L}} when list(L) -> L;
- {value,{hipe,X}} -> [X];
- _ -> []
- end,
- case catch hipe:compile(St#compile.module,
- St#compile.core_code,
- St#compile.code,
- Opts) of
- {ok, {Type,Bin}} when binary(Bin) ->
- {ok, embed_native_code(St, {Type,Bin})};
- {error, R} ->
- case IgnoreErrors of
- true ->
- Ws = [{St#compile.ifile,[{none,?MODULE,{native,R}}]}],
- {ok,St#compile{warnings=St#compile.warnings ++ Ws}};
- false ->
- Es = [{St#compile.ifile,[{none,?MODULE,{native,R}}]}],
- {error,St#compile{errors=St#compile.errors ++ Es}}
- end;
- {'EXIT',R} ->
- case IgnoreErrors of
- true ->
- Ws = [{St#compile.ifile,[{none,?MODULE,{native_crash,R}}]}],
- {ok,St#compile{warnings=St#compile.warnings ++ Ws}};
- false ->
- exit(R)
- end
- end.
-
-embed_native_code(St, {Architecture,NativeCode}) ->
- {ok, _, Chunks0} = beam_lib:all_chunks(St#compile.code),
- ChunkName = hipe_unified_loader:chunk_name(Architecture),
- Chunks1 = lists:keydelete(ChunkName, 1, Chunks0),
- Chunks = Chunks1 ++ [{ChunkName,NativeCode}],
- {ok, BeamPlusNative} = beam_lib:build_module(Chunks),
- St#compile{code=BeamPlusNative}.
-
-%% Returns true if the option is informative and therefore should be included
-%% in the option list of the compiled module.
-
-is_informative_option(beam) -> false;
-is_informative_option(report_warnings) -> false;
-is_informative_option(report_errors) -> false;
-is_informative_option(binary) -> false;
-is_informative_option(verbose) -> false;
-is_informative_option(_) -> true.
-
-save_binary(#compile{code=none}=St) -> {ok,St};
-save_binary(St) ->
- Tfile = tmpfile(St#compile.ofile), %Temp working file
- case write_binary(Tfile, St#compile.code, St) of
- ok ->
- case file:rename(Tfile, St#compile.ofile) of
- ok ->
- {ok,St};
- {error,_Error} ->
- file:delete(Tfile),
- Es = [{St#compile.ofile,[{none,?MODULE,{rename,Tfile}}]}],
- {error,St#compile{errors=St#compile.errors ++ Es}}
- end;
- {error,_Error} ->
- Es = [{Tfile,[{compile,write_error}]}],
- {error,St#compile{errors=St#compile.errors ++ Es}}
- end.
-
-write_binary(Name, Bin, St) ->
- Opts = case member(compressed, St#compile.options) of
- true -> [compressed];
- false -> []
- end,
- case file:write_file(Name, Bin, Opts) of
- ok -> ok;
- {error,_}=Error -> Error
- end.
-
-%% report_errors(State) -> ok
-%% report_warnings(State) -> ok
-
-report_errors(St) ->
- case member(report_errors, St#compile.options) of
- true ->
- foreach(fun ({{F,_L},Eds}) -> list_errors(F, Eds);
- ({F,Eds}) -> list_errors(F, Eds) end,
- St#compile.errors);
- false -> ok
- end.
-
-report_warnings(#compile{options=Opts,warnings=Ws0}) ->
- case member(report_warnings, Opts) of
- true ->
- Ws1 = flatmap(fun({{F,_L},Eds}) -> format_message(F, Eds);
- ({F,Eds}) -> format_message(F, Eds) end,
- Ws0),
- Ws = ordsets:from_list(Ws1),
- foreach(fun({_,Str}) -> io:put_chars(Str) end, Ws);
- false -> ok
- end.
-
-format_message(F, [{Line,Mod,E}|Es]) ->
- M = {Line,io_lib:format("~s:~w: Warning: ~s\n", [F,Line,Mod:format_error(E)])},
- [M|format_message(F, Es)];
-format_message(F, [{Mod,E}|Es]) ->
- M = {none,io_lib:format("~s: Warning: ~s\n", [F,Mod:format_error(E)])},
- [M|format_message(F, Es)];
-format_message(_, []) -> [].
-
-%% list_errors(File, ErrorDescriptors) -> ok
-
-list_errors(F, [{Line,Mod,E}|Es]) ->
- io:fwrite("~s:~w: ~s\n", [F,Line,Mod:format_error(E)]),
- list_errors(F, Es);
-list_errors(F, [{Mod,E}|Es]) ->
- io:fwrite("~s: ~s\n", [F,Mod:format_error(E)]),
- list_errors(F, Es);
-list_errors(_F, []) -> ok.
-
-%% erlfile(Dir, Base) -> ErlFile
-%% outfile(Base, Extension, Options) -> OutputFile
-%% objfile(Base, Target, Options) -> ObjFile
-%% tmpfile(ObjFile) -> TmpFile
-%% Work out the correct input and output file names.
-
-iofile(File) when atom(File) ->
- iofile(atom_to_list(File));
-iofile(File) ->
- {filename:dirname(File), filename:basename(File, ".erl")}.
-
-erlfile(Dir, Base, Suffix) ->
- filename:join(Dir, Base++Suffix).
-
-outfile(Base, Ext, Opts) when atom(Ext) ->
- outfile(Base, atom_to_list(Ext), Opts);
-outfile(Base, Ext, Opts) ->
- Obase = case keysearch(outdir, 1, Opts) of
- {value, {outdir, Odir}} -> filename:join(Odir, Base);
- _Other -> Base % Not found or bad format
- end,
- Obase++"."++Ext.
-
-objfile(Base, St) ->
- outfile(Base, "beam", St#compile.options).
-
-tmpfile(Ofile) ->
- reverse([$#|tl(reverse(Ofile))]).
-
-%% pre_defs(Options)
-%% inc_paths(Options)
-%% Extract the predefined macros and include paths from the option list.
-
-pre_defs([{d,M,V}|Opts]) ->
- [{M,V}|pre_defs(Opts)];
-pre_defs([{d,M}|Opts]) ->
- [M|pre_defs(Opts)];
-pre_defs([_|Opts]) ->
- pre_defs(Opts);
-pre_defs([]) -> [].
-
-inc_paths(Opts) ->
- [ P || {i,P} <- Opts, list(P) ].
-
-src_listing(Ext, St) ->
- listing(fun (Lf, {_Mod,_Exp,Fs}) -> do_src_listing(Lf, Fs);
- (Lf, Fs) -> do_src_listing(Lf, Fs) end,
- Ext, St).
-
-do_src_listing(Lf, Fs) ->
- foreach(fun (F) -> io:put_chars(Lf, [erl_pp:form(F),"\n"]) end,
- Fs).
-
-listing(Ext, St) ->
- listing(fun(Lf, Fs) -> beam_listing:module(Lf, Fs) end, Ext, St).
-
-listing(LFun, Ext, St) ->
- Lfile = outfile(St#compile.base, Ext, St#compile.options),
- case file:open(Lfile, [write,delayed_write]) of
- {ok,Lf} ->
- LFun(Lf, St#compile.code),
- ok = file:close(Lf),
- {ok,St};
- {error,_Error} ->
- Es = [{Lfile,[{none,compile,write_error}]}],
- {error,St#compile{errors=St#compile.errors ++ Es}}
- end.
-
-options() ->
- help(standard_passes()).
-
-help([{iff,Flag,{src_listing,Ext}}|T]) ->
- io:fwrite("~p - Generate .~s source listing file\n", [Flag,Ext]),
- help(T);
-help([{iff,Flag,{listing,Ext}}|T]) ->
- io:fwrite("~p - Generate .~s file\n", [Flag,Ext]),
- help(T);
-help([{iff,Flag,{Name,Fun}}|T]) when function(Fun) ->
- io:fwrite("~p - Run ~s\n", [Flag,Name]),
- help(T);
-help([{iff,_Flag,Action}|T]) ->
- help(Action),
- help(T);
-help([{unless,Flag,{pass,Pass}}|T]) ->
- io:fwrite("~p - Skip the ~s pass\n", [Flag,Pass]),
- help(T);
-help([{unless,no_postopt=Flag,List}|T]) when list(List) ->
- %% Hard-coded knowledgde here.
- io:fwrite("~p - Skip all post optimisation\n", [Flag]),
- help(List),
- help(T);
-help([{unless,_Flag,Action}|T]) ->
- help(Action),
- help(T);
-help([_|T]) ->
- help(T);
-help(_) ->
- ok.
-
-
-%% compile(AbsFileName, Outfilename, Options)
-%% Compile entry point for erl_compile.
-
-compile(File0, _OutFile, Options) ->
- File = shorten_filename(File0),
- case file(File, make_erl_options(Options)) of
- {ok,_Mod} -> ok;
- Other -> Other
- end.
-
-compile_beam(File0, _OutFile, Opts) ->
- File = shorten_filename(File0),
- case file(File, [from_beam|make_erl_options(Opts)]) of
- {ok,_Mod} -> ok;
- Other -> Other
- end.
-
-compile_asm(File0, _OutFile, Opts) ->
- File = shorten_filename(File0),
- case file(File, [asm|make_erl_options(Opts)]) of
- {ok,_Mod} -> ok;
- Other -> Other
- end.
-
-compile_core(File0, _OutFile, Opts) ->
- File = shorten_filename(File0),
- case file(File, [from_core|make_erl_options(Opts)]) of
- {ok,_Mod} -> ok;
- Other -> Other
- end.
-
-shorten_filename(Name0) ->
- {ok,Cwd} = file:get_cwd(),
- case lists:prefix(Cwd, Name0) of
- false -> Name0;
- true ->
- Name = case lists:nthtail(length(Cwd), Name0) of
- "/"++N -> N;
- N -> N
- end,
- Name
- end.
-
-%% Converts generic compiler options to specific options.
-
-make_erl_options(Opts) ->
-
- %% This way of extracting will work even if the record passed
- %% has more fields than known during compilation.
-
- Includes = Opts#options.includes,
- Defines = Opts#options.defines,
- Outdir = Opts#options.outdir,
- Warning = Opts#options.warning,
- Verbose = Opts#options.verbose,
- Specific = Opts#options.specific,
- OutputType = Opts#options.output_type,
- Cwd = Opts#options.cwd,
-
- Options =
- case Verbose of
- true -> [verbose];
- false -> []
- end ++
- case Warning of
- 0 -> [];
- _ -> [report_warnings]
- end ++
- map(
- fun ({Name, Value}) ->
- {d, Name, Value};
- (Name) ->
- {d, Name}
- end,
- Defines) ++
- case OutputType of
- undefined -> [];
- jam -> [jam];
- beam -> [beam];
- native -> [native]
- end,
-
- Options++[report_errors, {cwd, Cwd}, {outdir, Outdir}|
- map(fun(Dir) -> {i, Dir} end, Includes)]++Specific.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_lib.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_lib.erl
deleted file mode 100644
index 3a6158286f..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_lib.erl
+++ /dev/null
@@ -1,509 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: core_lib.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
-%%
-%% Purpose: Core Erlang abstract syntax functions.
-
--module(core_lib).
-
--export([get_anno/1,set_anno/2]).
--export([is_atomic/1,is_literal/1,is_literal_list/1,
- is_simple/1,is_simple_list/1,is_simple_top/1]).
--export([literal_value/1,make_literal/1]).
--export([make_values/1]).
--export([map/2, fold/3, mapfold/3]).
--export([is_var_used/2]).
-
-%% -compile([export_all]).
-
--include("core_parse.hrl").
-
-%% get_anno(Core) -> Anno.
-%% set_anno(Core, Anno) -> Core.
-%% Generic get/set annotation.
-
-get_anno(C) -> element(2, C).
-set_anno(C, A) -> setelement(2, C, A).
-
-%% is_atomic(Expr) -> true | false.
-
-is_atomic(#c_char{}) -> true;
-is_atomic(#c_int{}) -> true;
-is_atomic(#c_float{}) -> true;
-is_atomic(#c_atom{}) -> true;
-is_atomic(#c_string{}) -> true;
-is_atomic(#c_nil{}) -> true;
-is_atomic(#c_fname{}) -> true;
-is_atomic(_) -> false.
-
-%% is_literal(Expr) -> true | false.
-
-is_literal(#c_cons{hd=H,tl=T}) ->
- case is_literal(H) of
- true -> is_literal(T);
- false -> false
- end;
-is_literal(#c_tuple{es=Es}) -> is_literal_list(Es);
-is_literal(#c_binary{segments=Es}) -> is_lit_bin(Es);
-is_literal(E) -> is_atomic(E).
-
-is_literal_list(Es) -> lists:all(fun is_literal/1, Es).
-
-is_lit_bin(Es) ->
- lists:all(fun (#c_bitstr{val=E,size=S}) ->
- is_literal(E) and is_literal(S)
- end, Es).
-
-%% is_simple(Expr) -> true | false.
-
-is_simple(#c_var{}) -> true;
-is_simple(#c_cons{hd=H,tl=T}) ->
- case is_simple(H) of
- true -> is_simple(T);
- false -> false
- end;
-is_simple(#c_tuple{es=Es}) -> is_simple_list(Es);
-is_simple(#c_binary{segments=Es}) -> is_simp_bin(Es);
-is_simple(E) -> is_atomic(E).
-
-is_simple_list(Es) -> lists:all(fun is_simple/1, Es).
-
-is_simp_bin(Es) ->
- lists:all(fun (#c_bitstr{val=E,size=S}) ->
- is_simple(E) and is_simple(S)
- end, Es).
-
-%% is_simple_top(Expr) -> true | false.
-%% Only check if the top-level is a simple.
-
-is_simple_top(#c_var{}) -> true;
-is_simple_top(#c_cons{}) -> true;
-is_simple_top(#c_tuple{}) -> true;
-is_simple_top(#c_binary{}) -> true;
-is_simple_top(E) -> is_atomic(E).
-
-%% literal_value(LitExpr) -> Value.
-%% Return the value of LitExpr.
-
-literal_value(#c_char{val=C}) -> C;
-literal_value(#c_int{val=I}) -> I;
-literal_value(#c_float{val=F}) -> F;
-literal_value(#c_atom{val=A}) -> A;
-literal_value(#c_string{val=S}) -> S;
-literal_value(#c_nil{}) -> [];
-literal_value(#c_cons{hd=H,tl=T}) ->
- [literal_value(H)|literal_value(T)];
-literal_value(#c_tuple{es=Es}) ->
- list_to_tuple(literal_value_list(Es)).
-
-literal_value_list(Vals) -> lists:map(fun literal_value/1, Vals).
-
-%% make_literal(Value) -> LitExpr.
-%% Make a literal expression from an Erlang value.
-
-make_literal(I) when integer(I) -> #c_int{val=I};
-make_literal(F) when float(F) -> #c_float{val=F};
-make_literal(A) when atom(A) -> #c_atom{val=A};
-make_literal([]) -> #c_nil{};
-make_literal([H|T]) ->
- #c_cons{hd=make_literal(H),tl=make_literal(T)};
-make_literal(T) when tuple(T) ->
- #c_tuple{es=make_literal_list(tuple_to_list(T))}.
-
-make_literal_list(Vals) -> lists:map(fun make_literal/1, Vals).
-
-%% make_values([CoreExpr] | CoreExpr) -> #c_values{} | CoreExpr.
-%% Make a suitable values structure, expr or values, depending on
-%% Expr.
-
-make_values([E]) -> E;
-make_values([H|_]=Es) -> #c_values{anno=get_anno(H),es=Es};
-make_values([]) -> #c_values{es=[]};
-make_values(E) -> E.
-
-%% map(MapFun, CoreExpr) -> CoreExpr.
-%% This function traverses the core parse format, at each level
-%% applying the submited argument function, assumed to do the real
-%% work.
-%%
-%% The "eager" style, where each component of a construct are
-%% descended to before the construct itself, admits that some
-%% companion functions (the F:s) may be made simpler, since it may be
-%% safely assumed that no lower illegal instanced will be
-%% created/uncovered by actions on the current level.
-
-map(F, #c_tuple{es=Es}=R) ->
- F(R#c_tuple{es=map_list(F, Es)});
-map(F, #c_cons{hd=Hd, tl=Tl}=R) ->
- F(R#c_cons{hd=map(F, Hd),
- tl=map(F, Tl)});
-map(F, #c_values{es=Es}=R) ->
- F(R#c_values{es=map_list(F, Es)});
-
-map(F, #c_alias{var=Var, pat=Pat}=R) ->
- F(R#c_alias{var=map(F, Var),
- pat=map(F, Pat)});
-
-map(F, #c_module{defs=Defs}=R) ->
- F(R#c_module{defs=map_list(F, Defs)});
-map(F, #c_def{val=Val}=R) ->
- F(R#c_def{val=map(F, Val)});
-
-map(F, #c_fun{vars=Vars, body=Body}=R) ->
- F(R#c_fun{vars=map_list(F, Vars),
- body=map(F, Body)});
-map(F, #c_let{vars=Vs, arg=Arg, body=Body}=R) ->
- F(R#c_let{vars=map_list(F, Vs),
- arg=map(F, Arg),
- body=map(F, Body)});
-map(F, #c_letrec{defs=Fs,body=Body}=R) ->
- F(R#c_letrec{defs=map_list(F, Fs),
- body=map(F, Body)});
-map(F, #c_seq{arg=Arg, body=Body}=R) ->
- F(R#c_seq{arg=map(F, Arg),
- body=map(F, Body)});
-map(F, #c_case{arg=Arg, clauses=Clauses}=R) ->
- F(R#c_case{arg=map(F, Arg),
- clauses=map_list(F, Clauses)});
-map(F, #c_clause{pats=Ps, guard=Guard, body=Body}=R) ->
- F(R#c_clause{pats=map_list(F, Ps),
- guard=map(F, Guard),
- body=map(F, Body)});
-map(F, #c_receive{clauses=Cls, timeout=Tout, action=Act}=R) ->
- F(R#c_receive{clauses=map_list(F, Cls),
- timeout=map(F, Tout),
- action=map(F, Act)});
-map(F, #c_apply{op=Op,args=Args}=R) ->
- F(R#c_apply{op=map(F, Op),
- args=map_list(F, Args)});
-map(F, #c_call{module=M,name=N,args=Args}=R) ->
- F(R#c_call{module=map(F, M),
- name=map(F, N),
- args=map_list(F, Args)});
-map(F, #c_primop{name=N,args=Args}=R) ->
- F(R#c_primop{name=map(F, N),
- args=map_list(F, Args)});
-map(F, #c_try{arg=Expr,vars=Vars,body=Body,evars=Evars,handler=Handler}=R) ->
- F(R#c_try{arg=map(F, Expr),
- vars=map(F, Vars),
- body=map(F, Body),
- evars=map(F, Evars),
- handler=map(F, Handler)});
-map(F, #c_catch{body=Body}=R) ->
- F(R#c_catch{body=map(F, Body)});
-map(F, T) -> F(T). %Atomic nodes.
-
-map_list(F, L) -> lists:map(fun (E) -> map(F, E) end, L).
-
-%% fold(FoldFun, Accumulator, CoreExpr) -> Accumulator.
-%% This function traverses the core parse format, at each level
-%% applying the submited argument function, assumed to do the real
-%% work, and keeping the accumulated result in the A (accumulator)
-%% argument.
-
-fold(F, Acc, #c_tuple{es=Es}=R) ->
- F(R, fold_list(F, Acc, Es));
-fold(F, Acc, #c_cons{hd=Hd, tl=Tl}=R) ->
- F(R, fold(F, fold(F, Acc, Hd), Tl));
-fold(F, Acc, #c_values{es=Es}=R) ->
- F(R, fold_list(F, Acc, Es));
-
-fold(F, Acc, #c_alias{pat=P,var=V}=R) ->
- F(R, fold(F, fold(F, Acc, P), V));
-
-fold(F, Acc, #c_module{defs=Defs}=R) ->
- F(R, fold_list(F, Acc, Defs));
-fold(F, Acc, #c_def{val=Val}=R) ->
- F(R, fold(F, Acc, Val));
-
-fold(F, Acc, #c_fun{vars=Vars, body=Body}=R) ->
- F(R, fold(F, fold_list(F, Acc, Vars), Body));
-fold(F, Acc, #c_let{vars=Vs, arg=Arg, body=Body}=R) ->
- F(R, fold(F, fold(F, fold_list(F, Acc, Vs), Arg), Body));
-fold(F, Acc, #c_letrec{defs=Fs,body=Body}=R) ->
- F(R, fold(F, fold_list(F, Acc, Fs), Body));
-fold(F, Acc, #c_seq{arg=Arg, body=Body}=R) ->
- F(R, fold(F, fold(F, Acc, Arg), Body));
-fold(F, Acc, #c_case{arg=Arg, clauses=Clauses}=R) ->
- F(R, fold_list(F, fold(F, Acc, Arg), Clauses));
-fold(F, Acc, #c_clause{pats=Ps,guard=G,body=B}=R) ->
- F(R, fold(F, fold(F, fold_list(F, Acc, Ps), G), B));
-fold(F, Acc, #c_receive{clauses=Cl, timeout=Ti, action=Ac}=R) ->
- F(R, fold_list(F, fold(F, fold(F, Acc, Ac), Ti), Cl));
-fold(F, Acc, #c_apply{op=Op, args=Args}=R) ->
- F(R, fold_list(F, fold(F, Acc, Op), Args));
-fold(F, Acc, #c_call{module=Mod,name=Name,args=Args}=R) ->
- F(R, fold_list(F, fold(F, fold(F, Acc, Mod), Name), Args));
-fold(F, Acc, #c_primop{name=Name,args=Args}=R) ->
- F(R, fold_list(F, fold(F, Acc, Name), Args));
-fold(F, Acc, #c_try{arg=E,vars=Vs,body=Body,evars=Evs,handler=H}=R) ->
- NewB = fold(F, fold_list(F, fold(F, Acc, E), Vs), Body),
- F(R, fold(F, fold_list(F, NewB, Evs), H));
-fold(F, Acc, #c_catch{body=Body}=R) ->
- F(R, fold(F, Acc, Body));
-fold(F, Acc, T) -> %Atomic nodes
- F(T, Acc).
-
-fold_list(F, Acc, L) ->
- lists:foldl(fun (E, A) -> fold(F, A, E) end, Acc, L).
-
-%% mapfold(MapfoldFun, Accumulator, CoreExpr) -> {CoreExpr,Accumulator}.
-%% This function traverses the core parse format, at each level
-%% applying the submited argument function, assumed to do the real
-%% work, and keeping the accumulated result in the A (accumulator)
-%% argument.
-
-mapfold(F, Acc0, #c_tuple{es=Es0}=R) ->
- {Es1,Acc1} = mapfold_list(F, Acc0, Es0),
- F(R#c_tuple{es=Es1}, Acc1);
-mapfold(F, Acc0, #c_cons{hd=H0,tl=T0}=R) ->
- {H1,Acc1} = mapfold(F, Acc0, H0),
- {T1,Acc2} = mapfold(F, Acc1, T0),
- F(R#c_cons{hd=H1,tl=T1}, Acc2);
-mapfold(F, Acc0, #c_values{es=Es0}=R) ->
- {Es1,Acc1} = mapfold_list(F, Acc0, Es0),
- F(R#c_values{es=Es1}, Acc1);
-
-mapfold(F, Acc0, #c_alias{pat=P0,var=V0}=R) ->
- {P1,Acc1} = mapfold(F, Acc0, P0),
- {V1,Acc2} = mapfold(F, Acc1, V0),
- F(R#c_alias{pat=P1,var=V1}, Acc2);
-
-mapfold(F, Acc0, #c_module{defs=D0}=R) ->
- {D1,Acc1} = mapfold_list(F, Acc0, D0),
- F(R#c_module{defs=D1}, Acc1);
-mapfold(F, Acc0, #c_def{val=V0}=R) ->
- {V1,Acc1} = mapfold(F, Acc0, V0),
- F(R#c_def{val=V1}, Acc1);
-
-mapfold(F, Acc0, #c_fun{vars=Vs0, body=B0}=R) ->
- {Vs1,Acc1} = mapfold_list(F, Acc0, Vs0),
- {B1,Acc2} = mapfold(F, Acc1, B0),
- F(R#c_fun{vars=Vs1,body=B1}, Acc2);
-mapfold(F, Acc0, #c_let{vars=Vs0, arg=A0, body=B0}=R) ->
- {Vs1,Acc1} = mapfold_list(F, Acc0, Vs0),
- {A1,Acc2} = mapfold(F, Acc1, A0),
- {B1,Acc3} = mapfold(F, Acc2, B0),
- F(R#c_let{vars=Vs1,arg=A1,body=B1}, Acc3);
-mapfold(F, Acc0, #c_letrec{defs=Fs0,body=B0}=R) ->
- {Fs1,Acc1} = mapfold_list(F, Acc0, Fs0),
- {B1,Acc2} = mapfold(F, Acc1, B0),
- F(R#c_letrec{defs=Fs1,body=B1}, Acc2);
-mapfold(F, Acc0, #c_seq{arg=A0, body=B0}=R) ->
- {A1,Acc1} = mapfold(F, Acc0, A0),
- {B1,Acc2} = mapfold(F, Acc1, B0),
- F(R#c_seq{arg=A1,body=B1}, Acc2);
-mapfold(F, Acc0, #c_case{arg=A0,clauses=Cs0}=R) ->
- {A1,Acc1} = mapfold(F, Acc0, A0),
- {Cs1,Acc2} = mapfold_list(F, Acc1, Cs0),
- F(R#c_case{arg=A1,clauses=Cs1}, Acc2);
-mapfold(F, Acc0, #c_clause{pats=Ps0,guard=G0,body=B0}=R) ->
- {Ps1,Acc1} = mapfold_list(F, Acc0, Ps0),
- {G1,Acc2} = mapfold(F, Acc1, G0),
- {B1,Acc3} = mapfold(F, Acc2, B0),
- F(R#c_clause{pats=Ps1,guard=G1,body=B1}, Acc3);
-mapfold(F, Acc0, #c_receive{clauses=Cs0,timeout=T0,action=A0}=R) ->
- {T1,Acc1} = mapfold(F, Acc0, T0),
- {Cs1,Acc2} = mapfold_list(F, Acc1, Cs0),
- {A1,Acc3} = mapfold(F, Acc2, A0),
- F(R#c_receive{clauses=Cs1,timeout=T1,action=A1}, Acc3);
-mapfold(F, Acc0, #c_apply{op=Op0, args=As0}=R) ->
- {Op1,Acc1} = mapfold(F, Acc0, Op0),
- {As1,Acc2} = mapfold_list(F, Acc1, As0),
- F(R#c_apply{op=Op1,args=As1}, Acc2);
-mapfold(F, Acc0, #c_call{module=M0,name=N0,args=As0}=R) ->
- {M1,Acc1} = mapfold(F, Acc0, M0),
- {N1,Acc2} = mapfold(F, Acc1, N0),
- {As1,Acc3} = mapfold_list(F, Acc2, As0),
- F(R#c_call{module=M1,name=N1,args=As1}, Acc3);
-mapfold(F, Acc0, #c_primop{name=N0, args=As0}=R) ->
- {N1,Acc1} = mapfold(F, Acc0, N0),
- {As1,Acc2} = mapfold_list(F, Acc1, As0),
- F(R#c_primop{name=N1,args=As1}, Acc2);
-mapfold(F, Acc0, #c_try{arg=E0,vars=Vs0,body=B0,evars=Evs0,handler=H0}=R) ->
- {E1,Acc1} = mapfold(F, Acc0, E0),
- {Vs1,Acc2} = mapfold_list(F, Acc1, Vs0),
- {B1,Acc3} = mapfold(F, Acc2, B0),
- {Evs1,Acc4} = mapfold_list(F, Acc3, Evs0),
- {H1,Acc5} = mapfold(F, Acc4, H0),
- F(R#c_try{arg=E1,vars=Vs1,body=B1,evars=Evs1,handler=H1}, Acc5);
-mapfold(F, Acc0, #c_catch{body=B0}=R) ->
- {B1,Acc1} = mapfold(F, Acc0, B0),
- F(R#c_catch{body=B1}, Acc1);
-mapfold(F, Acc, T) -> %Atomic nodes
- F(T, Acc).
-
-mapfold_list(F, Acc, L) ->
- lists:mapfoldl(fun (E, A) -> mapfold(F, A, E) end, Acc, L).
-
-%% is_var_used(VarName, Expr) -> true | false.
-%% Test if the variable VarName is used in Expr.
-
-is_var_used(V, B) -> vu_body(V, B).
-
-vu_body(V, #c_values{es=Es}) ->
- vu_expr_list(V, Es);
-vu_body(V, Body) ->
- vu_expr(V, Body).
-
-vu_expr(V, #c_var{name=V2}) -> V =:= V2;
-vu_expr(V, #c_cons{hd=H,tl=T}) ->
- case vu_expr(V, H) of
- true -> true;
- false -> vu_expr(V, T)
- end;
-vu_expr(V, #c_tuple{es=Es}) ->
- vu_expr_list(V, Es);
-vu_expr(V, #c_binary{segments=Ss}) ->
- vu_seg_list(V, Ss);
-vu_expr(V, #c_fun{vars=Vs,body=B}) ->
- %% Variables in fun shadow previous variables
- case vu_var_list(V, Vs) of
- true -> false;
- false -> vu_body(V, B)
- end;
-vu_expr(V, #c_let{vars=Vs,arg=Arg,body=B}) ->
- case vu_body(V, Arg) of
- true -> true;
- false ->
- %% Variables in let shadow previous variables.
- case vu_var_list(V, Vs) of
- true -> false;
- false -> vu_body(V, B)
- end
- end;
-vu_expr(V, #c_letrec{defs=Fs,body=B}) ->
- case lists:any(fun (#c_def{val=Fb}) -> vu_body(V, Fb) end, Fs) of
- true -> true;
- false -> vu_body(V, B)
- end;
-vu_expr(V, #c_seq{arg=Arg,body=B}) ->
- case vu_expr(V, Arg) of
- true -> true;
- false -> vu_body(V, B)
- end;
-vu_expr(V, #c_case{arg=Arg,clauses=Cs}) ->
- case vu_expr(V, Arg) of
- true -> true;
- false -> vu_clauses(V, Cs)
- end;
-vu_expr(V, #c_receive{clauses=Cs,timeout=T,action=A}) ->
- case vu_clauses(V, Cs) of
- true -> true;
- false ->
- case vu_expr(V, T) of
- true -> true;
- false -> vu_body(V, A)
- end
- end;
-vu_expr(V, #c_apply{op=Op,args=As}) ->
- vu_expr_list(V, [Op|As]);
-vu_expr(V, #c_call{module=M,name=N,args=As}) ->
- vu_expr_list(V, [M,N|As]);
-vu_expr(V, #c_primop{args=As}) -> %Name is an atom
- vu_expr_list(V, As);
-vu_expr(V, #c_catch{body=B}) ->
- vu_body(V, B);
-vu_expr(V, #c_try{arg=E,vars=Vs,body=B,evars=Evs,handler=H}) ->
- case vu_body(V, E) of
- true -> true;
- false ->
- %% Variables shadow previous ones.
- case case vu_var_list(V, Vs) of
- true -> false;
- false -> vu_body(V, B)
- end of
- true -> true;
- false ->
- case vu_var_list(V, Evs) of
- true -> false;
- false -> vu_body(V, H)
- end
- end
- end;
-vu_expr(_, _) -> false. %Everything else
-
-vu_expr_list(V, Es) ->
- lists:any(fun(E) -> vu_expr(V, E) end, Es).
-
-vu_seg_list(V, Ss) ->
- lists:any(fun (#c_bitstr{val=Val,size=Size}) ->
- case vu_expr(V, Val) of
- true -> true;
- false -> vu_expr(V, Size)
- end
- end, Ss).
-
-%% vu_clause(VarName, Clause) -> true | false.
-%% vu_clauses(VarName, [Clause]) -> true | false.
-%% Have to get the pattern results right.
-
-vu_clause(V, #c_clause{pats=Ps,guard=G,body=B}) ->
- case vu_pattern_list(V, Ps) of
- {true,_Shad} -> true; %It is used
- {false,true} -> false; %Shadowed
- {false,false} -> %Not affected
- case vu_expr(V, G) of
- true -> true;
- false ->vu_body(V, B)
- end
- end.
-
-vu_clauses(V, Cs) ->
- lists:any(fun(C) -> vu_clause(V, C) end, Cs).
-
-%% vu_pattern(VarName, Pattern) -> {Used,Shadow}.
-%% vu_pattern_list(VarName, [Pattern]) -> {Used,Shadow}.
-%% Binaries complicate patterns as a variable can both be properly
-%% used, in a bit segment size, and shadow. They can also do both.
-
-%%vu_pattern(V, Pat) -> vu_pattern(V, Pat, {false,false}).
-
-vu_pattern(V, #c_var{name=V2}, St) ->
- setelement(2, St, V =:= V2);
-vu_pattern(V, #c_cons{hd=H,tl=T}, St0) ->
- case vu_pattern(V, H, St0) of
- {true,true}=St1 -> St1; %Nothing more to know
- St1 -> vu_pattern(V, T, St1)
- end;
-vu_pattern(V, #c_tuple{es=Es}, St) ->
- vu_pattern_list(V, Es, St);
-vu_pattern(V, #c_binary{segments=Ss}, St) ->
- vu_pat_seg_list(V, Ss, St);
-vu_pattern(V, #c_alias{var=Var,pat=P}, St0) ->
- case vu_pattern(V, Var, St0) of
- {true,true}=St1 -> St1;
- St1 -> vu_pattern(V, P, St1)
- end;
-vu_pattern(_, _, St) -> St.
-
-vu_pattern_list(V, Ps) -> vu_pattern_list(V, Ps, {false,false}).
-
-vu_pattern_list(V, Ps, St0) ->
- lists:foldl(fun(P, St) -> vu_pattern(V, P, St) end, St0, Ps).
-
-vu_pat_seg_list(V, Ss, St) ->
- lists:foldl(fun (#c_bitstr{val=Val,size=Size}, St0) ->
- case vu_pattern(V, Val, St0) of
- {true,true}=St1 -> St1;
- {_Used,Shad} -> {vu_expr(V, Size),Shad}
- end
- end, St, Ss).
-
-%% vu_var_list(VarName, [Var]) -> true | false.
-
-vu_var_list(V, Vs) ->
- lists:any(fun (#c_var{name=V2}) -> V =:= V2 end, Vs).
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_lint.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_lint.erl
deleted file mode 100644
index 2946fcb8c0..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_lint.erl
+++ /dev/null
@@ -1,515 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: core_lint.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
-%%
-%% Purpose : Do necessary checking of Core Erlang code.
-
-%% Check Core module for errors. Seeing this module is used in the
-%% compiler after optimisations wedone more checking than would be
-%% necessary after just parsing. Don't check all constructs.
-%%
-%% We check the following:
-%%
-%% All referred functions, called and exported, are defined.
-%% Format of export list.
-%% Format of attributes
-%% Used variables are defined.
-%% Variables in let and funs.
-%% Patterns case clauses.
-%% Values only as multiple values/variables/patterns.
-%% Return same number of values as requested
-%% Correct number of arguments
-%%
-%% Checks to add:
-%%
-%% Consistency of values/variables
-%% Consistency of function return values/calls.
-%%
-%% We keep the names defined variables and functions in a ordered list
-%% of variable names and function name/arity pairs.
-
--module(core_lint).
-
-
--export([module/1,module/2,format_error/1]).
-
--import(lists, [reverse/1,all/2,foldl/3]).
--import(ordsets, [add_element/2,is_element/2,union/2]).
-%-import(ordsets, [subtract/2]).
-
--include("core_parse.hrl").
-
-%% Define the lint state record.
-
--record(lint, {module=[], %Current module
- func=[], %Current function
- errors=[], %Errors
- warnings=[]}). %Warnings
-
-%% Keep track of defined
--record(def, {vars=[],
- funs=[]}).
-
-%%-deftype retcount() -> any | unknown | int().
-
-%% format_error(Error)
-%% Return a string describing the error.
-
-format_error(invalid_exports) -> "invalid exports";
-format_error(invalid_attributes) -> "invalid attributes";
-format_error({undefined_function,{F,A}}) ->
- io_lib:format("function ~w/~w undefined", [F,A]);
-format_error({undefined_function,{F1,A1},{F2,A2}}) ->
- io_lib:format("undefined function ~w/~w in ~w/~w", [F1,A1,F2,A2]);
-format_error({illegal_expr,{F,A}}) ->
- io_lib:format("illegal expression in ~w/~w", [F,A]);
-format_error({illegal_guard,{F,A}}) ->
- io_lib:format("illegal guard expression in ~w/~w", [F,A]);
-format_error({illegal_pattern,{F,A}}) ->
- io_lib:format("illegal pattern in ~w/~w", [F,A]);
-format_error({illegal_try,{F,A}}) ->
- io_lib:format("illegal try expression in ~w/~w", [F,A]);
-format_error({pattern_mismatch,{F,A}}) ->
- io_lib:format("pattern count mismatch in ~w/~w", [F,A]);
-format_error({return_mismatch,{F,A}}) ->
- io_lib:format("return count mismatch in ~w/~w", [F,A]);
-format_error({arg_mismatch,{F,A}}) ->
- io_lib:format("argument count mismatch in ~w/~w", [F,A]);
-format_error({unbound_var,N,{F,A}}) ->
- io_lib:format("unbound variable ~s in ~w/~w", [N,F,A]);
-format_error({duplicate_var,N,{F,A}}) ->
- io_lib:format("duplicate variable ~s in ~w/~w", [N,F,A]);
-format_error({not_var,{F,A}}) ->
- io_lib:format("expecting variable in ~w/~w", [F,A]);
-format_error({not_pattern,{F,A}}) ->
- io_lib:format("expecting pattern in ~w/~w", [F,A]);
-format_error({not_bs_pattern,{F,A}}) ->
- io_lib:format("expecting bit syntax pattern in ~w/~w", [F,A]).
-
-%% module(CoreMod) ->
-%% module(CoreMod, [CompileOption]) ->
-%% {ok,[Warning]} | {error,[Error],[Warning]}
-
-module(M) -> module(M, []).
-
-module(#c_module{name=M,exports=Es,attrs=As,defs=Ds}, _Opts) ->
- Defined = defined_funcs(Ds),
- St0 = #lint{module=M#c_atom.val},
- St1 = check_exports(Es, St0),
- St2 = check_attrs(As, St1),
- St3 = module_defs(Ds, Defined, St2),
- St4 = check_state(Es, Defined, St3),
- return_status(St4).
-
-%% defined_funcs([FuncDef]) -> [Fname].
-
-defined_funcs(Fs) ->
- foldl(fun (#c_def{name=#c_fname{id=I,arity=A}}, Def) ->
- add_element({I,A}, Def)
- end, [], Fs).
-
-%% return_status(State) ->
-%% {ok,[Warning]} | {error,[Error],[Warning]}
-%% Pack errors and warnings properly and return ok | error.
-
-return_status(St) ->
- Ws = reverse(St#lint.warnings),
- case reverse(St#lint.errors) of
- [] -> {ok,[{St#lint.module,Ws}]};
- Es -> {error,[{St#lint.module,Es}],[{St#lint.module,Ws}]}
- end.
-
-%% add_error(ErrorDescriptor, State) -> State'
-%% add_warning(ErrorDescriptor, State) -> State'
-%% Note that we don't use line numbers here.
-
-add_error(E, St) -> St#lint{errors=[{none,core_lint,E}|St#lint.errors]}.
-
-%%add_warning(W, St) -> St#lint{warnings=[{none,core_lint,W}|St#lint.warnings]}.
-
-check_exports(Es, St) ->
- case all(fun (#c_fname{id=Name,arity=Arity}) when
- atom(Name), integer(Arity) -> true;
- (_) -> false
- end, Es) of
- true -> St;
- false -> add_error(invalid_exports, St)
- end.
-
-check_attrs(As, St) ->
- case all(fun (#c_def{name=#c_atom{},val=V}) -> core_lib:is_literal(V);
- (_) -> false
- end, As) of
- true -> St;
- false -> add_error(invalid_attributes, St)
- end.
-
-check_state(Es, Defined, St) ->
- foldl(fun (#c_fname{id=N,arity=A}, St1) ->
- F = {N,A},
- case is_element(F, Defined) of
- true -> St1;
- false -> add_error({undefined_function,F}, St)
- end
- end, St, Es).
-% Undef = subtract(Es, Defined),
-% St1 = foldl(fun (F, St) -> add_error({undefined_function,F}, St) end,
-% St0, Undef),
-% St1.
-
-%% module_defs(CoreBody, Defined, State) -> State.
-
-module_defs(B, Def, St) ->
- %% Set top level function name.
- foldl(fun (Func, St0) ->
- #c_fname{id=F,arity=A} = Func#c_def.name,
- St1 = St0#lint{func={F,A}},
- function(Func, Def, St1)
- end, St, B).
-
-%% functions([Fdef], Defined, State) -> State.
-
-functions(Fs, Def, St0) ->
- foldl(fun (F, St) -> function(F, Def, St) end, St0, Fs).
-
-%% function(CoreFunc, Defined, State) -> State.
-
-function(#c_def{name=#c_fname{},val=B}, Def, St) ->
- %% Body must be a fun!
- case B of
- #c_fun{} -> expr(B, Def, any, St);
- _ -> add_error({illegal_expr,St#lint.func}, St)
- end.
-
-%% body(Expr, Defined, RetCount, State) -> State.
-
-body(#c_values{es=Es}, Def, Rt, St) ->
- return_match(Rt, length(Es), expr_list(Es, Def, St));
-body(E, Def, Rt, St0) ->
- St1 = expr(E, Def, Rt, St0),
- case core_lib:is_simple_top(E) of
- true -> return_match(Rt, 1, St1);
- false -> St1
- end.
-
-%% guard(Expr, Defined, State) -> State.
-%% Guards are boolean expressions with test wrapped in a protected.
-
-guard(Expr, Def, St) -> gexpr(Expr, Def, 1, St).
-
-%% guard_list([Expr], Defined, State) -> State.
-
-%% guard_list(Es, Def, St0) ->
-%% foldl(fun (E, St) -> guard(E, Def, St) end, St0, Es).
-
-%% gbody(Expr, Defined, RetCount, State) -> State.
-
-gbody(#c_values{es=Es}, Def, Rt, St) ->
- return_match(Rt, length(Es), gexpr_list(Es, Def, St));
-gbody(E, Def, Rt, St0) ->
- St1 = gexpr(E, Def, Rt, St0),
- case core_lib:is_simple_top(E) of
- true -> return_match(Rt, 1, St1);
- false -> St1
- end.
-
-gexpr(#c_var{name=N}, Def, _Rt, St) -> expr_var(N, Def, St);
-gexpr(#c_int{}, _Def, _Rt, St) -> St;
-gexpr(#c_float{}, _Def, _Rt, St) -> St;
-gexpr(#c_atom{}, _Def, _Rt, St) -> St;
-gexpr(#c_char{}, _Def, _Rt, St) -> St;
-gexpr(#c_string{}, _Def, _Rt, St) -> St;
-gexpr(#c_nil{}, _Def, _Rt, St) -> St;
-gexpr(#c_cons{hd=H,tl=T}, Def, _Rt, St) ->
- gexpr_list([H,T], Def, St);
-gexpr(#c_tuple{es=Es}, Def, _Rt, St) ->
- gexpr_list(Es, Def, St);
-gexpr(#c_binary{segments=Ss}, Def, _Rt, St) ->
- gbitstr_list(Ss, Def, St);
-gexpr(#c_seq{arg=Arg,body=B}, Def, Rt, St0) ->
- St1 = gexpr(Arg, Def, any, St0), %Ignore values
- gbody(B, Def, Rt, St1);
-gexpr(#c_let{vars=Vs,arg=Arg,body=B}, Def, Rt, St0) ->
- St1 = gbody(Arg, Def, let_varcount(Vs), St0), %This is a guard body
- {Lvs,St2} = variable_list(Vs, St1),
- gbody(B, union(Lvs, Def), Rt, St2);
-gexpr(#c_call{module=#c_atom{val=erlang},
- name=#c_atom{},
- args=As}, Def, 1, St) ->
- gexpr_list(As, Def, St);
-gexpr(#c_primop{name=N,args=As}, Def, _Rt, St0) when record(N, c_atom) ->
- gexpr_list(As, Def, St0);
-gexpr(#c_try{arg=E,vars=[#c_var{name=X}],body=#c_var{name=X},
- evars=[#c_var{},#c_var{},#c_var{}],handler=#c_atom{val=false}},
- Def, Rt, St) ->
- gbody(E, Def, Rt, St);
-gexpr(_, _, _, St) ->
- add_error({illegal_guard,St#lint.func}, St).
-
-%% gexpr_list([Expr], Defined, State) -> State.
-
-gexpr_list(Es, Def, St0) ->
- foldl(fun (E, St) -> gexpr(E, Def, 1, St) end, St0, Es).
-
-%% gbitstr_list([Elem], Defined, State) -> State.
-
-gbitstr_list(Es, Def, St0) ->
- foldl(fun (E, St) -> gbitstr(E, Def, St) end, St0, Es).
-
-gbitstr(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Def, St0) ->
- St1 = bit_type(U, T, Fs, St0),
- gexpr_list([V,S], Def, St1).
-
-%% expr(Expr, Defined, RetCount, State) -> State.
-
-expr(#c_var{name=N}, Def, _Rt, St) -> expr_var(N, Def, St);
-expr(#c_int{}, _Def, _Rt, St) -> St;
-expr(#c_float{}, _Def, _Rt, St) -> St;
-expr(#c_atom{}, _Def, _Rt, St) -> St;
-expr(#c_char{}, _Def, _Rt, St) -> St;
-expr(#c_string{}, _Def, _Rt, St) -> St;
-expr(#c_nil{}, _Def, _Rt, St) -> St;
-expr(#c_cons{hd=H,tl=T}, Def, _Rt, St) ->
- expr_list([H,T], Def, St);
-expr(#c_tuple{es=Es}, Def, _Rt, St) ->
- expr_list(Es, Def, St);
-expr(#c_binary{segments=Ss}, Def, _Rt, St) ->
- bitstr_list(Ss, Def, St);
-expr(#c_fname{id=I,arity=A}, Def, _Rt, St) ->
- expr_fname({I,A}, Def, St);
-expr(#c_fun{vars=Vs,body=B}, Def, Rt, St0) ->
- {Vvs,St1} = variable_list(Vs, St0),
- return_match(Rt, 1, body(B, union(Vvs, Def), any, St1));
-expr(#c_seq{arg=Arg,body=B}, Def, Rt, St0) ->
- St1 = expr(Arg, Def, any, St0), %Ignore values
- body(B, Def, Rt, St1);
-expr(#c_let{vars=Vs,arg=Arg,body=B}, Def, Rt, St0) ->
- St1 = body(Arg, Def, let_varcount(Vs), St0), %This is a body
- {Lvs,St2} = variable_list(Vs, St1),
- body(B, union(Lvs, Def), Rt, St2);
-expr(#c_letrec{defs=Fs,body=B}, Def0, Rt, St0) ->
- Def1 = union(defined_funcs(Fs), Def0), %All defined stuff
- St1 = functions(Fs, Def1, St0),
- body(B, Def1, Rt, St1#lint{func=St0#lint.func});
-expr(#c_case{arg=Arg,clauses=Cs}, Def, Rt, St0) ->
- Pc = case_patcount(Cs),
- St1 = body(Arg, Def, Pc, St0),
- clauses(Cs, Def, Pc, Rt, St1);
-expr(#c_receive{clauses=Cs,timeout=T,action=A}, Def, Rt, St0) ->
- St1 = expr(T, Def, 1, St0),
- St2 = body(A, Def, Rt, St1),
- clauses(Cs, Def, 1, Rt, St2);
-expr(#c_apply{op=Op,args=As}, Def, _Rt, St0) ->
- St1 = apply_op(Op, Def, length(As), St0),
- expr_list(As, Def, St1);
-expr(#c_call{module=M,name=N,args=As}, Def, _Rt, St0) ->
- St1 = expr(M, Def, 1, St0),
- St2 = expr(N, Def, 1, St1),
- expr_list(As, Def, St2);
-expr(#c_primop{name=N,args=As}, Def, _Rt, St0) when record(N, c_atom) ->
- expr_list(As, Def, St0);
-expr(#c_catch{body=B}, Def, Rt, St) ->
- return_match(Rt, 1, body(B, Def, 1, St));
-expr(#c_try{arg=A,vars=Vs,body=B,evars=Evs,handler=H}, Def, Rt, St0) ->
- St1 = case length(Evs) of
- 2 -> St0;
- _ -> add_error({illegal_try,St0#lint.func}, St0)
- end,
- St2 = body(A, Def, let_varcount(Vs), St1),
- {Ns,St3} = variable_list(Vs, St2),
- St4 = body(B, union(Ns, Def), Rt, St3),
- {Ens,St5} = variable_list(Evs, St4),
- body(H, union(Ens, Def), Rt, St5);
-expr(_, _, _, St) ->
- %%io:fwrite("clint: ~p~n", [Other]),
- add_error({illegal_expr,St#lint.func}, St).
-
-%% expr_list([Expr], Defined, State) -> State.
-
-expr_list(Es, Def, St0) ->
- foldl(fun (E, St) -> expr(E, Def, 1, St) end, St0, Es).
-
-%% bitstr_list([Elem], Defined, State) -> State.
-
-bitstr_list(Es, Def, St0) ->
- foldl(fun (E, St) -> bitstr(E, Def, St) end, St0, Es).
-
-bitstr(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Def, St0) ->
- St1 = bit_type(U, T, Fs, St0),
- expr_list([V,S], Def, St1).
-
-%% apply_op(Op, Defined, ArgCount, State) -> State.
-%% A apply op is either an fname or an expression.
-
-apply_op(#c_fname{id=I,arity=A}, Def, Ac, St0) ->
- St1 = expr_fname({I,A}, Def, St0),
- arg_match(Ac, A, St1);
-apply_op(E, Def, _, St) -> expr(E, Def, 1, St). %Hard to check
-
-%% expr_var(VarName, Defined, State) -> State.
-
-expr_var(N, Def, St) ->
- case is_element(N, Def) of
- true -> St;
- false -> add_error({unbound_var,N,St#lint.func}, St)
- end.
-
-%% expr_fname(Fname, Defined, State) -> State.
-
-expr_fname(Fname, Def, St) ->
- case is_element(Fname, Def) of
- true -> St;
- false -> add_error({undefined_function,Fname,St#lint.func}, St)
- end.
-
-%% let_varcount([Var]) -> int().
-
-let_varcount([]) -> any; %Ignore values
-let_varcount(Es) -> length(Es).
-
-%% case_patcount([Clause]) -> int().
-
-case_patcount([#c_clause{pats=Ps}|_]) -> length(Ps).
-
-%% clauses([Clause], Defined, PatCount, RetCount, State) -> State.
-
-clauses(Cs, Def, Pc, Rt, St0) ->
- foldl(fun (C, St) -> clause(C, Def, Pc, Rt, St) end, St0, Cs).
-
-%% clause(Clause, Defined, PatCount, RetCount, State) -> State.
-
-clause(#c_clause{pats=Ps,guard=G,body=B}, Def0, Pc, Rt, St0) ->
- St1 = pattern_match(Pc, length(Ps), St0),
- {Pvs,St2} = pattern_list(Ps, Def0, St1),
- Def1 = union(Pvs, Def0),
- St3 = guard(G, Def1, St2),
- body(B, Def1, Rt, St3).
-
-%% variable(Var, [PatVar], State) -> {[VarName],State}.
-
-variable(#c_var{name=N}, Ps, St) ->
- case is_element(N, Ps) of
- true -> {[],add_error({duplicate_var,N,St#lint.func}, St)};
- false -> {[N],St}
- end;
-variable(_, Def, St) -> {Def,add_error({not_var,St#lint.func}, St)}.
-
-%% variable_list([Var], State) -> {[Var],State}.
-%% variable_list([Var], [PatVar], State) -> {[Var],State}.
-
-variable_list(Vs, St) -> variable_list(Vs, [], St).
-
-variable_list(Vs, Ps, St) ->
- foldl(fun (V, {Ps0,St0}) ->
- {Vvs,St1} = variable(V, Ps0, St0),
- {union(Vvs, Ps0),St1}
- end, {Ps,St}, Vs).
-
-%% pattern(Pattern, Defined, State) -> {[PatVar],State}.
-%% pattern(Pattern, Defined, [PatVar], State) -> {[PatVar],State}.
-%% Patterns are complicated by sizes in binaries. These are pure
-%% input variables which create no bindings. We, therefor, need to
-%% carry around the original defined variables to get the correct
-%% handling.
-
-%% pattern(P, Def, St) -> pattern(P, Def, [], St).
-
-pattern(#c_var{name=N}, Def, Ps, St) ->
- pat_var(N, Def, Ps, St);
-pattern(#c_int{}, _Def, Ps, St) -> {Ps,St};
-pattern(#c_float{}, _Def, Ps, St) -> {Ps,St};
-pattern(#c_atom{}, _Def, Ps, St) -> {Ps,St};
-pattern(#c_char{}, _Def, Ps, St) -> {Ps,St};
-pattern(#c_string{}, _Def, Ps, St) -> {Ps,St};
-pattern(#c_nil{}, _Def, Ps, St) -> {Ps,St};
-pattern(#c_cons{hd=H,tl=T}, Def, Ps, St) ->
- pattern_list([H,T], Def, Ps, St);
-pattern(#c_tuple{es=Es}, Def, Ps, St) ->
- pattern_list(Es, Def, Ps, St);
-pattern(#c_binary{segments=Ss}, Def, Ps, St) ->
- pat_bin(Ss, Def, Ps, St);
-pattern(#c_alias{var=V,pat=P}, Def, Ps, St0) ->
- {Vvs,St1} = variable(V, Ps, St0),
- pattern(P, Def, union(Vvs, Ps), St1);
-pattern(_, _, Ps, St) -> {Ps,add_error({not_pattern,St#lint.func}, St)}.
-
-pat_var(N, _Def, Ps, St) ->
- case is_element(N, Ps) of
- true -> {Ps,add_error({duplicate_var,N,St#lint.func}, St)};
- false -> {add_element(N, Ps),St}
- end.
-
-%% pat_bin_list([Elem], Defined, [PatVar], State) -> {[PatVar],State}.
-
-pat_bin(Es, Def, Ps0, St0) ->
- foldl(fun (E, {Ps,St}) -> pat_segment(E, Def, Ps, St) end, {Ps0,St0}, Es).
-
-pat_segment(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Def, Ps, St0) ->
- St1 = bit_type(U, T, Fs, St0),
- St2 = pat_bit_expr(S, T, Def, St1),
- pattern(V, Def, Ps, St2);
-pat_segment(_, _, Ps, St) ->
- {Ps,add_error({not_bs_pattern,St#lint.func}, St)}.
-
-%% pat_bit_expr(SizePat, Type, Defined, State) -> State.
-%% Check the Size pattern, this is an input! Be a bit tough here.
-
-pat_bit_expr(#c_int{val=I}, _, _, St) when I >= 0 -> St;
-pat_bit_expr(#c_var{name=N}, _, Def, St) ->
- expr_var(N, Def, St);
-pat_bit_expr(#c_atom{val=all}, binary, _Def, St) -> St;
-pat_bit_expr(_, _, _, St) ->
- add_error({illegal_expr,St#lint.func}, St).
-
-bit_type(Unit, Type, Flags, St) ->
- U = core_lib:literal_value(Unit),
- T = core_lib:literal_value(Type),
- Fs = core_lib:literal_value(Flags),
- case erl_bits:set_bit_type(default, [T,{unit,U}|Fs]) of
- {ok,_,_} -> St;
- {error,E} -> add_error({E,St#lint.func}, St)
- end.
-
-%% pattern_list([Var], Defined, State) -> {[PatVar],State}.
-%% pattern_list([Var], Defined, [PatVar], State) -> {[PatVar],State}.
-
-pattern_list(Pats, Def, St) -> pattern_list(Pats, Def, [], St).
-
-pattern_list(Pats, Def, Ps0, St0) ->
- foldl(fun (P, {Ps,St}) -> pattern(P, Def, Ps, St) end, {Ps0,St0}, Pats).
-
-%% pattern_match(Required, Supplied, State) -> State.
-%% Check that the required number of patterns match the supplied.
-
-pattern_match(N, N, St) -> St;
-pattern_match(_Req, _Sup, St) ->
- add_error({pattern_mismatch,St#lint.func}, St).
-
-%% return_match(Required, Supplied, State) -> State.
-%% Check that the required number of return values match the supplied.
-
-return_match(any, _Sup, St) -> St;
-return_match(_Req, unknown, St) -> St;
-return_match(N, N, St) -> St;
-return_match(_Req, _Sup, St) ->
- add_error({return_mismatch,St#lint.func}, St).
-
-%% arg_match(Required, Supplied, State) -> State.
-
-arg_match(_Req, unknown, St) -> St;
-arg_match(N, N, St) -> St;
-arg_match(_Req, _Sup, St) ->
- add_error({arg_mismatch,St#lint.func}, St).
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_parse.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_parse.erl
deleted file mode 100644
index 942845bef7..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_parse.erl
+++ /dev/null
@@ -1,4911 +0,0 @@
--module(core_parse).
--define(THIS_MODULE, core_parse).
--export([parse/1, parse_and_scan/1, format_error/1]).
-
--export([abstract/1,abstract/2,normalise/1]).
-
-%% The following directive is needed for (significantly) faster compilation
-%% of the generated .erl file by the HiPE compiler. Please do not remove.
--compile([{hipe,[{regalloc,linear_scan}]}]).
-
--include("core_parse.hrl").
-
-tok_val(T) -> element(3, T).
-tok_line(T) -> element(2, T).
-
-abstract(T, _N) -> abstract(T).
-
-abstract(Term) -> core_lib:make_literal(Term).
-
-normalise(Core) -> core_lib:literal_value(Core).
-
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: core_parse.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
-%%
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-% The parser generator will insert appropriate declarations before this line.%
-
-parse(Tokens) ->
- case catch yeccpars1(Tokens, false, 0, [], []) of
- error ->
- Errorline =
- if Tokens == [] -> 0; true -> element(2, hd(Tokens)) end,
- {error,
- {Errorline, ?THIS_MODULE, "syntax error at or after this line."}};
- Other ->
- Other
- end.
-
-parse_and_scan({Mod, Fun, Args}) ->
- case apply(Mod, Fun, Args) of
- {eof, _} ->
- {ok, eof};
- {error, Descriptor, _} ->
- {error, Descriptor};
- {ok, Tokens, _} ->
- yeccpars1(Tokens, {Mod, Fun, Args}, 0, [], [])
- end.
-
-format_error(Message) ->
- case io_lib:deep_char_list(Message) of
- true ->
- Message;
- _ ->
- io_lib:write(Message)
- end.
-
-% To be used in grammar files to throw an error message to the parser toplevel.
-% Doesn't have to be exported!
-return_error(Line, Message) ->
- throw({error, {Line, ?THIS_MODULE, Message}}).
-
-
-% Don't change yeccpars1/6 too much, it is called recursively by yeccpars2/8!
-yeccpars1([Token | Tokens], Tokenizer, State, States, Vstack) ->
- yeccpars2(State, element(1, Token), States, Vstack, Token, Tokens,
- Tokenizer);
-yeccpars1([], {M, F, A}, State, States, Vstack) ->
- case catch apply(M, F, A) of
- {eof, Endline} ->
- {error, {Endline, ?THIS_MODULE, "end_of_file"}};
- {error, Descriptor, _Endline} ->
- {error, Descriptor};
- {'EXIT', Reason} ->
- {error, {0, ?THIS_MODULE, Reason}};
- {ok, Tokens, _Endline} ->
- case catch yeccpars1(Tokens, {M, F, A}, State, States, Vstack) of
- error ->
- Errorline = element(2, hd(Tokens)),
- {error, {Errorline, ?THIS_MODULE,
- "syntax error at or after this line."}};
- Other ->
- Other
- end
- end;
-yeccpars1([], false, State, States, Vstack) ->
- yeccpars2(State, '$end', States, Vstack, {'$end', 999999}, [], false).
-
-% For internal use only.
-yeccerror(Token) ->
- {error,
- {element(2, Token), ?THIS_MODULE,
- ["syntax error before: ", yecctoken2string(Token)]}}.
-
-yecctoken2string({atom, _, A}) -> io_lib:write(A);
-yecctoken2string({integer,_,N}) -> io_lib:write(N);
-yecctoken2string({float,_,F}) -> io_lib:write(F);
-yecctoken2string({char,_,C}) -> io_lib:write_char(C);
-yecctoken2string({var,_,V}) -> io_lib:format('~s', [V]);
-yecctoken2string({string,_,S}) -> io_lib:write_string(S);
-yecctoken2string({reserved_symbol, _, A}) -> io_lib:format('~w', [A]);
-yecctoken2string({_Cat, _, Val}) -> io_lib:format('~w', [Val]);
-
-yecctoken2string({'dot', _}) -> io_lib:format('~w', ['.']);
-yecctoken2string({'$end', _}) ->
- [];
-yecctoken2string({Other, _}) when atom(Other) ->
- io_lib:format('~w', [Other]);
-yecctoken2string(Other) ->
- io_lib:write(Other).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
-yeccpars2(0, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 1, [0 | __Ss], [__T | __Stack]);
-yeccpars2(0, 'module', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 2, [0 | __Ss], [__T | __Stack]);
-yeccpars2(0, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(1, 'module', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 313, [1 | __Ss], [__T | __Stack]);
-yeccpars2(1, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(2, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 4, [2 | __Ss], [__T | __Stack]);
-yeccpars2(2, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(3, '$end', _, __Stack, _, _, _) ->
- {ok, hd(__Stack)};
-yeccpars2(3, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(4, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 5, [4 | __Ss], [__T | __Stack]);
-yeccpars2(4, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(5, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 11, [5 | __Ss], [__T | __Stack]);
-yeccpars2(5, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 306, [5 | __Ss], [__T | __Stack]);
-yeccpars2(5, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(6, 'attributes', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 7, [6 | __Ss], [__T | __Stack]);
-yeccpars2(6, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(7, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 276, [7 | __Ss], [__T | __Stack]);
-yeccpars2(7, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(8, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 9, [8 | __Ss], [__T | __Stack]);
-yeccpars2(8, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 11, [8 | __Ss], [__T | __Stack]);
-yeccpars2(8, __Cat, __Ss, __Stack, __T, __Ts, __Tzr) ->
- __Val = [],
- yeccpars2(13, __Cat, [8 | __Ss], [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(9, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 11, [9 | __Ss], [__T | __Stack]);
-yeccpars2(9, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(10, '=', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 20, [10 | __Ss], [__T | __Stack]);
-yeccpars2(10, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(11, '/', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 18, [11 | __Ss], [__T | __Stack]);
-yeccpars2(11, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(12, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 9, [12 | __Ss], [__T | __Stack]);
-yeccpars2(12, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 11, [12 | __Ss], [__T | __Stack]);
-yeccpars2(12, __Cat, __Ss, __Stack, __T, __Ts, __Tzr) ->
- __Val = [],
- yeccpars2(17, __Cat, [12 | __Ss], [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(13, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(module_defs, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(14, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(anno_function_name, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(15, 'end', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 16, [15 | __Ss], [__T | __Stack]);
-yeccpars2(15, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(16, __Cat, __Ss, [__6,__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_module{name = #c_atom{val = tok_val(__2)}, exports = __3, attrs = __4, defs = __5},
- __Nss = lists:nthtail(5, __Ss),
- yeccpars2(yeccgoto(module_definition, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(17, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1|__2],
- __Nss = lists:nthtail(1, __Ss),
- yeccpars2(yeccgoto(function_definitions, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(18, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 19, [18 | __Ss], [__T | __Stack]);
-yeccpars2(18, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(19, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_fname{id = tok_val(__1), arity = tok_val(__3)},
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(function_name, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(20, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [20 | __Ss], [__T | __Stack]);
-yeccpars2(20, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 21, [20 | __Ss], [__T | __Stack]);
-yeccpars2(20, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(21, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [21 | __Ss], [__T | __Stack]);
-yeccpars2(21, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(22, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_def{name = __1, val = __3},
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(function_definition, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(23, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 25, [23 | __Ss], [__T | __Stack]);
-yeccpars2(23, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(24, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(anno_fun, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(25, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 27, [25 | __Ss], [__T | __Stack]);
-yeccpars2(25, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 26, [25 | __Ss], [__T | __Stack]);
-yeccpars2(25, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [25 | __Ss], [__T | __Stack]);
-yeccpars2(25, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(26, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [26 | __Ss], [__T | __Stack]);
-yeccpars2(26, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(27, '->', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 265, [27 | __Ss], [__T | __Stack]);
-yeccpars2(27, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(28, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 263, [28 | __Ss], [__T | __Stack]);
-yeccpars2(28, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1],
- yeccpars2(yeccgoto(anno_variables, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(29, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 32, [29 | __Ss], [__T | __Stack]);
-yeccpars2(29, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(30, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_var{name = tok_val(__1)},
- yeccpars2(yeccgoto(variable, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(31, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(anno_variable, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(32, '->', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 33, [32 | __Ss], [__T | __Stack]);
-yeccpars2(32, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(33, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [33 | __Ss], [__T | __Stack]);
-yeccpars2(33, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(34, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 247, [34 | __Ss], [__T | __Stack]);
-yeccpars2(34, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(35, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [35 | __Ss], [__T | __Stack]);
-yeccpars2(35, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(36, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 240, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [36 | __Ss], [__T | __Stack]);
-yeccpars2(36, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(37, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 149, [37 | __Ss], [__T | __Stack]);
-yeccpars2(37, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(38, __Cat, __Ss, [__6,__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_fun{vars = __3, body = __6},
- __Nss = lists:nthtail(5, __Ss),
- yeccpars2(yeccgoto(fun_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(39, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(40, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [40 | __Ss], [__T | __Stack]);
-yeccpars2(40, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(41, '/', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 18, [41 | __Ss], [__T | __Stack]);
-yeccpars2(41, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_atom{val = tok_val(__1)},
- yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(42, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(43, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(44, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [44 | __Ss], [__T | __Stack]);
-yeccpars2(44, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(45, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(46, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [46 | __Ss], [__T | __Stack]);
-yeccpars2(46, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(47, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(48, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [48 | __Ss], [__T | __Stack]);
-yeccpars2(48, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(49, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(50, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_char{val = tok_val(__1)},
- yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(51, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(52, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [52 | __Ss], [__T | __Stack]);
-yeccpars2(52, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(53, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(anno_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(54, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_float{val = tok_val(__1)},
- yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(55, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(56, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(57, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_int{val = tok_val(__1)},
- yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(58, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 83, [58 | __Ss], [__T | __Stack]);
-yeccpars2(58, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 26, [58 | __Ss], [__T | __Stack]);
-yeccpars2(58, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [58 | __Ss], [__T | __Stack]);
-yeccpars2(58, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(59, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(60, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 9, [60 | __Ss], [__T | __Stack]);
-yeccpars2(60, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 11, [60 | __Ss], [__T | __Stack]);
-yeccpars2(60, __Cat, __Ss, __Stack, __T, __Ts, __Tzr) ->
- __Val = [],
- yeccpars2(210, __Cat, [60 | __Ss], [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(61, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(62, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_nil{},
- yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(63, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 208, [63 | __Ss], [__T | __Stack]);
-yeccpars2(63, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(64, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(65, 'after', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 99, [65 | __Ss], [__T | __Stack]);
-yeccpars2(65, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 97, [65 | __Ss], [__T | __Stack]);
-yeccpars2(65, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 96, [65 | __Ss], [__T | __Stack]);
-yeccpars2(65, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [65 | __Ss], [__T | __Stack]);
-yeccpars2(65, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [65 | __Ss], [__T | __Stack]);
-yeccpars2(65, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [65 | __Ss], [__T | __Stack]);
-yeccpars2(65, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [65 | __Ss], [__T | __Stack]);
-yeccpars2(65, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [65 | __Ss], [__T | __Stack]);
-yeccpars2(65, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [65 | __Ss], [__T | __Stack]);
-yeccpars2(65, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 95, [65 | __Ss], [__T | __Stack]);
-yeccpars2(65, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 98, [65 | __Ss], [__T | __Stack]);
-yeccpars2(65, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 114, [65 | __Ss], [__T | __Stack]);
-yeccpars2(65, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(66, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(67, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(68, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(69, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_string{val = tok_val(__1)},
- yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(70, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [70 | __Ss], [__T | __Stack]);
-yeccpars2(70, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(71, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(72, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(73, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(74, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 77, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [74 | __Ss], [__T | __Stack]);
-yeccpars2(74, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(75, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 79, [75 | __Ss], [__T | __Stack]);
-yeccpars2(75, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1],
- yeccpars2(yeccgoto(anno_expressions, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(76, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 78, [76 | __Ss], [__T | __Stack]);
-yeccpars2(76, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(77, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_tuple{es = []},
- __Nss = lists:nthtail(1, __Ss),
- yeccpars2(yeccgoto(tuple, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(78, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_tuple{es = __2},
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(tuple, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(79, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [79 | __Ss], [__T | __Stack]);
-yeccpars2(79, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(80, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1|__3],
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(anno_expressions, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(81, 'of', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 82, [81 | __Ss], [__T | __Stack]);
-yeccpars2(81, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(82, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 83, [82 | __Ss], [__T | __Stack]);
-yeccpars2(82, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 26, [82 | __Ss], [__T | __Stack]);
-yeccpars2(82, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [82 | __Ss], [__T | __Stack]);
-yeccpars2(82, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(83, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 92, [83 | __Ss], [__T | __Stack]);
-yeccpars2(83, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 26, [83 | __Ss], [__T | __Stack]);
-yeccpars2(83, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [83 | __Ss], [__T | __Stack]);
-yeccpars2(83, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(84, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1],
- yeccpars2(yeccgoto(let_vars, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(85, '->', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 86, [85 | __Ss], [__T | __Stack]);
-yeccpars2(85, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(86, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [86 | __Ss], [__T | __Stack]);
-yeccpars2(86, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(87, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 88, [87 | __Ss], [__T | __Stack]);
-yeccpars2(87, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(88, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 83, [88 | __Ss], [__T | __Stack]);
-yeccpars2(88, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 26, [88 | __Ss], [__T | __Stack]);
-yeccpars2(88, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [88 | __Ss], [__T | __Stack]);
-yeccpars2(88, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(89, '->', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 90, [89 | __Ss], [__T | __Stack]);
-yeccpars2(89, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(90, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [90 | __Ss], [__T | __Stack]);
-yeccpars2(90, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(91, __Cat, __Ss, [__10,__9,__8,__7,__6,__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = if length(__8) == 2 -> #c_try{arg = __2, vars = __4, body = __6, evars = __8, handler = __10}; true -> return_error(tok_line(__7),"expected 2 exception variables in 'try'") end,
- __Nss = lists:nthtail(9, __Ss),
- yeccpars2(yeccgoto(try_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(92, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [],
- __Nss = lists:nthtail(1, __Ss),
- yeccpars2(yeccgoto(let_vars, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(93, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 94, [93 | __Ss], [__T | __Stack]);
-yeccpars2(93, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(94, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __2,
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(let_vars, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(95, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 190, [95 | __Ss], [__T | __Stack]);
-yeccpars2(95, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(96, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 97, [96 | __Ss], [__T | __Stack]);
-yeccpars2(96, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [96 | __Ss], [__T | __Stack]);
-yeccpars2(96, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [96 | __Ss], [__T | __Stack]);
-yeccpars2(96, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [96 | __Ss], [__T | __Stack]);
-yeccpars2(96, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [96 | __Ss], [__T | __Stack]);
-yeccpars2(96, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [96 | __Ss], [__T | __Stack]);
-yeccpars2(96, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 115, [96 | __Ss], [__T | __Stack]);
-yeccpars2(96, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [96 | __Ss], [__T | __Stack]);
-yeccpars2(96, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 95, [96 | __Ss], [__T | __Stack]);
-yeccpars2(96, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 98, [96 | __Ss], [__T | __Stack]);
-yeccpars2(96, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 114, [96 | __Ss], [__T | __Stack]);
-yeccpars2(96, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(97, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 182, [97 | __Ss], [__T | __Stack]);
-yeccpars2(97, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [97 | __Ss], [__T | __Stack]);
-yeccpars2(97, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [97 | __Ss], [__T | __Stack]);
-yeccpars2(97, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [97 | __Ss], [__T | __Stack]);
-yeccpars2(97, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [97 | __Ss], [__T | __Stack]);
-yeccpars2(97, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [97 | __Ss], [__T | __Stack]);
-yeccpars2(97, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 115, [97 | __Ss], [__T | __Stack]);
-yeccpars2(97, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [97 | __Ss], [__T | __Stack]);
-yeccpars2(97, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 95, [97 | __Ss], [__T | __Stack]);
-yeccpars2(97, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 98, [97 | __Ss], [__T | __Stack]);
-yeccpars2(97, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 114, [97 | __Ss], [__T | __Stack]);
-yeccpars2(97, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(98, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [98 | __Ss], [__T | __Stack]);
-yeccpars2(98, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [98 | __Ss], [__T | __Stack]);
-yeccpars2(98, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [98 | __Ss], [__T | __Stack]);
-yeccpars2(98, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [98 | __Ss], [__T | __Stack]);
-yeccpars2(98, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [98 | __Ss], [__T | __Stack]);
-yeccpars2(98, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 115, [98 | __Ss], [__T | __Stack]);
-yeccpars2(98, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [98 | __Ss], [__T | __Stack]);
-yeccpars2(98, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 95, [98 | __Ss], [__T | __Stack]);
-yeccpars2(98, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 98, [98 | __Ss], [__T | __Stack]);
-yeccpars2(98, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 114, [98 | __Ss], [__T | __Stack]);
-yeccpars2(98, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 149, [98 | __Ss], [__T | __Stack]);
-yeccpars2(98, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(99, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [99 | __Ss], [__T | __Stack]);
-yeccpars2(99, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(100, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 97, [100 | __Ss], [__T | __Stack]);
-yeccpars2(100, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 96, [100 | __Ss], [__T | __Stack]);
-yeccpars2(100, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [100 | __Ss], [__T | __Stack]);
-yeccpars2(100, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [100 | __Ss], [__T | __Stack]);
-yeccpars2(100, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [100 | __Ss], [__T | __Stack]);
-yeccpars2(100, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [100 | __Ss], [__T | __Stack]);
-yeccpars2(100, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [100 | __Ss], [__T | __Stack]);
-yeccpars2(100, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [100 | __Ss], [__T | __Stack]);
-yeccpars2(100, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 95, [100 | __Ss], [__T | __Stack]);
-yeccpars2(100, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 98, [100 | __Ss], [__T | __Stack]);
-yeccpars2(100, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 114, [100 | __Ss], [__T | __Stack]);
-yeccpars2(100, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1],
- yeccpars2(yeccgoto(anno_clauses, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(101, 'after', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 99, [101 | __Ss], [__T | __Stack]);
-yeccpars2(101, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(102, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1],
- yeccpars2(yeccgoto(clause_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(103, '=', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 162, [103 | __Ss], [__T | __Stack]);
-yeccpars2(103, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(anno_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(104, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_atom{val = tok_val(__1)},
- yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(105, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(atomic_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(106, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(other_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(107, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(other_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(108, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(anno_clause, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(109, 'when', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 164, [109 | __Ss], [__T | __Stack]);
-yeccpars2(109, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(110, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(other_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(111, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(anno_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(112, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = begin
- {T,A} = __2, #c_receive{clauses = [], timeout = T, action = A}
- end,
- __Nss = lists:nthtail(1, __Ss),
- yeccpars2(yeccgoto(receive_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(113, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(other_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(114, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [114 | __Ss], [__T | __Stack]);
-yeccpars2(114, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [114 | __Ss], [__T | __Stack]);
-yeccpars2(114, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [114 | __Ss], [__T | __Stack]);
-yeccpars2(114, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [114 | __Ss], [__T | __Stack]);
-yeccpars2(114, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [114 | __Ss], [__T | __Stack]);
-yeccpars2(114, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 115, [114 | __Ss], [__T | __Stack]);
-yeccpars2(114, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [114 | __Ss], [__T | __Stack]);
-yeccpars2(114, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 95, [114 | __Ss], [__T | __Stack]);
-yeccpars2(114, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 98, [114 | __Ss], [__T | __Stack]);
-yeccpars2(114, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 114, [114 | __Ss], [__T | __Stack]);
-yeccpars2(114, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 118, [114 | __Ss], [__T | __Stack]);
-yeccpars2(114, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(115, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [115 | __Ss], [__T | __Stack]);
-yeccpars2(115, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [115 | __Ss], [__T | __Stack]);
-yeccpars2(115, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [115 | __Ss], [__T | __Stack]);
-yeccpars2(115, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [115 | __Ss], [__T | __Stack]);
-yeccpars2(115, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [115 | __Ss], [__T | __Stack]);
-yeccpars2(115, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 26, [115 | __Ss], [__T | __Stack]);
-yeccpars2(115, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [115 | __Ss], [__T | __Stack]);
-yeccpars2(115, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 95, [115 | __Ss], [__T | __Stack]);
-yeccpars2(115, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 98, [115 | __Ss], [__T | __Stack]);
-yeccpars2(115, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 114, [115 | __Ss], [__T | __Stack]);
-yeccpars2(115, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(116, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 120, [116 | __Ss], [__T | __Stack]);
-yeccpars2(116, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1],
- yeccpars2(yeccgoto(anno_patterns, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(117, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 119, [117 | __Ss], [__T | __Stack]);
-yeccpars2(117, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(118, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_tuple{es = []},
- __Nss = lists:nthtail(1, __Ss),
- yeccpars2(yeccgoto(tuple_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(119, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_tuple{es = __2},
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(tuple_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(120, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [120 | __Ss], [__T | __Stack]);
-yeccpars2(120, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [120 | __Ss], [__T | __Stack]);
-yeccpars2(120, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [120 | __Ss], [__T | __Stack]);
-yeccpars2(120, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [120 | __Ss], [__T | __Stack]);
-yeccpars2(120, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [120 | __Ss], [__T | __Stack]);
-yeccpars2(120, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 115, [120 | __Ss], [__T | __Stack]);
-yeccpars2(120, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [120 | __Ss], [__T | __Stack]);
-yeccpars2(120, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 95, [120 | __Ss], [__T | __Stack]);
-yeccpars2(120, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 98, [120 | __Ss], [__T | __Stack]);
-yeccpars2(120, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 114, [120 | __Ss], [__T | __Stack]);
-yeccpars2(120, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(121, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1|__3],
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(anno_patterns, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(122, '=', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 162, [122 | __Ss], [__T | __Stack]);
-yeccpars2(122, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(123, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 159, [123 | __Ss], [__T | __Stack]);
-yeccpars2(123, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(124, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 125, [124 | __Ss], [__T | __Stack]);
-yeccpars2(124, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(anno_variable, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(125, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 126, [125 | __Ss], [__T | __Stack]);
-yeccpars2(125, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(126, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 129, [126 | __Ss], [__T | __Stack]);
-yeccpars2(126, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 142, [126 | __Ss], [__T | __Stack]);
-yeccpars2(126, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 140, [126 | __Ss], [__T | __Stack]);
-yeccpars2(126, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 131, [126 | __Ss], [__T | __Stack]);
-yeccpars2(126, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 137, [126 | __Ss], [__T | __Stack]);
-yeccpars2(126, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 138, [126 | __Ss], [__T | __Stack]);
-yeccpars2(126, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 133, [126 | __Ss], [__T | __Stack]);
-yeccpars2(126, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 130, [126 | __Ss], [__T | __Stack]);
-yeccpars2(126, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(127, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 128, [127 | __Ss], [__T | __Stack]);
-yeccpars2(127, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(128, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = core_lib:set_anno(__2,__4),
- __Nss = lists:nthtail(4, __Ss),
- yeccpars2(yeccgoto(anno_variable, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(129, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 129, [129 | __Ss], [__T | __Stack]);
-yeccpars2(129, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 142, [129 | __Ss], [__T | __Stack]);
-yeccpars2(129, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 140, [129 | __Ss], [__T | __Stack]);
-yeccpars2(129, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 131, [129 | __Ss], [__T | __Stack]);
-yeccpars2(129, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 137, [129 | __Ss], [__T | __Stack]);
-yeccpars2(129, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 138, [129 | __Ss], [__T | __Stack]);
-yeccpars2(129, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 133, [129 | __Ss], [__T | __Stack]);
-yeccpars2(129, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 149, [129 | __Ss], [__T | __Stack]);
-yeccpars2(129, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(130, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [],
- __Nss = lists:nthtail(1, __Ss),
- yeccpars2(yeccgoto(annotation, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(131, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = tok_val(__1),
- yeccpars2(yeccgoto(atomic_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(132, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(133, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = tok_val(__1),
- yeccpars2(yeccgoto(atomic_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(134, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(135, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 147, [135 | __Ss], [__T | __Stack]);
-yeccpars2(135, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1],
- yeccpars2(yeccgoto(constants, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(136, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 146, [136 | __Ss], [__T | __Stack]);
-yeccpars2(136, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(137, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = tok_val(__1),
- yeccpars2(yeccgoto(atomic_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(138, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = tok_val(__1),
- yeccpars2(yeccgoto(atomic_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(139, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [],
- yeccpars2(yeccgoto(atomic_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(140, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = tok_val(__1),
- yeccpars2(yeccgoto(atomic_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(141, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(142, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 129, [142 | __Ss], [__T | __Stack]);
-yeccpars2(142, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 142, [142 | __Ss], [__T | __Stack]);
-yeccpars2(142, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 144, [142 | __Ss], [__T | __Stack]);
-yeccpars2(142, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 140, [142 | __Ss], [__T | __Stack]);
-yeccpars2(142, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 131, [142 | __Ss], [__T | __Stack]);
-yeccpars2(142, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 137, [142 | __Ss], [__T | __Stack]);
-yeccpars2(142, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 138, [142 | __Ss], [__T | __Stack]);
-yeccpars2(142, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 133, [142 | __Ss], [__T | __Stack]);
-yeccpars2(142, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(143, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 145, [143 | __Ss], [__T | __Stack]);
-yeccpars2(143, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(144, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = {},
- __Nss = lists:nthtail(1, __Ss),
- yeccpars2(yeccgoto(tuple_constant, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(145, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = list_to_tuple(__2),
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(tuple_constant, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(146, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __2,
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(annotation, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(147, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 129, [147 | __Ss], [__T | __Stack]);
-yeccpars2(147, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 142, [147 | __Ss], [__T | __Stack]);
-yeccpars2(147, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 140, [147 | __Ss], [__T | __Stack]);
-yeccpars2(147, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 131, [147 | __Ss], [__T | __Stack]);
-yeccpars2(147, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 137, [147 | __Ss], [__T | __Stack]);
-yeccpars2(147, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 138, [147 | __Ss], [__T | __Stack]);
-yeccpars2(147, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 133, [147 | __Ss], [__T | __Stack]);
-yeccpars2(147, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(148, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1|__3],
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(constants, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(149, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = {nil,tok_line(__1)},
- __Nss = lists:nthtail(1, __Ss),
- yeccpars2(yeccgoto(nil, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(150, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 151, [150 | __Ss], [__T | __Stack]);
-yeccpars2(150, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 154, [150 | __Ss], [__T | __Stack]);
-yeccpars2(150, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 152, [150 | __Ss], [__T | __Stack]);
-yeccpars2(150, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(151, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 129, [151 | __Ss], [__T | __Stack]);
-yeccpars2(151, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 142, [151 | __Ss], [__T | __Stack]);
-yeccpars2(151, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 140, [151 | __Ss], [__T | __Stack]);
-yeccpars2(151, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 131, [151 | __Ss], [__T | __Stack]);
-yeccpars2(151, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 137, [151 | __Ss], [__T | __Stack]);
-yeccpars2(151, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 138, [151 | __Ss], [__T | __Stack]);
-yeccpars2(151, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 133, [151 | __Ss], [__T | __Stack]);
-yeccpars2(151, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(152, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [],
- yeccpars2(yeccgoto(tail_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(153, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__2|__3],
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(cons_constant, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(154, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 129, [154 | __Ss], [__T | __Stack]);
-yeccpars2(154, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 142, [154 | __Ss], [__T | __Stack]);
-yeccpars2(154, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 140, [154 | __Ss], [__T | __Stack]);
-yeccpars2(154, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 131, [154 | __Ss], [__T | __Stack]);
-yeccpars2(154, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 137, [154 | __Ss], [__T | __Stack]);
-yeccpars2(154, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 138, [154 | __Ss], [__T | __Stack]);
-yeccpars2(154, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 133, [154 | __Ss], [__T | __Stack]);
-yeccpars2(154, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(155, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 156, [155 | __Ss], [__T | __Stack]);
-yeccpars2(155, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(156, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __2,
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(tail_constant, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(157, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 151, [157 | __Ss], [__T | __Stack]);
-yeccpars2(157, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 154, [157 | __Ss], [__T | __Stack]);
-yeccpars2(157, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 152, [157 | __Ss], [__T | __Stack]);
-yeccpars2(157, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(158, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__2|__3],
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(tail_constant, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(159, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 126, [159 | __Ss], [__T | __Stack]);
-yeccpars2(159, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(160, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 161, [160 | __Ss], [__T | __Stack]);
-yeccpars2(160, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(161, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = core_lib:set_anno(__2,__4),
- __Nss = lists:nthtail(4, __Ss),
- yeccpars2(yeccgoto(anno_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(162, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [162 | __Ss], [__T | __Stack]);
-yeccpars2(162, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [162 | __Ss], [__T | __Stack]);
-yeccpars2(162, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [162 | __Ss], [__T | __Stack]);
-yeccpars2(162, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [162 | __Ss], [__T | __Stack]);
-yeccpars2(162, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [162 | __Ss], [__T | __Stack]);
-yeccpars2(162, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 115, [162 | __Ss], [__T | __Stack]);
-yeccpars2(162, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [162 | __Ss], [__T | __Stack]);
-yeccpars2(162, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 95, [162 | __Ss], [__T | __Stack]);
-yeccpars2(162, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 98, [162 | __Ss], [__T | __Stack]);
-yeccpars2(162, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 114, [162 | __Ss], [__T | __Stack]);
-yeccpars2(162, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(163, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_alias{var = __1, pat = __3},
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(other_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(164, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [164 | __Ss], [__T | __Stack]);
-yeccpars2(164, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(165, '->', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 166, [165 | __Ss], [__T | __Stack]);
-yeccpars2(165, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(166, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [166 | __Ss], [__T | __Stack]);
-yeccpars2(166, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(167, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_clause{pats = __1, guard = __3, body = __5},
- __Nss = lists:nthtail(4, __Ss),
- yeccpars2(yeccgoto(clause, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(168, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = begin
- {T,A} = __3, #c_receive{clauses = __2, timeout = T, action = A}
- end,
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(receive_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(169, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1|__2],
- __Nss = lists:nthtail(1, __Ss),
- yeccpars2(yeccgoto(anno_clauses, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(170, '->', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 171, [170 | __Ss], [__T | __Stack]);
-yeccpars2(170, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(171, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [171 | __Ss], [__T | __Stack]);
-yeccpars2(171, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(172, __Cat, __Ss, [__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = {__2,__4},
- __Nss = lists:nthtail(3, __Ss),
- yeccpars2(yeccgoto(timeout, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(173, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 174, [173 | __Ss], [__T | __Stack]);
-yeccpars2(173, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 177, [173 | __Ss], [__T | __Stack]);
-yeccpars2(173, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 175, [173 | __Ss], [__T | __Stack]);
-yeccpars2(173, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(174, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [174 | __Ss], [__T | __Stack]);
-yeccpars2(174, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [174 | __Ss], [__T | __Stack]);
-yeccpars2(174, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [174 | __Ss], [__T | __Stack]);
-yeccpars2(174, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [174 | __Ss], [__T | __Stack]);
-yeccpars2(174, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [174 | __Ss], [__T | __Stack]);
-yeccpars2(174, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 115, [174 | __Ss], [__T | __Stack]);
-yeccpars2(174, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [174 | __Ss], [__T | __Stack]);
-yeccpars2(174, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 95, [174 | __Ss], [__T | __Stack]);
-yeccpars2(174, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 98, [174 | __Ss], [__T | __Stack]);
-yeccpars2(174, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 114, [174 | __Ss], [__T | __Stack]);
-yeccpars2(174, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(175, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_nil{},
- yeccpars2(yeccgoto(tail_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(176, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_cons{hd = __2, tl = __3},
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(cons_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(177, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [177 | __Ss], [__T | __Stack]);
-yeccpars2(177, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [177 | __Ss], [__T | __Stack]);
-yeccpars2(177, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [177 | __Ss], [__T | __Stack]);
-yeccpars2(177, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [177 | __Ss], [__T | __Stack]);
-yeccpars2(177, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [177 | __Ss], [__T | __Stack]);
-yeccpars2(177, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 115, [177 | __Ss], [__T | __Stack]);
-yeccpars2(177, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [177 | __Ss], [__T | __Stack]);
-yeccpars2(177, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 95, [177 | __Ss], [__T | __Stack]);
-yeccpars2(177, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 98, [177 | __Ss], [__T | __Stack]);
-yeccpars2(177, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 114, [177 | __Ss], [__T | __Stack]);
-yeccpars2(177, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(178, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 179, [178 | __Ss], [__T | __Stack]);
-yeccpars2(178, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(179, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __2,
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(tail_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(180, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 174, [180 | __Ss], [__T | __Stack]);
-yeccpars2(180, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 177, [180 | __Ss], [__T | __Stack]);
-yeccpars2(180, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 175, [180 | __Ss], [__T | __Stack]);
-yeccpars2(180, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(181, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_cons{hd = __2, tl = __3},
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(tail_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(182, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [],
- __Nss = lists:nthtail(1, __Ss),
- yeccpars2(yeccgoto(clause_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(183, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 184, [183 | __Ss], [__T | __Stack]);
-yeccpars2(183, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(184, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __2,
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(clause_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(185, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 187, [185 | __Ss], [__T | __Stack]);
-yeccpars2(185, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(186, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 159, [186 | __Ss], [__T | __Stack]);
-yeccpars2(186, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(anno_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(187, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 126, [187 | __Ss], [__T | __Stack]);
-yeccpars2(187, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(188, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 189, [188 | __Ss], [__T | __Stack]);
-yeccpars2(188, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(189, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = core_lib:set_anno(__2,__4),
- __Nss = lists:nthtail(4, __Ss),
- yeccpars2(yeccgoto(anno_clause, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(190, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 191, [190 | __Ss], [__T | __Stack]);
-yeccpars2(190, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 194, [190 | __Ss], [__T | __Stack]);
-yeccpars2(190, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(191, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 200, [191 | __Ss], [__T | __Stack]);
-yeccpars2(191, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(192, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 198, [192 | __Ss], [__T | __Stack]);
-yeccpars2(192, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1],
- yeccpars2(yeccgoto(segment_patterns, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(193, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 196, [193 | __Ss], [__T | __Stack]);
-yeccpars2(193, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(194, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 195, [194 | __Ss], [__T | __Stack]);
-yeccpars2(194, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(195, __Cat, __Ss, [__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_binary{segments = []},
- __Nss = lists:nthtail(3, __Ss),
- yeccpars2(yeccgoto(binary_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(196, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 197, [196 | __Ss], [__T | __Stack]);
-yeccpars2(196, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(197, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_binary{segments = __3},
- __Nss = lists:nthtail(4, __Ss),
- yeccpars2(yeccgoto(binary_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(198, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 191, [198 | __Ss], [__T | __Stack]);
-yeccpars2(198, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(199, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1|__3],
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(segment_patterns, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(200, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [200 | __Ss], [__T | __Stack]);
-yeccpars2(200, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [200 | __Ss], [__T | __Stack]);
-yeccpars2(200, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [200 | __Ss], [__T | __Stack]);
-yeccpars2(200, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [200 | __Ss], [__T | __Stack]);
-yeccpars2(200, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [200 | __Ss], [__T | __Stack]);
-yeccpars2(200, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 115, [200 | __Ss], [__T | __Stack]);
-yeccpars2(200, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [200 | __Ss], [__T | __Stack]);
-yeccpars2(200, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 95, [200 | __Ss], [__T | __Stack]);
-yeccpars2(200, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 98, [200 | __Ss], [__T | __Stack]);
-yeccpars2(200, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 114, [200 | __Ss], [__T | __Stack]);
-yeccpars2(200, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(201, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 202, [201 | __Ss], [__T | __Stack]);
-yeccpars2(201, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(202, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 203, [202 | __Ss], [__T | __Stack]);
-yeccpars2(202, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(203, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 205, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [203 | __Ss], [__T | __Stack]);
-yeccpars2(203, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(204, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = case __5 of [S,U,T,Fs] -> #c_bitstr{val = __3, size = S, unit = U, type = T, flags = Fs}; true -> return_error(tok_line(__1),"expected 4 arguments in binary segment") end,
- __Nss = lists:nthtail(4, __Ss),
- yeccpars2(yeccgoto(segment_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(205, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [],
- __Nss = lists:nthtail(1, __Ss),
- yeccpars2(yeccgoto(arg_list, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(206, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 207, [206 | __Ss], [__T | __Stack]);
-yeccpars2(206, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(207, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __2,
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(arg_list, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(208, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 203, [208 | __Ss], [__T | __Stack]);
-yeccpars2(208, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(209, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = begin
- Name = #c_atom{val = tok_val(__2)}, #c_primop{name = Name, args = __3}
- end,
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(primop_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(210, 'in', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 211, [210 | __Ss], [__T | __Stack]);
-yeccpars2(210, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(211, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [211 | __Ss], [__T | __Stack]);
-yeccpars2(211, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(212, __Cat, __Ss, [__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_letrec{defs = __2, body = __4},
- __Nss = lists:nthtail(3, __Ss),
- yeccpars2(yeccgoto(letrec_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(213, '=', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 214, [213 | __Ss], [__T | __Stack]);
-yeccpars2(213, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(214, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [214 | __Ss], [__T | __Stack]);
-yeccpars2(214, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(215, 'in', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 216, [215 | __Ss], [__T | __Stack]);
-yeccpars2(215, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(216, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [216 | __Ss], [__T | __Stack]);
-yeccpars2(216, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(217, __Cat, __Ss, [__6,__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_let{vars = __2, arg = __4, body = __6},
- __Nss = lists:nthtail(5, __Ss),
- yeccpars2(yeccgoto(let_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(218, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [218 | __Ss], [__T | __Stack]);
-yeccpars2(218, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(219, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_seq{arg = __2, body = __3},
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(sequence, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(220, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_catch{body = __2},
- __Nss = lists:nthtail(1, __Ss),
- yeccpars2(yeccgoto(catch_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(221, 'of', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 222, [221 | __Ss], [__T | __Stack]);
-yeccpars2(221, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(222, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 97, [222 | __Ss], [__T | __Stack]);
-yeccpars2(222, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 96, [222 | __Ss], [__T | __Stack]);
-yeccpars2(222, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [222 | __Ss], [__T | __Stack]);
-yeccpars2(222, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [222 | __Ss], [__T | __Stack]);
-yeccpars2(222, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [222 | __Ss], [__T | __Stack]);
-yeccpars2(222, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [222 | __Ss], [__T | __Stack]);
-yeccpars2(222, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [222 | __Ss], [__T | __Stack]);
-yeccpars2(222, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [222 | __Ss], [__T | __Stack]);
-yeccpars2(222, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 95, [222 | __Ss], [__T | __Stack]);
-yeccpars2(222, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 98, [222 | __Ss], [__T | __Stack]);
-yeccpars2(222, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 114, [222 | __Ss], [__T | __Stack]);
-yeccpars2(222, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(223, 'end', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 224, [223 | __Ss], [__T | __Stack]);
-yeccpars2(223, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(224, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_case{arg = __2, clauses = __4},
- __Nss = lists:nthtail(4, __Ss),
- yeccpars2(yeccgoto(case_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(225, ':', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 226, [225 | __Ss], [__T | __Stack]);
-yeccpars2(225, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(226, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [226 | __Ss], [__T | __Stack]);
-yeccpars2(226, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(227, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 203, [227 | __Ss], [__T | __Stack]);
-yeccpars2(227, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(228, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_call{module = __2, name = __4, args = __5},
- __Nss = lists:nthtail(4, __Ss),
- yeccpars2(yeccgoto(call_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(229, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 203, [229 | __Ss], [__T | __Stack]);
-yeccpars2(229, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(230, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_apply{op = __2, args = __3},
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(application_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(231, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 232, [231 | __Ss], [__T | __Stack]);
-yeccpars2(231, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 235, [231 | __Ss], [__T | __Stack]);
-yeccpars2(231, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 233, [231 | __Ss], [__T | __Stack]);
-yeccpars2(231, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(232, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [232 | __Ss], [__T | __Stack]);
-yeccpars2(232, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(233, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_nil{},
- yeccpars2(yeccgoto(tail, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(234, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_cons{hd = __2, tl = __3},
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(cons, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(235, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [235 | __Ss], [__T | __Stack]);
-yeccpars2(235, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(236, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 237, [236 | __Ss], [__T | __Stack]);
-yeccpars2(236, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(237, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __2,
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(tail, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(238, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 232, [238 | __Ss], [__T | __Stack]);
-yeccpars2(238, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 235, [238 | __Ss], [__T | __Stack]);
-yeccpars2(238, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 233, [238 | __Ss], [__T | __Stack]);
-yeccpars2(238, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(239, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_cons{hd = __2, tl = __3},
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(tail, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(240, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_values{es = []},
- __Nss = lists:nthtail(1, __Ss),
- yeccpars2(yeccgoto(expression, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(241, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 242, [241 | __Ss], [__T | __Stack]);
-yeccpars2(241, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(242, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_values{es = __2},
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(expression, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(243, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 244, [243 | __Ss], [__T | __Stack]);
-yeccpars2(243, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(244, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 126, [244 | __Ss], [__T | __Stack]);
-yeccpars2(244, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(245, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 246, [245 | __Ss], [__T | __Stack]);
-yeccpars2(245, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(246, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = core_lib:set_anno(__2,__4),
- __Nss = lists:nthtail(4, __Ss),
- yeccpars2(yeccgoto(anno_expression, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(247, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 248, [247 | __Ss], [__T | __Stack]);
-yeccpars2(247, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 251, [247 | __Ss], [__T | __Stack]);
-yeccpars2(247, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(248, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 257, [248 | __Ss], [__T | __Stack]);
-yeccpars2(248, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(249, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 255, [249 | __Ss], [__T | __Stack]);
-yeccpars2(249, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1],
- yeccpars2(yeccgoto(segments, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(250, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 253, [250 | __Ss], [__T | __Stack]);
-yeccpars2(250, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(251, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 252, [251 | __Ss], [__T | __Stack]);
-yeccpars2(251, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(252, __Cat, __Ss, [__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_binary{segments = []},
- __Nss = lists:nthtail(3, __Ss),
- yeccpars2(yeccgoto(binary, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(253, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 254, [253 | __Ss], [__T | __Stack]);
-yeccpars2(253, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(254, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_binary{segments = __3},
- __Nss = lists:nthtail(4, __Ss),
- yeccpars2(yeccgoto(binary, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(255, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 248, [255 | __Ss], [__T | __Stack]);
-yeccpars2(255, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(256, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1|__3],
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(segments, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(257, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [257 | __Ss], [__T | __Stack]);
-yeccpars2(257, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(258, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 259, [258 | __Ss], [__T | __Stack]);
-yeccpars2(258, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(259, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 260, [259 | __Ss], [__T | __Stack]);
-yeccpars2(259, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(260, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [260 | __Ss], [__T | __Stack]);
-yeccpars2(260, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(261, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 262, [261 | __Ss], [__T | __Stack]);
-yeccpars2(261, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(262, __Cat, __Ss, [__7,__6,__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = case __6 of [S,U,T,Fs] -> #c_bitstr{val = __3, size = S, unit = U, type = T, flags = Fs}; true -> return_error(tok_line(__1),"expected 4 arguments in binary segment") end,
- __Nss = lists:nthtail(6, __Ss),
- yeccpars2(yeccgoto(segment, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(263, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 26, [263 | __Ss], [__T | __Stack]);
-yeccpars2(263, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [263 | __Ss], [__T | __Stack]);
-yeccpars2(263, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(264, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1|__3],
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(anno_variables, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(265, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 65, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 48, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 70, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 63, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 44, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 40, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 46, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 60, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 58, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 23, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 52, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 41, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 34, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 37, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 74, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 36, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 35, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 30, [265 | __Ss], [__T | __Stack]);
-yeccpars2(265, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(266, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_fun{vars = [], body = __5},
- __Nss = lists:nthtail(4, __Ss),
- yeccpars2(yeccgoto(fun_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(267, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 125, [267 | __Ss], [__T | __Stack]);
-yeccpars2(267, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(268, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 269, [268 | __Ss], [__T | __Stack]);
-yeccpars2(268, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(269, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 126, [269 | __Ss], [__T | __Stack]);
-yeccpars2(269, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(270, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 271, [270 | __Ss], [__T | __Stack]);
-yeccpars2(270, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(271, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = core_lib:set_anno(__2,__4),
- __Nss = lists:nthtail(4, __Ss),
- yeccpars2(yeccgoto(anno_fun, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(272, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 273, [272 | __Ss], [__T | __Stack]);
-yeccpars2(272, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(273, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 126, [273 | __Ss], [__T | __Stack]);
-yeccpars2(273, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(274, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 275, [274 | __Ss], [__T | __Stack]);
-yeccpars2(274, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(275, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = core_lib:set_anno(__2,__4),
- __Nss = lists:nthtail(4, __Ss),
- yeccpars2(yeccgoto(anno_function_name, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(276, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 278, [276 | __Ss], [__T | __Stack]);
-yeccpars2(276, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 277, [276 | __Ss], [__T | __Stack]);
-yeccpars2(276, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(277, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [],
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(module_attribute, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(278, '=', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 284, [278 | __Ss], [__T | __Stack]);
-yeccpars2(278, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(279, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 282, [279 | __Ss], [__T | __Stack]);
-yeccpars2(279, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1],
- yeccpars2(yeccgoto(attribute_list, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(280, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 281, [280 | __Ss], [__T | __Stack]);
-yeccpars2(280, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(281, __Cat, __Ss, [__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __3,
- __Nss = lists:nthtail(3, __Ss),
- yeccpars2(yeccgoto(module_attribute, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(282, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 278, [282 | __Ss], [__T | __Stack]);
-yeccpars2(282, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(283, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1|__3],
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(attribute_list, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(284, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 285, [284 | __Ss], [__T | __Stack]);
-yeccpars2(284, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 290, [284 | __Ss], [__T | __Stack]);
-yeccpars2(284, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [284 | __Ss], [__T | __Stack]);
-yeccpars2(284, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [284 | __Ss], [__T | __Stack]);
-yeccpars2(284, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [284 | __Ss], [__T | __Stack]);
-yeccpars2(284, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [284 | __Ss], [__T | __Stack]);
-yeccpars2(284, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [284 | __Ss], [__T | __Stack]);
-yeccpars2(284, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(285, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 285, [285 | __Ss], [__T | __Stack]);
-yeccpars2(285, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 290, [285 | __Ss], [__T | __Stack]);
-yeccpars2(285, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [285 | __Ss], [__T | __Stack]);
-yeccpars2(285, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [285 | __Ss], [__T | __Stack]);
-yeccpars2(285, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [285 | __Ss], [__T | __Stack]);
-yeccpars2(285, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [285 | __Ss], [__T | __Stack]);
-yeccpars2(285, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [285 | __Ss], [__T | __Stack]);
-yeccpars2(285, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 149, [285 | __Ss], [__T | __Stack]);
-yeccpars2(285, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(286, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(287, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(288, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_def{name = #c_atom{val = tok_val(__1)}, val = __3},
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(attribute, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(289, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(290, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 285, [290 | __Ss], [__T | __Stack]);
-yeccpars2(290, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 290, [290 | __Ss], [__T | __Stack]);
-yeccpars2(290, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 293, [290 | __Ss], [__T | __Stack]);
-yeccpars2(290, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [290 | __Ss], [__T | __Stack]);
-yeccpars2(290, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [290 | __Ss], [__T | __Stack]);
-yeccpars2(290, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [290 | __Ss], [__T | __Stack]);
-yeccpars2(290, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [290 | __Ss], [__T | __Stack]);
-yeccpars2(290, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [290 | __Ss], [__T | __Stack]);
-yeccpars2(290, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(291, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 295, [291 | __Ss], [__T | __Stack]);
-yeccpars2(291, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1],
- yeccpars2(yeccgoto(literals, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(292, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 294, [292 | __Ss], [__T | __Stack]);
-yeccpars2(292, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(293, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_tuple{es = []},
- __Nss = lists:nthtail(1, __Ss),
- yeccpars2(yeccgoto(tuple_literal, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(294, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_tuple{es = __2},
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(tuple_literal, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(295, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 285, [295 | __Ss], [__T | __Stack]);
-yeccpars2(295, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 290, [295 | __Ss], [__T | __Stack]);
-yeccpars2(295, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [295 | __Ss], [__T | __Stack]);
-yeccpars2(295, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [295 | __Ss], [__T | __Stack]);
-yeccpars2(295, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [295 | __Ss], [__T | __Stack]);
-yeccpars2(295, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [295 | __Ss], [__T | __Stack]);
-yeccpars2(295, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [295 | __Ss], [__T | __Stack]);
-yeccpars2(295, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(296, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1|__3],
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(literals, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(297, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 298, [297 | __Ss], [__T | __Stack]);
-yeccpars2(297, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 301, [297 | __Ss], [__T | __Stack]);
-yeccpars2(297, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 299, [297 | __Ss], [__T | __Stack]);
-yeccpars2(297, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(298, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 285, [298 | __Ss], [__T | __Stack]);
-yeccpars2(298, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 290, [298 | __Ss], [__T | __Stack]);
-yeccpars2(298, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [298 | __Ss], [__T | __Stack]);
-yeccpars2(298, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [298 | __Ss], [__T | __Stack]);
-yeccpars2(298, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [298 | __Ss], [__T | __Stack]);
-yeccpars2(298, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [298 | __Ss], [__T | __Stack]);
-yeccpars2(298, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [298 | __Ss], [__T | __Stack]);
-yeccpars2(298, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(299, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_nil{},
- yeccpars2(yeccgoto(tail_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(300, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_cons{hd = __2, tl = __3},
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(cons_literal, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(301, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 285, [301 | __Ss], [__T | __Stack]);
-yeccpars2(301, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 290, [301 | __Ss], [__T | __Stack]);
-yeccpars2(301, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 69, [301 | __Ss], [__T | __Stack]);
-yeccpars2(301, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 104, [301 | __Ss], [__T | __Stack]);
-yeccpars2(301, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 54, [301 | __Ss], [__T | __Stack]);
-yeccpars2(301, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 57, [301 | __Ss], [__T | __Stack]);
-yeccpars2(301, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 50, [301 | __Ss], [__T | __Stack]);
-yeccpars2(301, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(302, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 303, [302 | __Ss], [__T | __Stack]);
-yeccpars2(302, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(303, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __2,
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(tail_literal, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(304, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 298, [304 | __Ss], [__T | __Stack]);
-yeccpars2(304, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 301, [304 | __Ss], [__T | __Stack]);
-yeccpars2(304, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 299, [304 | __Ss], [__T | __Stack]);
-yeccpars2(304, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(305, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_cons{hd = __2, tl = __3},
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(tail_literal, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(306, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [],
- __Nss = lists:nthtail(1, __Ss),
- yeccpars2(yeccgoto(module_export, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(307, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 311, [307 | __Ss], [__T | __Stack]);
-yeccpars2(307, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1],
- yeccpars2(yeccgoto(exported_names, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(308, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 310, [308 | __Ss], [__T | __Stack]);
-yeccpars2(308, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(309, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __1,
- yeccpars2(yeccgoto(exported_name, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(310, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = __2,
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(module_export, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(311, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 11, [311 | __Ss], [__T | __Stack]);
-yeccpars2(311, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(312, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = [__1|__3],
- __Nss = lists:nthtail(2, __Ss),
- yeccpars2(yeccgoto(exported_names, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(313, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 314, [313 | __Ss], [__T | __Stack]);
-yeccpars2(313, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(314, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 5, [314 | __Ss], [__T | __Stack]);
-yeccpars2(314, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(315, 'attributes', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 7, [315 | __Ss], [__T | __Stack]);
-yeccpars2(315, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(316, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 9, [316 | __Ss], [__T | __Stack]);
-yeccpars2(316, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 11, [316 | __Ss], [__T | __Stack]);
-yeccpars2(316, __Cat, __Ss, __Stack, __T, __Ts, __Tzr) ->
- __Val = [],
- yeccpars2(13, __Cat, [316 | __Ss], [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(317, 'end', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 318, [317 | __Ss], [__T | __Stack]);
-yeccpars2(317, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(318, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 319, [318 | __Ss], [__T | __Stack]);
-yeccpars2(318, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(319, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 126, [319 | __Ss], [__T | __Stack]);
-yeccpars2(319, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(320, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
- yeccpars1(__Ts, __Tzr, 321, [320 | __Ss], [__T | __Stack]);
-yeccpars2(320, _, _, _, __T, _, _) ->
- yeccerror(__T);
-yeccpars2(321, __Cat, __Ss, [__10,__9,__8,__7,__6,__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
- __Val = #c_module{anno = __9, name = tok_val(__3), exports = __4, attrs = __5, defs = __6},
- __Nss = lists:nthtail(9, __Ss),
- yeccpars2(yeccgoto(module_definition, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
-yeccpars2(__Other, _, _, _, _, _, _) ->
- exit({parser, __Other, missing_state_in_action_table}).
-
-yeccgoto(anno_clause, 65) ->
- 100;
-yeccgoto(anno_clause, 100) ->
- 100;
-yeccgoto(anno_clause, 222) ->
- 100;
-yeccgoto(anno_clauses, 65) ->
- 101;
-yeccgoto(anno_clauses, 100) ->
- 169;
-yeccgoto(anno_clauses, 222) ->
- 223;
-yeccgoto(anno_expression, 33) ->
- 38;
-yeccgoto(anno_expression, 36) ->
- 75;
-yeccgoto(anno_expression, 37) ->
- 231;
-yeccgoto(anno_expression, 40) ->
- 229;
-yeccgoto(anno_expression, 44) ->
- 225;
-yeccgoto(anno_expression, 46) ->
- 221;
-yeccgoto(anno_expression, 48) ->
- 220;
-yeccgoto(anno_expression, 52) ->
- 218;
-yeccgoto(anno_expression, 70) ->
- 81;
-yeccgoto(anno_expression, 74) ->
- 75;
-yeccgoto(anno_expression, 79) ->
- 75;
-yeccgoto(anno_expression, 86) ->
- 87;
-yeccgoto(anno_expression, 90) ->
- 91;
-yeccgoto(anno_expression, 99) ->
- 170;
-yeccgoto(anno_expression, 164) ->
- 165;
-yeccgoto(anno_expression, 166) ->
- 167;
-yeccgoto(anno_expression, 171) ->
- 172;
-yeccgoto(anno_expression, 203) ->
- 75;
-yeccgoto(anno_expression, 211) ->
- 212;
-yeccgoto(anno_expression, 214) ->
- 215;
-yeccgoto(anno_expression, 216) ->
- 217;
-yeccgoto(anno_expression, 218) ->
- 219;
-yeccgoto(anno_expression, 226) ->
- 227;
-yeccgoto(anno_expression, 232) ->
- 238;
-yeccgoto(anno_expression, 235) ->
- 236;
-yeccgoto(anno_expression, 257) ->
- 258;
-yeccgoto(anno_expression, 260) ->
- 75;
-yeccgoto(anno_expression, 265) ->
- 266;
-yeccgoto(anno_expressions, 36) ->
- 241;
-yeccgoto(anno_expressions, 74) ->
- 76;
-yeccgoto(anno_expressions, 79) ->
- 80;
-yeccgoto(anno_expressions, 203) ->
- 206;
-yeccgoto(anno_expressions, 260) ->
- 261;
-yeccgoto(anno_fun, 20) ->
- 22;
-yeccgoto(anno_function_name, 8) ->
- 10;
-yeccgoto(anno_function_name, 12) ->
- 10;
-yeccgoto(anno_function_name, 60) ->
- 10;
-yeccgoto(anno_function_name, 316) ->
- 10;
-yeccgoto(anno_pattern, 65) ->
- 102;
-yeccgoto(anno_pattern, 96) ->
- 102;
-yeccgoto(anno_pattern, 97) ->
- 116;
-yeccgoto(anno_pattern, 98) ->
- 173;
-yeccgoto(anno_pattern, 100) ->
- 102;
-yeccgoto(anno_pattern, 114) ->
- 116;
-yeccgoto(anno_pattern, 120) ->
- 116;
-yeccgoto(anno_pattern, 162) ->
- 163;
-yeccgoto(anno_pattern, 174) ->
- 180;
-yeccgoto(anno_pattern, 177) ->
- 178;
-yeccgoto(anno_pattern, 200) ->
- 201;
-yeccgoto(anno_pattern, 222) ->
- 102;
-yeccgoto(anno_patterns, 97) ->
- 183;
-yeccgoto(anno_patterns, 114) ->
- 117;
-yeccgoto(anno_patterns, 120) ->
- 121;
-yeccgoto(anno_variable, 25) ->
- 28;
-yeccgoto(anno_variable, 58) ->
- 84;
-yeccgoto(anno_variable, 65) ->
- 103;
-yeccgoto(anno_variable, 82) ->
- 84;
-yeccgoto(anno_variable, 83) ->
- 28;
-yeccgoto(anno_variable, 88) ->
- 84;
-yeccgoto(anno_variable, 96) ->
- 103;
-yeccgoto(anno_variable, 97) ->
- 103;
-yeccgoto(anno_variable, 98) ->
- 103;
-yeccgoto(anno_variable, 100) ->
- 103;
-yeccgoto(anno_variable, 114) ->
- 103;
-yeccgoto(anno_variable, 115) ->
- 122;
-yeccgoto(anno_variable, 120) ->
- 103;
-yeccgoto(anno_variable, 162) ->
- 103;
-yeccgoto(anno_variable, 174) ->
- 103;
-yeccgoto(anno_variable, 177) ->
- 103;
-yeccgoto(anno_variable, 200) ->
- 103;
-yeccgoto(anno_variable, 222) ->
- 103;
-yeccgoto(anno_variable, 263) ->
- 28;
-yeccgoto(anno_variables, 25) ->
- 29;
-yeccgoto(anno_variables, 83) ->
- 93;
-yeccgoto(anno_variables, 263) ->
- 264;
-yeccgoto(annotation, 125) ->
- 127;
-yeccgoto(annotation, 159) ->
- 160;
-yeccgoto(annotation, 187) ->
- 188;
-yeccgoto(annotation, 244) ->
- 245;
-yeccgoto(annotation, 269) ->
- 270;
-yeccgoto(annotation, 273) ->
- 274;
-yeccgoto(annotation, 319) ->
- 320;
-yeccgoto(application_expr, 33) ->
- 39;
-yeccgoto(application_expr, 35) ->
- 39;
-yeccgoto(application_expr, 36) ->
- 39;
-yeccgoto(application_expr, 37) ->
- 39;
-yeccgoto(application_expr, 40) ->
- 39;
-yeccgoto(application_expr, 44) ->
- 39;
-yeccgoto(application_expr, 46) ->
- 39;
-yeccgoto(application_expr, 48) ->
- 39;
-yeccgoto(application_expr, 52) ->
- 39;
-yeccgoto(application_expr, 70) ->
- 39;
-yeccgoto(application_expr, 74) ->
- 39;
-yeccgoto(application_expr, 79) ->
- 39;
-yeccgoto(application_expr, 86) ->
- 39;
-yeccgoto(application_expr, 90) ->
- 39;
-yeccgoto(application_expr, 99) ->
- 39;
-yeccgoto(application_expr, 164) ->
- 39;
-yeccgoto(application_expr, 166) ->
- 39;
-yeccgoto(application_expr, 171) ->
- 39;
-yeccgoto(application_expr, 203) ->
- 39;
-yeccgoto(application_expr, 211) ->
- 39;
-yeccgoto(application_expr, 214) ->
- 39;
-yeccgoto(application_expr, 216) ->
- 39;
-yeccgoto(application_expr, 218) ->
- 39;
-yeccgoto(application_expr, 226) ->
- 39;
-yeccgoto(application_expr, 232) ->
- 39;
-yeccgoto(application_expr, 235) ->
- 39;
-yeccgoto(application_expr, 257) ->
- 39;
-yeccgoto(application_expr, 260) ->
- 39;
-yeccgoto(application_expr, 265) ->
- 39;
-yeccgoto(arg_list, 202) ->
- 204;
-yeccgoto(arg_list, 208) ->
- 209;
-yeccgoto(arg_list, 227) ->
- 228;
-yeccgoto(arg_list, 229) ->
- 230;
-yeccgoto(atomic_constant, 126) ->
- 132;
-yeccgoto(atomic_constant, 129) ->
- 132;
-yeccgoto(atomic_constant, 142) ->
- 132;
-yeccgoto(atomic_constant, 147) ->
- 132;
-yeccgoto(atomic_constant, 151) ->
- 132;
-yeccgoto(atomic_constant, 154) ->
- 132;
-yeccgoto(atomic_literal, 33) ->
- 42;
-yeccgoto(atomic_literal, 35) ->
- 42;
-yeccgoto(atomic_literal, 36) ->
- 42;
-yeccgoto(atomic_literal, 37) ->
- 42;
-yeccgoto(atomic_literal, 40) ->
- 42;
-yeccgoto(atomic_literal, 44) ->
- 42;
-yeccgoto(atomic_literal, 46) ->
- 42;
-yeccgoto(atomic_literal, 48) ->
- 42;
-yeccgoto(atomic_literal, 52) ->
- 42;
-yeccgoto(atomic_literal, 65) ->
- 105;
-yeccgoto(atomic_literal, 70) ->
- 42;
-yeccgoto(atomic_literal, 74) ->
- 42;
-yeccgoto(atomic_literal, 79) ->
- 42;
-yeccgoto(atomic_literal, 86) ->
- 42;
-yeccgoto(atomic_literal, 90) ->
- 42;
-yeccgoto(atomic_literal, 96) ->
- 105;
-yeccgoto(atomic_literal, 97) ->
- 105;
-yeccgoto(atomic_literal, 98) ->
- 105;
-yeccgoto(atomic_literal, 99) ->
- 42;
-yeccgoto(atomic_literal, 100) ->
- 105;
-yeccgoto(atomic_literal, 114) ->
- 105;
-yeccgoto(atomic_literal, 115) ->
- 105;
-yeccgoto(atomic_literal, 120) ->
- 105;
-yeccgoto(atomic_literal, 162) ->
- 105;
-yeccgoto(atomic_literal, 164) ->
- 42;
-yeccgoto(atomic_literal, 166) ->
- 42;
-yeccgoto(atomic_literal, 171) ->
- 42;
-yeccgoto(atomic_literal, 174) ->
- 105;
-yeccgoto(atomic_literal, 177) ->
- 105;
-yeccgoto(atomic_literal, 200) ->
- 105;
-yeccgoto(atomic_literal, 203) ->
- 42;
-yeccgoto(atomic_literal, 211) ->
- 42;
-yeccgoto(atomic_literal, 214) ->
- 42;
-yeccgoto(atomic_literal, 216) ->
- 42;
-yeccgoto(atomic_literal, 218) ->
- 42;
-yeccgoto(atomic_literal, 222) ->
- 105;
-yeccgoto(atomic_literal, 226) ->
- 42;
-yeccgoto(atomic_literal, 232) ->
- 42;
-yeccgoto(atomic_literal, 235) ->
- 42;
-yeccgoto(atomic_literal, 257) ->
- 42;
-yeccgoto(atomic_literal, 260) ->
- 42;
-yeccgoto(atomic_literal, 265) ->
- 42;
-yeccgoto(atomic_literal, 284) ->
- 286;
-yeccgoto(atomic_literal, 285) ->
- 286;
-yeccgoto(atomic_literal, 290) ->
- 286;
-yeccgoto(atomic_literal, 295) ->
- 286;
-yeccgoto(atomic_literal, 298) ->
- 286;
-yeccgoto(atomic_literal, 301) ->
- 286;
-yeccgoto(atomic_pattern, 65) ->
- 106;
-yeccgoto(atomic_pattern, 96) ->
- 106;
-yeccgoto(atomic_pattern, 97) ->
- 106;
-yeccgoto(atomic_pattern, 98) ->
- 106;
-yeccgoto(atomic_pattern, 100) ->
- 106;
-yeccgoto(atomic_pattern, 114) ->
- 106;
-yeccgoto(atomic_pattern, 115) ->
- 106;
-yeccgoto(atomic_pattern, 120) ->
- 106;
-yeccgoto(atomic_pattern, 162) ->
- 106;
-yeccgoto(atomic_pattern, 174) ->
- 106;
-yeccgoto(atomic_pattern, 177) ->
- 106;
-yeccgoto(atomic_pattern, 200) ->
- 106;
-yeccgoto(atomic_pattern, 222) ->
- 106;
-yeccgoto(attribute, 276) ->
- 279;
-yeccgoto(attribute, 282) ->
- 279;
-yeccgoto(attribute_list, 276) ->
- 280;
-yeccgoto(attribute_list, 282) ->
- 283;
-yeccgoto(binary, 33) ->
- 43;
-yeccgoto(binary, 35) ->
- 43;
-yeccgoto(binary, 36) ->
- 43;
-yeccgoto(binary, 37) ->
- 43;
-yeccgoto(binary, 40) ->
- 43;
-yeccgoto(binary, 44) ->
- 43;
-yeccgoto(binary, 46) ->
- 43;
-yeccgoto(binary, 48) ->
- 43;
-yeccgoto(binary, 52) ->
- 43;
-yeccgoto(binary, 70) ->
- 43;
-yeccgoto(binary, 74) ->
- 43;
-yeccgoto(binary, 79) ->
- 43;
-yeccgoto(binary, 86) ->
- 43;
-yeccgoto(binary, 90) ->
- 43;
-yeccgoto(binary, 99) ->
- 43;
-yeccgoto(binary, 164) ->
- 43;
-yeccgoto(binary, 166) ->
- 43;
-yeccgoto(binary, 171) ->
- 43;
-yeccgoto(binary, 203) ->
- 43;
-yeccgoto(binary, 211) ->
- 43;
-yeccgoto(binary, 214) ->
- 43;
-yeccgoto(binary, 216) ->
- 43;
-yeccgoto(binary, 218) ->
- 43;
-yeccgoto(binary, 226) ->
- 43;
-yeccgoto(binary, 232) ->
- 43;
-yeccgoto(binary, 235) ->
- 43;
-yeccgoto(binary, 257) ->
- 43;
-yeccgoto(binary, 260) ->
- 43;
-yeccgoto(binary, 265) ->
- 43;
-yeccgoto(binary_pattern, 65) ->
- 107;
-yeccgoto(binary_pattern, 96) ->
- 107;
-yeccgoto(binary_pattern, 97) ->
- 107;
-yeccgoto(binary_pattern, 98) ->
- 107;
-yeccgoto(binary_pattern, 100) ->
- 107;
-yeccgoto(binary_pattern, 114) ->
- 107;
-yeccgoto(binary_pattern, 115) ->
- 107;
-yeccgoto(binary_pattern, 120) ->
- 107;
-yeccgoto(binary_pattern, 162) ->
- 107;
-yeccgoto(binary_pattern, 174) ->
- 107;
-yeccgoto(binary_pattern, 177) ->
- 107;
-yeccgoto(binary_pattern, 200) ->
- 107;
-yeccgoto(binary_pattern, 222) ->
- 107;
-yeccgoto(call_expr, 33) ->
- 45;
-yeccgoto(call_expr, 35) ->
- 45;
-yeccgoto(call_expr, 36) ->
- 45;
-yeccgoto(call_expr, 37) ->
- 45;
-yeccgoto(call_expr, 40) ->
- 45;
-yeccgoto(call_expr, 44) ->
- 45;
-yeccgoto(call_expr, 46) ->
- 45;
-yeccgoto(call_expr, 48) ->
- 45;
-yeccgoto(call_expr, 52) ->
- 45;
-yeccgoto(call_expr, 70) ->
- 45;
-yeccgoto(call_expr, 74) ->
- 45;
-yeccgoto(call_expr, 79) ->
- 45;
-yeccgoto(call_expr, 86) ->
- 45;
-yeccgoto(call_expr, 90) ->
- 45;
-yeccgoto(call_expr, 99) ->
- 45;
-yeccgoto(call_expr, 164) ->
- 45;
-yeccgoto(call_expr, 166) ->
- 45;
-yeccgoto(call_expr, 171) ->
- 45;
-yeccgoto(call_expr, 203) ->
- 45;
-yeccgoto(call_expr, 211) ->
- 45;
-yeccgoto(call_expr, 214) ->
- 45;
-yeccgoto(call_expr, 216) ->
- 45;
-yeccgoto(call_expr, 218) ->
- 45;
-yeccgoto(call_expr, 226) ->
- 45;
-yeccgoto(call_expr, 232) ->
- 45;
-yeccgoto(call_expr, 235) ->
- 45;
-yeccgoto(call_expr, 257) ->
- 45;
-yeccgoto(call_expr, 260) ->
- 45;
-yeccgoto(call_expr, 265) ->
- 45;
-yeccgoto(case_expr, 33) ->
- 47;
-yeccgoto(case_expr, 35) ->
- 47;
-yeccgoto(case_expr, 36) ->
- 47;
-yeccgoto(case_expr, 37) ->
- 47;
-yeccgoto(case_expr, 40) ->
- 47;
-yeccgoto(case_expr, 44) ->
- 47;
-yeccgoto(case_expr, 46) ->
- 47;
-yeccgoto(case_expr, 48) ->
- 47;
-yeccgoto(case_expr, 52) ->
- 47;
-yeccgoto(case_expr, 70) ->
- 47;
-yeccgoto(case_expr, 74) ->
- 47;
-yeccgoto(case_expr, 79) ->
- 47;
-yeccgoto(case_expr, 86) ->
- 47;
-yeccgoto(case_expr, 90) ->
- 47;
-yeccgoto(case_expr, 99) ->
- 47;
-yeccgoto(case_expr, 164) ->
- 47;
-yeccgoto(case_expr, 166) ->
- 47;
-yeccgoto(case_expr, 171) ->
- 47;
-yeccgoto(case_expr, 203) ->
- 47;
-yeccgoto(case_expr, 211) ->
- 47;
-yeccgoto(case_expr, 214) ->
- 47;
-yeccgoto(case_expr, 216) ->
- 47;
-yeccgoto(case_expr, 218) ->
- 47;
-yeccgoto(case_expr, 226) ->
- 47;
-yeccgoto(case_expr, 232) ->
- 47;
-yeccgoto(case_expr, 235) ->
- 47;
-yeccgoto(case_expr, 257) ->
- 47;
-yeccgoto(case_expr, 260) ->
- 47;
-yeccgoto(case_expr, 265) ->
- 47;
-yeccgoto(catch_expr, 33) ->
- 49;
-yeccgoto(catch_expr, 35) ->
- 49;
-yeccgoto(catch_expr, 36) ->
- 49;
-yeccgoto(catch_expr, 37) ->
- 49;
-yeccgoto(catch_expr, 40) ->
- 49;
-yeccgoto(catch_expr, 44) ->
- 49;
-yeccgoto(catch_expr, 46) ->
- 49;
-yeccgoto(catch_expr, 48) ->
- 49;
-yeccgoto(catch_expr, 52) ->
- 49;
-yeccgoto(catch_expr, 70) ->
- 49;
-yeccgoto(catch_expr, 74) ->
- 49;
-yeccgoto(catch_expr, 79) ->
- 49;
-yeccgoto(catch_expr, 86) ->
- 49;
-yeccgoto(catch_expr, 90) ->
- 49;
-yeccgoto(catch_expr, 99) ->
- 49;
-yeccgoto(catch_expr, 164) ->
- 49;
-yeccgoto(catch_expr, 166) ->
- 49;
-yeccgoto(catch_expr, 171) ->
- 49;
-yeccgoto(catch_expr, 203) ->
- 49;
-yeccgoto(catch_expr, 211) ->
- 49;
-yeccgoto(catch_expr, 214) ->
- 49;
-yeccgoto(catch_expr, 216) ->
- 49;
-yeccgoto(catch_expr, 218) ->
- 49;
-yeccgoto(catch_expr, 226) ->
- 49;
-yeccgoto(catch_expr, 232) ->
- 49;
-yeccgoto(catch_expr, 235) ->
- 49;
-yeccgoto(catch_expr, 257) ->
- 49;
-yeccgoto(catch_expr, 260) ->
- 49;
-yeccgoto(catch_expr, 265) ->
- 49;
-yeccgoto(clause, 65) ->
- 108;
-yeccgoto(clause, 96) ->
- 185;
-yeccgoto(clause, 100) ->
- 108;
-yeccgoto(clause, 222) ->
- 108;
-yeccgoto(clause_pattern, 65) ->
- 109;
-yeccgoto(clause_pattern, 96) ->
- 109;
-yeccgoto(clause_pattern, 100) ->
- 109;
-yeccgoto(clause_pattern, 222) ->
- 109;
-yeccgoto(cons, 33) ->
- 51;
-yeccgoto(cons, 35) ->
- 51;
-yeccgoto(cons, 36) ->
- 51;
-yeccgoto(cons, 37) ->
- 51;
-yeccgoto(cons, 40) ->
- 51;
-yeccgoto(cons, 44) ->
- 51;
-yeccgoto(cons, 46) ->
- 51;
-yeccgoto(cons, 48) ->
- 51;
-yeccgoto(cons, 52) ->
- 51;
-yeccgoto(cons, 70) ->
- 51;
-yeccgoto(cons, 74) ->
- 51;
-yeccgoto(cons, 79) ->
- 51;
-yeccgoto(cons, 86) ->
- 51;
-yeccgoto(cons, 90) ->
- 51;
-yeccgoto(cons, 99) ->
- 51;
-yeccgoto(cons, 164) ->
- 51;
-yeccgoto(cons, 166) ->
- 51;
-yeccgoto(cons, 171) ->
- 51;
-yeccgoto(cons, 203) ->
- 51;
-yeccgoto(cons, 211) ->
- 51;
-yeccgoto(cons, 214) ->
- 51;
-yeccgoto(cons, 216) ->
- 51;
-yeccgoto(cons, 218) ->
- 51;
-yeccgoto(cons, 226) ->
- 51;
-yeccgoto(cons, 232) ->
- 51;
-yeccgoto(cons, 235) ->
- 51;
-yeccgoto(cons, 257) ->
- 51;
-yeccgoto(cons, 260) ->
- 51;
-yeccgoto(cons, 265) ->
- 51;
-yeccgoto(cons_constant, 126) ->
- 134;
-yeccgoto(cons_constant, 129) ->
- 134;
-yeccgoto(cons_constant, 142) ->
- 134;
-yeccgoto(cons_constant, 147) ->
- 134;
-yeccgoto(cons_constant, 151) ->
- 134;
-yeccgoto(cons_constant, 154) ->
- 134;
-yeccgoto(cons_literal, 284) ->
- 287;
-yeccgoto(cons_literal, 285) ->
- 287;
-yeccgoto(cons_literal, 290) ->
- 287;
-yeccgoto(cons_literal, 295) ->
- 287;
-yeccgoto(cons_literal, 298) ->
- 287;
-yeccgoto(cons_literal, 301) ->
- 287;
-yeccgoto(cons_pattern, 65) ->
- 110;
-yeccgoto(cons_pattern, 96) ->
- 110;
-yeccgoto(cons_pattern, 97) ->
- 110;
-yeccgoto(cons_pattern, 98) ->
- 110;
-yeccgoto(cons_pattern, 100) ->
- 110;
-yeccgoto(cons_pattern, 114) ->
- 110;
-yeccgoto(cons_pattern, 115) ->
- 110;
-yeccgoto(cons_pattern, 120) ->
- 110;
-yeccgoto(cons_pattern, 162) ->
- 110;
-yeccgoto(cons_pattern, 174) ->
- 110;
-yeccgoto(cons_pattern, 177) ->
- 110;
-yeccgoto(cons_pattern, 200) ->
- 110;
-yeccgoto(cons_pattern, 222) ->
- 110;
-yeccgoto(constant, 126) ->
- 135;
-yeccgoto(constant, 129) ->
- 150;
-yeccgoto(constant, 142) ->
- 135;
-yeccgoto(constant, 147) ->
- 135;
-yeccgoto(constant, 151) ->
- 157;
-yeccgoto(constant, 154) ->
- 155;
-yeccgoto(constants, 126) ->
- 136;
-yeccgoto(constants, 142) ->
- 143;
-yeccgoto(constants, 147) ->
- 148;
-yeccgoto(exported_name, 5) ->
- 307;
-yeccgoto(exported_name, 311) ->
- 307;
-yeccgoto(exported_names, 5) ->
- 308;
-yeccgoto(exported_names, 311) ->
- 312;
-yeccgoto(expression, 33) ->
- 53;
-yeccgoto(expression, 35) ->
- 243;
-yeccgoto(expression, 36) ->
- 53;
-yeccgoto(expression, 37) ->
- 53;
-yeccgoto(expression, 40) ->
- 53;
-yeccgoto(expression, 44) ->
- 53;
-yeccgoto(expression, 46) ->
- 53;
-yeccgoto(expression, 48) ->
- 53;
-yeccgoto(expression, 52) ->
- 53;
-yeccgoto(expression, 70) ->
- 53;
-yeccgoto(expression, 74) ->
- 53;
-yeccgoto(expression, 79) ->
- 53;
-yeccgoto(expression, 86) ->
- 53;
-yeccgoto(expression, 90) ->
- 53;
-yeccgoto(expression, 99) ->
- 53;
-yeccgoto(expression, 164) ->
- 53;
-yeccgoto(expression, 166) ->
- 53;
-yeccgoto(expression, 171) ->
- 53;
-yeccgoto(expression, 203) ->
- 53;
-yeccgoto(expression, 211) ->
- 53;
-yeccgoto(expression, 214) ->
- 53;
-yeccgoto(expression, 216) ->
- 53;
-yeccgoto(expression, 218) ->
- 53;
-yeccgoto(expression, 226) ->
- 53;
-yeccgoto(expression, 232) ->
- 53;
-yeccgoto(expression, 235) ->
- 53;
-yeccgoto(expression, 257) ->
- 53;
-yeccgoto(expression, 260) ->
- 53;
-yeccgoto(expression, 265) ->
- 53;
-yeccgoto(fun_expr, 20) ->
- 24;
-yeccgoto(fun_expr, 21) ->
- 268;
-yeccgoto(fun_expr, 33) ->
- 55;
-yeccgoto(fun_expr, 35) ->
- 55;
-yeccgoto(fun_expr, 36) ->
- 55;
-yeccgoto(fun_expr, 37) ->
- 55;
-yeccgoto(fun_expr, 40) ->
- 55;
-yeccgoto(fun_expr, 44) ->
- 55;
-yeccgoto(fun_expr, 46) ->
- 55;
-yeccgoto(fun_expr, 48) ->
- 55;
-yeccgoto(fun_expr, 52) ->
- 55;
-yeccgoto(fun_expr, 70) ->
- 55;
-yeccgoto(fun_expr, 74) ->
- 55;
-yeccgoto(fun_expr, 79) ->
- 55;
-yeccgoto(fun_expr, 86) ->
- 55;
-yeccgoto(fun_expr, 90) ->
- 55;
-yeccgoto(fun_expr, 99) ->
- 55;
-yeccgoto(fun_expr, 164) ->
- 55;
-yeccgoto(fun_expr, 166) ->
- 55;
-yeccgoto(fun_expr, 171) ->
- 55;
-yeccgoto(fun_expr, 203) ->
- 55;
-yeccgoto(fun_expr, 211) ->
- 55;
-yeccgoto(fun_expr, 214) ->
- 55;
-yeccgoto(fun_expr, 216) ->
- 55;
-yeccgoto(fun_expr, 218) ->
- 55;
-yeccgoto(fun_expr, 226) ->
- 55;
-yeccgoto(fun_expr, 232) ->
- 55;
-yeccgoto(fun_expr, 235) ->
- 55;
-yeccgoto(fun_expr, 257) ->
- 55;
-yeccgoto(fun_expr, 260) ->
- 55;
-yeccgoto(fun_expr, 265) ->
- 55;
-yeccgoto(function_definition, 8) ->
- 12;
-yeccgoto(function_definition, 12) ->
- 12;
-yeccgoto(function_definition, 60) ->
- 12;
-yeccgoto(function_definition, 316) ->
- 12;
-yeccgoto(function_definitions, 8) ->
- 13;
-yeccgoto(function_definitions, 12) ->
- 17;
-yeccgoto(function_definitions, 60) ->
- 210;
-yeccgoto(function_definitions, 316) ->
- 13;
-yeccgoto(function_name, 5) ->
- 309;
-yeccgoto(function_name, 8) ->
- 14;
-yeccgoto(function_name, 9) ->
- 272;
-yeccgoto(function_name, 12) ->
- 14;
-yeccgoto(function_name, 33) ->
- 56;
-yeccgoto(function_name, 35) ->
- 56;
-yeccgoto(function_name, 36) ->
- 56;
-yeccgoto(function_name, 37) ->
- 56;
-yeccgoto(function_name, 40) ->
- 56;
-yeccgoto(function_name, 44) ->
- 56;
-yeccgoto(function_name, 46) ->
- 56;
-yeccgoto(function_name, 48) ->
- 56;
-yeccgoto(function_name, 52) ->
- 56;
-yeccgoto(function_name, 60) ->
- 14;
-yeccgoto(function_name, 70) ->
- 56;
-yeccgoto(function_name, 74) ->
- 56;
-yeccgoto(function_name, 79) ->
- 56;
-yeccgoto(function_name, 86) ->
- 56;
-yeccgoto(function_name, 90) ->
- 56;
-yeccgoto(function_name, 99) ->
- 56;
-yeccgoto(function_name, 164) ->
- 56;
-yeccgoto(function_name, 166) ->
- 56;
-yeccgoto(function_name, 171) ->
- 56;
-yeccgoto(function_name, 203) ->
- 56;
-yeccgoto(function_name, 211) ->
- 56;
-yeccgoto(function_name, 214) ->
- 56;
-yeccgoto(function_name, 216) ->
- 56;
-yeccgoto(function_name, 218) ->
- 56;
-yeccgoto(function_name, 226) ->
- 56;
-yeccgoto(function_name, 232) ->
- 56;
-yeccgoto(function_name, 235) ->
- 56;
-yeccgoto(function_name, 257) ->
- 56;
-yeccgoto(function_name, 260) ->
- 56;
-yeccgoto(function_name, 265) ->
- 56;
-yeccgoto(function_name, 311) ->
- 309;
-yeccgoto(function_name, 316) ->
- 14;
-yeccgoto(let_expr, 33) ->
- 59;
-yeccgoto(let_expr, 35) ->
- 59;
-yeccgoto(let_expr, 36) ->
- 59;
-yeccgoto(let_expr, 37) ->
- 59;
-yeccgoto(let_expr, 40) ->
- 59;
-yeccgoto(let_expr, 44) ->
- 59;
-yeccgoto(let_expr, 46) ->
- 59;
-yeccgoto(let_expr, 48) ->
- 59;
-yeccgoto(let_expr, 52) ->
- 59;
-yeccgoto(let_expr, 70) ->
- 59;
-yeccgoto(let_expr, 74) ->
- 59;
-yeccgoto(let_expr, 79) ->
- 59;
-yeccgoto(let_expr, 86) ->
- 59;
-yeccgoto(let_expr, 90) ->
- 59;
-yeccgoto(let_expr, 99) ->
- 59;
-yeccgoto(let_expr, 164) ->
- 59;
-yeccgoto(let_expr, 166) ->
- 59;
-yeccgoto(let_expr, 171) ->
- 59;
-yeccgoto(let_expr, 203) ->
- 59;
-yeccgoto(let_expr, 211) ->
- 59;
-yeccgoto(let_expr, 214) ->
- 59;
-yeccgoto(let_expr, 216) ->
- 59;
-yeccgoto(let_expr, 218) ->
- 59;
-yeccgoto(let_expr, 226) ->
- 59;
-yeccgoto(let_expr, 232) ->
- 59;
-yeccgoto(let_expr, 235) ->
- 59;
-yeccgoto(let_expr, 257) ->
- 59;
-yeccgoto(let_expr, 260) ->
- 59;
-yeccgoto(let_expr, 265) ->
- 59;
-yeccgoto(let_vars, 58) ->
- 213;
-yeccgoto(let_vars, 82) ->
- 85;
-yeccgoto(let_vars, 88) ->
- 89;
-yeccgoto(letrec_expr, 33) ->
- 61;
-yeccgoto(letrec_expr, 35) ->
- 61;
-yeccgoto(letrec_expr, 36) ->
- 61;
-yeccgoto(letrec_expr, 37) ->
- 61;
-yeccgoto(letrec_expr, 40) ->
- 61;
-yeccgoto(letrec_expr, 44) ->
- 61;
-yeccgoto(letrec_expr, 46) ->
- 61;
-yeccgoto(letrec_expr, 48) ->
- 61;
-yeccgoto(letrec_expr, 52) ->
- 61;
-yeccgoto(letrec_expr, 70) ->
- 61;
-yeccgoto(letrec_expr, 74) ->
- 61;
-yeccgoto(letrec_expr, 79) ->
- 61;
-yeccgoto(letrec_expr, 86) ->
- 61;
-yeccgoto(letrec_expr, 90) ->
- 61;
-yeccgoto(letrec_expr, 99) ->
- 61;
-yeccgoto(letrec_expr, 164) ->
- 61;
-yeccgoto(letrec_expr, 166) ->
- 61;
-yeccgoto(letrec_expr, 171) ->
- 61;
-yeccgoto(letrec_expr, 203) ->
- 61;
-yeccgoto(letrec_expr, 211) ->
- 61;
-yeccgoto(letrec_expr, 214) ->
- 61;
-yeccgoto(letrec_expr, 216) ->
- 61;
-yeccgoto(letrec_expr, 218) ->
- 61;
-yeccgoto(letrec_expr, 226) ->
- 61;
-yeccgoto(letrec_expr, 232) ->
- 61;
-yeccgoto(letrec_expr, 235) ->
- 61;
-yeccgoto(letrec_expr, 257) ->
- 61;
-yeccgoto(letrec_expr, 260) ->
- 61;
-yeccgoto(letrec_expr, 265) ->
- 61;
-yeccgoto(literal, 284) ->
- 288;
-yeccgoto(literal, 285) ->
- 297;
-yeccgoto(literal, 290) ->
- 291;
-yeccgoto(literal, 295) ->
- 291;
-yeccgoto(literal, 298) ->
- 304;
-yeccgoto(literal, 301) ->
- 302;
-yeccgoto(literals, 290) ->
- 292;
-yeccgoto(literals, 295) ->
- 296;
-yeccgoto(module_attribute, 6) ->
- 8;
-yeccgoto(module_attribute, 315) ->
- 316;
-yeccgoto(module_definition, 0) ->
- 3;
-yeccgoto(module_defs, 8) ->
- 15;
-yeccgoto(module_defs, 316) ->
- 317;
-yeccgoto(module_export, 4) ->
- 6;
-yeccgoto(module_export, 314) ->
- 315;
-yeccgoto(nil, 33) ->
- 62;
-yeccgoto(nil, 35) ->
- 62;
-yeccgoto(nil, 36) ->
- 62;
-yeccgoto(nil, 37) ->
- 62;
-yeccgoto(nil, 40) ->
- 62;
-yeccgoto(nil, 44) ->
- 62;
-yeccgoto(nil, 46) ->
- 62;
-yeccgoto(nil, 48) ->
- 62;
-yeccgoto(nil, 52) ->
- 62;
-yeccgoto(nil, 65) ->
- 62;
-yeccgoto(nil, 70) ->
- 62;
-yeccgoto(nil, 74) ->
- 62;
-yeccgoto(nil, 79) ->
- 62;
-yeccgoto(nil, 86) ->
- 62;
-yeccgoto(nil, 90) ->
- 62;
-yeccgoto(nil, 96) ->
- 62;
-yeccgoto(nil, 97) ->
- 62;
-yeccgoto(nil, 98) ->
- 62;
-yeccgoto(nil, 99) ->
- 62;
-yeccgoto(nil, 100) ->
- 62;
-yeccgoto(nil, 114) ->
- 62;
-yeccgoto(nil, 115) ->
- 62;
-yeccgoto(nil, 120) ->
- 62;
-yeccgoto(nil, 126) ->
- 139;
-yeccgoto(nil, 129) ->
- 139;
-yeccgoto(nil, 142) ->
- 139;
-yeccgoto(nil, 147) ->
- 139;
-yeccgoto(nil, 151) ->
- 139;
-yeccgoto(nil, 154) ->
- 139;
-yeccgoto(nil, 162) ->
- 62;
-yeccgoto(nil, 164) ->
- 62;
-yeccgoto(nil, 166) ->
- 62;
-yeccgoto(nil, 171) ->
- 62;
-yeccgoto(nil, 174) ->
- 62;
-yeccgoto(nil, 177) ->
- 62;
-yeccgoto(nil, 200) ->
- 62;
-yeccgoto(nil, 203) ->
- 62;
-yeccgoto(nil, 211) ->
- 62;
-yeccgoto(nil, 214) ->
- 62;
-yeccgoto(nil, 216) ->
- 62;
-yeccgoto(nil, 218) ->
- 62;
-yeccgoto(nil, 222) ->
- 62;
-yeccgoto(nil, 226) ->
- 62;
-yeccgoto(nil, 232) ->
- 62;
-yeccgoto(nil, 235) ->
- 62;
-yeccgoto(nil, 257) ->
- 62;
-yeccgoto(nil, 260) ->
- 62;
-yeccgoto(nil, 265) ->
- 62;
-yeccgoto(nil, 284) ->
- 62;
-yeccgoto(nil, 285) ->
- 62;
-yeccgoto(nil, 290) ->
- 62;
-yeccgoto(nil, 295) ->
- 62;
-yeccgoto(nil, 298) ->
- 62;
-yeccgoto(nil, 301) ->
- 62;
-yeccgoto(other_pattern, 65) ->
- 111;
-yeccgoto(other_pattern, 96) ->
- 186;
-yeccgoto(other_pattern, 97) ->
- 111;
-yeccgoto(other_pattern, 98) ->
- 111;
-yeccgoto(other_pattern, 100) ->
- 111;
-yeccgoto(other_pattern, 114) ->
- 111;
-yeccgoto(other_pattern, 115) ->
- 123;
-yeccgoto(other_pattern, 120) ->
- 111;
-yeccgoto(other_pattern, 162) ->
- 111;
-yeccgoto(other_pattern, 174) ->
- 111;
-yeccgoto(other_pattern, 177) ->
- 111;
-yeccgoto(other_pattern, 200) ->
- 111;
-yeccgoto(other_pattern, 222) ->
- 111;
-yeccgoto(primop_expr, 33) ->
- 64;
-yeccgoto(primop_expr, 35) ->
- 64;
-yeccgoto(primop_expr, 36) ->
- 64;
-yeccgoto(primop_expr, 37) ->
- 64;
-yeccgoto(primop_expr, 40) ->
- 64;
-yeccgoto(primop_expr, 44) ->
- 64;
-yeccgoto(primop_expr, 46) ->
- 64;
-yeccgoto(primop_expr, 48) ->
- 64;
-yeccgoto(primop_expr, 52) ->
- 64;
-yeccgoto(primop_expr, 70) ->
- 64;
-yeccgoto(primop_expr, 74) ->
- 64;
-yeccgoto(primop_expr, 79) ->
- 64;
-yeccgoto(primop_expr, 86) ->
- 64;
-yeccgoto(primop_expr, 90) ->
- 64;
-yeccgoto(primop_expr, 99) ->
- 64;
-yeccgoto(primop_expr, 164) ->
- 64;
-yeccgoto(primop_expr, 166) ->
- 64;
-yeccgoto(primop_expr, 171) ->
- 64;
-yeccgoto(primop_expr, 203) ->
- 64;
-yeccgoto(primop_expr, 211) ->
- 64;
-yeccgoto(primop_expr, 214) ->
- 64;
-yeccgoto(primop_expr, 216) ->
- 64;
-yeccgoto(primop_expr, 218) ->
- 64;
-yeccgoto(primop_expr, 226) ->
- 64;
-yeccgoto(primop_expr, 232) ->
- 64;
-yeccgoto(primop_expr, 235) ->
- 64;
-yeccgoto(primop_expr, 257) ->
- 64;
-yeccgoto(primop_expr, 260) ->
- 64;
-yeccgoto(primop_expr, 265) ->
- 64;
-yeccgoto(receive_expr, 33) ->
- 66;
-yeccgoto(receive_expr, 35) ->
- 66;
-yeccgoto(receive_expr, 36) ->
- 66;
-yeccgoto(receive_expr, 37) ->
- 66;
-yeccgoto(receive_expr, 40) ->
- 66;
-yeccgoto(receive_expr, 44) ->
- 66;
-yeccgoto(receive_expr, 46) ->
- 66;
-yeccgoto(receive_expr, 48) ->
- 66;
-yeccgoto(receive_expr, 52) ->
- 66;
-yeccgoto(receive_expr, 70) ->
- 66;
-yeccgoto(receive_expr, 74) ->
- 66;
-yeccgoto(receive_expr, 79) ->
- 66;
-yeccgoto(receive_expr, 86) ->
- 66;
-yeccgoto(receive_expr, 90) ->
- 66;
-yeccgoto(receive_expr, 99) ->
- 66;
-yeccgoto(receive_expr, 164) ->
- 66;
-yeccgoto(receive_expr, 166) ->
- 66;
-yeccgoto(receive_expr, 171) ->
- 66;
-yeccgoto(receive_expr, 203) ->
- 66;
-yeccgoto(receive_expr, 211) ->
- 66;
-yeccgoto(receive_expr, 214) ->
- 66;
-yeccgoto(receive_expr, 216) ->
- 66;
-yeccgoto(receive_expr, 218) ->
- 66;
-yeccgoto(receive_expr, 226) ->
- 66;
-yeccgoto(receive_expr, 232) ->
- 66;
-yeccgoto(receive_expr, 235) ->
- 66;
-yeccgoto(receive_expr, 257) ->
- 66;
-yeccgoto(receive_expr, 260) ->
- 66;
-yeccgoto(receive_expr, 265) ->
- 66;
-yeccgoto(segment, 247) ->
- 249;
-yeccgoto(segment, 255) ->
- 249;
-yeccgoto(segment_pattern, 190) ->
- 192;
-yeccgoto(segment_pattern, 198) ->
- 192;
-yeccgoto(segment_patterns, 190) ->
- 193;
-yeccgoto(segment_patterns, 198) ->
- 199;
-yeccgoto(segments, 247) ->
- 250;
-yeccgoto(segments, 255) ->
- 256;
-yeccgoto(sequence, 33) ->
- 67;
-yeccgoto(sequence, 35) ->
- 67;
-yeccgoto(sequence, 36) ->
- 67;
-yeccgoto(sequence, 37) ->
- 67;
-yeccgoto(sequence, 40) ->
- 67;
-yeccgoto(sequence, 44) ->
- 67;
-yeccgoto(sequence, 46) ->
- 67;
-yeccgoto(sequence, 48) ->
- 67;
-yeccgoto(sequence, 52) ->
- 67;
-yeccgoto(sequence, 70) ->
- 67;
-yeccgoto(sequence, 74) ->
- 67;
-yeccgoto(sequence, 79) ->
- 67;
-yeccgoto(sequence, 86) ->
- 67;
-yeccgoto(sequence, 90) ->
- 67;
-yeccgoto(sequence, 99) ->
- 67;
-yeccgoto(sequence, 164) ->
- 67;
-yeccgoto(sequence, 166) ->
- 67;
-yeccgoto(sequence, 171) ->
- 67;
-yeccgoto(sequence, 203) ->
- 67;
-yeccgoto(sequence, 211) ->
- 67;
-yeccgoto(sequence, 214) ->
- 67;
-yeccgoto(sequence, 216) ->
- 67;
-yeccgoto(sequence, 218) ->
- 67;
-yeccgoto(sequence, 226) ->
- 67;
-yeccgoto(sequence, 232) ->
- 67;
-yeccgoto(sequence, 235) ->
- 67;
-yeccgoto(sequence, 257) ->
- 67;
-yeccgoto(sequence, 260) ->
- 67;
-yeccgoto(sequence, 265) ->
- 67;
-yeccgoto(single_expression, 33) ->
- 68;
-yeccgoto(single_expression, 35) ->
- 68;
-yeccgoto(single_expression, 36) ->
- 68;
-yeccgoto(single_expression, 37) ->
- 68;
-yeccgoto(single_expression, 40) ->
- 68;
-yeccgoto(single_expression, 44) ->
- 68;
-yeccgoto(single_expression, 46) ->
- 68;
-yeccgoto(single_expression, 48) ->
- 68;
-yeccgoto(single_expression, 52) ->
- 68;
-yeccgoto(single_expression, 70) ->
- 68;
-yeccgoto(single_expression, 74) ->
- 68;
-yeccgoto(single_expression, 79) ->
- 68;
-yeccgoto(single_expression, 86) ->
- 68;
-yeccgoto(single_expression, 90) ->
- 68;
-yeccgoto(single_expression, 99) ->
- 68;
-yeccgoto(single_expression, 164) ->
- 68;
-yeccgoto(single_expression, 166) ->
- 68;
-yeccgoto(single_expression, 171) ->
- 68;
-yeccgoto(single_expression, 203) ->
- 68;
-yeccgoto(single_expression, 211) ->
- 68;
-yeccgoto(single_expression, 214) ->
- 68;
-yeccgoto(single_expression, 216) ->
- 68;
-yeccgoto(single_expression, 218) ->
- 68;
-yeccgoto(single_expression, 226) ->
- 68;
-yeccgoto(single_expression, 232) ->
- 68;
-yeccgoto(single_expression, 235) ->
- 68;
-yeccgoto(single_expression, 257) ->
- 68;
-yeccgoto(single_expression, 260) ->
- 68;
-yeccgoto(single_expression, 265) ->
- 68;
-yeccgoto(tail, 231) ->
- 234;
-yeccgoto(tail, 238) ->
- 239;
-yeccgoto(tail_constant, 150) ->
- 153;
-yeccgoto(tail_constant, 157) ->
- 158;
-yeccgoto(tail_literal, 297) ->
- 300;
-yeccgoto(tail_literal, 304) ->
- 305;
-yeccgoto(tail_pattern, 173) ->
- 176;
-yeccgoto(tail_pattern, 180) ->
- 181;
-yeccgoto(timeout, 65) ->
- 112;
-yeccgoto(timeout, 101) ->
- 168;
-yeccgoto(try_expr, 33) ->
- 71;
-yeccgoto(try_expr, 35) ->
- 71;
-yeccgoto(try_expr, 36) ->
- 71;
-yeccgoto(try_expr, 37) ->
- 71;
-yeccgoto(try_expr, 40) ->
- 71;
-yeccgoto(try_expr, 44) ->
- 71;
-yeccgoto(try_expr, 46) ->
- 71;
-yeccgoto(try_expr, 48) ->
- 71;
-yeccgoto(try_expr, 52) ->
- 71;
-yeccgoto(try_expr, 70) ->
- 71;
-yeccgoto(try_expr, 74) ->
- 71;
-yeccgoto(try_expr, 79) ->
- 71;
-yeccgoto(try_expr, 86) ->
- 71;
-yeccgoto(try_expr, 90) ->
- 71;
-yeccgoto(try_expr, 99) ->
- 71;
-yeccgoto(try_expr, 164) ->
- 71;
-yeccgoto(try_expr, 166) ->
- 71;
-yeccgoto(try_expr, 171) ->
- 71;
-yeccgoto(try_expr, 203) ->
- 71;
-yeccgoto(try_expr, 211) ->
- 71;
-yeccgoto(try_expr, 214) ->
- 71;
-yeccgoto(try_expr, 216) ->
- 71;
-yeccgoto(try_expr, 218) ->
- 71;
-yeccgoto(try_expr, 226) ->
- 71;
-yeccgoto(try_expr, 232) ->
- 71;
-yeccgoto(try_expr, 235) ->
- 71;
-yeccgoto(try_expr, 257) ->
- 71;
-yeccgoto(try_expr, 260) ->
- 71;
-yeccgoto(try_expr, 265) ->
- 71;
-yeccgoto(tuple, 33) ->
- 72;
-yeccgoto(tuple, 35) ->
- 72;
-yeccgoto(tuple, 36) ->
- 72;
-yeccgoto(tuple, 37) ->
- 72;
-yeccgoto(tuple, 40) ->
- 72;
-yeccgoto(tuple, 44) ->
- 72;
-yeccgoto(tuple, 46) ->
- 72;
-yeccgoto(tuple, 48) ->
- 72;
-yeccgoto(tuple, 52) ->
- 72;
-yeccgoto(tuple, 70) ->
- 72;
-yeccgoto(tuple, 74) ->
- 72;
-yeccgoto(tuple, 79) ->
- 72;
-yeccgoto(tuple, 86) ->
- 72;
-yeccgoto(tuple, 90) ->
- 72;
-yeccgoto(tuple, 99) ->
- 72;
-yeccgoto(tuple, 164) ->
- 72;
-yeccgoto(tuple, 166) ->
- 72;
-yeccgoto(tuple, 171) ->
- 72;
-yeccgoto(tuple, 203) ->
- 72;
-yeccgoto(tuple, 211) ->
- 72;
-yeccgoto(tuple, 214) ->
- 72;
-yeccgoto(tuple, 216) ->
- 72;
-yeccgoto(tuple, 218) ->
- 72;
-yeccgoto(tuple, 226) ->
- 72;
-yeccgoto(tuple, 232) ->
- 72;
-yeccgoto(tuple, 235) ->
- 72;
-yeccgoto(tuple, 257) ->
- 72;
-yeccgoto(tuple, 260) ->
- 72;
-yeccgoto(tuple, 265) ->
- 72;
-yeccgoto(tuple_constant, 126) ->
- 141;
-yeccgoto(tuple_constant, 129) ->
- 141;
-yeccgoto(tuple_constant, 142) ->
- 141;
-yeccgoto(tuple_constant, 147) ->
- 141;
-yeccgoto(tuple_constant, 151) ->
- 141;
-yeccgoto(tuple_constant, 154) ->
- 141;
-yeccgoto(tuple_literal, 284) ->
- 289;
-yeccgoto(tuple_literal, 285) ->
- 289;
-yeccgoto(tuple_literal, 290) ->
- 289;
-yeccgoto(tuple_literal, 295) ->
- 289;
-yeccgoto(tuple_literal, 298) ->
- 289;
-yeccgoto(tuple_literal, 301) ->
- 289;
-yeccgoto(tuple_pattern, 65) ->
- 113;
-yeccgoto(tuple_pattern, 96) ->
- 113;
-yeccgoto(tuple_pattern, 97) ->
- 113;
-yeccgoto(tuple_pattern, 98) ->
- 113;
-yeccgoto(tuple_pattern, 100) ->
- 113;
-yeccgoto(tuple_pattern, 114) ->
- 113;
-yeccgoto(tuple_pattern, 115) ->
- 113;
-yeccgoto(tuple_pattern, 120) ->
- 113;
-yeccgoto(tuple_pattern, 162) ->
- 113;
-yeccgoto(tuple_pattern, 174) ->
- 113;
-yeccgoto(tuple_pattern, 177) ->
- 113;
-yeccgoto(tuple_pattern, 200) ->
- 113;
-yeccgoto(tuple_pattern, 222) ->
- 113;
-yeccgoto(variable, 25) ->
- 31;
-yeccgoto(variable, 26) ->
- 267;
-yeccgoto(variable, 33) ->
- 73;
-yeccgoto(variable, 35) ->
- 73;
-yeccgoto(variable, 36) ->
- 73;
-yeccgoto(variable, 37) ->
- 73;
-yeccgoto(variable, 40) ->
- 73;
-yeccgoto(variable, 44) ->
- 73;
-yeccgoto(variable, 46) ->
- 73;
-yeccgoto(variable, 48) ->
- 73;
-yeccgoto(variable, 52) ->
- 73;
-yeccgoto(variable, 58) ->
- 31;
-yeccgoto(variable, 65) ->
- 31;
-yeccgoto(variable, 70) ->
- 73;
-yeccgoto(variable, 74) ->
- 73;
-yeccgoto(variable, 79) ->
- 73;
-yeccgoto(variable, 82) ->
- 31;
-yeccgoto(variable, 83) ->
- 31;
-yeccgoto(variable, 86) ->
- 73;
-yeccgoto(variable, 88) ->
- 31;
-yeccgoto(variable, 90) ->
- 73;
-yeccgoto(variable, 96) ->
- 124;
-yeccgoto(variable, 97) ->
- 31;
-yeccgoto(variable, 98) ->
- 31;
-yeccgoto(variable, 99) ->
- 73;
-yeccgoto(variable, 100) ->
- 31;
-yeccgoto(variable, 114) ->
- 31;
-yeccgoto(variable, 115) ->
- 124;
-yeccgoto(variable, 120) ->
- 31;
-yeccgoto(variable, 162) ->
- 31;
-yeccgoto(variable, 164) ->
- 73;
-yeccgoto(variable, 166) ->
- 73;
-yeccgoto(variable, 171) ->
- 73;
-yeccgoto(variable, 174) ->
- 31;
-yeccgoto(variable, 177) ->
- 31;
-yeccgoto(variable, 200) ->
- 31;
-yeccgoto(variable, 203) ->
- 73;
-yeccgoto(variable, 211) ->
- 73;
-yeccgoto(variable, 214) ->
- 73;
-yeccgoto(variable, 216) ->
- 73;
-yeccgoto(variable, 218) ->
- 73;
-yeccgoto(variable, 222) ->
- 31;
-yeccgoto(variable, 226) ->
- 73;
-yeccgoto(variable, 232) ->
- 73;
-yeccgoto(variable, 235) ->
- 73;
-yeccgoto(variable, 257) ->
- 73;
-yeccgoto(variable, 260) ->
- 73;
-yeccgoto(variable, 263) ->
- 31;
-yeccgoto(variable, 265) ->
- 73;
-yeccgoto(__Symbol, __State) ->
- exit({__Symbol, __State, missing_in_goto_table}).
-
-
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_parse.hrl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_parse.hrl
deleted file mode 100644
index aaf913a15a..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_parse.hrl
+++ /dev/null
@@ -1,111 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: core_parse.hrl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
-%%
-%% Purpose : Core Erlang syntax trees as records.
-
-%% It would be nice to incorporate some generic functions as well but
-%% this could make including this file difficult.
-
-%% Note: the annotation list is *always* the first record field.
-%% Thus it is possible to define the macros:
-%% -define(get_ann(X), element(2, X)).
-%% -define(set_ann(X, Y), setelement(2, X, Y)).
-
--record(c_int, {anno=[], val}). % val :: integer()
-
--record(c_float, {anno=[], val}). % val :: float()
-
--record(c_atom, {anno=[], val}). % val :: atom()
-
--record(c_char, {anno=[], val}). % val :: char()
-
--record(c_string, {anno=[], val}). % val :: string()
-
--record(c_nil, {anno=[]}).
-
--record(c_binary, {anno=[], segments}). % segments :: [#ce_bitstr{}]
-
--record(c_bitstr, {anno=[],val, % val :: Tree,
- size, % size :: Tree,
- unit, % unit :: integer(),
- type, % type :: atom(),
- flags}). % flags :: [atom()],
-
--record(c_cons, {anno=[], hd, % hd :: Tree,
- tl}). % tl :: Tree
-
--record(c_tuple, {anno=[], es}). % es :: [Tree]
-
--record(c_var, {anno=[], name}). % name :: integer() | atom()
-
--record(c_fname, {anno=[], id, % id :: atom(),
- arity}). % arity :: integer()
-
--record(c_values, {anno=[], es}). % es :: [Tree]
-
--record(c_fun, {anno=[], vars, % vars :: [Tree],
- body}). % body :: Tree
-
--record(c_seq, {anno=[], arg, % arg :: Tree,
- body}). % body :: Tree
-
--record(c_let, {anno=[], vars, % vars :: [Tree],
- arg, % arg :: Tree,
- body}). % body :: Tree
-
--record(c_letrec, {anno=[], defs, % defs :: [#ce_def{}],
- body}). % body :: Tree
-
--record(c_def, {anno=[], name, % name :: Tree,
- val}). % val :: Tree,
-
--record(c_case, {anno=[], arg, % arg :: Tree,
- clauses}). % clauses :: [Tree]
-
--record(c_clause, {anno=[], pats, % pats :: [Tree],
- guard, % guard :: Tree,
- body}). % body :: Tree
-
--record(c_alias, {anno=[], var, % var :: Tree,
- pat}). % pat :: Tree
-
--record(c_receive, {anno=[], clauses, % clauses :: [Tree],
- timeout, % timeout :: Tree,
- action}). % action :: Tree
-
--record(c_apply, {anno=[], op, % op :: Tree,
- args}). % args :: [Tree]
-
--record(c_call, {anno=[], module, % module :: Tree,
- name, % name :: Tree,
- args}). % args :: [Tree]
-
--record(c_primop, {anno=[], name, % name :: Tree,
- args}). % args :: [Tree]
-
--record(c_try, {anno=[], arg, % arg :: Tree,
- vars, % vars :: [Tree],
- body, % body :: Tree
- evars, % evars :: [Tree],
- handler}). % handler :: Tree
-
--record(c_catch, {anno=[], body}). % body :: Tree
-
--record(c_module, {anno=[], name, % name :: Tree,
- exports, % exports :: [Tree],
- attrs, % attrs :: [#ce_def{}],
- defs}). % defs :: [#ce_def{}]
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_pp.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_pp.erl
deleted file mode 100644
index 147a0dba6c..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_pp.erl
+++ /dev/null
@@ -1,430 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: core_pp.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
-%%
-%% Purpose : Core Erlang (naive) prettyprinter
-
--module(core_pp).
-
--export([format/1]).
-
--include("core_parse.hrl").
-
-%% ====================================================================== %%
-%% format(Node) -> Text
-%% Node = coreErlang()
-%% Text = string() | [Text]
-%%
-%% Prettyprint-formats (naively) an abstract Core Erlang syntax
-%% tree.
-
--record(ctxt, {class = term,
- indent = 0,
- item_indent = 2,
- body_indent = 4,
- tab_width = 8,
- line = 0}).
-
-format(Node) -> case catch format(Node, #ctxt{}) of
- {'EXIT',_} -> io_lib:format("~p",[Node]);
- Other -> Other
- end.
-
-maybe_anno(Node, Fun, Ctxt) ->
- As = core_lib:get_anno(Node),
- case get_line(As) of
- none ->
- maybe_anno(Node, Fun, Ctxt, As);
- Line ->
- if Line > Ctxt#ctxt.line ->
- [io_lib:format("%% Line ~w",[Line]),
- nl_indent(Ctxt),
- maybe_anno(Node, Fun, Ctxt#ctxt{line = Line}, As)
- ];
- true ->
- maybe_anno(Node, Fun, Ctxt, As)
- end
- end.
-
-maybe_anno(Node, Fun, Ctxt, As) ->
- case strip_line(As) of
- [] ->
- Fun(Node, Ctxt);
- List ->
- Ctxt1 = add_indent(Ctxt, 2),
- Ctxt2 = add_indent(Ctxt1, 3),
- ["( ",
- Fun(Node, Ctxt1),
- nl_indent(Ctxt1),
- "-| ",format_1(core_lib:make_literal(List), Ctxt2)," )"
- ]
- end.
-
-strip_line([A | As]) when integer(A) ->
- strip_line(As);
-strip_line([A | As]) ->
- [A | strip_line(As)];
-strip_line([]) ->
- [].
-
-get_line([L | _As]) when integer(L) ->
- L;
-get_line([_ | As]) ->
- get_line(As);
-get_line([]) ->
- none.
-
-format(Node, Ctxt) ->
- maybe_anno(Node, fun format_1/2, Ctxt).
-
-format_1(#c_char{val=C}, _) -> io_lib:write_char(C);
-format_1(#c_int{val=I}, _) -> integer_to_list(I);
-format_1(#c_float{val=F}, _) -> float_to_list(F);
-format_1(#c_atom{val=A}, _) -> core_atom(A);
-format_1(#c_nil{}, _) -> "[]";
-format_1(#c_string{val=S}, _) -> io_lib:write_string(S);
-format_1(#c_var{name=V}, _) ->
- %% Internal variable names may be:
- %% - atoms representing proper Erlang variable names, or
- %% any atoms that may be printed without single-quoting
- %% - nonnegative integers.
- %% It is important that when printing variables, no two names
- %% should ever map to the same string.
- if atom(V) ->
- S = atom_to_list(V),
- case S of
- [C | _] when C >= $A, C =< $Z ->
- %% Ordinary uppercase-prefixed names are
- %% printed just as they are.
- S;
- [$_ | _] ->
- %% Already "_"-prefixed names are prefixed
- %% with "_X", e.g. '_foo' => '_X_foo', to
- %% avoid generating things like "____foo" upon
- %% repeated writing and reading of code.
- %% ("_X_X_X_foo" is better.)
- [$_, $X | S];
- _ ->
- %% Plain atoms are prefixed with a single "_".
- %% E.g. foo => "_foo".
- [$_ | S]
- end;
- integer(V) ->
- %% Integers are also simply prefixed with "_".
- [$_ | integer_to_list(V)]
- end;
-format_1(#c_binary{segments=Segs}, Ctxt) ->
- ["#{",
- format_vseq(Segs, "", ",", add_indent(Ctxt, 2),
- fun format_bitstr/2),
- "}#"
- ];
-format_1(#c_tuple{es=Es}, Ctxt) ->
- [${,
- format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2),
- $}
- ];
-format_1(#c_cons{hd=H,tl=T}, Ctxt) ->
- Txt = ["["|format(H, add_indent(Ctxt, 1))],
- [Txt|format_list_tail(T, add_indent(Ctxt, width(Txt, Ctxt)))];
-format_1(#c_values{es=Es}, Ctxt) ->
- format_values(Es, Ctxt);
-format_1(#c_alias{var=V,pat=P}, Ctxt) ->
- Txt = [format(V, Ctxt)|" = "],
- [Txt|format(P, add_indent(Ctxt, width(Txt, Ctxt)))];
-format_1(#c_let{vars=Vs,arg=A,body=B}, Ctxt) ->
- Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
- ["let ",
- format_values(Vs, add_indent(Ctxt, 4)),
- " =",
- nl_indent(Ctxt1),
- format(A, Ctxt1),
- nl_indent(Ctxt),
- "in "
- | format(B, add_indent(Ctxt, 4))
- ];
-format_1(#c_letrec{defs=Fs,body=B}, Ctxt) ->
- Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
- ["letrec",
- nl_indent(Ctxt1),
- format_funcs(Fs, Ctxt1),
- nl_indent(Ctxt),
- "in "
- | format(B, add_indent(Ctxt, 4))
- ];
-format_1(#c_seq{arg=A,body=B}, Ctxt) ->
- Ctxt1 = add_indent(Ctxt, 4),
- ["do ",
- format(A, Ctxt1),
- nl_indent(Ctxt1)
- | format(B, Ctxt1)
- ];
-format_1(#c_case{arg=A,clauses=Cs}, Ctxt) ->
- Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.item_indent),
- ["case ",
- format(A, add_indent(Ctxt, 5)),
- " of",
- nl_indent(Ctxt1),
- format_clauses(Cs, Ctxt1),
- nl_indent(Ctxt)
- | "end"
- ];
-format_1(#c_receive{clauses=Cs,timeout=T,action=A}, Ctxt) ->
- Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.item_indent),
- ["receive",
- nl_indent(Ctxt1),
- format_clauses(Cs, Ctxt1),
- nl_indent(Ctxt),
- "after ",
- format(T, add_indent(Ctxt, 6)),
- " ->",
- nl_indent(Ctxt1),
- format(A, Ctxt1)
- ];
-format_1(#c_fname{id=I,arity=A}, _) ->
- [core_atom(I),$/,integer_to_list(A)];
-format_1(#c_fun{vars=Vs,body=B}, Ctxt) ->
- Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
- ["fun (",
- format_hseq(Vs, ",", add_indent(Ctxt, 5), fun format/2),
- ") ->",
- nl_indent(Ctxt1)
- | format(B, Ctxt1)
- ];
-format_1(#c_apply{op=O,args=As}, Ctxt0) ->
- Ctxt1 = add_indent(Ctxt0, 6), %"apply "
- Op = format(O, Ctxt1),
- Ctxt2 = add_indent(Ctxt0, 4),
- ["apply ",Op,
- nl_indent(Ctxt2),
- $(,format_hseq(As, ", ", add_indent(Ctxt2, 1), fun format/2),$)
- ];
-format_1(#c_call{module=M,name=N,args=As}, Ctxt0) ->
- Ctxt1 = add_indent(Ctxt0, 5), %"call "
- Mod = format(M, Ctxt1),
- Ctxt2 = add_indent(Ctxt1, width(Mod, Ctxt1)+1),
- Name = format(N, Ctxt2),
- Ctxt3 = add_indent(Ctxt0, 4),
- ["call ",Mod,":",Name,
- nl_indent(Ctxt3),
- $(,format_hseq(As, ", ", add_indent(Ctxt3, 1), fun format/2),$)
- ];
-format_1(#c_primop{name=N,args=As}, Ctxt0) ->
- Ctxt1 = add_indent(Ctxt0, 7), %"primop "
- Name = format(N, Ctxt1),
- Ctxt2 = add_indent(Ctxt0, 4),
- ["primop ",Name,
- nl_indent(Ctxt2),
- $(,format_hseq(As, ", ", add_indent(Ctxt2, 1), fun format/2),$)
- ];
-format_1(#c_catch{body=B}, Ctxt) ->
- Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
- ["catch",
- nl_indent(Ctxt1),
- format(B, Ctxt1)
- ];
-format_1(#c_try{arg=E,vars=Vs,body=B,evars=Evs,handler=H}, Ctxt) ->
- Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
- ["try",
- nl_indent(Ctxt1),
- format(E, Ctxt1),
- nl_indent(Ctxt),
- "of ",
- format_values(Vs, add_indent(Ctxt, 3)),
- " ->",
- nl_indent(Ctxt1),
- format(B, Ctxt1),
- nl_indent(Ctxt),
- "catch ",
- format_values(Evs, add_indent(Ctxt, 6)),
- " ->",
- nl_indent(Ctxt1)
- | format(H, Ctxt1)
- ];
-format_1(#c_def{name=N,val=V}, Ctxt) ->
- Ctxt1 = add_indent(set_class(Ctxt, expr), Ctxt#ctxt.body_indent),
- [format(N, Ctxt),
- " =",
- nl_indent(Ctxt1)
- | format(V, Ctxt1)
- ];
-format_1(#c_module{name=N,exports=Es,attrs=As,defs=Ds}, Ctxt) ->
- Mod = ["module ", format(N, Ctxt)],
- [Mod," [",
- format_vseq(Es,
- "", ",",
- add_indent(set_class(Ctxt, term), width(Mod, Ctxt)+2),
- fun format/2),
- "]",
- nl_indent(Ctxt),
- " attributes [",
- format_vseq(As,
- "", ",",
- add_indent(set_class(Ctxt, def), 16),
- fun format/2),
- "]",
- nl_indent(Ctxt),
- format_funcs(Ds, Ctxt),
- nl_indent(Ctxt)
- | "end"
- ];
-format_1(Type, _) ->
- ["** Unsupported type: ",
- io_lib:write(Type)
- | " **"
- ].
-
-format_funcs(Fs, Ctxt) ->
- format_vseq(Fs,
- "", "",
- set_class(Ctxt, def),
- fun format/2).
-
-format_values(Vs, Ctxt) ->
- [$<,
- format_hseq(Vs, ",", add_indent(Ctxt, 1), fun format/2),
- $>].
-
-format_bitstr(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Ctxt0) ->
- Vs = [S, U, T, Fs],
- Ctxt1 = add_indent(Ctxt0, 2),
- Val = format(V, Ctxt1),
- Ctxt2 = add_indent(Ctxt1, width(Val, Ctxt1) + 2),
- ["#<", Val, ">(", format_hseq(Vs,",", Ctxt2, fun format/2), $)].
-
-format_clauses(Cs, Ctxt) ->
- format_vseq(Cs, "", "", set_class(Ctxt, clause),
- fun format_clause/2).
-
-format_clause(Node, Ctxt) ->
- maybe_anno(Node, fun format_clause_1/2, Ctxt).
-
-format_clause_1(#c_clause{pats=Ps,guard=G,body=B}, Ctxt) ->
- Ptxt = format_values(Ps, Ctxt),
- Ctxt2 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
- [Ptxt,
- " when ",
- format_guard(G, add_indent(set_class(Ctxt, expr),
- width(Ptxt, Ctxt) + 6)),
- " ->",
- nl_indent(Ctxt2)
- | format(B, set_class(Ctxt2, expr))
- ].
-
-format_guard(Node, Ctxt) ->
- maybe_anno(Node, fun format_guard_1/2, Ctxt).
-
-format_guard_1(#c_call{module=M,name=N,args=As}, Ctxt0) ->
- Ctxt1 = add_indent(Ctxt0, 5), %"call "
- Mod = format(M, Ctxt1),
- Ctxt2 = add_indent(Ctxt1, width(Mod, Ctxt1)+1),
- Name = format(N, Ctxt2),
- Ctxt3 = add_indent(Ctxt0, 4),
- ["call ",Mod,":",Name,
- nl_indent(Ctxt3),
- $(,format_vseq(As, "",",", add_indent(Ctxt3, 1), fun format_guard/2),$)
- ];
-format_guard_1(E, Ctxt) -> format_1(E, Ctxt). %Anno already done
-
-%% format_hseq([Thing], Separator, Context, Fun) -> Txt.
-%% Format a sequence horizontally on the same line with Separator between.
-
-format_hseq([H], _, Ctxt, Fun) ->
- Fun(H, Ctxt);
-format_hseq([H|T], Sep, Ctxt, Fun) ->
- Txt = [Fun(H, Ctxt)|Sep],
- Ctxt1 = add_indent(Ctxt, width(Txt, Ctxt)),
- [Txt|format_hseq(T, Sep, Ctxt1, Fun)];
-format_hseq([], _, _, _) -> "".
-
-%% format_vseq([Thing], LinePrefix, LineSuffix, Context, Fun) -> Txt.
-%% Format a sequence vertically in indented lines adding LinePrefix
-%% to the beginning of each line and LineSuffix to the end of each
-%% line. No prefix on the first line or suffix on the last line.
-
-format_vseq([H], _Pre, _Suf, Ctxt, Fun) ->
- Fun(H, Ctxt);
-format_vseq([H|T], Pre, Suf, Ctxt, Fun) ->
- [Fun(H, Ctxt),Suf,nl_indent(Ctxt),Pre|
- format_vseq(T, Pre, Suf, Ctxt, Fun)];
-format_vseq([], _, _, _, _) -> "".
-
-format_list_tail(#c_nil{anno=[]}, _) -> "]";
-format_list_tail(#c_cons{anno=[],hd=H,tl=T}, Ctxt) ->
- Txt = [$,|format(H, Ctxt)],
- Ctxt1 = add_indent(Ctxt, width(Txt, Ctxt)),
- [Txt|format_list_tail(T, Ctxt1)];
-format_list_tail(Tail, Ctxt) ->
- ["|",format(Tail, add_indent(Ctxt, 1)),"]"].
-
-indent(Ctxt) -> indent(Ctxt#ctxt.indent, Ctxt).
-
-indent(N, _) when N =< 0 -> "";
-indent(N, Ctxt) ->
- T = Ctxt#ctxt.tab_width,
- string:chars($\t, N div T, string:chars($\s, N rem T)).
-
-nl_indent(Ctxt) -> [$\n|indent(Ctxt)].
-
-
-unindent(T, Ctxt) ->
- unindent(T, Ctxt#ctxt.indent, Ctxt, []).
-
-unindent(T, N, _, C) when N =< 0 ->
- [T|C];
-unindent([$\s|T], N, Ctxt, C) ->
- unindent(T, N - 1, Ctxt, C);
-unindent([$\t|T], N, Ctxt, C) ->
- Tab = Ctxt#ctxt.tab_width,
- if N >= Tab ->
- unindent(T, N - Tab, Ctxt, C);
- true ->
- unindent([string:chars($\s, Tab - N)|T], 0, Ctxt, C)
- end;
-unindent([L|T], N, Ctxt, C) when list(L) ->
- unindent(L, N, Ctxt, [T|C]);
-unindent([H|T], _, _, C) ->
- [H|[T|C]];
-unindent([], N, Ctxt, [H|T]) ->
- unindent(H, N, Ctxt, T);
-unindent([], _, _, []) -> [].
-
-
-width(Txt, Ctxt) ->
- case catch width(Txt, 0, Ctxt, []) of
- {'EXIT',_} -> exit({bad_text,Txt});
- Other -> Other
- end.
-
-width([$\t|T], A, Ctxt, C) ->
- width(T, A + Ctxt#ctxt.tab_width, Ctxt, C);
-width([$\n|T], _, Ctxt, C) ->
- width(unindent([T|C], Ctxt), Ctxt);
-width([H|T], A, Ctxt, C) when list(H) ->
- width(H, A, Ctxt, [T|C]);
-width([_|T], A, Ctxt, C) ->
- width(T, A + 1, Ctxt, C);
-width([], A, Ctxt, [H|T]) ->
- width(H, A, Ctxt, T);
-width([], A, _, []) -> A.
-
-add_indent(Ctxt, Dx) ->
- Ctxt#ctxt{indent = Ctxt#ctxt.indent + Dx}.
-
-set_class(Ctxt, Class) ->
- Ctxt#ctxt{class = Class}.
-
-core_atom(A) -> io_lib:write_string(atom_to_list(A), $').
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_scan.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_scan.erl
deleted file mode 100644
index f53c3c1631..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_scan.erl
+++ /dev/null
@@ -1,495 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: core_scan.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
-%%
-%% Purpose: Scanner for Core Erlang.
-
-%% For handling ISO 8859-1 (Latin-1) we use the following type
-%% information:
-%%
-%% 000 - 037 NUL - US control
-%% 040 - 057 SPC - / punctuation
-%% 060 - 071 0 - 9 digit
-%% 072 - 100 : - @ punctuation
-%% 101 - 132 A - Z uppercase
-%% 133 - 140 [ - ` punctuation
-%% 141 - 172 a - z lowercase
-%% 173 - 176 { - ~ punctuation
-%% 177 DEL control
-%% 200 - 237 control
-%% 240 - 277 NBSP - ¿ punctuation
-%% 300 - 326 À - Ö uppercase
-%% 327 × punctuation
-%% 330 - 336 Ø - Þ uppercase
-%% 337 - 366 ß - ö lowercase
-%% 367 ÷ punctuation
-%% 370 - 377 ø - ÿ lowercase
-%%
-%% Many punctuation characters region have special meaning. Must
-%% watch using × \327, bvery close to x \170
-
--module(core_scan).
-
--export([string/1,string/2,tokens/3,format_error/1]).
-
--import(lists, [reverse/1]).
-
-%% tokens(Continuation, CharList, StartPos) ->
-%% {done, {ok, [Tok], EndPos}, Rest} |
-%% {done, {error,{ErrorPos,core_scan,What}, EndPos}, Rest} |
-%% {more, Continuation'}
-%% This is the main function into the re-entrant scanner. It calls the
-%% re-entrant pre-scanner until this says done, then calls scan/1 on
-%% the result.
-%%
-%% The continuation has the form:
-%% {RestChars,CharsSoFar,CurrentPos,StartPos}
-
-tokens([], Chars, Pos) -> %First call
- tokens({[],[],Pos,Pos}, Chars, Pos);
-tokens({Chars,SoFar0,Cp,Sp}, MoreChars, _) ->
- In = Chars ++ MoreChars,
- case pre_scan(In, SoFar0, Cp) of
- {done,_,[],Ep} -> %Found nothing
- {done,{eof,Ep},[]};
- {done,_,SoFar1,Ep} -> %Got complete tokens
- Res = case scan(reverse(SoFar1), Sp) of
- {ok,Toks} -> {ok,Toks,Ep};
- {error,E} -> {error,E,Ep}
- end,
- {done,Res,[]};
- {more,Rest,SoFar1,Cp1} -> %Missing end token
- {more,{Rest,SoFar1,Cp1,Sp}};
- Other -> %An error has occurred
- {done,Other,[]}
- end.
-
-%% string([Char]) ->
-%% string([Char], StartPos) ->
-%% {ok, [Tok], EndPos} |
-%% {error,{Pos,core_scan,What}, EndPos}
-
-string(Cs) -> string(Cs, 1).
-
-string(Cs, Sp) ->
- %% Add an 'eof' to always get correct handling.
- case string_pre_scan(Cs, [], Sp) of
- {done,_,SoFar,Ep} -> %Got tokens
- case scan(reverse(SoFar), Sp) of
- {ok,Toks} -> {ok,Toks,Ep};
- {error,E} -> {error,E,Ep}
- end;
- Other -> Other %An error has occurred
- end.
-
-%% string_pre_scan(Cs, SoFar0, StartPos) ->
-%% {done,Rest,SoFar,EndPos} | {error,E,EndPos}.
-
-string_pre_scan(Cs, SoFar0, Sp) ->
- case pre_scan(Cs, SoFar0, Sp) of
- {done,Rest,SoFar1,Ep} -> %Got complete tokens
- {done,Rest,SoFar1,Ep};
- {more,Rest,SoFar1,Ep} -> %Missing end token
- string_pre_scan(Rest ++ eof, SoFar1, Ep);
- Other -> Other %An error has occurred
- end.
-
-%% format_error(Error)
-%% Return a string describing the error.
-
-format_error({string,Quote,Head}) ->
- ["unterminated " ++ string_thing(Quote) ++
- " starting with " ++ io_lib:write_string(Head,Quote)];
-format_error({illegal,Type}) -> io_lib:fwrite("illegal ~w", [Type]);
-format_error(char) -> "unterminated character";
-format_error(scan) -> "premature end";
-format_error({base,Base}) -> io_lib:fwrite("illegal base '~w'", [Base]);
-format_error(float) -> "bad float";
-format_error(Other) -> io_lib:write(Other).
-
-string_thing($') -> "atom";
-string_thing($") -> "string".
-
-%% Re-entrant pre-scanner.
-%%
-%% If the input list of characters is insufficient to build a term the
-%% scanner returns a request for more characters and a continuation to be
-%% used when trying to build a term with more characters. To indicate
-%% end-of-file the input character list should be replaced with 'eof'
-%% as an empty list has meaning.
-%%
-%% When more characters are need inside a comment, string or quoted
-%% atom, which can become rather long, instead of pushing the
-%% characters read so far back onto RestChars to be reread, a special
-%% reentry token is returned indicating the middle of a construct.
-%% The token is the start character as an atom, '%', '"' and '\''.
-
-%% pre_scan([Char], SoFar, StartPos) ->
-%% {done,RestChars,ScannedChars,NewPos} |
-%% {more,RestChars,ScannedChars,NewPos} |
-%% {error,{ErrorPos,core_scan,Description},NewPos}.
-%% Main pre-scan function. It has been split into 2 functions because of
-%% efficiency, with a good indexing compiler it would be unnecessary.
-
-pre_scan([C|Cs], SoFar, Pos) ->
- pre_scan(C, Cs, SoFar, Pos);
-pre_scan([], SoFar, Pos) ->
- {more,[],SoFar,Pos};
-pre_scan(eof, SoFar, Pos) ->
- {done,eof,SoFar,Pos}.
-
-%% pre_scan(Char, [Char], SoFar, Pos)
-
-pre_scan($$, Cs0, SoFar0, Pos) ->
- case pre_char(Cs0, [$$|SoFar0]) of
- {Cs,SoFar} ->
- pre_scan(Cs, SoFar, Pos);
- more ->
- {more,[$$|Cs0],SoFar0, Pos};
- error ->
- pre_error(char, Pos, Pos)
- end;
-pre_scan($', Cs, SoFar, Pos) ->
- pre_string(Cs, $', '\'', Pos, [$'|SoFar], Pos);
-pre_scan({'\'',Sp}, Cs, SoFar, Pos) -> %Re-entering quoted atom
- pre_string(Cs, $', '\'', Sp, SoFar, Pos);
-pre_scan($", Cs, SoFar, Pos) ->
- pre_string(Cs, $", '"', Pos, [$"|SoFar], Pos);
-pre_scan({'"',Sp}, Cs, SoFar, Pos) -> %Re-entering string
- pre_string(Cs, $", '"', Sp, SoFar, Pos);
-pre_scan($%, Cs, SoFar, Pos) ->
- pre_comment(Cs, SoFar, Pos);
-pre_scan('%', Cs, SoFar, Pos) -> %Re-entering comment
- pre_comment(Cs, SoFar, Pos);
-pre_scan($\n, Cs, SoFar, Pos) ->
- pre_scan(Cs, [$\n|SoFar], Pos+1);
-pre_scan(C, Cs, SoFar, Pos) ->
- pre_scan(Cs, [C|SoFar], Pos).
-
-%% pre_string([Char], Quote, Reent, StartPos, SoFar, Pos)
-
-pre_string([Q|Cs], Q, _, _, SoFar, Pos) ->
- pre_scan(Cs, [Q|SoFar], Pos);
-pre_string([$\n|Cs], Q, Reent, Sp, SoFar, Pos) ->
- pre_string(Cs, Q, Reent, Sp, [$\n|SoFar], Pos+1);
-pre_string([$\\|Cs0], Q, Reent, Sp, SoFar0, Pos) ->
- case pre_escape(Cs0, SoFar0) of
- {Cs,SoFar} ->
- pre_string(Cs, Q, Reent, Sp, SoFar, Pos);
- more ->
- {more,[{Reent,Sp},$\\|Cs0],SoFar0,Pos};
- error ->
- pre_string_error(Q, Sp, SoFar0, Pos)
- end;
-pre_string([C|Cs], Q, Reent, Sp, SoFar, Pos) ->
- pre_string(Cs, Q, Reent, Sp, [C|SoFar], Pos);
-pre_string([], _, Reent, Sp, SoFar, Pos) ->
- {more,[{Reent,Sp}],SoFar,Pos};
-pre_string(eof, Q, _, Sp, SoFar, Pos) ->
- pre_string_error(Q, Sp, SoFar, Pos).
-
-pre_string_error(Q, Sp, SoFar, Pos) ->
- S = reverse(string:substr(SoFar, 1, string:chr(SoFar, Q)-1)),
- pre_error({string,Q,string:substr(S, 1, 16)}, Sp, Pos).
-
-pre_char([C|Cs], SoFar) -> pre_char(C, Cs, SoFar);
-pre_char([], _) -> more;
-pre_char(eof, _) -> error.
-
-pre_char($\\, Cs, SoFar) ->
- pre_escape(Cs, SoFar);
-pre_char(C, Cs, SoFar) ->
- {Cs,[C|SoFar]}.
-
-pre_escape([$^|Cs0], SoFar) ->
- case Cs0 of
- [C3|Cs] ->
- {Cs,[C3,$^,$\\|SoFar]};
- [] -> more;
- eof -> error
- end;
-pre_escape([C|Cs], SoFar) ->
- {Cs,[C,$\\|SoFar]};
-pre_escape([], _) -> more;
-pre_escape(eof, _) -> error.
-
-%% pre_comment([Char], SoFar, Pos)
-%% Comments are replaced by one SPACE.
-
-pre_comment([$\n|Cs], SoFar, Pos) ->
- pre_scan(Cs, [$\n,$\s|SoFar], Pos+1); %Terminate comment
-pre_comment([_|Cs], SoFar, Pos) ->
- pre_comment(Cs, SoFar, Pos);
-pre_comment([], SoFar, Pos) ->
- {more,['%'],SoFar,Pos};
-pre_comment(eof, Sofar, Pos) ->
- pre_scan(eof, [$\s|Sofar], Pos).
-
-pre_error(E, Epos, Pos) ->
- {error,{Epos,core_scan,E}, Pos}.
-
-%% scan(CharList, StartPos)
-%% This takes a list of characters and tries to tokenise them.
-%%
-%% The token list is built in reverse order (in a stack) to save appending
-%% and then reversed when all the tokens have been collected. Most tokens
-%% are built in the same way.
-%%
-%% Returns:
-%% {ok,[Tok]}
-%% {error,{ErrorPos,core_scan,What}}
-
-scan(Cs, Pos) ->
- scan1(Cs, [], Pos).
-
-%% scan1(Characters, TokenStack, Position)
-%% Scan a list of characters into tokens.
-
-scan1([$\n|Cs], Toks, Pos) -> %Skip newline
- scan1(Cs, Toks, Pos+1);
-scan1([C|Cs], Toks, Pos) when C >= $\000, C =< $\s -> %Skip control chars
- scan1(Cs, Toks, Pos);
-scan1([C|Cs], Toks, Pos) when C >= $\200, C =< $\240 ->
- scan1(Cs, Toks, Pos);
-scan1([C|Cs], Toks, Pos) when C >= $a, C =< $z -> %Keywords
- scan_key_word(C, Cs, Toks, Pos);
-scan1([C|Cs], Toks, Pos) when C >= $ß, C =< $ÿ, C /= $÷ ->
- scan_key_word(C, Cs, Toks, Pos);
-scan1([C|Cs], Toks, Pos) when C >= $A, C =< $Z -> %Variables
- scan_variable(C, Cs, Toks, Pos);
-scan1([C|Cs], Toks, Pos) when C >= $À, C =< $Þ, C /= $× ->
- scan_variable(C, Cs, Toks, Pos);
-scan1([C|Cs], Toks, Pos) when C >= $0, C =< $9 -> %Numbers
- scan_number(C, Cs, Toks, Pos);
-scan1([$-,C|Cs], Toks, Pos) when C >= $0, C =< $9 -> %Signed numbers
- scan_signed_number($-, C, Cs, Toks, Pos);
-scan1([$+,C|Cs], Toks, Pos) when C >= $0, C =< $9 -> %Signed numbers
- scan_signed_number($+, C, Cs, Toks, Pos);
-scan1([$_|Cs], Toks, Pos) -> %_ variables
- scan_variable($_, Cs, Toks, Pos);
-scan1([$$|Cs0], Toks, Pos) -> %Character constant
- {C,Cs,Pos1} = scan_char(Cs0, Pos),
- scan1(Cs, [{char,Pos,C}|Toks], Pos1);
-scan1([$'|Cs0], Toks, Pos) -> %Atom (always quoted)
- {S,Cs1,Pos1} = scan_string(Cs0, $', Pos),
- case catch list_to_atom(S) of
- A when atom(A) ->
- scan1(Cs1, [{atom,Pos,A}|Toks], Pos1);
- _Error -> scan_error({illegal,atom}, Pos)
- end;
-scan1([$"|Cs0], Toks, Pos) -> %String
- {S,Cs1,Pos1} = scan_string(Cs0, $", Pos),
- scan1(Cs1, [{string,Pos,S}|Toks], Pos1);
-%% Punctuation characters and operators, first recognise multiples.
-scan1("->" ++ Cs, Toks, Pos) ->
- scan1(Cs, [{'->',Pos}|Toks], Pos);
-scan1("-|" ++ Cs, Toks, Pos) ->
- scan1(Cs, [{'-|',Pos}|Toks], Pos);
-scan1([C|Cs], Toks, Pos) -> %Punctuation character
- P = list_to_atom([C]),
- scan1(Cs, [{P,Pos}|Toks], Pos);
-scan1([], Toks0, _) ->
- Toks = reverse(Toks0),
- {ok,Toks}.
-
-%% scan_key_word(FirstChar, CharList, Tokens, Pos)
-%% scan_variable(FirstChar, CharList, Tokens, Pos)
-
-scan_key_word(C, Cs0, Toks, Pos) ->
- {Wcs,Cs} = scan_name(Cs0, []),
- case catch list_to_atom([C|reverse(Wcs)]) of
- Name when atom(Name) ->
- scan1(Cs, [{Name,Pos}|Toks], Pos);
- _Error -> scan_error({illegal,atom}, Pos)
- end.
-
-scan_variable(C, Cs0, Toks, Pos) ->
- {Wcs,Cs} = scan_name(Cs0, []),
- case catch list_to_atom([C|reverse(Wcs)]) of
- Name when atom(Name) ->
- scan1(Cs, [{var,Pos,Name}|Toks], Pos);
- _Error -> scan_error({illegal,var}, Pos)
- end.
-
-%% scan_name(Cs) -> lists:splitwith(fun (C) -> name_char(C) end, Cs).
-
-scan_name([C|Cs], Ncs) ->
- case name_char(C) of
- true -> scan_name(Cs, [C|Ncs]);
- false -> {Ncs,[C|Cs]} %Must rebuild here, sigh!
- end;
-scan_name([], Ncs) ->
- {Ncs,[]}.
-
-name_char(C) when C >= $a, C =< $z -> true;
-name_char(C) when C >= $ß, C =< $ÿ, C /= $÷ -> true;
-name_char(C) when C >= $A, C =< $Z -> true;
-name_char(C) when C >= $À, C =< $Þ, C /= $× -> true;
-name_char(C) when C >= $0, C =< $9 -> true;
-name_char($_) -> true;
-name_char($@) -> true;
-name_char(_) -> false.
-
-%% scan_string(CharList, QuoteChar, Pos) -> {StringChars,RestChars,NewPos}.
-
-scan_string(Cs, Q, Pos) ->
- scan_string(Cs, [], Q, Pos).
-
-scan_string([Q|Cs], Scs, Q, Pos) ->
- {reverse(Scs),Cs,Pos};
-scan_string([$\n|Cs], Scs, Q, Pos) ->
- scan_string(Cs, [$\n|Scs], Q, Pos+1);
-scan_string([$\\|Cs0], Scs, Q, Pos) ->
- {C,Cs,Pos1} = scan_escape(Cs0, Pos),
- scan_string(Cs, [C|Scs], Q, Pos1);
-scan_string([C|Cs], Scs, Q, Pos) ->
- scan_string(Cs, [C|Scs], Q, Pos).
-
-%% scan_char(Chars, Pos) -> {Char,RestChars,NewPos}.
-%% Read a single character from a character constant. The pre-scan
-%% phase has checked for errors here.
-
-scan_char([$\\|Cs], Pos) ->
- scan_escape(Cs, Pos);
-scan_char([$\n|Cs], Pos) -> %Newline
- {$\n,Cs,Pos+1};
-scan_char([C|Cs], Pos) ->
- {C,Cs,Pos}.
-
-scan_escape([O1,O2,O3|Cs], Pos) when %\<1-3> octal digits
- O1 >= $0, O1 =< $7, O2 >= $0, O2 =< $7, O3 >= $0, O3 =< $7 ->
- Val = (O1*8 + O2)*8 + O3 - 73*$0,
- {Val,Cs,Pos};
-scan_escape([O1,O2|Cs], Pos) when
- O1 >= $0, O1 =< $7, O2 >= $0, O2 =< $7 ->
- Val = (O1*8 + O2) - 9*$0,
- {Val,Cs,Pos};
-scan_escape([O1|Cs], Pos) when
- O1 >= $0, O1 =< $7 ->
- {O1 - $0,Cs,Pos};
-scan_escape([$^,C|Cs], Pos) -> %\^X -> CTL-X
- Val = C band 31,
- {Val,Cs,Pos};
-%scan_escape([$\n,C1|Cs],Pos) ->
-% {C1,Cs,Pos+1};
-%scan_escape([C,C1|Cs],Pos) when C >= $\000, C =< $\s ->
-% {C1,Cs,Pos};
-scan_escape([$\n|Cs],Pos) ->
- {$\n,Cs,Pos+1};
-scan_escape([C0|Cs],Pos) ->
- C = escape_char(C0),
- {C,Cs,Pos}.
-
-escape_char($n) -> $\n; %\n = LF
-escape_char($r) -> $\r; %\r = CR
-escape_char($t) -> $\t; %\t = TAB
-escape_char($v) -> $\v; %\v = VT
-escape_char($b) -> $\b; %\b = BS
-escape_char($f) -> $\f; %\f = FF
-escape_char($e) -> $\e; %\e = ESC
-escape_char($s) -> $\s; %\s = SPC
-escape_char($d) -> $\d; %\d = DEL
-escape_char(C) -> C.
-
-%% scan_number(Char, CharList, TokenStack, Pos)
-%% We can handle simple radix notation:
-%% # - the digits read in that base
-%% - the digits in base 10
-%% .
-%% .E+-
-%%
-%% Except for explicitly based integers we build a list of all the
-%% characters and then use list_to_integer/1 or list_to_float/1 to
-%% generate the value.
-
-%% SPos == Start position
-%% CPos == Current position
-
-scan_number(C, Cs0, Toks, Pos) ->
- {Ncs,Cs,Pos1} = scan_integer(Cs0, [C], Pos),
- scan_after_int(Cs, Ncs, Toks, Pos, Pos1).
-
-scan_signed_number(S, C, Cs0, Toks, Pos) ->
- {Ncs,Cs,Pos1} = scan_integer(Cs0, [C,S], Pos),
- scan_after_int(Cs, Ncs, Toks, Pos, Pos1).
-
-scan_integer([C|Cs], Stack, Pos) when C >= $0, C =< $9 ->
- scan_integer(Cs, [C|Stack], Pos);
-scan_integer(Cs, Stack, Pos) ->
- {Stack,Cs,Pos}.
-
-scan_after_int([$.,C|Cs0], Ncs0, Toks, SPos, CPos) when C >= $0, C =< $9 ->
- {Ncs,Cs,CPos1} = scan_integer(Cs0, [C,$.|Ncs0], CPos),
- scan_after_fraction(Cs, Ncs, Toks, SPos, CPos1);
-scan_after_int([$#|Cs], Ncs, Toks, SPos, CPos) ->
- case list_to_integer(reverse(Ncs)) of
- Base when Base >= 2, Base =< 16 ->
- scan_based_int(Cs, 0, Base, Toks, SPos, CPos);
- Base ->
- scan_error({base,Base}, CPos)
- end;
-scan_after_int(Cs, Ncs, Toks, SPos, CPos) ->
- N = list_to_integer(reverse(Ncs)),
- scan1(Cs, [{integer,SPos,N}|Toks], CPos).
-
-scan_based_int([C|Cs], SoFar, Base, Toks, SPos, CPos) when
- C >= $0, C =< $9, C < Base + $0 ->
- Next = SoFar * Base + (C - $0),
- scan_based_int(Cs, Next, Base, Toks, SPos, CPos);
-scan_based_int([C|Cs], SoFar, Base, Toks, SPos, CPos) when
- C >= $a, C =< $f, C < Base + $a - 10 ->
- Next = SoFar * Base + (C - $a + 10),
- scan_based_int(Cs, Next, Base, Toks, SPos, CPos);
-scan_based_int([C|Cs], SoFar, Base, Toks, SPos, CPos) when
- C >= $A, C =< $F, C < Base + $A - 10 ->
- Next = SoFar * Base + (C - $A + 10),
- scan_based_int(Cs, Next, Base, Toks, SPos, CPos);
-scan_based_int(Cs, SoFar, _, Toks, SPos, CPos) ->
- scan1(Cs, [{integer,SPos,SoFar}|Toks], CPos).
-
-scan_after_fraction([$E|Cs], Ncs, Toks, SPos, CPos) ->
- scan_exponent(Cs, [$E|Ncs], Toks, SPos, CPos);
-scan_after_fraction([$e|Cs], Ncs, Toks, SPos, CPos) ->
- scan_exponent(Cs, [$E|Ncs], Toks, SPos, CPos);
-scan_after_fraction(Cs, Ncs, Toks, SPos, CPos) ->
- case catch list_to_float(reverse(Ncs)) of
- N when float(N) ->
- scan1(Cs, [{float,SPos,N}|Toks], CPos);
- _Error -> scan_error({illegal,float}, SPos)
- end.
-
-%% scan_exponent(CharList, NumberCharStack, TokenStack, StartPos, CurPos)
-%% Generate an error here if E{+|-} not followed by any digits.
-
-scan_exponent([$+|Cs], Ncs, Toks, SPos, CPos) ->
- scan_exponent1(Cs, [$+|Ncs], Toks, SPos, CPos);
-scan_exponent([$-|Cs], Ncs, Toks, SPos, CPos) ->
- scan_exponent1(Cs, [$-|Ncs], Toks, SPos, CPos);
-scan_exponent(Cs, Ncs, Toks, SPos, CPos) ->
- scan_exponent1(Cs, Ncs, Toks, SPos, CPos).
-
-scan_exponent1([C|Cs0], Ncs0, Toks, SPos, CPos) when C >= $0, C =< $9 ->
- {Ncs,Cs,CPos1} = scan_integer(Cs0, [C|Ncs0], CPos),
- case catch list_to_float(reverse(Ncs)) of
- N when float(N) ->
- scan1(Cs, [{float,SPos,N}|Toks], CPos1);
- _Error -> scan_error({illegal,float}, SPos)
- end;
-scan_exponent1(_, _, _, _, CPos) ->
- scan_error(float, CPos).
-
-scan_error(In, Pos) ->
- {error,{Pos,core_scan,In}}.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/erl_bifs.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/erl_bifs.erl
deleted file mode 100644
index 088f44f9fd..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/erl_bifs.erl
+++ /dev/null
@@ -1,486 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: erl_bifs.erl,v 1.2 2009/09/17 09:46:19 kostis Exp $
-%%
-%% Purpose: Information about the Erlang built-in functions.
-
--module(erl_bifs).
-
--export([is_bif/3, is_guard_bif/3, is_pure/3, is_safe/3]).
-
-
-%% =====================================================================
-%% is_bif(Module, Name, Arity) -> boolean()
-%%
-%% Module = Name = atom()
-%% Arity = integer()
-%%
-%% Returns `true' if the function `Module:Name/Arity' is a Built-In
-%% Function (BIF) of Erlang. BIFs "come with the implementation",
-%% and can be assumed to exist and have the same behaviour in any
-%% later versions of the same implementation of the language. Being
-%% a BIF does *not* imply that the function belongs to the module
-%% `erlang', nor that it is implemented in C or assembler (cf.
-%% `erlang:is_builtin/3'), or that it is auto-imported by the
-%% compiler (cf. `erl_internal:bif/3').
-
-is_bif(erlang, '!', 2) -> true;
-is_bif(erlang, '*', 2) -> true;
-is_bif(erlang, '+', 1) -> true;
-is_bif(erlang, '+', 2) -> true;
-is_bif(erlang, '++', 2) -> true;
-is_bif(erlang, '-', 1) -> true;
-is_bif(erlang, '-', 2) -> true;
-is_bif(erlang, '--', 2) -> true;
-is_bif(erlang, '/', 2) -> true;
-is_bif(erlang, '/=', 2) -> true;
-is_bif(erlang, '<', 2) -> true;
-is_bif(erlang, '=/=', 2) -> true;
-is_bif(erlang, '=:=', 2) -> true;
-is_bif(erlang, '=<', 2) -> true;
-is_bif(erlang, '==', 2) -> true;
-is_bif(erlang, '>', 2) -> true;
-is_bif(erlang, '>=', 2) -> true;
-is_bif(erlang, 'and', 2) -> true;
-is_bif(erlang, 'band', 2) -> true;
-is_bif(erlang, 'bnot', 1) -> true;
-is_bif(erlang, 'bor', 2) -> true;
-is_bif(erlang, 'bsl', 2) -> true;
-is_bif(erlang, 'bsr', 2) -> true;
-is_bif(erlang, 'bxor', 2) -> true;
-is_bif(erlang, 'div', 2) -> true;
-is_bif(erlang, 'not', 1) -> true;
-is_bif(erlang, 'or', 2) -> true;
-is_bif(erlang, 'rem', 2) -> true;
-is_bif(erlang, 'xor', 2) -> true;
-is_bif(erlang, abs, 1) -> true;
-is_bif(erlang, append_element, 2) -> true;
-is_bif(erlang, apply, 2) -> true;
-is_bif(erlang, apply, 3) -> true;
-is_bif(erlang, atom_to_list, 1) -> true;
-is_bif(erlang, binary_to_list, 1) -> true;
-is_bif(erlang, binary_to_list, 3) -> true;
-is_bif(erlang, binary_to_term, 1) -> true;
-is_bif(erlang, cancel_timer, 1) -> true;
-is_bif(erlang, concat_binary, 1) -> true;
-is_bif(erlang, date, 0) -> true;
-is_bif(erlang, demonitor, 1) -> true;
-is_bif(erlang, disconnect_node, 1) -> true;
-is_bif(erlang, display, 1) -> true;
-is_bif(erlang, element, 2) -> true;
-is_bif(erlang, erase, 0) -> true;
-is_bif(erlang, erase, 1) -> true;
-is_bif(erlang, error, 1) -> true;
-is_bif(erlang, error, 2) -> true;
-is_bif(erlang, exit, 1) -> true;
-is_bif(erlang, exit, 2) -> true;
-is_bif(erlang, fault, 1) -> true;
-is_bif(erlang, fault, 2) -> true;
-is_bif(erlang, float, 1) -> true;
-is_bif(erlang, float_to_list, 1) -> true;
-is_bif(erlang, fun_info, 1) -> true;
-is_bif(erlang, fun_info, 2) -> true;
-is_bif(erlang, fun_to_list, 1) -> true;
-is_bif(erlang, get, 0) -> true;
-is_bif(erlang, get, 1) -> true;
-is_bif(erlang, get_cookie, 0) -> true;
-is_bif(erlang, get_keys, 1) -> true;
-is_bif(erlang, group_leader, 0) -> true;
-is_bif(erlang, group_leader, 2) -> true;
-is_bif(erlang, halt, 0) -> false;
-is_bif(erlang, halt, 1) -> false;
-is_bif(erlang, hash, 2) -> false;
-is_bif(erlang, hd, 1) -> true;
-is_bif(erlang, info, 1) -> true;
-is_bif(erlang, integer_to_list, 1) -> true;
-is_bif(erlang, is_alive, 0) -> true;
-is_bif(erlang, is_atom, 1) -> true;
-is_bif(erlang, is_binary, 1) -> true;
-is_bif(erlang, is_boolean, 1) -> true;
-is_bif(erlang, is_builtin, 3) -> true;
-is_bif(erlang, is_constant, 1) -> true;
-is_bif(erlang, is_float, 1) -> true;
-is_bif(erlang, is_function, 1) -> true;
-is_bif(erlang, is_integer, 1) -> true;
-is_bif(erlang, is_list, 1) -> true;
-is_bif(erlang, is_number, 1) -> true;
-is_bif(erlang, is_pid, 1) -> true;
-is_bif(erlang, is_port, 1) -> true;
-is_bif(erlang, is_process_alive, 1) -> true;
-is_bif(erlang, is_record, 3) -> true;
-is_bif(erlang, is_reference, 1) -> true;
-is_bif(erlang, is_tuple, 1) -> true;
-is_bif(erlang, length, 1) -> true;
-is_bif(erlang, link, 1) -> true;
-is_bif(erlang, list_to_atom, 1) -> true;
-is_bif(erlang, list_to_binary, 1) -> true;
-is_bif(erlang, list_to_float, 1) -> true;
-is_bif(erlang, list_to_integer, 1) -> true;
-is_bif(erlang, list_to_pid, 1) -> true;
-is_bif(erlang, list_to_tuple, 1) -> true;
-is_bif(erlang, loaded, 0) -> true;
-is_bif(erlang, localtime, 0) -> true;
-is_bif(erlang, localtime_to_universaltime, 1) -> true;
-is_bif(erlang, make_ref, 0) -> true;
-is_bif(erlang, make_tuple, 2) -> true;
-is_bif(erlang, md5, 1) -> true;
-is_bif(erlang, md5_final, 1) -> true;
-is_bif(erlang, md5_init, 0) -> true;
-is_bif(erlang, md5_update, 2) -> true;
-is_bif(erlang, monitor, 2) -> true;
-is_bif(erlang, monitor_node, 2) -> true;
-is_bif(erlang, node, 0) -> true;
-is_bif(erlang, node, 1) -> true;
-is_bif(erlang, nodes, 0) -> true;
-is_bif(erlang, now, 0) -> true;
-is_bif(erlang, open_port, 2) -> true;
-is_bif(erlang, phash, 2) -> true;
-is_bif(erlang, pid_to_list, 1) -> true;
-is_bif(erlang, port_close, 2) -> true;
-is_bif(erlang, port_command, 2) -> true;
-is_bif(erlang, port_connect, 2) -> true;
-is_bif(erlang, port_control, 3) -> true;
-is_bif(erlang, port_info, 2) -> true;
-is_bif(erlang, port_to_list, 1) -> true;
-is_bif(erlang, ports, 0) -> true;
-is_bif(erlang, pre_loaded, 0) -> true;
-is_bif(erlang, process_display, 2) -> true;
-is_bif(erlang, process_flag, 2) -> true;
-is_bif(erlang, process_flag, 3) -> true;
-is_bif(erlang, process_info, 1) -> true;
-is_bif(erlang, process_info, 2) -> true;
-is_bif(erlang, processes, 0) -> true;
-is_bif(erlang, put, 2) -> true;
-is_bif(erlang, read_timer, 1) -> true;
-is_bif(erlang, ref_to_list, 1) -> true;
-is_bif(erlang, register, 2) -> true;
-is_bif(erlang, registered, 0) -> true;
-is_bif(erlang, resume_process, 1) -> true;
-is_bif(erlang, round, 1) -> true;
-is_bif(erlang, self, 0) -> true;
-is_bif(erlang, send_after, 3) -> true;
-is_bif(erlang, set_cookie, 2) -> true;
-is_bif(erlang, setelement, 3) -> true;
-is_bif(erlang, size, 1) -> true;
-is_bif(erlang, spawn, 1) -> true;
-is_bif(erlang, spawn, 2) -> true;
-is_bif(erlang, spawn, 3) -> true;
-is_bif(erlang, spawn, 4) -> true;
-is_bif(erlang, spawn_link, 1) -> true;
-is_bif(erlang, spawn_link, 2) -> true;
-is_bif(erlang, spawn_link, 3) -> true;
-is_bif(erlang, spawn_link, 4) -> true;
-is_bif(erlang, spawn_opt, 4) -> true;
-is_bif(erlang, split_binary, 2) -> true;
-is_bif(erlang, start_timer, 3) -> true;
-is_bif(erlang, statistics, 1) -> true;
-is_bif(erlang, suspend_process, 1) -> true;
-is_bif(erlang, system_flag, 2) -> true;
-is_bif(erlang, system_info, 1) -> true;
-is_bif(erlang, term_to_binary, 1) -> true;
-is_bif(erlang, term_to_binary, 2) -> true;
-is_bif(erlang, throw, 1) -> true;
-is_bif(erlang, time, 0) -> true;
-is_bif(erlang, tl, 1) -> true;
-is_bif(erlang, trace, 3) -> true;
-is_bif(erlang, trace_info, 2) -> true;
-is_bif(erlang, trace_pattern, 2) -> true;
-is_bif(erlang, trace_pattern, 3) -> true;
-is_bif(erlang, trunc, 1) -> true;
-is_bif(erlang, tuple_to_list, 1) -> true;
-is_bif(erlang, universaltime, 0) -> true;
-is_bif(erlang, universaltime_to_localtime, 1) -> true;
-is_bif(erlang, unlink, 1) -> true;
-is_bif(erlang, unregister, 1) -> true;
-is_bif(erlang, whereis, 1) -> true;
-is_bif(erlang, yield, 0) -> true;
-is_bif(lists, append, 2) -> true;
-is_bif(lists, reverse, 1) -> true;
-is_bif(lists, reverse, 2) -> true;
-is_bif(lists, subtract, 2) -> true;
-is_bif(math, acos, 1) -> true;
-is_bif(math, acosh, 1) -> true;
-is_bif(math, asin, 1) -> true;
-is_bif(math, asinh, 1) -> true;
-is_bif(math, atan, 1) -> true;
-is_bif(math, atan2, 2) -> true;
-is_bif(math, atanh, 1) -> true;
-is_bif(math, cos, 1) -> true;
-is_bif(math, cosh, 1) -> true;
-is_bif(math, erf, 1) -> true;
-is_bif(math, erfc, 1) -> true;
-is_bif(math, exp, 1) -> true;
-is_bif(math, log, 1) -> true;
-is_bif(math, log10, 1) -> true;
-is_bif(math, pow, 2) -> true;
-is_bif(math, sin, 1) -> true;
-is_bif(math, sinh, 1) -> true;
-is_bif(math, sqrt, 1) -> true;
-is_bif(math, tan, 1) -> true;
-is_bif(math, tanh, 1) -> true;
-is_bif(_, _, _) -> false.
-
-
-%% =====================================================================
-%% is_guard_bif(Module, Name, Arity) -> boolean()
-%%
-%% Module = Name = atom()
-%% Arity = integer()
-%%
-%% Returns `true' if the built-in function `Module:Name/Arity' may
-%% be called from a clause guard. Note that such "guard BIFs" are
-%% not necessarily "pure", since some (notably `erlang:self/0') may
-%% depend on the current state, nor "safe", since many guard BIFs
-%% can fail. Also note that even a "pure" function could be
-%% unsuitable for calling from a guard because of its time or space
-%% complexity.
-
-is_guard_bif(erlang, '*', 2) -> true;
-is_guard_bif(erlang, '+', 1) -> true;
-is_guard_bif(erlang, '+', 2) -> true;
-is_guard_bif(erlang, '-', 1) -> true;
-is_guard_bif(erlang, '-', 2) -> true;
-is_guard_bif(erlang, '/', 2) -> true;
-is_guard_bif(erlang, '/=', 2) -> true;
-is_guard_bif(erlang, '<', 2) -> true;
-is_guard_bif(erlang, '=/=', 2) -> true;
-is_guard_bif(erlang, '=:=', 2) -> true;
-is_guard_bif(erlang, '=<', 2) -> true;
-is_guard_bif(erlang, '==', 2) -> true;
-is_guard_bif(erlang, '>', 2) -> true;
-is_guard_bif(erlang, '>=', 2) -> true;
-is_guard_bif(erlang, 'and', 2) -> true;
-is_guard_bif(erlang, 'band', 2) -> true;
-is_guard_bif(erlang, 'bnot', 1) -> true;
-is_guard_bif(erlang, 'bor', 2) -> true;
-is_guard_bif(erlang, 'bsl', 2) -> true;
-is_guard_bif(erlang, 'bsr', 2) -> true;
-is_guard_bif(erlang, 'bxor', 2) -> true;
-is_guard_bif(erlang, 'div', 2) -> true;
-is_guard_bif(erlang, 'not', 1) -> true;
-is_guard_bif(erlang, 'or', 2) -> true;
-is_guard_bif(erlang, 'rem', 2) -> true;
-is_guard_bif(erlang, 'xor', 2) -> true;
-is_guard_bif(erlang, abs, 1) -> true;
-is_guard_bif(erlang, element, 2) -> true;
-is_guard_bif(erlang, error, 1) -> true; % unorthodox
-is_guard_bif(erlang, exit, 1) -> true; % unorthodox
-is_guard_bif(erlang, fault, 1) -> true; % unorthodox
-is_guard_bif(erlang, float, 1) -> true; % (the type coercion function)
-is_guard_bif(erlang, hd, 1) -> true;
-is_guard_bif(erlang, is_atom, 1) -> true;
-is_guard_bif(erlang, is_boolean, 1) -> true;
-is_guard_bif(erlang, is_binary, 1) -> true;
-is_guard_bif(erlang, is_constant, 1) -> true;
-is_guard_bif(erlang, is_float, 1) -> true;
-is_guard_bif(erlang, is_function, 1) -> true;
-is_guard_bif(erlang, is_integer, 1) -> true;
-is_guard_bif(erlang, is_list, 1) -> true;
-is_guard_bif(erlang, is_number, 1) -> true;
-is_guard_bif(erlang, is_pid, 1) -> true;
-is_guard_bif(erlang, is_port, 1) -> true;
-is_guard_bif(erlang, is_reference, 1) -> true;
-is_guard_bif(erlang, is_tuple, 1) -> true;
-is_guard_bif(erlang, length, 1) -> true;
-is_guard_bif(erlang, list_to_atom, 1) -> true; % unorthodox
-is_guard_bif(erlang, node, 0) -> true; % (not pure)
-is_guard_bif(erlang, node, 1) -> true; % (not pure)
-is_guard_bif(erlang, round, 1) -> true;
-is_guard_bif(erlang, self, 0) -> true; % (not pure)
-is_guard_bif(erlang, size, 1) -> true;
-is_guard_bif(erlang, throw, 1) -> true; % unorthodox
-is_guard_bif(erlang, tl, 1) -> true;
-is_guard_bif(erlang, trunc, 1) -> true;
-is_guard_bif(math, acos, 1) -> true; % unorthodox
-is_guard_bif(math, acosh, 1) -> true; % unorthodox
-is_guard_bif(math, asin, 1) -> true; % unorthodox
-is_guard_bif(math, asinh, 1) -> true; % unorthodox
-is_guard_bif(math, atan, 1) -> true; % unorthodox
-is_guard_bif(math, atan2, 2) -> true; % unorthodox
-is_guard_bif(math, atanh, 1) -> true; % unorthodox
-is_guard_bif(math, cos, 1) -> true; % unorthodox
-is_guard_bif(math, cosh, 1) -> true; % unorthodox
-is_guard_bif(math, erf, 1) -> true; % unorthodox
-is_guard_bif(math, erfc, 1) -> true; % unorthodox
-is_guard_bif(math, exp, 1) -> true; % unorthodox
-is_guard_bif(math, log, 1) -> true; % unorthodox
-is_guard_bif(math, log10, 1) -> true; % unorthodox
-is_guard_bif(math, pow, 2) -> true; % unorthodox
-is_guard_bif(math, sin, 1) -> true; % unorthodox
-is_guard_bif(math, sinh, 1) -> true; % unorthodox
-is_guard_bif(math, sqrt, 1) -> true; % unorthodox
-is_guard_bif(math, tan, 1) -> true; % unorthodox
-is_guard_bif(math, tanh, 1) -> true; % unorthodox
-is_guard_bif(_, _, _) -> false.
-
-
-%% =====================================================================
-%% is_pure(Module, Name, Arity) -> boolean()
-%%
-%% Module = Name = atom()
-%% Arity = integer()
-%%
-%% Returns `true' if the function `Module:Name/Arity' does not
-%% affect the state, nor depend on the state, although its
-%% evaluation is not guaranteed to complete normally for all input.
-
-is_pure(erlang, '*', 2) -> true;
-is_pure(erlang, '+', 1) -> true; % (even for non-numbers)
-is_pure(erlang, '+', 2) -> true;
-is_pure(erlang, '++', 2) -> true;
-is_pure(erlang, '-', 1) -> true;
-is_pure(erlang, '-', 2) -> true;
-is_pure(erlang, '--', 2) -> true;
-is_pure(erlang, '/', 2) -> true;
-is_pure(erlang, '/=', 2) -> true;
-is_pure(erlang, '<', 2) -> true;
-is_pure(erlang, '=/=', 2) -> true;
-is_pure(erlang, '=:=', 2) -> true;
-is_pure(erlang, '=<', 2) -> true;
-is_pure(erlang, '==', 2) -> true;
-is_pure(erlang, '>', 2) -> true;
-is_pure(erlang, '>=', 2) -> true;
-is_pure(erlang, 'and', 2) -> true;
-is_pure(erlang, 'band', 2) -> true;
-is_pure(erlang, 'bnot', 1) -> true;
-is_pure(erlang, 'bor', 2) -> true;
-is_pure(erlang, 'bsl', 2) -> true;
-is_pure(erlang, 'bsr', 2) -> true;
-is_pure(erlang, 'bxor', 2) -> true;
-is_pure(erlang, 'div', 2) -> true;
-is_pure(erlang, 'not', 1) -> true;
-is_pure(erlang, 'or', 2) -> true;
-is_pure(erlang, 'rem', 2) -> true;
-is_pure(erlang, 'xor', 2) -> true;
-is_pure(erlang, abs, 1) -> true;
-is_pure(erlang, atom_to_list, 1) -> true;
-is_pure(erlang, binary_to_list, 1) -> true;
-is_pure(erlang, binary_to_list, 3) -> true;
-is_pure(erlang, concat_binary, 1) -> true;
-is_pure(erlang, element, 2) -> true;
-is_pure(erlang, float, 1) -> true;
-is_pure(erlang, float_to_list, 1) -> true;
-is_pure(erlang, hash, 2) -> false;
-is_pure(erlang, hd, 1) -> true;
-is_pure(erlang, integer_to_list, 1) -> true;
-is_pure(erlang, is_atom, 1) -> true;
-is_pure(erlang, is_boolean, 1) -> true;
-is_pure(erlang, is_binary, 1) -> true;
-is_pure(erlang, is_builtin, 3) -> true;
-is_pure(erlang, is_constant, 1) -> true;
-is_pure(erlang, is_float, 1) -> true;
-is_pure(erlang, is_function, 1) -> true;
-is_pure(erlang, is_integer, 1) -> true;
-is_pure(erlang, is_list, 1) -> true;
-is_pure(erlang, is_number, 1) -> true;
-is_pure(erlang, is_pid, 1) -> true;
-is_pure(erlang, is_port, 1) -> true;
-is_pure(erlang, is_record, 3) -> true;
-is_pure(erlang, is_reference, 1) -> true;
-is_pure(erlang, is_tuple, 1) -> true;
-is_pure(erlang, length, 1) -> true;
-is_pure(erlang, list_to_atom, 1) -> true;
-is_pure(erlang, list_to_binary, 1) -> true;
-is_pure(erlang, list_to_float, 1) -> true;
-is_pure(erlang, list_to_integer, 1) -> true;
-is_pure(erlang, list_to_pid, 1) -> true;
-is_pure(erlang, list_to_tuple, 1) -> true;
-is_pure(erlang, phash, 2) -> false;
-is_pure(erlang, pid_to_list, 1) -> true;
-is_pure(erlang, round, 1) -> true;
-is_pure(erlang, setelement, 3) -> true;
-is_pure(erlang, size, 1) -> true;
-is_pure(erlang, split_binary, 2) -> true;
-is_pure(erlang, term_to_binary, 1) -> true;
-is_pure(erlang, tl, 1) -> true;
-is_pure(erlang, trunc, 1) -> true;
-is_pure(erlang, tuple_to_list, 1) -> true;
-is_pure(lists, append, 2) -> true;
-is_pure(lists, subtract, 2) -> true;
-is_pure(math, acos, 1) -> true;
-is_pure(math, acosh, 1) -> true;
-is_pure(math, asin, 1) -> true;
-is_pure(math, asinh, 1) -> true;
-is_pure(math, atan, 1) -> true;
-is_pure(math, atan2, 2) -> true;
-is_pure(math, atanh, 1) -> true;
-is_pure(math, cos, 1) -> true;
-is_pure(math, cosh, 1) -> true;
-is_pure(math, erf, 1) -> true;
-is_pure(math, erfc, 1) -> true;
-is_pure(math, exp, 1) -> true;
-is_pure(math, log, 1) -> true;
-is_pure(math, log10, 1) -> true;
-is_pure(math, pow, 2) -> true;
-is_pure(math, sin, 1) -> true;
-is_pure(math, sinh, 1) -> true;
-is_pure(math, sqrt, 1) -> true;
-is_pure(math, tan, 1) -> true;
-is_pure(math, tanh, 1) -> true;
-is_pure(_, _, _) -> false.
-
-
-%% =====================================================================
-%% is_safe(Module, Name, Arity) -> boolean()
-%%
-%% Module = Name = atom()
-%% Arity = integer()
-%%
-%% Returns `true' if the function `Module:Name/Arity' is completely
-%% effect free, i.e., if its evaluation always completes normally
-%% and does not affect the state (although the value it returns
-%% might depend on the state).
-
-is_safe(erlang, '/=', 2) -> true;
-is_safe(erlang, '<', 2) -> true;
-is_safe(erlang, '=/=', 2) -> true;
-is_safe(erlang, '=:=', 2) -> true;
-is_safe(erlang, '=<', 2) -> true;
-is_safe(erlang, '==', 2) -> true;
-is_safe(erlang, '>', 2) -> true;
-is_safe(erlang, '>=', 2) -> true;
-is_safe(erlang, date, 0) -> true;
-is_safe(erlang, get, 0) -> true;
-is_safe(erlang, get, 1) -> true;
-is_safe(erlang, get_cookie, 0) -> true;
-is_safe(erlang, get_keys, 1) -> true;
-is_safe(erlang, group_leader, 0) -> true;
-is_safe(erlang, is_alive, 0) -> true;
-is_safe(erlang, is_atom, 1) -> true;
-is_safe(erlang, is_boolean, 1) -> true;
-is_safe(erlang, is_binary, 1) -> true;
-is_safe(erlang, is_constant, 1) -> true;
-is_safe(erlang, is_float, 1) -> true;
-is_safe(erlang, is_function, 1) -> true;
-is_safe(erlang, is_integer, 1) -> true;
-is_safe(erlang, is_list, 1) -> true;
-is_safe(erlang, is_number, 1) -> true;
-is_safe(erlang, is_pid, 1) -> true;
-is_safe(erlang, is_port, 1) -> true;
-is_safe(erlang, is_record, 3) -> true;
-is_safe(erlang, is_reference, 1) -> true;
-is_safe(erlang, is_tuple, 1) -> true;
-is_safe(erlang, make_ref, 0) -> true;
-is_safe(erlang, node, 0) -> true;
-is_safe(erlang, nodes, 0) -> true;
-is_safe(erlang, ports, 0) -> true;
-is_safe(erlang, pre_loaded, 0) -> true;
-is_safe(erlang, processes, 0) -> true;
-is_safe(erlang, registered, 0) -> true;
-is_safe(erlang, self, 0) -> true;
-is_safe(erlang, term_to_binary, 1) -> true;
-is_safe(erlang, time, 0) -> true;
-is_safe(_, _, _) -> false.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/rec_env.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/rec_env.erl
deleted file mode 100644
index 0dd31b71ea..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/rec_env.erl
+++ /dev/null
@@ -1,611 +0,0 @@
-%% =====================================================================
-%% This library is free software; you can redistribute it and/or modify
-%% it under the terms of the GNU Lesser General Public License as
-%% published by the Free Software Foundation; either version 2 of the
-%% License, or (at your option) any later version.
-%%
-%% This library is distributed in the hope that it will be useful, but
-%% WITHOUT ANY WARRANTY; without even the implied warranty of
-%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%% Lesser General Public License for more details.
-%%
-%% You should have received a copy of the GNU Lesser General Public
-%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-%% USA
-%%
-%% $Id: rec_env.erl,v 1.2 2009/09/17 09:46:19 kostis Exp $
-%%
-%% @author Richard Carlsson
-%% @copyright 1999-2004 Richard Carlsson
-%% @doc Abstract environments, supporting self-referential bindings and
-%% automatic new-key generation.
-
-%% The current implementation is based on Erlang standard library
-%% dictionaries.
-
-%%% -define(DEBUG, true).
-
--module(rec_env).
-
--export([bind/3, bind_list/3, bind_recursive/4, delete/2, empty/0,
- get/2, is_defined/2, is_empty/1, keys/1, lookup/2, new_key/1,
- new_key/2, new_keys/2, new_keys/3, size/1, to_list/1]).
-
--ifdef(DEBUG).
--export([test/1, test_custom/1, test_custom/2]).
--endif.
-
--ifdef(DEBUG).
-%% Code for testing:
-%%@hidden
-test(N) ->
- test_0(integer, N).
-
-%%@hidden
-test_custom(N) ->
- F = fun (X) -> list_to_atom("X"++integer_to_list(X)) end,
- test_custom(F, N).
-
-%%@hidden
-test_custom(F, N) ->
- test_0({custom, F}, N).
-
-test_0(Type, N) ->
- put(new_key_calls, 0),
- put(new_key_retries, 0),
- put(new_key_max, 0),
- Env = test_1(Type, N, empty()),
- io:fwrite("\ncalls: ~w.\n", [get(new_key_calls)]),
- io:fwrite("\nretries: ~w.\n", [get(new_key_retries)]),
- io:fwrite("\nmax: ~w.\n", [get(new_key_max)]),
- dict:to_list(element(1,Env)).
-
-test_1(integer = Type, N, Env) when integer(N), N > 0 ->
- Key = new_key(Env),
- test_1(Type, N - 1, bind(Key, value, Env));
-test_1({custom, F} = Type, N, Env) when integer(N), N > 0 ->
- Key = new_key(F, Env),
- test_1(Type, N - 1, bind(Key, value, Env));
-test_1(_,0, Env) ->
- Env.
--endif.
-
-
-%% Representation:
-%%
-%% environment() = [Mapping]
-%%
-%% Mapping = {map, Dict} | {rec, Dict, Dict}
-%% Dict = dict:dictionary()
-%%
-%% An empty environment is a list containing a single `{map, Dict}'
-%% element - empty lists are not valid environments. To find a key in an
-%% environment, it is searched for in each mapping in the list, in
-%% order, until it the key is found in some mapping, or the end of the
-%% list is reached. In a 'rec' mapping, we keep the original dictionary
-%% together with a version where entries may have been deleted - this
-%% makes it possible to garbage collect the entire 'rec' mapping when
-%% all its entries are unused (for example, by being shadowed by later
-%% definitions).
-
-
-
-%% =====================================================================
-%% @type environment(). An abstract environment.
-
-
-%% =====================================================================
-%% @spec empty() -> environment()
-%%
-%% @doc Returns an empty environment.
-
-empty() ->
- [{map, dict:new()}].
-
-
-%% =====================================================================
-%% @spec is_empty(Env::environment()) -> boolean()
-%%
-%% @doc Returns true
if the environment is empty, otherwise
-%% false
.
-
-is_empty([{map, Dict} | Es]) ->
- N = dict:size(Dict),
- if N /= 0 -> false;
- Es == [] -> true;
- true -> is_empty(Es)
- end;
-is_empty([{rec, Dict, _} | Es]) ->
- N = dict:size(Dict),
- if N /= 0 -> false;
- Es == [] -> true;
- true -> is_empty(Es)
- end.
-
-
-%% =====================================================================
-%% @spec size(Env::environment()) -> integer()
-%%
-%% @doc Returns the number of entries in an environment.
-
-%% (The name 'size' cannot be used in local calls, since there exists a
-%% built-in function with the same name.)
-
-size(Env) ->
- env_size(Env).
-
-env_size([{map, Dict}]) ->
- dict:size(Dict);
-env_size([{map, Dict} | Env]) ->
- dict:size(Dict) + env_size(Env);
-env_size([{rec, Dict, _Dict0} | Env]) ->
- dict:size(Dict) + env_size(Env).
-
-
-%% =====================================================================
-%% @spec is_defined(Key, Env) -> boolean()
-%%
-%% Key = term()
-%% Env = environment()
-%%
-%% @doc Returns true
if Key
is bound in the
-%% environment, otherwise false
.
-
-is_defined(Key, [{map, Dict} | Env]) ->
- case dict:is_key(Key, Dict) of
- true ->
- true;
- false when Env == [] ->
- false;
- false ->
- is_defined(Key, Env)
- end;
-is_defined(Key, [{rec, Dict, _Dict0} | Env]) ->
- case dict:is_key(Key, Dict) of
- true ->
- true;
- false ->
- is_defined(Key, Env)
- end.
-
-
-%% =====================================================================
-%% @spec keys(Env::environment()) -> [term()]
-%%
-%% @doc Returns the ordered list of all keys in the environment.
-
-keys(Env) ->
- lists:sort(keys(Env, [])).
-
-keys([{map, Dict}], S) ->
- dict:fetch_keys(Dict) ++ S;
-keys([{map, Dict} | Env], S) ->
- keys(Env, dict:fetch_keys(Dict) ++ S);
-keys([{rec, Dict, _Dict0} | Env], S) ->
- keys(Env, dict:fetch_keys(Dict) ++ S).
-
-
-%% =====================================================================
-%% @spec to_list(Env) -> [{Key, Value}]
-%%
-%% Env = environment()
-%% Key = term()
-%% Value = term()
-%%
-%% @doc Returns an ordered list of {Key, Value}
pairs for
-%% all keys in Env
. Value
is the same as that
-%% returned by {@link get/2}.
-
-to_list(Env) ->
- lists:sort(to_list(Env, [])).
-
-to_list([{map, Dict}], S) ->
- dict:to_list(Dict) ++ S;
-to_list([{map, Dict} | Env], S) ->
- to_list(Env, dict:to_list(Dict) ++ S);
-to_list([{rec, Dict, _Dict0} | Env], S) ->
- to_list(Env, dict:to_list(Dict) ++ S).
-
-
-%% =====================================================================
-%% @spec bind(Key, Value, Env) -> environment()
-%%
-%% Key = term()
-%% Value = term()
-%% Env = environment()
-%%
-%% @doc Make a nonrecursive entry. This binds Key
to
-%% Value
. If the key already existed in the environment,
-%% the old entry is replaced.
-
-%% Note that deletion is done to free old bindings so they can be
-%% garbage collected.
-
-bind(Key, Value, [{map, Dict}]) ->
- [{map, dict:store(Key, Value, Dict)}];
-bind(Key, Value, [{map, Dict} | Env]) ->
- [{map, dict:store(Key, Value, Dict)} | delete_any(Key, Env)];
-bind(Key, Value, Env) ->
- [{map, dict:store(Key, Value, dict:new())} | delete_any(Key, Env)].
-
-
-%% =====================================================================
-%% @spec bind_list(Keys, Values, Env) -> environment()
-%%
-%% Keys = [term()]
-%% Values = [term()]
-%% Env = environment()
-%%
-%% @doc Make N nonrecursive entries. This binds each key in
-%% Keys
to the corresponding value in
-%% Values
. If some key already existed in the environment,
-%% the previous entry is replaced. If Keys
does not have
-%% the same length as Values
, an exception is generated.
-
-bind_list(Ks, Vs, [{map, Dict}]) ->
- [{map, store_list(Ks, Vs, Dict)}];
-bind_list(Ks, Vs, [{map, Dict} | Env]) ->
- [{map, store_list(Ks, Vs, Dict)} | delete_list(Ks, Env)];
-bind_list(Ks, Vs, Env) ->
- [{map, store_list(Ks, Vs, dict:new())} | delete_list(Ks, Env)].
-
-store_list([K | Ks], [V | Vs], Dict) ->
- store_list(Ks, Vs, dict:store(K, V, Dict));
-store_list([], _, Dict) ->
- Dict.
-
-delete_list([K | Ks], Env) ->
- delete_list(Ks, delete_any(K, Env));
-delete_list([], Env) ->
- Env.
-
-%% By not calling `delete' unless we have to, we avoid unnecessary
-%% rewriting of the data.
-
-delete_any(Key, Env) ->
- case is_defined(Key, Env) of
- true ->
- delete(Key, Env);
- false ->
- Env
- end.
-
-%% =====================================================================
-%% @spec delete(Key, Env) -> environment()
-%%
-%% Key = term()
-%% Env = environment()
-%%
-%% @doc Delete an entry. This removes Key
from the
-%% environment.
-
-delete(Key, [{map, Dict} = E | Env]) ->
- case dict:is_key(Key, Dict) of
- true ->
- [{map, dict:erase(Key, Dict)} | Env];
- false ->
- delete_1(Key, Env, E)
- end;
-delete(Key, [{rec, Dict, Dict0} = E | Env]) ->
- case dict:is_key(Key, Dict) of
- true ->
- %% The Dict0 component must be preserved as it is until all
- %% keys in Dict have been deleted.
- Dict1 = dict:erase(Key, Dict),
- case dict:size(Dict1) of
- 0 ->
- Env; % the whole {rec,...} is now garbage
- _ ->
- [{rec, Dict1, Dict0} | Env]
- end;
- false ->
- [E | delete(Key, Env)]
- end.
-
-%% This is just like above, except we pass on the preceding 'map'
-%% mapping in the list to enable merging when removing 'rec' mappings.
-
-delete_1(Key, [{rec, Dict, Dict0} = E | Env], E1) ->
- case dict:is_key(Key, Dict) of
- true ->
- Dict1 = dict:erase(Key, Dict),
- case dict:size(Dict1) of
- 0 ->
- concat(E1, Env);
- _ ->
- [E1, {rec, Dict1, Dict0} | Env]
- end;
- false ->
- [E1, E | delete(Key, Env)]
- end.
-
-concat({map, D1}, [{map, D2} | Env]) ->
- [dict:merge(fun (_K, V1, _V2) -> V1 end, D1, D2) | Env];
-concat(E1, Env) ->
- [E1 | Env].
-
-
-%% =====================================================================
-%% @spec bind_recursive(Keys, Values, Fun, Env) -> NewEnv
-%%
-%% Keys = [term()]
-%% Values = [term()]
-%% Fun = (Value, Env) -> term()
-%% Env = environment()
-%% NewEnv = environment()
-%%
-%% @doc Make N recursive entries. This binds each key in
-%% Keys
to the value of Fun(Value, NewEnv)
for
-%% the corresponding Value
. If Keys
does not
-%% have the same length as Values
, an exception is
-%% generated. If some key already existed in the environment, the old
-%% entry is replaced.
-%%
-%% Note: the function Fun
is evaluated each time one of
-%% the stored keys is looked up, but only then.
-%%
-%% Examples:
-%%
-%% NewEnv = bind_recursive([foo, bar], [1, 2],
-%% fun (V, E) -> V end,
-%% Env)
-%%
-%% This does nothing interesting; get(foo, NewEnv)
yields
-%% 1
and get(bar, NewEnv)
yields
-%% 2
, but there is more overhead than if the {@link
-%% bind_list/3} function had been used.
-%%
-%%
-%% NewEnv = bind_recursive([foo, bar], [1, 2],
-%% fun (V, E) -> {V, E} end,
-%% Env)
-%%
-%% Here, however, get(foo, NewEnv)
will yield {1,
-%% NewEnv}
and get(bar, NewEnv)
will yield {2,
-%% NewEnv}
, i.e., the environment NewEnv
contains
-%% recursive bindings.
-
-bind_recursive([], [], _, Env) ->
- Env;
-bind_recursive(Ks, Vs, F, Env) ->
- F1 = fun (V) ->
- fun (Dict) -> F(V, [{rec, Dict, Dict} | Env]) end
- end,
- Dict = bind_recursive_1(Ks, Vs, F1, dict:new()),
- [{rec, Dict, Dict} | Env].
-
-bind_recursive_1([K | Ks], [V | Vs], F, Dict) ->
- bind_recursive_1(Ks, Vs, F, dict:store(K, F(V), Dict));
-bind_recursive_1([], [], _, Dict) ->
- Dict.
-
-
-%% =====================================================================
-%% @spec lookup(Key, Env) -> error | {ok, Value}
-%%
-%% Key = term()
-%% Env = environment()
-%% Value = term()
-%%
-%% @doc Returns {ok, Value}
if Key
is bound to
-%% Value
in Env
, and error
-%% otherwise.
-
-lookup(Key, [{map, Dict} | Env]) ->
- case dict:find(Key, Dict) of
- {ok, _}=Value ->
- Value;
- error when Env == [] ->
- error;
- error ->
- lookup(Key, Env)
- end;
-lookup(Key, [{rec, Dict, Dict0} | Env]) ->
- case dict:find(Key, Dict) of
- {ok, F} ->
- {ok, F(Dict0)};
- error ->
- lookup(Key, Env)
- end.
-
-
-%% =====================================================================
-%% @spec get(Key, Env) -> Value
-%%
-%% Key = term()
-%% Env = environment()
-%% Value = term()
-%%
-%% @doc Returns the value that Key
is bound to in
-%% Env
. Throws {undefined, Key}
if the key
-%% does not exist in Env
.
-
-get(Key, Env) ->
- case lookup(Key, Env) of
- {ok, Value} -> Value;
- error -> throw({undefined, Key})
- end.
-
-
-%% =====================================================================
-%% The key-generating algorithm could possibly be further improved. The
-%% important thing to keep in mind is, that when we need a new key, we
-%% are generally in mid-traversal of a syntax tree, and existing names
-%% in the tree may be closely grouped and evenly distributed or even
-%% forming a compact range (often having been generated by a "gensym",
-%% or by this very algorithm itself). This means that if we generate an
-%% identifier whose value is too close to those already seen (i.e.,
-%% which are in the environment), it is very probable that we will
-%% shadow a not-yet-seen identifier further down in the tree, the result
-%% being that we induce another later renaming, and end up renaming most
-%% of the identifiers, completely contrary to our intention. We need to
-%% generate new identifiers in a way that avoids such systematic
-%% collisions.
-%%
-%% One way of getting a new key to try when the previous attempt failed
-%% is of course to e.g. add one to the last tried value. However, in
-%% general it's a bad idea to try adjacent identifiers: the percentage
-%% of retries will typically increase a lot, so you may lose big on the
-%% extra lookups while gaining only a little from the quicker
-%% computation.
-%%
-%% We want an initial range that is large enough for most typical cases.
-%% If we start with, say, a range of 10, we might quickly use up most of
-%% the values in the range 1-10 (or 1-100) for new top-level variables -
-%% but as we start traversing the syntax tree, it is quite likely that
-%% exactly those variables will be encountered again (this depends on
-%% how the names in the tree were created), and will then need to be
-%% renamed. If we instead begin with a larger range, it is less likely
-%% that any top-level names that we introduce will shadow names that we
-%% will find in the tree. Of course we cannot know how large is large
-%% enough: for any initial range, there is some syntax tree that uses
-%% all the values in that range, and thus any top-level names introduced
-%% will shadow names in the tree. The point is to avoid this happening
-%% all the time - a range of about 1000 seems enough for most programs.
-%%
-%% The following values have been shown to work well:
-
--define(MINIMUM_RANGE, 1000).
--define(START_RANGE_FACTOR, 50).
--define(MAX_RETRIES, 2). % retries before enlarging range
--define(ENLARGE_FACTOR, 10). % range enlargment factor
-
--ifdef(DEBUG).
-%% If you want to use these process dictionary counters, make sure to
-%% initialise them to zero before you call any of the key-generating
-%% functions.
-%%
-%% new_key_calls total number of calls
-%% new_key_retries failed key generation attempts
-%% new_key_max maximum generated integer value
-%%
--define(measure_calls(),
- put(new_key_calls, 1 + get(new_key_calls))).
--define(measure_max_key(N),
- case N > get(new_key_max) of
- true ->
- put(new_key_max, N);
- false ->
- ok
- end).
--define(measure_retries(N),
- put(new_key_retries, get(new_key_retries) + N)).
--else.
--define(measure_calls(), ok).
--define(measure_max_key(N), ok).
--define(measure_retries(N), ok).
--endif.
-
-
-%% =====================================================================
-%% @spec new_key(Env::environment()) -> integer()
-%%
-%% @doc Returns an integer which is not already used as key in the
-%% environment. New integers are generated using an algorithm which
-%% tries to keep the values randomly distributed within a reasonably
-%% small range relative to the number of entries in the environment.
-%%
-%% This function uses the Erlang standard library module
-%% random
to generate new keys.
-%%
-%% Note that only the new key is returned; the environment itself is
-%% not updated by this function.
-
-new_key(Env) ->
- new_key(fun (X) -> X end, Env).
-
-
-%% =====================================================================
-%% @spec new_key(Function, Env) -> term()
-%%
-%% Function = (integer()) -> term()
-%% Env = environment()
-%%
-%% @doc Returns a term which is not already used as key in the
-%% environment. The term is generated by applying Function
-%% to an integer generated as in {@link new_key/1}.
-%%
-%% Note that only the generated term is returned; the environment
-%% itself is not updated by this function.
-
-new_key(F, Env) ->
- ?measure_calls(),
- R = start_range(Env),
-%%% io:fwrite("Start range: ~w.\n", [R]),
- new_key(R, F, Env).
-
-new_key(R, F, Env) ->
- new_key(generate(R, R), R, 0, F, Env).
-
-new_key(N, R, T, F, Env) when T < ?MAX_RETRIES ->
- A = F(N),
- case is_defined(A, Env) of
- true ->
-%%% io:fwrite("CLASH: ~w.\n", [A]),
- new_key(generate(N, R), R, T + 1, F, Env);
- false ->
- ?measure_max_key(N),
- ?measure_retries(T),
-%%% io:fwrite("New: ~w.\n", [N]),
- A
- end;
-new_key(N, R, _T, F, Env) ->
- %% Too many retries - enlarge the range and start over.
- ?measure_retries((_T + 1)),
- R1 = trunc(R * ?ENLARGE_FACTOR),
-%%% io:fwrite("**NEW RANGE**: ~w.\n", [R1]),
- new_key(generate(N, R1), R1, 0, F, Env).
-
-start_range(Env) ->
- max(env_size(Env) * ?START_RANGE_FACTOR, ?MINIMUM_RANGE).
-
-max(X, Y) when X > Y -> X;
-max(_, Y) -> Y.
-
-%% The previous key might or might not be used to compute the next key
-%% to be tried. It is currently not used.
-%%
-%% In order to avoid causing cascading renamings, it is important that
-%% this function does not generate values in order, but
-%% (pseudo-)randomly distributed over the range.
-
-generate(_N, Range) ->
- random:uniform(Range). % works well
-
-
-%% =====================================================================
-%% @spec new_keys(N, Env) -> [integer()]
-%%
-%% N = integer()
-%% Env = environment()
-%%
-%% @doc Returns a list of N
distinct integers that are not
-%% already used as keys in the environment. See {@link new_key/1} for
-%% details.
-
-new_keys(N, Env) when integer(N) ->
- new_keys(N, fun (X) -> X end, Env).
-
-
-%% =====================================================================
-%% @spec new_keys(N, Function, Env) -> [term()]
-%%
-%% N = integer()
-%% Function = (integer()) -> term()
-%% Env = environment()
-%%
-%% @doc Returns a list of N
distinct terms that are not
-%% already used as keys in the environment. See {@link new_key/3} for
-%% details.
-
-new_keys(N, F, Env) when integer(N) ->
- R = start_range(Env),
- new_keys(N, [], R, F, Env).
-
-new_keys(N, Ks, R, F, Env) when N > 0 ->
- Key = new_key(R, F, Env),
- Env1 = bind(Key, true, Env), % dummy binding
- new_keys(N - 1, [Key | Ks], R, F, Env1);
-new_keys(0, Ks, _, _, _) ->
- Ks.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_expand_pmod.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_expand_pmod.erl
deleted file mode 100644
index c5052b0e51..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_expand_pmod.erl
+++ /dev/null
@@ -1,425 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: sys_expand_pmod.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
-%%
--module(sys_expand_pmod).
-
-%% Expand function definition forms of parameterized module. We assume
-%% all record definitions, imports, queries, etc., have been expanded
-%% away. Any calls on the form 'foo(...)' must be calls to local
-%% functions. Auto-generated functions (module_info,...) have not yet
-%% been added to the function definitions, but are listed in 'defined'
-%% and 'exports'. The 'new/N' function is neither added to the
-%% definitions nor to the 'exports'/'defines' lists yet.
-
--export([forms/4]).
-
--record(pmod, {parameters, exports, defined, predef}).
-
-%% TODO: more abstract handling of predefined/static functions.
-
-forms(Fs0, Ps, Es0, Ds0) ->
- PreDef = [{module_info,0},{module_info,1}],
- forms(Fs0, Ps, Es0, Ds0, PreDef).
-
-forms(Fs0, Ps, Es0, Ds0, PreDef) ->
- St0 = #pmod{parameters=Ps,exports=Es0,defined=Ds0, predef=PreDef},
- {Fs1, St1} = forms(Fs0, St0),
- Es1 = update_function_names(Es0, St1),
- Ds1 = update_function_names(Ds0, St1),
- Fs2 = update_forms(Fs1, St1),
- {Fs2,Es1,Ds1}.
-
-%% This is extremely simplistic for now; all functions get an extra
-%% parameter, whether they need it or not, except for static functions.
-
-update_function_names(Es, St) ->
- [update_function_name(E, St) || E <- Es].
-
-update_function_name(E={F,A}, St) ->
- case ordsets:is_element(E, St#pmod.predef) of
- true -> E;
- false -> {F, A + 1}
- end.
-
-update_forms([{function,L,N,A,Cs}|Fs],St) ->
- [{function,L,N,A+1,Cs}|update_forms(Fs,St)];
-update_forms([F|Fs],St) ->
- [F|update_forms(Fs,St)];
-update_forms([],_St) ->
- [].
-
-%% Process the program forms.
-
-forms([F0|Fs0],St0) ->
- {F1,St1} = form(F0,St0),
- {Fs1,St2} = forms(Fs0,St1),
- {[F1|Fs1],St2};
-forms([], St0) ->
- {[], St0}.
-
-%% Only function definitions are of interest here. State is not updated.
-form({function,Line,Name0,Arity0,Clauses0},St) ->
- {Name,Arity,Clauses} = function(Name0, Arity0, Clauses0, St),
- {{function,Line,Name,Arity,Clauses},St};
-%% Pass anything else through
-form(F,St) -> {F,St}.
-
-function(Name, Arity, Clauses0, St) ->
- Clauses1 = clauses(Clauses0,St),
- {Name,Arity,Clauses1}.
-
-clauses([C|Cs],St) ->
- {clause,L,H,G,B} = clause(C,St),
- T = {tuple,L,[{var,L,V} || V <- ['_'|St#pmod.parameters]]},
- [{clause,L,H++[{match,L,T,{var,L,'THIS'}}],G,B}|clauses(Cs,St)];
-clauses([],_St) -> [].
-
-clause({clause,Line,H0,G0,B0},St) ->
- H1 = head(H0,St),
- G1 = guard(G0,St),
- B1 = exprs(B0,St),
- {clause,Line,H1,G1,B1}.
-
-head(Ps,St) -> patterns(Ps,St).
-
-patterns([P0|Ps],St) ->
- P1 = pattern(P0,St),
- [P1|patterns(Ps,St)];
-patterns([],_St) -> [].
-
-string_to_conses([], _Line, Tail) ->
- Tail;
-string_to_conses([E|Rest], Line, Tail) ->
- {cons, Line, {integer, Line, E}, string_to_conses(Rest, Line, Tail)}.
-
-pattern({var,Line,V},_St) -> {var,Line,V};
-pattern({match,Line,L0,R0},St) ->
- L1 = pattern(L0,St),
- R1 = pattern(R0,St),
- {match,Line,L1,R1};
-pattern({integer,Line,I},_St) -> {integer,Line,I};
-pattern({char,Line,C},_St) -> {char,Line,C};
-pattern({float,Line,F},_St) -> {float,Line,F};
-pattern({atom,Line,A},_St) -> {atom,Line,A};
-pattern({string,Line,S},_St) -> {string,Line,S};
-pattern({nil,Line},_St) -> {nil,Line};
-pattern({cons,Line,H0,T0},St) ->
- H1 = pattern(H0,St),
- T1 = pattern(T0,St),
- {cons,Line,H1,T1};
-pattern({tuple,Line,Ps0},St) ->
- Ps1 = pattern_list(Ps0,St),
- {tuple,Line,Ps1};
-pattern({bin,Line,Fs},St) ->
- Fs2 = pattern_grp(Fs,St),
- {bin,Line,Fs2};
-pattern({op,_Line,'++',{nil,_},R},St) ->
- pattern(R,St);
-pattern({op,_Line,'++',{cons,Li,{char,C2,I},T},R},St) ->
- pattern({cons,Li,{char,C2,I},{op,Li,'++',T,R}},St);
-pattern({op,_Line,'++',{cons,Li,{integer,L2,I},T},R},St) ->
- pattern({cons,Li,{integer,L2,I},{op,Li,'++',T,R}},St);
-pattern({op,_Line,'++',{string,Li,L},R},St) ->
- pattern(string_to_conses(L, Li, R),St);
-pattern({op,Line,Op,A},_St) ->
- {op,Line,Op,A};
-pattern({op,Line,Op,L,R},_St) ->
- {op,Line,Op,L,R}.
-
-pattern_grp([{bin_element,L1,E1,S1,T1} | Fs],St) ->
- S2 = case S1 of
- default ->
- default;
- _ ->
- expr(S1,St)
- end,
- T2 = case T1 of
- default ->
- default;
- _ ->
- bit_types(T1)
- end,
- [{bin_element,L1,expr(E1,St),S2,T2} | pattern_grp(Fs,St)];
-pattern_grp([],_St) ->
- [].
-
-bit_types([]) ->
- [];
-bit_types([Atom | Rest]) when atom(Atom) ->
- [Atom | bit_types(Rest)];
-bit_types([{Atom, Integer} | Rest]) when atom(Atom), integer(Integer) ->
- [{Atom, Integer} | bit_types(Rest)].
-
-pattern_list([P0|Ps],St) ->
- P1 = pattern(P0,St),
- [P1|pattern_list(Ps,St)];
-pattern_list([],_St) -> [].
-
-guard([G0|Gs],St) when list(G0) ->
- [guard0(G0,St) | guard(Gs,St)];
-guard(L,St) ->
- guard0(L,St).
-
-guard0([G0|Gs],St) ->
- G1 = guard_test(G0,St),
- [G1|guard0(Gs,St)];
-guard0([],_St) -> [].
-
-guard_test(Expr={call,Line,{atom,La,F},As0},St) ->
- case erl_internal:type_test(F, length(As0)) of
- true ->
- As1 = gexpr_list(As0,St),
- {call,Line,{atom,La,F},As1};
- _ ->
- gexpr(Expr,St)
- end;
-guard_test(Any,St) ->
- gexpr(Any,St).
-
-gexpr({var,L,V},_St) ->
- {var,L,V};
-% %% alternative implementation of accessing module parameters
-% case index(V,St#pmod.parameters) of
-% N when N > 0 ->
-% {call,L,{remote,L,{atom,L,erlang},{atom,L,element}},
-% [{integer,L,N+1},{var,L,'THIS'}]};
-% _ ->
-% {var,L,V}
-% end;
-gexpr({integer,Line,I},_St) -> {integer,Line,I};
-gexpr({char,Line,C},_St) -> {char,Line,C};
-gexpr({float,Line,F},_St) -> {float,Line,F};
-gexpr({atom,Line,A},_St) -> {atom,Line,A};
-gexpr({string,Line,S},_St) -> {string,Line,S};
-gexpr({nil,Line},_St) -> {nil,Line};
-gexpr({cons,Line,H0,T0},St) ->
- H1 = gexpr(H0,St),
- T1 = gexpr(T0,St),
- {cons,Line,H1,T1};
-gexpr({tuple,Line,Es0},St) ->
- Es1 = gexpr_list(Es0,St),
- {tuple,Line,Es1};
-gexpr({call,Line,{atom,La,F},As0},St) ->
- case erl_internal:guard_bif(F, length(As0)) of
- true -> As1 = gexpr_list(As0,St),
- {call,Line,{atom,La,F},As1}
- end;
-% Pre-expansion generated calls to erlang:is_record/3 must also be handled
-gexpr({call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,is_record}},As0},St)
- when length(As0) == 3 ->
- As1 = gexpr_list(As0,St),
- {call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,is_record}},As1};
-% Guard bif's can be remote, but only in the module erlang...
-gexpr({call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,F}},As0},St) ->
- case erl_internal:guard_bif(F, length(As0)) or
- erl_internal:arith_op(F, length(As0)) or
- erl_internal:comp_op(F, length(As0)) or
- erl_internal:bool_op(F, length(As0)) of
- true -> As1 = gexpr_list(As0,St),
- {call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,F}},As1}
- end;
-% Unfortunately, writing calls as {M,F}(...) is also allowed.
-gexpr({call,Line,{tuple,La,[{atom,Lb,erlang},{atom,Lc,F}]},As0},St) ->
- case erl_internal:guard_bif(F, length(As0)) or
- erl_internal:arith_op(F, length(As0)) or
- erl_internal:comp_op(F, length(As0)) or
- erl_internal:bool_op(F, length(As0)) of
- true -> As1 = gexpr_list(As0,St),
- {call,Line,{tuple,La,[{atom,Lb,erlang},{atom,Lc,F}]},As1}
- end;
-gexpr({bin,Line,Fs},St) ->
- Fs2 = pattern_grp(Fs,St),
- {bin,Line,Fs2};
-gexpr({op,Line,Op,A0},St) ->
- case erl_internal:arith_op(Op, 1) or
- erl_internal:bool_op(Op, 1) of
- true -> A1 = gexpr(A0,St),
- {op,Line,Op,A1}
- end;
-gexpr({op,Line,Op,L0,R0},St) ->
- case erl_internal:arith_op(Op, 2) or
- erl_internal:bool_op(Op, 2) or
- erl_internal:comp_op(Op, 2) of
- true ->
- L1 = gexpr(L0,St),
- R1 = gexpr(R0,St),
- {op,Line,Op,L1,R1}
- end.
-
-gexpr_list([E0|Es],St) ->
- E1 = gexpr(E0,St),
- [E1|gexpr_list(Es,St)];
-gexpr_list([],_St) -> [].
-
-exprs([E0|Es],St) ->
- E1 = expr(E0,St),
- [E1|exprs(Es,St)];
-exprs([],_St) -> [].
-
-expr({var,L,V},_St) ->
- {var,L,V};
-% case index(V,St#pmod.parameters) of
-% N when N > 0 ->
-% {call,L,{remote,L,{atom,L,erlang},{atom,L,element}},
-% [{integer,L,N+1},{var,L,'THIS'}]};
-% _ ->
-% {var,L,V}
-% end;
-expr({integer,Line,I},_St) -> {integer,Line,I};
-expr({float,Line,F},_St) -> {float,Line,F};
-expr({atom,Line,A},_St) -> {atom,Line,A};
-expr({string,Line,S},_St) -> {string,Line,S};
-expr({char,Line,C},_St) -> {char,Line,C};
-expr({nil,Line},_St) -> {nil,Line};
-expr({cons,Line,H0,T0},St) ->
- H1 = expr(H0,St),
- T1 = expr(T0,St),
- {cons,Line,H1,T1};
-expr({lc,Line,E0,Qs0},St) ->
- Qs1 = lc_quals(Qs0,St),
- E1 = expr(E0,St),
- {lc,Line,E1,Qs1};
-expr({tuple,Line,Es0},St) ->
- Es1 = expr_list(Es0,St),
- {tuple,Line,Es1};
-expr({block,Line,Es0},St) ->
- Es1 = exprs(Es0,St),
- {block,Line,Es1};
-expr({'if',Line,Cs0},St) ->
- Cs1 = icr_clauses(Cs0,St),
- {'if',Line,Cs1};
-expr({'case',Line,E0,Cs0},St) ->
- E1 = expr(E0,St),
- Cs1 = icr_clauses(Cs0,St),
- {'case',Line,E1,Cs1};
-expr({'receive',Line,Cs0},St) ->
- Cs1 = icr_clauses(Cs0,St),
- {'receive',Line,Cs1};
-expr({'receive',Line,Cs0,To0,ToEs0},St) ->
- To1 = expr(To0,St),
- ToEs1 = exprs(ToEs0,St),
- Cs1 = icr_clauses(Cs0,St),
- {'receive',Line,Cs1,To1,ToEs1};
-expr({'try',Line,Es0,Scs0,Ccs0,As0},St) ->
- Es1 = exprs(Es0,St),
- Scs1 = icr_clauses(Scs0,St),
- Ccs1 = icr_clauses(Ccs0,St),
- As1 = exprs(As0,St),
- {'try',Line,Es1,Scs1,Ccs1,As1};
-expr({'fun',Line,Body,Info},St) ->
- case Body of
- {clauses,Cs0} ->
- Cs1 = fun_clauses(Cs0,St),
- {'fun',Line,{clauses,Cs1},Info};
- {function,F,A} ->
- {F1,A1} = update_function_name({F,A},St),
- if A1 == A ->
- {'fun',Line,{function,F,A},Info};
- true ->
- %% Must rewrite local fun-name to a fun that does a
- %% call with the extra THIS parameter.
- As = make_vars(A, Line),
- As1 = As ++ [{var,Line,'THIS'}],
- Call = {call,Line,{atom,Line,F1},As1},
- Cs = [{clause,Line,As,[],[Call]}],
- {'fun',Line,{clauses,Cs},Info}
- end;
- {function,M,F,A} -> %This is an error in lint!
- {'fun',Line,{function,M,F,A},Info}
- end;
-expr({call,Lc,{atom,_,new}=Name,As0},#pmod{parameters=Ps}=St)
- when length(As0) =:= length(Ps) ->
- %% The new() function does not take a 'THIS' argument (it's static).
- As1 = expr_list(As0,St),
- {call,Lc,Name,As1};
-expr({call,Lc,{atom,_,module_info}=Name,As0},St)
- when length(As0) == 0; length(As0) == 1 ->
- %% The module_info/0 and module_info/1 functions are also static.
- As1 = expr_list(As0,St),
- {call,Lc,Name,As1};
-expr({call,Lc,{atom,Lf,F},As0},St) ->
- %% Local function call - needs THIS parameter.
- As1 = expr_list(As0,St),
- {call,Lc,{atom,Lf,F},As1 ++ [{var,0,'THIS'}]};
-expr({call,Line,F0,As0},St) ->
- %% Other function call
- F1 = expr(F0,St),
- As1 = expr_list(As0,St),
- {call,Line,F1,As1};
-expr({'catch',Line,E0},St) ->
- E1 = expr(E0,St),
- {'catch',Line,E1};
-expr({match,Line,P0,E0},St) ->
- E1 = expr(E0,St),
- P1 = pattern(P0,St),
- {match,Line,P1,E1};
-expr({bin,Line,Fs},St) ->
- Fs2 = pattern_grp(Fs,St),
- {bin,Line,Fs2};
-expr({op,Line,Op,A0},St) ->
- A1 = expr(A0,St),
- {op,Line,Op,A1};
-expr({op,Line,Op,L0,R0},St) ->
- L1 = expr(L0,St),
- R1 = expr(R0,St),
- {op,Line,Op,L1,R1};
-%% The following are not allowed to occur anywhere!
-expr({remote,Line,M0,F0},St) ->
- M1 = expr(M0,St),
- F1 = expr(F0,St),
- {remote,Line,M1,F1}.
-
-expr_list([E0|Es],St) ->
- E1 = expr(E0,St),
- [E1|expr_list(Es,St)];
-expr_list([],_St) -> [].
-
-icr_clauses([C0|Cs],St) ->
- C1 = clause(C0,St),
- [C1|icr_clauses(Cs,St)];
-icr_clauses([],_St) -> [].
-
-lc_quals([{generate,Line,P0,E0}|Qs],St) ->
- E1 = expr(E0,St),
- P1 = pattern(P0,St),
- [{generate,Line,P1,E1}|lc_quals(Qs,St)];
-lc_quals([E0|Qs],St) ->
- E1 = expr(E0,St),
- [E1|lc_quals(Qs,St)];
-lc_quals([],_St) -> [].
-
-fun_clauses([C0|Cs],St) ->
- C1 = clause(C0,St),
- [C1|fun_clauses(Cs,St)];
-fun_clauses([],_St) -> [].
-
-% %% Return index from 1 upwards, or 0 if not in the list.
-%
-% index(X,Ys) -> index(X,Ys,1).
-%
-% index(X,[X|Ys],A) -> A;
-% index(X,[Y|Ys],A) -> index(X,Ys,A+1);
-% index(X,[],A) -> 0.
-
-make_vars(N, L) ->
- make_vars(1, N, L).
-
-make_vars(N, M, L) when N =< M ->
- V = list_to_atom("X"++integer_to_list(N)),
- [{var,L,V} | make_vars(N + 1, M, L)];
-make_vars(_, _, _) ->
- [].
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_pre_attributes.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_pre_attributes.erl
deleted file mode 100644
index 6e68611c66..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_pre_attributes.erl
+++ /dev/null
@@ -1,212 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: sys_pre_attributes.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
-%%
-%% Purpose : Transform Erlang compiler attributes
-
--module(sys_pre_attributes).
-
--export([parse_transform/2]).
-
--define(OPTION_TAG, attributes).
-
--record(state, {forms,
- pre_ops = [],
- post_ops = [],
- options}).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Inserts, deletes and replaces Erlang compiler attributes.
-%%
-%% Valid options are:
-%%
-%% {attribute, insert, AttrName, NewAttrVal}
-%% {attribute, replace, AttrName, NewAttrVal} % replace first occurrence
-%% {attribute, delete, AttrName}
-%%
-%% The transformation is performed in two passes:
-%%
-%% pre_transform
-%% -------------
-%% Searches for attributes in the list of Forms in order to
-%% delete or replace them. 'delete' will delete all occurrences
-%% of attributes with the given name. 'replace' will replace the
-%% first occurrence of the attribute. This pass is will only be
-%% performed if there are replace or delete operations stated
-%% as options.
-%%
-%% post_transform
-%% -------------
-%% Looks up the module attribute and inserts the new attributes
-%% directly after. This pass will only be performed if there are
-%% any attributes left to be inserted after pre_transform. The left
-%% overs will be those replace operations that not has been performed
-%% due to that the pre_transform pass did not find the attribute plus
-%% all insert operations.
-
-parse_transform(Forms, Options) ->
- S = #state{forms = Forms, options = Options},
- S2 = init_transform(S),
- report_verbose("Pre options: ~p~n", [S2#state.pre_ops], S2),
- report_verbose("Post options: ~p~n", [S2#state.post_ops], S2),
- S3 = pre_transform(S2),
- S4 = post_transform(S3),
- S4#state.forms.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Computes the lists of pre_ops and post_ops that are
-%% used in the real transformation.
-init_transform(S) ->
- case S#state.options of
- Options when list(Options) ->
- init_transform(Options, S);
- Option ->
- init_transform([Option], S)
- end.
-
-init_transform([{attribute, insert, Name, Val} | Tail], S) ->
- Op = {insert, Name, Val},
- PostOps = [Op | S#state.post_ops],
- init_transform(Tail, S#state{post_ops = PostOps});
-init_transform([{attribute, replace, Name, Val} | Tail], S) ->
- Op = {replace, Name, Val},
- PreOps = [Op | S#state.pre_ops],
- PostOps = [Op | S#state.post_ops],
- init_transform(Tail, S#state{pre_ops = PreOps, post_ops = PostOps});
-init_transform([{attribute, delete, Name} | Tail], S) ->
- Op = {delete, Name},
- PreOps = [Op | S#state.pre_ops],
- init_transform(Tail, S#state{pre_ops = PreOps});
-init_transform([], S) ->
- S;
-init_transform([_ | T], S) ->
- init_transform(T, S);
-init_transform(BadOpt, S) ->
- report_error("Illegal option (ignored): ~p~n", [BadOpt], S),
- S.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Handle delete and perhaps replace
-
-pre_transform(S) when S#state.pre_ops == [] ->
- S;
-pre_transform(S) ->
- pre_transform(S#state.forms, [], S).
-
-pre_transform([H | T], Acc, S) ->
- case H of
- {attribute, Line, Name, Val} ->
- case lists:keysearch(Name, 2, S#state.pre_ops) of
- false ->
- pre_transform(T, [H | Acc], S);
-
- {value, {replace, Name, NewVal}} ->
- report_warning("Replace attribute ~p: ~p -> ~p~n",
- [Name, Val, NewVal],
- S),
- New = {attribute, Line, Name, NewVal},
- Pre = lists:keydelete(Name, 2, S#state.pre_ops),
- Post = lists:keydelete(Name, 2, S#state.post_ops),
- S2 = S#state{pre_ops = Pre, post_ops = Post},
- if
- Pre == [] ->
- %% No need to search the rest of the Forms
- Forms = lists:reverse(Acc, [New | T]),
- S2#state{forms = Forms};
- true ->
- pre_transform(T, [New | Acc], S2)
- end;
-
- {value, {delete, Name}} ->
- report_warning("Delete attribute ~p: ~p~n",
- [Name, Val],
- S),
- pre_transform(T, Acc, S)
- end;
- _Any ->
- pre_transform(T, [H | Acc], S)
- end;
-pre_transform([], Acc, S) ->
- S#state{forms = lists:reverse(Acc)}.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Handle insert and perhaps replace
-
-post_transform(S) when S#state.post_ops == [] ->
- S;
-post_transform(S) ->
- post_transform(S#state.forms, [], S).
-
-post_transform([H | T], Acc, S) ->
- case H of
- {attribute, Line, module, Val} ->
- Acc2 = lists:reverse([{attribute, Line, module, Val} | Acc]),
- Forms = Acc2 ++ attrs(S#state.post_ops, Line, S) ++ T,
- S#state{forms = Forms, post_ops = []};
- _Any ->
- post_transform(T, [H | Acc], S)
- end;
-post_transform([], Acc, S) ->
- S#state{forms = lists:reverse(Acc)}.
-
-attrs([{replace, Name, NewVal} | T], Line, S) ->
- report_verbose("Insert attribute ~p: ~p~n", [Name, NewVal], S),
- [{attribute, Line, Name, NewVal} | attrs(T, Line, S)];
-attrs([{insert, Name, NewVal} | T], Line, S) ->
- report_verbose("Insert attribute ~p: ~p~n", [Name, NewVal], S),
- [{attribute, Line, Name, NewVal} | attrs(T, Line, S)];
-attrs([], _, _) ->
- [].
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Report functions.
-%%
-%% Errors messages are controlled with the 'report_errors' compiler option
-%% Warning messages are controlled with the 'report_warnings' compiler option
-%% Verbose messages are controlled with the 'verbose' compiler option
-
-report_error(Format, Args, S) ->
- case is_error(S) of
- true ->
- io:format("~p: * ERROR * " ++ Format, [?MODULE | Args]);
- false ->
- ok
- end.
-
-report_warning(Format, Args, S) ->
- case is_warning(S) of
- true ->
- io:format("~p: * WARNING * " ++ Format, [?MODULE | Args]);
- false ->
- ok
- end.
-
-report_verbose(Format, Args, S) ->
- case is_verbose(S) of
- true ->
- io:format("~p: " ++ Format, [?MODULE | Args]);
- false ->
- ok
- end.
-
-is_error(S) ->
- lists:member(report_errors, S#state.options) or is_verbose(S).
-
-is_warning(S) ->
- lists:member(report_warnings, S#state.options) or is_verbose(S).
-
-is_verbose(S) ->
- lists:member(verbose, S#state.options).
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_pre_expand.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_pre_expand.erl
deleted file mode 100644
index 5e7c1c8bbd..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_pre_expand.erl
+++ /dev/null
@@ -1,1026 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: sys_pre_expand.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
-%%
-%% Purpose : Expand some source Erlang constructions. This is part of the
-%% pre-processing phase.
-
-%% N.B. Although structs (tagged tuples) are not yet allowed in the
-%% language there is code included in pattern/2 and expr/3 (commented out)
-%% that handles them by transforming them to tuples.
-
--module(sys_pre_expand).
-
-%% Main entry point.
--export([module/2]).
-
--import(ordsets, [from_list/1,add_element/2,
- union/1,union/2,intersection/1,intersection/2,subtract/2]).
--import(lists, [member/2,map/2,foldl/3,foldr/3,sort/1,reverse/1,duplicate/2]).
-
--include("../my_include/erl_bits.hrl").
-
--record(expand, {module=[], %Module name
- parameters=undefined, %Module parameters
- package="", %Module package
- exports=[], %Exports
- imports=[], %Imports
- mod_imports, %Module Imports
- compile=[], %Compile flags
- records=dict:new(), %Record definitions
- attributes=[], %Attributes
- defined=[], %Defined functions
- vcount=0, %Variable counter
- func=[], %Current function
- arity=[], %Arity for current function
- fcount=0, %Local fun count
- fun_index=0, %Global index for funs
- bitdefault,
- bittypes
- }).
-
-%% module(Forms, CompileOptions)
-%% {ModuleName,Exports,TransformedForms}
-%% Expand the forms in one module. N.B.: the lists of predefined
-%% exports and imports are really ordsets!
-
-module(Fs, Opts) ->
- %% Set pre-defined exported functions.
- PreExp = [{module_info,0},{module_info,1}],
-
- %% Set pre-defined module imports.
- PreModImp = [{erlang,erlang},{packages,packages}],
-
- %% Build initial expand record.
- St0 = #expand{exports=PreExp,
- mod_imports=dict:from_list(PreModImp),
- compile=Opts,
- defined=PreExp,
- bitdefault = erl_bits:system_bitdefault(),
- bittypes = erl_bits:system_bittypes()
- },
- %% Expand the functions.
- {Tfs,St1} = forms(Fs, foldl(fun define_function/2, St0, Fs)),
- {Efs,St2} = expand_pmod(Tfs, St1),
- %% Get the correct list of exported functions.
- Exports = case member(export_all, St2#expand.compile) of
- true -> St2#expand.defined;
- false -> St2#expand.exports
- end,
- %% Generate all functions from stored info.
- {Ats,St3} = module_attrs(St2#expand{exports = Exports}),
- {Mfs,St4} = module_predef_funcs(St3),
- {St4#expand.module, St4#expand.exports, Ats ++ Efs ++ Mfs,
- St4#expand.compile}.
-
-expand_pmod(Fs0, St) ->
- case St#expand.parameters of
- undefined ->
- {Fs0,St};
- Ps ->
- {Fs1,Xs,Ds} = sys_expand_pmod:forms(Fs0, Ps,
- St#expand.exports,
- St#expand.defined),
- A = length(Ps),
- Vs = [{var,0,V} || V <- Ps],
- N = {atom,0,St#expand.module},
- B = [{tuple,0,[N|Vs]}],
- F = {function,0,new,A,[{clause,0,Vs,[],B}]},
- As = St#expand.attributes,
- {[F|Fs1],St#expand{exports=add_element({new,A}, Xs),
- defined=add_element({new,A}, Ds),
- attributes = [{abstract, true} | As]}}
- end.
-
-%% -type define_function(Form, State) -> State.
-%% Add function to defined if form a function.
-
-define_function({function,_,N,A,_Cs}, St) ->
- St#expand{defined=add_element({N,A}, St#expand.defined)};
-define_function(_, St) -> St.
-
-module_attrs(St) ->
- {[{attribute,0,Name,Val} || {Name,Val} <- St#expand.attributes],St}.
-
-module_predef_funcs(St) ->
- PreDef = [{module_info,0},{module_info,1}],
- PreExp = PreDef,
- {[{function,0,module_info,0,
- [{clause,0,[],[],
- [{call,0,{remote,0,{atom,0,erlang},{atom,0,get_module_info}},
- [{atom,0,St#expand.module}]}]}]},
- {function,0,module_info,1,
- [{clause,0,[{var,0,'X'}],[],
- [{call,0,{remote,0,{atom,0,erlang},{atom,0,get_module_info}},
- [{atom,0,St#expand.module},{var,0,'X'}]}]}]}],
- St#expand{defined=union(from_list(PreDef), St#expand.defined),
- exports=union(from_list(PreExp), St#expand.exports)}}.
-
-%% forms(Forms, State) ->
-%% {TransformedForms,State'}
-%% Process the forms. Attributes are lost and just affect the state.
-%% Ignore uninteresting forms like eof and type.
-
-forms([{attribute,_,Name,Val}|Fs0], St0) ->
- St1 = attribute(Name, Val, St0),
- forms(Fs0, St1);
-forms([{function,L,N,A,Cs}|Fs0], St0) ->
- {Ff,St1} = function(L, N, A, Cs, St0),
- {Fs,St2} = forms(Fs0, St1),
- {[Ff|Fs],St2};
-forms([_|Fs], St) -> forms(Fs, St);
-forms([], St) -> {[],St}.
-
-%% -type attribute(Attribute, Value, State) ->
-%% State.
-%% Process an attribute, this just affects the state.
-
-attribute(module, {Module, As}, St) ->
- M = package_to_string(Module),
- St#expand{module=list_to_atom(M),
- package = packages:strip_last(M),
- parameters=As};
-attribute(module, Module, St) ->
- M = package_to_string(Module),
- St#expand{module=list_to_atom(M),
- package = packages:strip_last(M)};
-attribute(export, Es, St) ->
- St#expand{exports=union(from_list(Es), St#expand.exports)};
-attribute(import, Is, St) ->
- import(Is, St);
-attribute(compile, C, St) when list(C) ->
- St#expand{compile=St#expand.compile ++ C};
-attribute(compile, C, St) ->
- St#expand{compile=St#expand.compile ++ [C]};
-attribute(record, {Name,Defs}, St) ->
- St#expand{records=dict:store(Name, normalise_fields(Defs),
- St#expand.records)};
-attribute(file, _File, St) -> St; %This is ignored
-attribute(Name, Val, St) when list(Val) ->
- St#expand{attributes=St#expand.attributes ++ [{Name,Val}]};
-attribute(Name, Val, St) ->
- St#expand{attributes=St#expand.attributes ++ [{Name,[Val]}]}.
-
-function(L, N, A, Cs0, St0) ->
- {Cs,St} = clauses(Cs0, St0#expand{func=N,arity=A,fcount=0}),
- {{function,L,N,A,Cs},St}.
-
-%% -type clauses([Clause], State) ->
-%% {[TransformedClause],State}.
-%% Expand function clauses.
-
-clauses([{clause,Line,H0,G0,B0}|Cs0], St0) ->
- {H,Hvs,_Hus,St1} = head(H0, St0),
- {G,Gvs,_Gus,St2} = guard(G0, Hvs, St1),
- {B,_Bvs,_Bus,St3} = exprs(B0, union(Hvs, Gvs), St2),
- {Cs,St4} = clauses(Cs0, St3),
- {[{clause,Line,H,G,B}|Cs],St4};
-clauses([], St) -> {[],St}.
-
-%% head(HeadPatterns, State) ->
-%% {TransformedPatterns,Variables,UsedVariables,State'}
-
-head(As, St) -> pattern_list(As, St).
-
-%% pattern(Pattern, State) ->
-%% {TransformedPattern,Variables,UsedVariables,State'}
-%% BITS: added used variables for bit patterns with varaible length
-%%
-
-pattern({var,_,'_'}=Var, St) -> %Ignore anonymous variable.
- {Var,[],[],St};
-pattern({var,_,V}=Var, St) ->
- {Var,[V],[],St};
-pattern({char,_,_}=Char, St) ->
- {Char,[],[],St};
-pattern({integer,_,_}=Int, St) ->
- {Int,[],[],St};
-pattern({float,_,_}=Float, St) ->
- {Float,[],[],St};
-pattern({atom,_,_}=Atom, St) ->
- {Atom,[],[],St};
-pattern({string,_,_}=String, St) ->
- {String,[],[],St};
-pattern({nil,_}=Nil, St) ->
- {Nil,[],[],St};
-pattern({cons,Line,H,T}, St0) ->
- {TH,THvs,Hus,St1} = pattern(H, St0),
- {TT,TTvs,Tus,St2} = pattern(T, St1),
- {{cons,Line,TH,TT},union(THvs, TTvs),union(Hus,Tus),St2};
-pattern({tuple,Line,Ps}, St0) ->
- {TPs,TPsvs,Tus,St1} = pattern_list(Ps, St0),
- {{tuple,Line,TPs},TPsvs,Tus,St1};
-%%pattern({struct,Line,Tag,Ps}, St0) ->
-%% {TPs,TPsvs,St1} = pattern_list(Ps, St0),
-%% {{tuple,Line,[{atom,Line,Tag}|TPs]},TPsvs,St1};
-pattern({record_field,_,_,_}=M, St) ->
- {expand_package(M, St), [], [], St}; % must be a package name
-pattern({record_index,Line,Name,Field}, St) ->
- {index_expr(Line, Field, Name, record_fields(Name, St)),[],[],St};
-pattern({record,Line,Name,Pfs}, St0) ->
- Fs = record_fields(Name, St0),
- {TMs,TMsvs,Us,St1} = pattern_list(pattern_fields(Fs, Pfs), St0),
- {{tuple,Line,[{atom,Line,Name}|TMs]},TMsvs,Us,St1};
-pattern({bin,Line,Es0}, St0) ->
- {Es1,Esvs,Esus,St1} = pattern_bin(Es0, St0),
- {{bin,Line,Es1},Esvs,Esus,St1};
-pattern({op,_,'++',{nil,_},R}, St) ->
- pattern(R, St);
-pattern({op,_,'++',{cons,Li,H,T},R}, St) ->
- pattern({cons,Li,H,{op,Li,'++',T,R}}, St);
-pattern({op,_,'++',{string,Li,L},R}, St) ->
- pattern(string_to_conses(Li, L, R), St);
-pattern({match,Line,Pat1, Pat2}, St0) ->
- {TH,Hvt,Hus,St1} = pattern(Pat2, St0),
- {TT,Tvt,Tus,St2} = pattern(Pat1, St1),
- {{match,Line,TT,TH}, union(Hvt,Tvt), union(Hus,Tus), St2};
-%% Compile-time pattern expressions, including unary operators.
-pattern({op,Line,Op,A}, St) ->
- { erl_eval:partial_eval({op,Line,Op,A}), [], [], St};
-pattern({op,Line,Op,L,R}, St) ->
- { erl_eval:partial_eval({op,Line,Op,L,R}), [], [], St}.
-
-pattern_list([P0|Ps0], St0) ->
- {P,Pvs,Pus,St1} = pattern(P0, St0),
- {Ps,Psvs,Psus,St2} = pattern_list(Ps0, St1),
- {[P|Ps],union(Pvs, Psvs),union(Pus, Psus),St2};
-pattern_list([], St) -> {[],[],[],St}.
-
-%% guard(Guard, VisibleVariables, State) ->
-%% {TransformedGuard,NewVariables,UsedVariables,State'}
-%% Transform a list of guard tests. We KNOW that this has been checked
-%% and what the guards test are. Use expr for transforming the guard
-%% expressions.
-
-guard([G0|Gs0], Vs, St0) ->
- {G,Hvs,Hus,St1} = guard_tests(G0, Vs, St0),
- {Gs,Tvs,Tus,St2} = guard(Gs0, Vs, St1),
- {[G|Gs],union(Hvs, Tvs),union(Hus, Tus),St2};
-guard([], _, St) -> {[],[],[],St}.
-
-guard_tests([Gt0|Gts0], Vs, St0) ->
- {Gt1,Gvs,Gus,St1} = guard_test(Gt0, Vs, St0),
- {Gts1,Gsvs,Gsus,St2} = guard_tests(Gts0, union(Gvs, Vs), St1),
- {[Gt1|Gts1],union(Gvs, Gsvs),union(Gus, Gsus),St2};
-guard_tests([], _, St) -> {[],[],[],St}.
-
-guard_test({call,Line,{atom,_,record},[A,{atom,_,Name}]}, Vs, St) ->
- record_test_in_guard(Line, A, Name, Vs, St);
-guard_test({call,Line,{atom,Lt,Tname},As}, Vs, St) ->
- %% XXX This is ugly. We can remove this workaround if/when
- %% we'll allow 'andalso' in guards. For now, we must have
- %% different code in guards and in bodies.
- Test = {remote,Lt,
- {atom,Lt,erlang},
- {atom,Lt,normalise_test(Tname, length(As))}},
- put(sys_pre_expand_in_guard, yes),
- R = expr({call,Line,Test,As}, Vs, St),
- erase(sys_pre_expand_in_guard),
- R;
-guard_test(Test, Vs, St) ->
- %% XXX See the previous clause.
- put(sys_pre_expand_in_guard, yes),
- R = expr(Test, Vs, St),
- erase(sys_pre_expand_in_guard),
- R.
-
-%% record_test(Line, Term, Name, Vs, St) -> TransformedExpr
-%% Generate code for is_record/1.
-
-record_test(Line, Term, Name, Vs, St) ->
- case get(sys_pre_expand_in_guard) of
- undefined ->
- record_test_in_body(Line, Term, Name, Vs, St);
- yes ->
- record_test_in_guard(Line, Term, Name, Vs, St)
- end.
-
-record_test_in_guard(Line, Term, Name, Vs, St) ->
- %% Notes: (1) To keep is_record/3 properly atomic (e.g. when inverted
- %% using 'not'), we cannot convert it to an instruction
- %% sequence here. It must remain a single call.
- %% (2) Later passes assume that the last argument (the size)
- %% is a literal.
- %% (3) We don't want calls to erlang:is_record/3 (in the source code)
- %% confused we the internal instruction. (Reason: (2) above +
- %% code bloat.)
- %% (4) Xref may be run on the abstract code, so the name in the
- %% abstract code must be erlang:is_record/3.
- %% (5) To achive both (3) and (4) at the same time, set the name
- %% here to erlang:is_record/3, but mark it as compiler-generated.
- %% The v3_core pass will change the name to erlang:internal_is_record/3.
- Fs = record_fields(Name, St),
- expr({call,-Line,{remote,-Line,{atom,-Line,erlang},{atom,-Line,is_record}},
- [Term,{atom,Line,Name},{integer,Line,length(Fs)+1}]},
- Vs, St).
-
-record_test_in_body(Line, Expr, Name, Vs, St0) ->
- %% As Expr may have side effects, we must evaluate it
- %% first and bind the value to a new variable.
- %% We must use also handle the case that Expr does not
- %% evaluate to a tuple properly.
- Fs = record_fields(Name, St0),
- {Var,St} = new_var(Line, St0),
-
- expr({block,Line,
- [{match,Line,Var,Expr},
- {op,Line,
- 'andalso',
- {call,Line,{atom,Line,is_tuple},[Var]},
- {op,Line,'andalso',
- {op,Line,'=:=',
- {call,Line,{atom,Line,size},[Var]},
- {integer,Line,length(Fs)+1}},
- {op,Line,'=:=',
- {call,Line,{atom,Line,element},[{integer,Line,1},Var]},
- {atom,Line,Name}}}}]}, Vs, St).
-
-normalise_test(atom, 1) -> is_atom;
-normalise_test(binary, 1) -> is_binary;
-normalise_test(constant, 1) -> is_constant;
-normalise_test(float, 1) -> is_float;
-normalise_test(function, 1) -> is_function;
-normalise_test(integer, 1) -> is_integer;
-normalise_test(list, 1) -> is_list;
-normalise_test(number, 1) -> is_number;
-normalise_test(pid, 1) -> is_pid;
-normalise_test(port, 1) -> is_port;
-normalise_test(reference, 1) -> is_reference;
-normalise_test(tuple, 1) -> is_tuple;
-normalise_test(Name, _) -> Name.
-
-%% exprs(Expressions, VisibleVariables, State) ->
-%% {TransformedExprs,NewVariables,UsedVariables,State'}
-
-exprs([E0|Es0], Vs, St0) ->
- {E,Evs,Eus,St1} = expr(E0, Vs, St0),
- {Es,Esvs,Esus,St2} = exprs(Es0, union(Evs, Vs), St1),
- {[E|Es],union(Evs, Esvs),union(Eus, Esus),St2};
-exprs([], _, St) -> {[],[],[],St}.
-
-%% expr(Expression, VisibleVariables, State) ->
-%% {TransformedExpression,NewVariables,UsedVariables,State'}
-
-expr({var,_,V}=Var, _Vs, St) ->
- {Var,[],[V],St};
-expr({char,_,_}=Char, _Vs, St) ->
- {Char,[],[],St};
-expr({integer,_,_}=Int, _Vs, St) ->
- {Int,[],[],St};
-expr({float,_,_}=Float, _Vs, St) ->
- {Float,[],[],St};
-expr({atom,_,_}=Atom, _Vs, St) ->
- {Atom,[],[],St};
-expr({string,_,_}=String, _Vs, St) ->
- {String,[],[],St};
-expr({nil,_}=Nil, _Vs, St) ->
- {Nil,[],[],St};
-expr({cons,Line,H0,T0}, Vs, St0) ->
- {H,Hvs,Hus,St1} = expr(H0, Vs, St0),
- {T,Tvs,Tus,St2} = expr(T0, Vs, St1),
- {{cons,Line,H,T},union(Hvs, Tvs),union(Hus, Tus),St2};
-expr({lc,Line,E0,Qs0}, Vs, St0) ->
- {E1,Qs1,_,Lvs,Lus,St1} = lc_tq(Line, E0, Qs0, {nil,Line}, Vs, St0),
- {{lc,Line,E1,Qs1},Lvs,Lus,St1};
-expr({tuple,Line,Es0}, Vs, St0) ->
- {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0),
- {{tuple,Line,Es1},Esvs,Esus,St1};
-%%expr({struct,Line,Tag,Es0}, Vs, St0) ->
-%% {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0),
-%% {{tuple,Line,[{atom,Line,Tag}|Es1]},Esvs,Esus,St1};
-expr({record_field,_,_,_}=M, _Vs, St) ->
- {expand_package(M, St), [], [], St}; % must be a package name
-expr({record_index,Line,Name,F}, Vs, St) ->
- I = index_expr(Line, F, Name, record_fields(Name, St)),
- expr(I, Vs, St);
-expr({record,Line,Name,Is}, Vs, St) ->
- expr({tuple,Line,[{atom,Line,Name}|
- record_inits(record_fields(Name, St), Is)]},
- Vs, St);
-expr({record_field,Line,R,Name,F}, Vs, St) ->
- I = index_expr(Line, F, Name, record_fields(Name, St)),
- expr({call,Line,{atom,Line,element},[I,R]}, Vs, St);
-expr({record,_,R,Name,Us}, Vs, St0) ->
- {Ue,St1} = record_update(R, Name, record_fields(Name, St0), Us, St0),
- expr(Ue, Vs, St1);
-expr({bin,Line,Es0}, Vs, St0) ->
- {Es1,Esvs,Esus,St1} = expr_bin(Es0, Vs, St0),
- {{bin,Line,Es1},Esvs,Esus,St1};
-expr({block,Line,Es0}, Vs, St0) ->
- {Es,Esvs,Esus,St1} = exprs(Es0, Vs, St0),
- {{block,Line,Es},Esvs,Esus,St1};
-expr({'if',Line,Cs0}, Vs, St0) ->
- {Cs,Csvss,Csuss,St1} = icr_clauses(Cs0, Vs, St0),
- All = new_in_all(Vs, Csvss),
- {{'if',Line,Cs},All,union(Csuss),St1};
-expr({'case',Line,E0,Cs0}, Vs, St0) ->
- {E,Evs,Eus,St1} = expr(E0, Vs, St0),
- {Cs,Csvss,Csuss,St2} = icr_clauses(Cs0, union(Evs, Vs), St1),
- All = new_in_all(Vs, Csvss),
- {{'case',Line,E,Cs},union(Evs, All),union([Eus|Csuss]),St2};
-expr({'cond',Line,Cs}, Vs, St0) ->
- {V,St1} = new_var(Line,St0),
- expr(cond_clauses(Cs,V), Vs, St1);
-expr({'receive',Line,Cs0}, Vs, St0) ->
- {Cs,Csvss,Csuss,St1} = icr_clauses(Cs0, Vs, St0),
- All = new_in_all(Vs, Csvss),
- {{'receive',Line,Cs},All,union(Csuss),St1};
-expr({'receive',Line,Cs0,To0,ToEs0}, Vs, St0) ->
- {To,Tovs,Tous,St1} = expr(To0, Vs, St0),
- {ToEs,ToEsvs,_ToEsus,St2} = exprs(ToEs0, Vs, St1),
- {Cs,Csvss,Csuss,St3} = icr_clauses(Cs0, Vs, St2),
- All = new_in_all(Vs, [ToEsvs|Csvss]),
- {{'receive',Line,Cs,To,ToEs},union(Tovs, All),union([Tous|Csuss]),St3};
-expr({'fun',Line,Body}, Vs, St) ->
- fun_tq(Line, Body, Vs, St);
-%%% expr({call,_,{atom,La,this_module},[]}, _Vs, St) ->
-%%% {{atom,La,St#expand.module}, [], [], St};
-%%% expr({call,_,{atom,La,this_package},[]}, _Vs, St) ->
-%%% {{atom,La,list_to_atom(St#expand.package)}, [], [], St};
-%%% expr({call,_,{atom,La,this_package},[{atom,_,Name}]}, _Vs, St) ->
-%%% M = packages:concat(St#expand.package,Name),
-%%% {{atom,La,list_to_atom(M)}, [], [], St};
-%%% expr({call,Line,{atom,La,this_package},[A]}, Vs, St) ->
-%%% M = {call,Line,{remote,La,{atom,La,packages},{atom,La,concat}},
-%%% [{string,La,St#expand.package}, A]},
-%%% expr({call,Line,{atom,Line,list_to_atom},[M]}, Vs, St);
-expr({call,Line,{atom,_,is_record},[A,{atom,_,Name}]}, Vs, St) ->
- record_test(Line, A, Name, Vs, St);
-expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}},
- [A,{atom,_,Name}]}, Vs, St) ->
- record_test(Line, A, Name, Vs, St);
-expr({call,Line,{atom,La,N},As0}, Vs, St0) ->
- {As,Asvs,Asus,St1} = expr_list(As0, Vs, St0),
- Ar = length(As),
- case erl_internal:bif(N, Ar) of
- true ->
- {{call,Line,{remote,La,{atom,La,erlang},{atom,La,N}},As},
- Asvs,Asus,St1};
- false ->
- case imported(N, Ar, St1) of
- {yes,Mod} ->
- {{call,Line,{remote,La,{atom,La,Mod},{atom,La,N}},As},
- Asvs,Asus,St1};
- no ->
- case {N,Ar} of
- {record_info,2} ->
- record_info_call(Line, As, St1);
- _ ->
- {{call,Line,{atom,La,N},As},Asvs,Asus,St1}
- end
- end
- end;
-expr({call,Line,{record_field,_,_,_}=M,As0}, Vs, St0) ->
- expr({call,Line,expand_package(M, St0),As0}, Vs, St0);
-expr({call,Line,{remote,Lr,M,F},As0}, Vs, St0) ->
- M1 = expand_package(M, St0),
- {[M2,F1|As1],Asvs,Asus,St1} = expr_list([M1,F|As0], Vs, St0),
- {{call,Line,{remote,Lr,M2,F1},As1},Asvs,Asus,St1};
-expr({call,Line,{tuple,_,[{atom,_,_}=M,{atom,_,_}=F]},As}, Vs, St) ->
- %% Rewrite {Mod,Function}(Args...) to Mod:Function(Args...).
- expr({call,Line,{remote,Line,M,F},As}, Vs, St);
-expr({call,Line,F,As0}, Vs, St0) ->
- {[Fun1|As1],Asvs,Asus,St1} = expr_list([F|As0], Vs, St0),
- {{call,Line,Fun1,As1},Asvs,Asus,St1};
-expr({'try',Line,Es0,Scs0,Ccs0,As0}, Vs, St0) ->
- {Es1,Esvs,Esus,St1} = exprs(Es0, Vs, St0),
- Cvs = union(Esvs, Vs),
- {Scs1,Scsvss,Scsuss,St2} = icr_clauses(Scs0, Cvs, St1),
- {Ccs1,Ccsvss,Ccsuss,St3} = icr_clauses(Ccs0, Cvs, St2),
- Csvss = Scsvss ++ Ccsvss,
- Csuss = Scsuss ++ Ccsuss,
- All = new_in_all(Vs, Csvss),
- {As1,Asvs,Asus,St4} = exprs(As0, Cvs, St3),
- {{'try',Line,Es1,Scs1,Ccs1,As1}, union([Asvs,Esvs,All]),
- union([Esus,Asus|Csuss]), St4};
-expr({'catch',Line,E0}, Vs, St0) ->
- %% Catch exports no new variables.
- {E,_Evs,Eus,St1} = expr(E0, Vs, St0),
- {{'catch',Line,E},[],Eus,St1};
-expr({match,Line,P0,E0}, Vs, St0) ->
- {E,Evs,Eus,St1} = expr(E0, Vs, St0),
- {P,Pvs,Pus,St2} = pattern(P0, St1),
- {{match,Line,P,E},
- union(subtract(Pvs, Vs), Evs),
- union(intersection(Pvs, Vs), union(Eus,Pus)),St2};
-expr({op,L,'andalso',E1,E2}, Vs, St0) ->
- {V,St1} = new_var(L,St0),
- E = make_bool_switch(L,E1,V,
- make_bool_switch(L,E2,V,{atom,L,true},
- {atom,L,false}),
- {atom,L,false}),
- expr(E, Vs, St1);
-expr({op,L,'orelse',E1,E2}, Vs, St0) ->
- {V,St1} = new_var(L,St0),
- E = make_bool_switch(L,E1,V,{atom,L,true},
- make_bool_switch(L,E2,V,{atom,L,true},
- {atom,L,false})),
- expr(E, Vs, St1);
-expr({op,Line,'++',{lc,Ll,E0,Qs0},M0}, Vs, St0) ->
- {E1,Qs1,M1,Lvs,Lus,St1} = lc_tq(Ll, E0, Qs0, M0, Vs, St0),
- {{op,Line,'++',{lc,Ll,E1,Qs1},M1},Lvs,Lus,St1};
-expr({op,_,'++',{string,L1,S1},{string,_,S2}}, _Vs, St) ->
- {{string,L1,S1 ++ S2},[],[],St};
-expr({op,Ll,'++',{string,L1,S1}=Str,R0}, Vs, St0) ->
- {R1,Rvs,Rus,St1} = expr(R0, Vs, St0),
- E = case R1 of
- {string,_,S2} -> {string,L1,S1 ++ S2};
- _Other when length(S1) < 8 -> string_to_conses(L1, S1, R1);
- _Other -> {op,Ll,'++',Str,R1}
- end,
- {E,Rvs,Rus,St1};
-expr({op,Ll,'++',{cons,Lc,H,T},L2}, Vs, St) ->
- expr({cons,Ll,H,{op,Lc,'++',T,L2}}, Vs, St);
-expr({op,_,'++',{nil,_},L2}, Vs, St) ->
- expr(L2, Vs, St);
-expr({op,Line,Op,A0}, Vs, St0) ->
- {A,Avs,Aus,St1} = expr(A0, Vs, St0),
- {{op,Line,Op,A},Avs,Aus,St1};
-expr({op,Line,Op,L0,R0}, Vs, St0) ->
- {L,Lvs,Lus,St1} = expr(L0, Vs, St0),
- {R,Rvs,Rus,St2} = expr(R0, Vs, St1),
- {{op,Line,Op,L,R},union(Lvs, Rvs),union(Lus, Rus),St2}.
-
-expr_list([E0|Es0], Vs, St0) ->
- {E,Evs,Eus,St1} = expr(E0, Vs, St0),
- {Es,Esvs,Esus,St2} = expr_list(Es0, Vs, St1),
- {[E|Es],union(Evs, Esvs),union(Eus, Esus),St2};
-expr_list([], _, St) ->
- {[],[],[],St}.
-
-%% icr_clauses([Clause], [VisibleVariable], State) ->
-%% {[TransformedClause],[[NewVariable]],[[UsedVariable]],State'}
-%% Be very careful here to return the variables that are really used
-%% and really new.
-
-icr_clauses([], _, St) ->
- {[],[[]],[],St};
-icr_clauses(Clauses, Vs, St) ->
- icr_clauses2(Clauses, Vs, St).
-
-icr_clauses2([{clause,Line,H0,G0,B0}|Cs0], Vs, St0) ->
- {H,Hvs,Hus,St1} = head(H0, St0), %Hvs is really used!
- {G,Gvs,Gus,St2} = guard(G0, union(Hvs, Vs), St1),
- {B,Bvs,Bus,St3} = exprs(B0, union([Vs,Hvs,Gvs]), St2),
- New = subtract(union([Hvs,Gvs,Bvs]), Vs), %Really new
- Used = intersection(union([Hvs,Hus,Gus,Bus]), Vs), %Really used
- {Cs,Csvs,Csus,St4} = icr_clauses2(Cs0, Vs, St3),
- {[{clause,Line,H,G,B}|Cs],[New|Csvs],[Used|Csus],St4};
-icr_clauses2([], _, St) ->
- {[],[],[],St}.
-
-%% lc_tq(Line, Expr, Qualifiers, More, [VisibleVar], State) ->
-%% {TransExpr,[TransQual],TransMore,[NewVar],[UsedVar],State'}
-
-lc_tq(Line, E0, [{generate,Lg,P0,G0}|Qs0], M0, Vs, St0) ->
- {G1,Gvs,Gus,St1} = expr(G0, Vs, St0),
- {P1,Pvs,Pus,St2} = pattern(P0, St1),
- {E1,Qs1,M1,Lvs,Lus,St3} = lc_tq(Line, E0, Qs0, M0, union(Pvs, Vs), St2),
- {E1,[{generate,Lg,P1,G1}|Qs1],M1,
- union(Gvs, Lvs),union([Gus,Pus,Lus]),St3};
-lc_tq(Line, E0, [F0|Qs0], M0, Vs, St0) ->
- %% Allow record/2 and expand out as guard test.
- case erl_lint:is_guard_test(F0) of
- true ->
- {F1,Fvs,_Fus,St1} = guard_tests([F0], Vs, St0),
- {E1,Qs1,M1,Lvs,Lus,St2} = lc_tq(Line, E0, Qs0, M0, union(Fvs, Vs), St1),
- {E1,F1++Qs1,M1,Lvs,Lus,St2};
- false ->
- {F1,Fvs,_Fus,St1} = expr(F0, Vs, St0),
- {E1,Qs1,M1,Lvs,Lus,St2} = lc_tq(Line, E0, Qs0, M0, union(Fvs, Vs), St1),
- {E1,[F1|Qs1],M1,Lvs,Lus,St2}
- end;
-lc_tq(_Line, E0, [], M0, Vs, St0) ->
- {E1,Evs,Eus,St1} = expr(E0, Vs, St0),
- {M1,Mvs,Mus,St2} = expr(M0, Vs, St1),
- {E1,[],M1,union(Evs, Mvs),union(Eus, Mus),St2}.
-
-%% fun_tq(Line, Body, VisibleVariables, State) ->
-%% {Fun,NewVariables,UsedVariables,State'}
-%% Transform an "explicit" fun {'fun', Line, {clauses, Cs}} into an
-%% extended form {'fun', Line, {clauses, Cs}, Info}, unless it is the
-%% name of a BIF (erl_lint has checked that it is not an import).
-%% Process the body sequence directly to get the new and used variables.
-%% "Implicit" funs {'fun', Line, {function, F, A}} are not changed.
-
-fun_tq(Lf, {function,F,A}, Vs, St0) ->
- {As,St1} = new_vars(A, Lf, St0),
- Cs = [{clause,Lf,As,[],[{call,Lf,{atom,Lf,F},As}]}],
- case erl_internal:bif(F, A) of
- true ->
- fun_tq(Lf, {clauses,Cs}, Vs, St1);
- false ->
- Index = St0#expand.fun_index,
- Uniq = erlang:hash(Cs, (1 bsl 27)-1),
- {Fname,St2} = new_fun_name(St1),
- {{'fun',Lf,{function,F,A},{Index,Uniq,Fname}},[],[],
- St2#expand{fun_index=Index+1}}
- end;
-fun_tq(Lf, {clauses,Cs0}, Vs, St0) ->
- Uniq = erlang:hash(Cs0, (1 bsl 27)-1),
- {Cs1,_Hvss,Frees,St1} = fun_clauses(Cs0, Vs, St0),
- Ufrees = union(Frees),
- Index = St1#expand.fun_index,
- {Fname,St2} = new_fun_name(St1),
- {{'fun',Lf,{clauses,Cs1},{Index,Uniq,Fname}},[],Ufrees,
- St2#expand{fun_index=Index+1}}.
-
-fun_clauses([{clause,L,H0,G0,B0}|Cs0], Vs, St0) ->
- {H,Hvs,Hus,St1} = head(H0, St0),
- {G,Gvs,Gus,St2} = guard(G0, union(Hvs, Vs), St1),
- {B,Bvs,Bus,St3} = exprs(B0, union([Vs,Hvs,Gvs]), St2),
- %% Free variables cannot be new anywhere in the clause.
- Free = subtract(union([Gus,Hus,Bus]), union([Hvs,Gvs,Bvs])),
- %%io:format(" Gus :~p~n Bvs :~p~n Bus :~p~n Free:~p~n" ,[Gus,Bvs,Bus,Free]),
- {Cs,Hvss,Frees,St4} = fun_clauses(Cs0, Vs, St3),
- {[{clause,L,H,G,B}|Cs],[Hvs|Hvss],[Free|Frees],St4};
-fun_clauses([], _, St) -> {[],[],[],St}.
-
-%% new_fun_name(State) -> {FunName,State}.
-
-new_fun_name(#expand{func=F,arity=A,fcount=I}=St) ->
- Name = "-" ++ atom_to_list(F) ++ "/" ++ integer_to_list(A)
- ++ "-fun-" ++ integer_to_list(I) ++ "-",
- {list_to_atom(Name),St#expand{fcount=I+1}}.
-
-
-%% normalise_fields([RecDef]) -> [Field].
-%% Normalise the field definitions to always have a default value. If
-%% none has been given then use 'undefined'.
-
-normalise_fields(Fs) ->
- map(fun ({record_field,Lf,Field}) ->
- {record_field,Lf,Field,{atom,Lf,undefined}};
- (F) -> F end, Fs).
-
-%% record_fields(RecordName, State)
-%% find_field(FieldName, Fields)
-
-record_fields(R, St) -> dict:fetch(R, St#expand.records).
-
-find_field(F, [{record_field,_,{atom,_,F},Val}|_]) -> {ok,Val};
-find_field(F, [_|Fs]) -> find_field(F, Fs);
-find_field(_, []) -> error.
-
-%% field_names(RecFields) -> [Name].
-%% Return a list of the field names structures.
-
-field_names(Fs) ->
- map(fun ({record_field,_,Field,_Val}) -> Field end, Fs).
-
-%% index_expr(Line, FieldExpr, Name, Fields) -> IndexExpr.
-%% Return an expression which evaluates to the index of a
-%% field. Currently only handle the case where the field is an
-%% atom. This expansion must be passed through expr again.
-
-index_expr(Line, {atom,_,F}, _Name, Fs) ->
- {integer,Line,index_expr(F, Fs, 2)}.
-
-index_expr(F, [{record_field,_,{atom,_,F},_}|_], I) -> I;
-index_expr(F, [_|Fs], I) ->
- index_expr(F, Fs, I+1).
-
-%% pattern_fields([RecDefField], [Match]) -> [Pattern].
-%% Build a list of match patterns for the record tuple elements.
-%% This expansion must be passed through pattern again. N.B. We are
-%% scanning the record definition field list!
-
-pattern_fields(Fs, Ms) ->
- Wildcard = record_wildcard_init(Ms),
- map(fun ({record_field,L,{atom,_,F},_}) ->
- case find_field(F, Ms) of
- {ok,Match} -> Match;
- error when Wildcard =:= none -> {var,L,'_'};
- error -> Wildcard
- end end,
- Fs).
-
-%% record_inits([RecDefField], [Init]) -> [InitExpr].
-%% Build a list of initialisation expressions for the record tuple
-%% elements. This expansion must be passed through expr
-%% again. N.B. We are scanning the record definition field list!
-
-record_inits(Fs, Is) ->
- WildcardInit = record_wildcard_init(Is),
- map(fun ({record_field,_,{atom,_,F},D}) ->
- case find_field(F, Is) of
- {ok,Init} -> Init;
- error when WildcardInit =:= none -> D;
- error -> WildcardInit
- end end,
- Fs).
-
-record_wildcard_init([{record_field,_,{var,_,'_'},D}|_]) -> D;
-record_wildcard_init([_|Is]) -> record_wildcard_init(Is);
-record_wildcard_init([]) -> none.
-
-%% record_update(Record, RecordName, [RecDefField], [Update], State) ->
-%% {Expr,State'}
-%% Build an expression to update fields in a record returning a new
-%% record. Try to be smart and optimise this. This expansion must be
-%% passed through expr again.
-
-record_update(R, Name, Fs, Us0, St0) ->
- Line = element(2, R),
- {Pre,Us,St1} = record_exprs(Us0, St0),
- Nf = length(Fs), %# of record fields
- Nu = length(Us), %# of update fields
- Nc = Nf - Nu, %# of copy fields
-
- %% We need a new variable for the record expression
- %% to guarantee that it is only evaluated once.
- {Var,St2} = new_var(Line, St1),
-
- %% Try to be intelligent about which method of updating record to use.
- {Update,St} =
- if
- Nu == 0 -> {R,St2}; %No fields updated
- Nu =< Nc -> %Few fields updated
- {record_setel(Var, Name, Fs, Us), St2};
- true -> %The wide area inbetween
- record_match(Var, Name, Fs, Us, St2)
- end,
- {{block,element(2, R),Pre ++ [{match,Line,Var,R},Update]},St}.
-
-%% record_match(Record, RecordName, [RecDefField], [Update], State)
-%% Build a 'case' expression to modify record fields.
-
-record_match(R, Name, Fs, Us, St0) ->
- {Ps,News,St1} = record_upd_fs(Fs, Us, St0),
- Lr = element(2, hd(Us)),
- {{'case',Lr,R,
- [{clause,Lr,[{tuple,Lr,[{atom,Lr,Name}|Ps]}],[],
- [{tuple,Lr,[{atom,Lr,Name}|News]}]},
- {clause,Lr,[{var,Lr,'_'}],[],
- [call_error(Lr, {tuple,Lr,[{atom,Lr,badrecord},{atom,Lr,Name}]})]}
- ]},
- St1}.
-
-record_upd_fs([{record_field,Lf,{atom,_La,F},_Val}|Fs], Us, St0) ->
- {P,St1} = new_var(Lf, St0),
- {Ps,News,St2} = record_upd_fs(Fs, Us, St1),
- case find_field(F, Us) of
- {ok,New} -> {[P|Ps],[New|News],St2};
- error -> {[P|Ps],[P|News],St2}
- end;
-record_upd_fs([], _, St) -> {[],[],St}.
-
-%% record_setel(Record, RecordName, [RecDefField], [Update])
-%% Build a nested chain of setelement calls to build the
-%% updated record tuple.
-
-record_setel(R, Name, Fs, Us0) ->
- Us1 = foldl(fun ({record_field,Lf,Field,Val}, Acc) ->
- I = index_expr(Lf, Field, Name, Fs),
- [{I,Lf,Val}|Acc]
- end, [], Us0),
- Us = sort(Us1),
- Lr = element(2, hd(Us)),
- Wildcards = duplicate(length(Fs), {var,Lr,'_'}),
- {'case',Lr,R,
- [{clause,Lr,[{tuple,Lr,[{atom,Lr,Name}|Wildcards]}],[],
- [foldr(fun ({I,Lf,Val}, Acc) ->
- {call,Lf,{atom,Lf,setelement},[I,Acc,Val]} end,
- R, Us)]},
- {clause,Lr,[{var,Lr,'_'}],[],
- [call_error(Lr, {tuple,Lr,[{atom,Lr,badrecord},{atom,Lr,Name}]})]}]}.
-
-%% Expand a call to record_info/2. We have checked that it is not
-%% shadowed by an import.
-
-record_info_call(Line, [{atom,_Li,Info},{atom,_Ln,Name}], St) ->
- case Info of
- size ->
- {{integer,Line,1+length(record_fields(Name, St))},[],[],St};
- fields ->
- {make_list(field_names(record_fields(Name, St)), Line),
- [],[],St}
- end.
-
-%% Break out expressions from an record update list and bind to new
-%% variables. The idea is that we will evaluate all update expressions
-%% before starting to update the record.
-
-record_exprs(Us, St) ->
- record_exprs(Us, St, [], []).
-
-record_exprs([{record_field,Lf,{atom,_La,_F}=Name,Val}=Field0|Us], St0, Pre, Fs) ->
- case is_simple_val(Val) of
- true ->
- record_exprs(Us, St0, Pre, [Field0|Fs]);
- false ->
- {Var,St} = new_var(Lf, St0),
- Bind = {match,Lf,Var,Val},
- Field = {record_field,Lf,Name,Var},
- record_exprs(Us, St, [Bind|Pre], [Field|Fs])
- end;
-record_exprs([], St, Pre, Fs) ->
- {reverse(Pre),Fs,St}.
-
-is_simple_val({var,_,_}) -> true;
-is_simple_val({atom,_,_}) -> true;
-is_simple_val({integer,_,_}) -> true;
-is_simple_val({float,_,_}) -> true;
-is_simple_val({nil,_}) -> true;
-is_simple_val(_) -> false.
-
-%% pattern_bin([Element], State) -> {[Element],[Variable],[UsedVar],State}.
-
-pattern_bin(Es0, St) ->
- Es1 = bin_expand_strings(Es0),
- foldr(fun (E, Acc) -> pattern_element(E, Acc) end, {[],[],[],St}, Es1).
-
-pattern_element({bin_element,Line,Expr,Size,Type}, {Es,Esvs,Esus,St0}) ->
- {Expr1,Vs1,Us1,St1} = pattern(Expr, St0),
- {Size1,Vs2,Us2,St2} = pat_bit_size(Size, St1),
- {Size2,Type1} = make_bit_type(Line, Size1,Type),
- {[{bin_element,Line,Expr1,Size2,Type1}|Es],
- union([Vs1,Vs2,Esvs]),union([Us1,Us2,Esus]),St2}.
-
-pat_bit_size(default, St) -> {default,[],[],St};
-pat_bit_size({atom,_La,all}=All, St) -> {All,[],[],St};
-pat_bit_size({var,_Lv,V}=Var, St) -> {Var,[],[V],St};
-pat_bit_size(Size, St) ->
- Line = element(2, Size),
- {value,Sz,_} = erl_eval:expr(Size, erl_eval:new_bindings()),
- {{integer,Line,Sz},[],[],St}.
-
-make_bit_type(Line, default, Type0) ->
- case erl_bits:set_bit_type(default, Type0) of
- {ok,all,Bt} -> {{atom,Line,all},erl_bits:as_list(Bt)};
- {ok,Size,Bt} -> {{integer,Line,Size},erl_bits:as_list(Bt)}
- end;
-make_bit_type(_Line, Size, Type0) -> %Integer or 'all'
- {ok,Size,Bt} = erl_bits:set_bit_type(Size, Type0),
- {Size,erl_bits:as_list(Bt)}.
-
-%% expr_bin([Element], [VisibleVar], State) ->
-%% {[Element],[NewVar],[UsedVar],State}.
-
-expr_bin(Es0, Vs, St) ->
- Es1 = bin_expand_strings(Es0),
- foldr(fun (E, Acc) -> bin_element(E, Vs, Acc) end, {[],[],[],St}, Es1).
-
-bin_element({bin_element,Line,Expr,Size,Type}, Vs, {Es,Esvs,Esus,St0}) ->
- {Expr1,Vs1,Us1,St1} = expr(Expr, Vs, St0),
- {Size1,Vs2,Us2,St2} = if Size == default -> {default,[],[],St1};
- true -> expr(Size, Vs, St1)
- end,
- {Size2,Type1} = make_bit_type(Line, Size1, Type),
- {[{bin_element,Line,Expr1,Size2,Type1}|Es],
- union([Vs1,Vs2,Esvs]),union([Us1,Us2,Esus]),St2}.
-
-bin_expand_strings(Es) ->
- foldr(fun ({bin_element,Line,{string,_,S},default,default}, Es1) ->
- foldr(fun (C, Es2) ->
- [{bin_element,Line,{char,Line,C},default,default}|Es2]
- end, Es1, S);
- (E, Es1) -> [E|Es1]
- end, [], Es).
-
-%% new_var_name(State) -> {VarName,State}.
-
-new_var_name(St) ->
- C = St#expand.vcount,
- {list_to_atom("pre" ++ integer_to_list(C)),St#expand{vcount=C+1}}.
-
-%% new_var(Line, State) -> {Var,State}.
-
-new_var(L, St0) ->
- {New,St1} = new_var_name(St0),
- {{var,L,New},St1}.
-
-%% new_vars(Count, Line, State) -> {[Var],State}.
-%% Make Count new variables.
-
-new_vars(N, L, St) -> new_vars(N, L, St, []).
-
-new_vars(N, L, St0, Vs) when N > 0 ->
- {V,St1} = new_var(L, St0),
- new_vars(N-1, L, St1, [V|Vs]);
-new_vars(0, _L, St, Vs) -> {Vs,St}.
-
-%% make_list(TermList, Line) -> ConsTerm.
-
-make_list(Ts, Line) ->
- foldr(fun (H, T) -> {cons,Line,H,T} end, {nil,Line}, Ts).
-
-string_to_conses(Line, Cs, Tail) ->
- foldr(fun (C, T) -> {cons,Line,{char,Line,C},T} end, Tail, Cs).
-
-
-%% In syntax trees, module/package names are atoms or lists of atoms.
-
-package_to_string(A) when atom(A) -> atom_to_list(A);
-package_to_string(L) when list(L) -> packages:concat(L).
-
-expand_package({atom,L,A} = M, St) ->
- case dict:find(A, St#expand.mod_imports) of
- {ok, A1} ->
- {atom,L,A1};
- error ->
- case packages:is_segmented(A) of
- true ->
- M;
- false ->
- M1 = packages:concat(St#expand.package, A),
- {atom,L,list_to_atom(M1)}
- end
- end;
-expand_package(M, _St) ->
- case erl_parse:package_segments(M) of
- error ->
- M;
- M1 ->
- {atom,element(2,M),list_to_atom(package_to_string(M1))}
- end.
-
-%% Create a case-switch on true/false, generating badarg for all other
-%% values.
-
-make_bool_switch(L, E, V, T, F) ->
- make_bool_switch_1(L, E, V, [T], [F]).
-
-make_bool_switch_1(L, E, V, T, F) ->
- case get(sys_pre_expand_in_guard) of
- undefined -> make_bool_switch_body(L, E, V, T, F);
- yes -> make_bool_switch_guard(L, E, V, T, F)
- end.
-
-make_bool_switch_guard(_, E, _, [{atom,_,true}], [{atom,_,false}]) -> E;
-make_bool_switch_guard(L, E, V, T, F) ->
- NegL = -abs(L),
- {'case',NegL,E,
- [{clause,NegL,[{atom,NegL,true}],[],T},
- {clause,NegL,[{atom,NegL,false}],[],F},
- {clause,NegL,[V],[],[V]}
- ]}.
-
-make_bool_switch_body(L, E, V, T, F) ->
- NegL = -abs(L),
- {'case',NegL,E,
- [{clause,NegL,[{atom,NegL,true}],[],T},
- {clause,NegL,[{atom,NegL,false}],[],F},
- {clause,NegL,[V],[],
- [call_error(NegL,{tuple,NegL,[{atom,NegL,badarg},V]})]}
- ]}.
-
-%% Expand a list of cond-clauses to a sequence of case-switches.
-
-cond_clauses([{clause,L,[],[[E]],B}],V) ->
- make_bool_switch_1(L,E,V,B,[call_error(L,{atom,L,cond_clause})]);
-cond_clauses([{clause,L,[],[[E]],B} | Cs],V) ->
- make_bool_switch_1(L,E,V,B,[cond_clauses(Cs,V)]).
-
-%% call_error(Line, Reason) -> Expr.
-%% Build a call to erlang:error/1 with reason Reason.
-
-call_error(L, R) ->
- {call,L,{remote,L,{atom,L,erlang},{atom,L,error}},[R]}.
-
-%% new_in_all(Before, RegionList) -> NewInAll
-%% Return the variables new in all clauses.
-
-new_in_all(Before, Region) ->
- InAll = intersection(Region),
- subtract(InAll, Before).
-
-%% import(Line, Imports, State) ->
-%% State'
-%% imported(Name, Arity, State) ->
-%% {yes,Module} | no
-%% Handle import declarations and est for imported functions. No need to
-%% check when building imports as code is correct.
-
-import({Mod0,Fs}, St) ->
- Mod = list_to_atom(package_to_string(Mod0)),
- Mfs = from_list(Fs),
- St#expand{imports=add_imports(Mod, Mfs, St#expand.imports)};
-import(Mod0, St) ->
- Mod = package_to_string(Mod0),
- Key = list_to_atom(packages:last(Mod)),
- St#expand{mod_imports=dict:store(Key, list_to_atom(Mod),
- St#expand.mod_imports)}.
-
-add_imports(Mod, [F|Fs], Is) ->
- add_imports(Mod, Fs, orddict:store(F, Mod, Is));
-add_imports(_, [], Is) -> Is.
-
-imported(F, A, St) ->
- case orddict:find({F,A}, St#expand.imports) of
- {ok,Mod} -> {yes,Mod};
- error -> no
- end.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_codegen.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_codegen.erl
deleted file mode 100644
index 2af4d94655..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_codegen.erl
+++ /dev/null
@@ -1,1755 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: v3_codegen.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
-%%
-%% Purpose : Code generator for Beam.
-
-%% The following assumptions have been made:
-%%
-%% 1. Matches, i.e. things with {match,M,Ret} wrappers, only return
-%% values; no variables are exported. If the match would have returned
-%% extra variables then these have been transformed to multiple return
-%% values.
-%%
-%% 2. All BIF's called in guards are gc-safe so there is no need to
-%% put thing on the stack in the guard. While this would in principle
-%% work it would be difficult to keep track of the stack depth when
-%% trimming.
-%%
-%% The code generation uses variable lifetime information added by
-%% the v3_life module to save variables, allocate registers and
-%% move registers to the stack when necessary.
-%%
-%% We try to use a consistent variable name scheme throughout. The
-%% StackReg record is always called Bef,Int,Aft.
-
--module(v3_codegen).
-
-%% The main interface.
--export([module/2]).
-
--import(lists, [member/2,keymember/3,keysort/2,keysearch/3,append/1,
- map/2,flatmap/2,foldl/3,foldr/3,mapfoldl/3,
- sort/1,reverse/1,reverse/2]).
--import(v3_life, [vdb_find/2]).
-
-%%-compile([export_all]).
-
--include("v3_life.hrl").
-
-%% Main codegen structure.
--record(cg, {lcount=1, %Label counter
- mod, %Current module
- func, %Current function
- finfo, %Function info label
- fcode, %Function code label
- btype, %Type of bif used.
- bfail, %Fail label of bif
- break, %Break label
- recv, %Receive label
- is_top_block, %Boolean: top block or not
- functable = [], %Table of local functions:
- %[{{Name, Arity}, Label}...]
- in_catch=false, %Inside a catch or not.
- need_frame, %Need a stack frame.
- new_funs=true}). %Generate new fun instructions.
-
-%% Stack/register state record.
--record(sr, {reg=[], %Register table
- stk=[], %Stack table
- res=[]}). %Reserved regs: [{reserved,I,V}]
-
-module({Mod,Exp,Attr,Forms}, Options) ->
- NewFunsFlag = not member(no_new_funs, Options),
- {Fs,St} = functions(Forms, #cg{mod=Mod,new_funs=NewFunsFlag}),
- {ok,{Mod,Exp,Attr,Fs,St#cg.lcount}}.
-
-functions(Forms, St0) ->
- mapfoldl(fun (F, St) -> function(F, St) end, St0#cg{lcount=1}, Forms).
-
-function({function,Name,Arity,As0,Vb,Vdb}, St0) ->
- %%ok = io:fwrite("cg ~w:~p~n", [?LINE,{Name,Arity}]),
- St1 = St0#cg{func={Name,Arity}},
- {Fun,St2} = cg_fun(Vb, As0, Vdb, St1),
- Func0 = {function,Name,Arity,St2#cg.fcode,Fun},
- Func = bs_function(Func0),
- {Func,St2}.
-
-%% cg_fun([Lkexpr], [HeadVar], Vdb, State) -> {[Ainstr],State}
-
-cg_fun(Les, Hvs, Vdb, St0) ->
- {Name,Arity} = St0#cg.func,
- {Fi,St1} = new_label(St0), %FuncInfo label
- {Fl,St2} = local_func_label(Name, Arity, St1),
- %% Create initial stack/register state, clear unused arguments.
- Bef = clear_dead(#sr{reg=foldl(fun ({var,V}, Reg) ->
- put_reg(V, Reg)
- end, [], Hvs),
- stk=[]}, 0, Vdb),
- {B2,_Aft,St3} = cg_list(Les, 0, Vdb, Bef, St2#cg{btype=exit,
- bfail=Fi,
- finfo=Fi,
- fcode=Fl,
- is_top_block=true}),
- A = [{label,Fi},{func_info,{atom,St3#cg.mod},{atom,Name},Arity},
- {label,Fl}|B2],
- {A,St3}.
-
-%% cg(Lkexpr, Vdb, StackReg, State) -> {[Ainstr],StackReg,State}.
-%% Generate code for a kexpr.
-%% Split function into two steps for clarity, not efficiency.
-
-cg(Le, Vdb, Bef, St) ->
- cg(Le#l.ke, Le, Vdb, Bef, St).
-
-cg({block,Es}, Le, Vdb, Bef, St) ->
- block_cg(Es, Le, Vdb, Bef, St);
-cg({match,M,Rs}, Le, Vdb, Bef, St) ->
- match_cg(M, Rs, Le, Vdb, Bef, St);
-cg({match_fail,F}, Le, Vdb, Bef, St) ->
- match_fail_cg(F, Le, Vdb, Bef, St);
-cg({call,Func,As,Rs}, Le, Vdb, Bef, St) ->
- call_cg(Func, As, Rs, Le, Vdb, Bef, St);
-cg({enter,Func,As}, Le, Vdb, Bef, St) ->
- enter_cg(Func, As, Le, Vdb, Bef, St);
-cg({bif,Bif,As,Rs}, Le, Vdb, Bef, St) ->
- bif_cg(Bif, As, Rs, Le, Vdb, Bef, St);
-cg({receive_loop,Te,Rvar,Rm,Tes,Rs}, Le, Vdb, Bef, St) ->
- recv_loop_cg(Te, Rvar, Rm, Tes, Rs, Le, Vdb, Bef, St);
-cg(receive_next, Le, Vdb, Bef, St) ->
- recv_next_cg(Le, Vdb, Bef, St);
-cg(receive_accept, _Le, _Vdb, Bef, St) -> {[remove_message],Bef,St};
-cg({'try',Ta,Vs,Tb,Evs,Th,Rs}, Le, Vdb, Bef, St) ->
- try_cg(Ta, Vs, Tb, Evs, Th, Rs, Le, Vdb, Bef, St);
-cg({'catch',Cb,R}, Le, Vdb, Bef, St) ->
- catch_cg(Cb, R, Le, Vdb, Bef, St);
-cg({set,Var,Con}, Le, Vdb, Bef, St) -> set_cg(Var, Con, Le, Vdb, Bef, St);
-cg({return,Rs}, Le, Vdb, Bef, St) -> return_cg(Rs, Le, Vdb, Bef, St);
-cg({break,Bs}, Le, Vdb, Bef, St) -> break_cg(Bs, Le, Vdb, Bef, St);
-cg({need_heap,0}, _Le, _Vdb, Bef, St) ->
- {[],Bef,St};
-cg({need_heap,H}, _Le, _Vdb, Bef, St) ->
- {[{test_heap,H,max_reg(Bef#sr.reg)}],Bef,St}.
-
-%% cg_list([Kexpr], FirstI, Vdb, StackReg, St) -> {[Ainstr],StackReg,St}.
-
-cg_list(Kes, I, Vdb, Bef, St0) ->
- {Keis,{Aft,St1}} =
- flatmapfoldl(fun (Ke, {Inta,Sta}) ->
-% ok = io:fwrite(" %% ~p\n", [Inta]),
-% ok = io:fwrite("cgl:~p\n", [Ke]),
- {Keis,Intb,Stb} = cg(Ke, Vdb, Inta, Sta),
-% ok = io:fwrite(" ~p\n", [Keis]),
-% ok = io:fwrite(" %% ~p\n", [Intb]),
- {comment(Inta) ++ Keis,{Intb,Stb}}
- end, {Bef,St0}, need_heap(Kes, I)),
- {Keis,Aft,St1}.
-
-%% need_heap([Lkexpr], I, BifType) -> [Lkexpr].
-%% Insert need_heap instructions in Kexpr list. Try to be smart and
-%% collect them together as much as possible.
-
-need_heap(Kes0, I) ->
- {Kes1,{H,F}} = flatmapfoldr(fun (Ke, {H0,F0}) ->
- {Ns,H1,F1} = need_heap_1(Ke, H0, F0),
- {[Ke|Ns],{H1,F1}}
- end, {0,false}, Kes0),
- %% Prepend need_heap if necessary.
- Kes2 = need_heap_need(I, H, F) ++ Kes1,
-% ok = io:fwrite("need_heap: ~p~n",
-% [{{H,F},
-% map(fun (#l{ke={match,M,Rs}}) -> match;
-% (Lke) -> Lke#l.ke end, Kes2)}]),
- Kes2.
-
-need_heap_1(#l{ke={set,_,{binary,_}},i=I}, H, F) ->
- {need_heap_need(I, H, F),0,false};
-need_heap_1(#l{ke={set,_,Val}}, H, F) ->
- %% Just pass through adding to needed heap.
- {[],H + case Val of
- {cons,_} -> 2;
- {tuple,Es} -> 1 + length(Es);
- {string,S} -> 2 * length(S);
- _Other -> 0
- end,F};
-need_heap_1(#l{ke={call,_Func,_As,_Rs},i=I}, H, F) ->
- %% Calls generate a need if necessary and also force one.
- {need_heap_need(I, H, F),0,true};
-need_heap_1(#l{ke={bif,dsetelement,_As,_Rs},i=I}, H, F) ->
- {need_heap_need(I, H, F),0,true};
-need_heap_1(#l{ke={bif,{make_fun,_,_,_,_},_As,_Rs},i=I}, H, F) ->
- {need_heap_need(I, H, F),0,true};
-need_heap_1(#l{ke={bif,_Bif,_As,_Rs}}, H, F) ->
- {[],H,F};
-need_heap_1(#l{i=I}, H, F) ->
- %% Others kexprs generate a need if necessary but don't force.
- {need_heap_need(I, H, F),0,false}.
-
-need_heap_need(_I, 0, false) -> [];
-need_heap_need(I, H, _F) -> [#l{ke={need_heap,H},i=I}].
-
-
-%% match_cg(Match, [Ret], Le, Vdb, StackReg, State) ->
-%% {[Ainstr],StackReg,State}.
-%% Generate code for a match. First save all variables on the stack
-%% that are to survive after the match. We leave saved variables in
-%% their registers as they might actually be in the right place.
-%% Should test this.
-
-match_cg(M, Rs, Le, Vdb, Bef, St0) ->
- I = Le#l.i,
- {Sis,Int0} = adjust_stack(Bef, I, I+1, Vdb),
- {B,St1} = new_label(St0),
- {Mis,Int1,St2} = match_cg(M, none, Int0, St1#cg{break=B}),
- %% Put return values in registers.
- Reg = load_vars(Rs, Int1#sr.reg),
- {Sis ++ Mis ++ [{label,B}],
- clear_dead(Int1#sr{reg=Reg}, I, Vdb),
- St2#cg{break=St1#cg.break}}.
-
-%% match_cg(Match, Fail, StackReg, State) -> {[Ainstr],StackReg,State}.
-%% Generate code for a match tree. N.B. there is no need pass Vdb
-%% down as each level which uses this takes its own internal Vdb not
-%% the outer one.
-
-match_cg(Le, Fail, Bef, St) ->
- match_cg(Le#l.ke, Le, Fail, Bef, St).
-
-match_cg({alt,F,S}, _Le, Fail, Bef, St0) ->
- {Tf,St1} = new_label(St0),
- {Fis,Faft,St2} = match_cg(F, Tf, Bef, St1),
- {Sis,Saft,St3} = match_cg(S, Fail, Bef, St2),
- Aft = sr_merge(Faft, Saft),
- {Fis ++ [{label,Tf}] ++ Sis,Aft,St3};
-match_cg({select,V,Scs}, _Va, Fail, Bef, St) ->
- match_fmf(fun (S, F, Sta) ->
- select_cg(S, V, F, Fail, Bef, Sta) end,
- Fail, St, Scs);
-match_cg({guard,Gcs}, _Le, Fail, Bef, St) ->
- match_fmf(fun (G, F, Sta) -> guard_clause_cg(G, F, Bef, Sta) end,
- Fail, St, Gcs);
-match_cg({block,Es}, Le, _Fail, Bef, St) ->
- %% Must clear registers and stack of dead variables.
- Int = clear_dead(Bef, Le#l.i, Le#l.vdb),
- block_cg(Es, Le, Int, St).
-
-%% match_fail_cg(FailReason, Le, Vdb, StackReg, State) ->
-%% {[Ainstr],StackReg,State}.
-%% Generate code for the match_fail "call". N.B. there is no generic
-%% case for when the fail value has been created elsewhere.
-
-match_fail_cg({function_clause,As}, Le, Vdb, Bef, St) ->
- %% Must have the args in {x,0}, {x,1},...
- {Sis,Int} = cg_setup_call(As, Bef, Le#l.i, Vdb),
- {Sis ++ [{jump,{f,St#cg.finfo}}],
- Int#sr{reg=clear_regs(Int#sr.reg)},St};
-match_fail_cg({badmatch,Term}, Le, Vdb, Bef, St) ->
- R = cg_reg_arg(Term, Bef),
- Int0 = clear_dead(Bef, Le#l.i, Vdb),
- {Sis,Int} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb),
- {Sis ++ [{badmatch,R}],
- Int#sr{reg=clear_regs(Int0#sr.reg)},St};
-match_fail_cg({case_clause,Reason}, Le, Vdb, Bef, St) ->
- R = cg_reg_arg(Reason, Bef),
- Int0 = clear_dead(Bef, Le#l.i, Vdb),
- {Sis,Int} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb),
- {Sis++[{case_end,R}],
- Int#sr{reg=clear_regs(Bef#sr.reg)},St};
-match_fail_cg(if_clause, Le, Vdb, Bef, St) ->
- Int0 = clear_dead(Bef, Le#l.i, Vdb),
- {Sis,Int1} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb),
- {Sis++[if_end],Int1#sr{reg=clear_regs(Int1#sr.reg)},St};
-match_fail_cg({try_clause,Reason}, Le, Vdb, Bef, St) ->
- R = cg_reg_arg(Reason, Bef),
- Int0 = clear_dead(Bef, Le#l.i, Vdb),
- {Sis,Int} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb),
- {Sis ++ [{try_case_end,R}],
- Int#sr{reg=clear_regs(Int0#sr.reg)},St}.
-
-
-%% block_cg([Kexpr], Le, Vdb, StackReg, St) -> {[Ainstr],StackReg,St}.
-%% block_cg([Kexpr], Le, StackReg, St) -> {[Ainstr],StackReg,St}.
-
-block_cg(Es, Le, _Vdb, Bef, St) ->
- block_cg(Es, Le, Bef, St).
-
-block_cg(Es, Le, Bef, St0) ->
- case St0#cg.is_top_block of
- false ->
- cg_block(Es, Le#l.i, Le#l.vdb, Bef, St0);
- true ->
- {Keis,Aft,St1} = cg_block(Es, Le#l.i, Le#l.vdb, Bef,
- St0#cg{is_top_block=false,
- need_frame=false}),
- top_level_block(Keis, Aft, max_reg(Bef#sr.reg), St1)
- end.
-
-cg_block([], _I, _Vdb, Bef, St0) ->
- {[],Bef,St0};
-cg_block(Kes0, I, Vdb, Bef, St0) ->
- {Kes2,Int1,St1} =
- case basic_block(Kes0) of
- {Kes1,LastI,Args,Rest} ->
- Ke = hd(Kes1),
- Fb = Ke#l.i,
- cg_basic_block(Kes1, Fb, LastI, Args, Vdb, Bef, St0);
- {Kes1,Rest} ->
- cg_list(Kes1, I, Vdb, Bef, St0)
- end,
- {Kes3,Int2,St2} = cg_block(Rest, I, Vdb, Int1, St1),
- {Kes2 ++ Kes3,Int2,St2}.
-
-basic_block(Kes) -> basic_block(Kes, []).
-
-basic_block([], Acc) -> {reverse(Acc),[]};
-basic_block([Le|Les], Acc) ->
- case collect_block(Le#l.ke) of
- include -> basic_block(Les, [Le|Acc]);
- {block_end,As} -> {reverse(Acc, [Le]),Le#l.i,As,Les};
- no_block -> {reverse(Acc, [Le]),Les}
- end.
-
-collect_block({set,_,{binary,_}}) -> no_block;
-collect_block({set,_,_}) -> include;
-collect_block({call,{var,_}=Var,As,_Rs}) -> {block_end,As++[Var]};
-collect_block({call,Func,As,_Rs}) -> {block_end,As++func_vars(Func)};
-collect_block({enter,{var,_}=Var,As})-> {block_end,As++[Var]};
-collect_block({enter,Func,As}) -> {block_end,As++func_vars(Func)};
-collect_block({return,Rs}) -> {block_end,Rs};
-collect_block({break,Bs}) -> {block_end,Bs};
-collect_block({bif,_Bif,_As,_Rs}) -> include;
-collect_block(_) -> no_block.
-
-func_vars({remote,M,F}) when element(1, M) == var;
- element(1, F) == var ->
- [M,F];
-func_vars(_) -> [].
-
-%% cg_basic_block([Kexpr], FirstI, LastI, As, Vdb, StackReg, State) ->
-%% {[Ainstr],StackReg,State}.
-
-cg_basic_block(Kes, Fb, Lf, As, Vdb, Bef, St0) ->
- Res = make_reservation(As, 0),
- Regs0 = reserve(Res, Bef#sr.reg, Bef#sr.stk),
- Stk = extend_stack(Bef, Lf, Lf+1, Vdb),
- Int0 = Bef#sr{reg=Regs0,stk=Stk,res=Res},
- X0_v0 = x0_vars(As, Fb, Lf, Vdb),
- {Keis,{Aft,_,St1}} =
- flatmapfoldl(fun(Ke, St) -> cg_basic_block(Ke, St, Lf, Vdb) end,
- {Int0,X0_v0,St0}, need_heap(Kes, Fb)),
- {Keis,Aft,St1}.
-
-cg_basic_block(Ke, {Inta,X0v,Sta}, _Lf, Vdb) when element(1, Ke#l.ke) =:= need_heap ->
- {Keis,Intb,Stb} = cg(Ke, Vdb, Inta, Sta),
- {comment(Inta) ++ Keis, {Intb,X0v,Stb}};
-cg_basic_block(Ke, {Inta,X0_v1,Sta}, Lf, Vdb) ->
- {Sis,Intb} = save_carefully(Inta, Ke#l.i, Lf+1, Vdb),
- {X0_v2,Intc} = allocate_x0(X0_v1, Ke#l.i, Intb),
- Intd = reserve(Intc),
- {Keis,Inte,Stb} = cg(Ke, Vdb, Intd, Sta),
- {comment(Inta) ++ Sis ++ Keis, {Inte,X0_v2,Stb}}.
-
-make_reservation([], _) -> [];
-make_reservation([{var,V}|As], I) -> [{I,V}|make_reservation(As, I+1)];
-make_reservation([A|As], I) -> [{I,A}|make_reservation(As, I+1)].
-
-reserve(Sr) -> Sr#sr{reg=reserve(Sr#sr.res, Sr#sr.reg, Sr#sr.stk)}.
-
-reserve([{I,V}|Rs], [free|Regs], Stk) -> [{reserved,I,V}|reserve(Rs, Regs, Stk)];
-reserve([{I,V}|Rs], [{I,V}|Regs], Stk) -> [{I,V}|reserve(Rs, Regs, Stk)];
-reserve([{I,V}|Rs], [{I,Var}|Regs], Stk) ->
- case on_stack(Var, Stk) of
- true -> [{reserved,I,V}|reserve(Rs, Regs, Stk)];
- false -> [{I,Var}|reserve(Rs, Regs, Stk)]
- end;
-reserve([{I,V}|Rs], [{reserved,I,_}|Regs], Stk) ->
- [{reserved,I,V}|reserve(Rs, Regs, Stk)];
-%reserve([{I,V}|Rs], [Other|Regs], Stk) -> [Other|reserve(Rs, Regs, Stk)];
-reserve([{I,V}|Rs], [], Stk) -> [{reserved,I,V}|reserve(Rs, [], Stk)];
-reserve([], Regs, _) -> Regs.
-
-extend_stack(Bef, Fb, Lf, Vdb) ->
- Stk0 = clear_dead_stk(Bef#sr.stk, Fb, Vdb),
- Saves = [V || {V,F,L} <- Vdb,
- F < Fb,
- L >= Lf,
- not on_stack(V, Stk0)],
- Stk1 = foldl(fun (V, Stk) -> put_stack(V, Stk) end, Stk0, Saves),
- Bef#sr.stk ++ lists:duplicate(length(Stk1) - length(Bef#sr.stk), free).
-
-save_carefully(Bef, Fb, Lf, Vdb) ->
- Stk = Bef#sr.stk,
- %% New variables that are in use but not on stack.
- New = [ {V,F,L} || {V,F,L} <- Vdb,
- F < Fb,
- L >= Lf,
- not on_stack(V, Stk) ],
- Saves = [ V || {V,_,_} <- keysort(2, New) ],
- save_carefully(Saves, Bef, []).
-
-save_carefully([], Bef, Acc) -> {reverse(Acc),Bef};
-save_carefully([V|Vs], Bef, Acc) ->
- case put_stack_carefully(V, Bef#sr.stk) of
- error -> {reverse(Acc),Bef};
- Stk1 ->
- SrcReg = fetch_reg(V, Bef#sr.reg),
- Move = {move,SrcReg,fetch_stack(V, Stk1)},
- {x,_} = SrcReg, %Assertion - must be X register.
- save_carefully(Vs, Bef#sr{stk=Stk1}, [Move|Acc])
- end.
-
-x0_vars([], _Fb, _Lf, _Vdb) -> [];
-x0_vars([{var,V}|_], Fb, _Lf, Vdb) ->
- {V,F,_L} = VFL = vdb_find(V, Vdb),
- x0_vars1([VFL], Fb, F, Vdb);
-x0_vars([X0|_], Fb, Lf, Vdb) ->
- x0_vars1([{X0,Lf,Lf}], Fb, Lf, Vdb).
-
-x0_vars1(X0, Fb, Xf, Vdb) ->
- Vs0 = [VFL || {_V,F,L}=VFL <- Vdb,
- F >= Fb,
- L < Xf],
- Vs1 = keysort(3, Vs0),
- keysort(2, X0++Vs1).
-
-allocate_x0([], _, Bef) -> {[],Bef#sr{res=[]}};
-allocate_x0([{_,_,L}|Vs], I, Bef) when L =< I ->
- allocate_x0(Vs, I, Bef);
-allocate_x0([{V,_F,_L}=VFL|Vs], _, Bef) ->
- {[VFL|Vs],Bef#sr{res=reserve_x0(V, Bef#sr.res)}}.
-
-reserve_x0(V, [_|Res]) -> [{0,V}|Res];
-reserve_x0(V, []) -> [{0,V}].
-
-top_level_block(Keis, Bef, _MaxRegs, St0) when St0#cg.need_frame =:= false,
- length(Bef#sr.stk) =:= 0 ->
- %% This block need no stack frame. However, we still need to turn the
- %% stack frame upside down.
- MaxY = length(Bef#sr.stk)-1,
- Keis1 = flatmap(fun (Tuple) when tuple(Tuple) ->
- [turn_yregs(size(Tuple), Tuple, MaxY)];
- (Other) ->
- [Other]
- end, Keis),
- {Keis1, Bef, St0#cg{is_top_block=true}};
-top_level_block(Keis, Bef, MaxRegs, St0) ->
- %% This top block needs an allocate instruction before it, and a
- %% deallocate instruction before each return.
- FrameSz = length(Bef#sr.stk),
- MaxY = FrameSz-1,
- Keis1 = flatmap(fun ({call_only,Arity,Func}) ->
- [{call_last,Arity,Func,FrameSz}];
- ({call_ext_only,Arity,Func}) ->
- [{call_ext_last,Arity,Func,FrameSz}];
- ({apply_only,Arity}) ->
- [{apply_last,Arity,FrameSz}];
- (return) ->
- [{deallocate,FrameSz}, return];
- (Tuple) when tuple(Tuple) ->
- [turn_yregs(size(Tuple), Tuple, MaxY)];
- (Other) ->
- [Other]
- end, Keis),
- {[{allocate_zero,FrameSz,MaxRegs}|Keis1], Bef, St0#cg{is_top_block=true}}.
-
-%% turn_yregs(Size, Tuple, MaxY) -> Tuple'
-%% Renumber y register so that {y, 0} becomes {y, FrameSize-1},
-%% {y, FrameSize-1} becomes {y, 0} and so on. This is to make nested
-%% catches work. The code generation algorithm gives a lower register
-%% number to the outer catch, which is wrong.
-
-turn_yregs(0, Tp, _) -> Tp;
-turn_yregs(El, Tp, MaxY) when element(1, element(El, Tp)) == yy ->
- turn_yregs(El-1, setelement(El, Tp, {y,MaxY-element(2, element(El, Tp))}), MaxY);
-turn_yregs(El, Tp, MaxY) when list(element(El, Tp)) ->
- New = map(fun ({yy,YY}) -> {y,MaxY-YY};
- (Other) -> Other end, element(El, Tp)),
- turn_yregs(El-1, setelement(El, Tp, New), MaxY);
-turn_yregs(El, Tp, MaxY) ->
- turn_yregs(El-1, Tp, MaxY).
-
-%% select_cg(Sclause, V, TypeFail, ValueFail, StackReg, State) ->
-%% {Is,StackReg,State}.
-%% Selecting type and value needs two failure labels, TypeFail is the
-%% label to jump to of the next type test when this type fails, and
-%% ValueFail is the label when this type is correct but the value is
-%% wrong. These are different as in the second case there is no need
-%% to try the next type, it will always fail.
-
-select_cg(#l{ke={type_clause,cons,[S]}}, {var,V}, Tf, Vf, Bef, St) ->
- select_cons(S, V, Tf, Vf, Bef, St);
-select_cg(#l{ke={type_clause,nil,[S]}}, {var,V}, Tf, Vf, Bef, St) ->
- select_nil(S, V, Tf, Vf, Bef, St);
-select_cg(#l{ke={type_clause,binary,[S]}}, {var,V}, Tf, Vf, Bef, St) ->
- select_binary(S, V, Tf, Vf, Bef, St);
-select_cg(#l{ke={type_clause,bin_seg,S}}, {var,V}, Tf, Vf, Bef, St) ->
- select_bin_segs(S, V, Tf, Vf, Bef, St);
-select_cg(#l{ke={type_clause,bin_end,[S]}}, {var,V}, Tf, Vf, Bef, St) ->
- select_bin_end(S, V, Tf, Vf, Bef, St);
-select_cg(#l{ke={type_clause,Type,Scs}}, {var,V}, Tf, Vf, Bef, St0) ->
- {Vis,{Aft,St1}} =
- mapfoldl(fun (S, {Int,Sta}) ->
- {Val,Is,Inta,Stb} = select_val(S, V, Vf, Bef, Sta),
- {{Is,[Val]},{sr_merge(Int, Inta),Stb}}
- end, {void,St0}, Scs),
- OptVls = combine(lists:sort(combine(Vis))),
- {Vls,Sis,St2} = select_labels(OptVls, St1, [], []),
- {select_val_cg(Type, fetch_var(V, Bef), Vls, Tf, Vf, Sis), Aft, St2}.
-
-select_val_cg(tuple, R, [Arity,{f,Lbl}], Tf, Vf, [{label,Lbl}|Sis]) ->
- [{test,is_tuple,{f,Tf},[R]},{test,test_arity,{f,Vf},[R,Arity]}|Sis];
-select_val_cg(tuple, R, Vls, Tf, Vf, Sis) ->
- [{test,is_tuple,{f,Tf},[R]},{select_tuple_arity,R,{f,Vf},{list,Vls}}|Sis];
-select_val_cg(Type, R, [Val, {f,Lbl}], Fail, Fail, [{label,Lbl}|Sis]) ->
- [{test,is_eq_exact,{f,Fail},[R,{Type,Val}]}|Sis];
-select_val_cg(Type, R, [Val, {f,Lbl}], Tf, Vf, [{label,Lbl}|Sis]) ->
- [{test,select_type_test(Type),{f,Tf},[R]},
- {test,is_eq_exact,{f,Vf},[R,{Type,Val}]}|Sis];
-select_val_cg(Type, R, Vls0, Tf, Vf, Sis) ->
- Vls1 = map(fun ({f,Lbl}) -> {f,Lbl};
- (Value) -> {Type,Value}
- end, Vls0),
- [{test,select_type_test(Type),{f,Tf},[R]}, {select_val,R,{f,Vf},{list,Vls1}}|Sis].
-
-select_type_test(tuple) -> is_tuple;
-select_type_test(integer) -> is_integer;
-select_type_test(atom) -> is_atom;
-select_type_test(float) -> is_float.
-
-combine([{Is,Vs1}, {Is,Vs2}|Vis]) -> combine([{Is,Vs1 ++ Vs2}|Vis]);
-combine([V|Vis]) -> [V|combine(Vis)];
-combine([]) -> [].
-
-select_labels([{Is,Vs}|Vis], St0, Vls, Sis) ->
- {Lbl,St1} = new_label(St0),
- select_labels(Vis, St1, add_vls(Vs, Lbl, Vls), [[{label,Lbl}|Is]|Sis]);
-select_labels([], St, Vls, Sis) ->
- {Vls,append(Sis),St}.
-
-add_vls([V|Vs], Lbl, Acc) ->
- add_vls(Vs, Lbl, [V, {f,Lbl}|Acc]);
-add_vls([], _, Acc) -> Acc.
-
-select_cons(#l{ke={val_clause,{cons,Es},B},i=I,vdb=Vdb}, V, Tf, Vf, Bef, St0) ->
- {Eis,Int,St1} = select_extract_cons(V, Es, I, Vdb, Bef, St0),
- {Bis,Aft,St2} = match_cg(B, Vf, Int, St1),
- {[{test,is_nonempty_list,{f,Tf},[fetch_var(V, Bef)]}] ++ Eis ++ Bis,Aft,St2}.
-
-select_nil(#l{ke={val_clause,nil,B}}, V, Tf, Vf, Bef, St0) ->
- {Bis,Aft,St1} = match_cg(B, Vf, Bef, St0),
- {[{test,is_nil,{f,Tf},[fetch_var(V, Bef)]}] ++ Bis,Aft,St1}.
-
-select_binary(#l{ke={val_clause,{old_binary,Var},B}}=L,
- V, Tf, Vf, Bef, St) ->
- %% Currently handled in the same way as new binaries.
- select_binary(L#l{ke={val_clause,{binary,Var},B}}, V, Tf, Vf, Bef, St);
-select_binary(#l{ke={val_clause,{binary,{var,Ivar}},B},i=I,vdb=Vdb},
- V, Tf, Vf, Bef, St0) ->
- Int0 = clear_dead(Bef, I, Vdb),
- {Bis,Aft,St1} = match_cg(B, Vf, Int0, St0),
- {[{test,bs_start_match,{f,Tf},[fetch_var(V, Bef)]},{bs_save,Ivar}|Bis],
- Aft,St1}.
-
-select_bin_segs(Scs, Ivar, Tf, _Vf, Bef, St) ->
- match_fmf(fun(S, Fail, Sta) ->
- select_bin_seg(S, Ivar, Fail, Bef, Sta) end,
- Tf, St, Scs).
-
-select_bin_seg(#l{ke={val_clause,{bin_seg,Size,U,T,Fs,Es},B},i=I,vdb=Vdb},
- Ivar, Fail, Bef, St0) ->
- {Mis,Int,St1} = select_extract_bin(Es, Size, U, T, Fs, Fail,
- I, Vdb, Bef, St0),
- {Bis,Aft,St2} = match_cg(B, Fail, Int, St1),
- {[{bs_restore,Ivar}|Mis] ++ Bis,Aft,St2}.
-
-select_extract_bin([{var,Hd},{var,Tl}], Size0, Unit, Type, Flags, Vf,
- I, Vdb, Bef, St) ->
- SizeReg = get_bin_size_reg(Size0, Bef),
- {Es,Aft} =
- case vdb_find(Hd, Vdb) of
- {_,_,Lhd} when Lhd =< I ->
- {[{test,bs_skip_bits,{f,Vf},[SizeReg,Unit,{field_flags,Flags}]},
- {bs_save,Tl}],Bef};
- {_,_,_} ->
- Reg0 = put_reg(Hd, Bef#sr.reg),
- Int1 = Bef#sr{reg=Reg0},
- Rhd = fetch_reg(Hd, Reg0),
- Name = get_bits_instr(Type),
- {[{test,Name,{f,Vf},[SizeReg,Unit,{field_flags,Flags},Rhd]},
- {bs_save,Tl}],Int1}
- end,
- {Es,clear_dead(Aft, I, Vdb),St}.
-
-get_bin_size_reg({var,V}, Bef) ->
- fetch_var(V, Bef);
-get_bin_size_reg(Literal, _Bef) ->
- Literal.
-
-select_bin_end(#l{ke={val_clause,bin_end,B}},
- Ivar, Tf, Vf, Bef, St0) ->
- {Bis,Aft,St2} = match_cg(B, Vf, Bef, St0),
- {[{bs_restore,Ivar},{test,bs_test_tail,{f,Tf},[0]}|Bis],Aft,St2}.
-
-get_bits_instr(integer) -> bs_get_integer;
-get_bits_instr(float) -> bs_get_float;
-get_bits_instr(binary) -> bs_get_binary.
-
-select_val(#l{ke={val_clause,{tuple,Es},B},i=I,vdb=Vdb}, V, Vf, Bef, St0) ->
- {Eis,Int,St1} = select_extract_tuple(V, Es, I, Vdb, Bef, St0),
- {Bis,Aft,St2} = match_cg(B, Vf, Int, St1),
- {length(Es),Eis ++ Bis,Aft,St2};
-select_val(#l{ke={val_clause,{_,Val},B}}, _V, Vf, Bef, St0) ->
- {Bis,Aft,St1} = match_cg(B, Vf, Bef, St0),
- {Val,Bis,Aft,St1}.
-
-%% select_extract_tuple(Src, [V], I, Vdb, StackReg, State) ->
-%% {[E],StackReg,State}.
-%% Extract tuple elements, but only if they do not immediately die.
-
-select_extract_tuple(Src, Vs, I, Vdb, Bef, St) ->
- F = fun ({var,V}, {Int0,Elem}) ->
- case vdb_find(V, Vdb) of
- {V,_,L} when L =< I -> {[], {Int0,Elem+1}};
- _Other ->
- Reg1 = put_reg(V, Int0#sr.reg),
- Int1 = Int0#sr{reg=Reg1},
- Rsrc = fetch_var(Src, Int1),
- {[{get_tuple_element,Rsrc,Elem,fetch_reg(V, Reg1)}],
- {Int1,Elem+1}}
- end
- end,
- {Es,{Aft,_}} = flatmapfoldl(F, {Bef,0}, Vs),
- {Es,Aft,St}.
-
-select_extract_cons(Src, [{var,Hd}, {var,Tl}], I, Vdb, Bef, St) ->
- {Es,Aft} = case {vdb_find(Hd, Vdb), vdb_find(Tl, Vdb)} of
- {{_,_,Lhd}, {_,_,Ltl}} when Lhd =< I, Ltl =< I ->
- %% Both head and tail are dead. No need to generate
- %% any instruction.
- {[], Bef};
- _ ->
- %% At least one of head and tail will be used,
- %% but we must always fetch both. We will call
- %% clear_dead/2 to allow reuse of the register
- %% in case only of them is used.
-
- Reg0 = put_reg(Tl, put_reg(Hd, Bef#sr.reg)),
- Int0 = Bef#sr{reg=Reg0},
- Rsrc = fetch_var(Src, Int0),
- Rhd = fetch_reg(Hd, Reg0),
- Rtl = fetch_reg(Tl, Reg0),
- Int1 = clear_dead(Int0, I, Vdb),
- {[{get_list,Rsrc,Rhd,Rtl}], Int1}
- end,
- {Es,Aft,St}.
-
-
-guard_clause_cg(#l{ke={guard_clause,G,B},vdb=Vdb}, Fail, Bef, St0) ->
- {Gis,Int,St1} = guard_cg(G, Fail, Vdb, Bef, St0),
- {Bis,Aft,St2} = match_cg(B, Fail, Int, St1),
- {Gis ++ Bis,Aft,St2}.
-
-%% guard_cg(Guard, Fail, Vdb, StackReg, State) ->
-%% {[Ainstr],StackReg,State}.
-%% A guard is a boolean expression of tests. Tests return true or
-%% false. A fault in a test causes the test to return false. Tests
-%% never return the boolean, instead we generate jump code to go to
-%% the correct exit point. Primops and tests all go to the next
-%% instruction on success or jump to a failure label.
-
-guard_cg(#l{ke={protected,Ts,Rs},i=I,vdb=Pdb}, Fail, _Vdb, Bef, St) ->
- protected_cg(Ts, Rs, Fail, I, Pdb, Bef, St);
-guard_cg(#l{ke={block,Ts},i=I,vdb=Bdb}, Fail, _Vdb, Bef, St) ->
- guard_cg_list(Ts, Fail, I, Bdb, Bef, St);
-guard_cg(#l{ke={test,Test,As},i=I,vdb=_Tdb}, Fail, Vdb, Bef, St) ->
- test_cg(Test, As, Fail, I, Vdb, Bef, St);
-guard_cg(G, _Fail, Vdb, Bef, St) ->
- %%ok = io:fwrite("cg ~w: ~p~n", [?LINE,{G,Fail,Vdb,Bef}]),
- {Gis,Aft,St1} = cg(G, Vdb, Bef, St),
- %%ok = io:fwrite("cg ~w: ~p~n", [?LINE,{Aft}]),
- {Gis,Aft,St1}.
-
-%% protected_cg([Kexpr], [Ret], Fail, I, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
-%% Do a protected. Protecteds without return values are just done
-%% for effect, the return value is not checked, success passes on to
-%% the next instruction and failure jumps to Fail. If there are
-%% return values then these must be set to 'false' on failure,
-%% control always passes to the next instruction.
-
-protected_cg(Ts, [], Fail, I, Vdb, Bef, St0) ->
- %% Protect these calls, revert when done.
- {Tis,Aft,St1} = guard_cg_list(Ts, Fail, I, Vdb, Bef,
- St0#cg{btype=fail,bfail=Fail}),
- {Tis,Aft,St1#cg{btype=St0#cg.btype,bfail=St0#cg.bfail}};
-protected_cg(Ts, Rs, _Fail, I, Vdb, Bef, St0) ->
- {Pfail,St1} = new_label(St0),
- {Psucc,St2} = new_label(St1),
- {Tis,Aft,St3} = guard_cg_list(Ts, Pfail, I, Vdb, Bef,
- St2#cg{btype=fail,bfail=Pfail}),
- %%ok = io:fwrite("cg ~w: ~p~n", [?LINE,{Rs,I,Vdb,Aft}]),
- %% Set return values to false.
- Mis = map(fun ({var,V}) -> {move,{atom,false},fetch_var(V, Aft)} end, Rs),
- Live = {'%live',max_reg(Aft#sr.reg)},
- {Tis ++ [Live,{jump,{f,Psucc}},
- {label,Pfail}] ++ Mis ++ [Live,{label,Psucc}],
- Aft,St3#cg{btype=St0#cg.btype,bfail=St0#cg.bfail}}.
-
-%% test_cg(TestName, Args, Fail, I, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
-%% Generate test instruction. Use explicit fail label here.
-
-test_cg(Test, As, Fail, I, Vdb, Bef, St) ->
- case test_type(Test, length(As)) of
- {cond_op,Op} ->
- Ars = cg_reg_args(As, Bef),
- Int = clear_dead(Bef, I, Vdb),
- {[{test,Op,{f,Fail},Ars}],
- clear_dead(Int, I, Vdb),
- St};
- {rev_cond_op,Op} ->
- [S1,S2] = cg_reg_args(As, Bef),
- Int = clear_dead(Bef, I, Vdb),
- {[{test,Op,{f,Fail},[S2,S1]}],
- clear_dead(Int, I, Vdb),
- St}
- end.
-
-test_type(is_atom, 1) -> {cond_op,is_atom};
-test_type(is_boolean, 1) -> {cond_op,is_boolean};
-test_type(is_binary, 1) -> {cond_op,is_binary};
-test_type(is_constant, 1) -> {cond_op,is_constant};
-test_type(is_float, 1) -> {cond_op,is_float};
-test_type(is_function, 1) -> {cond_op,is_function};
-test_type(is_integer, 1) -> {cond_op,is_integer};
-test_type(is_list, 1) -> {cond_op,is_list};
-test_type(is_number, 1) -> {cond_op,is_number};
-test_type(is_pid, 1) -> {cond_op,is_pid};
-test_type(is_port, 1) -> {cond_op,is_port};
-test_type(is_reference, 1) -> {cond_op,is_reference};
-test_type(is_tuple, 1) -> {cond_op,is_tuple};
-test_type('=<', 2) -> {rev_cond_op,is_ge};
-test_type('>', 2) -> {rev_cond_op,is_lt};
-test_type('<', 2) -> {cond_op,is_lt};
-test_type('>=', 2) -> {cond_op,is_ge};
-test_type('==', 2) -> {cond_op,is_eq};
-test_type('/=', 2) -> {cond_op,is_ne};
-test_type('=:=', 2) -> {cond_op,is_eq_exact};
-test_type('=/=', 2) -> {cond_op,is_ne_exact};
-test_type(internal_is_record, 3) -> {cond_op,internal_is_record}.
-
-%% guard_cg_list([Kexpr], Fail, I, Vdb, StackReg, St) ->
-%% {[Ainstr],StackReg,St}.
-
-guard_cg_list(Kes, Fail, I, Vdb, Bef, St0) ->
- {Keis,{Aft,St1}} =
- flatmapfoldl(fun (Ke, {Inta,Sta}) ->
- {Keis,Intb,Stb} =
- guard_cg(Ke, Fail, Vdb, Inta, Sta),
- {comment(Inta) ++ Keis,{Intb,Stb}}
- end, {Bef,St0}, need_heap(Kes, I)),
- {Keis,Aft,St1}.
-
-%% match_fmf(Fun, LastFail, State, [Clause]) -> {Is,Aft,State}.
-%% This is a special flatmapfoldl for match code gen where we
-%% generate a "failure" label for each clause. The last clause uses
-%% an externally generated failure label, LastFail. N.B. We do not
-%% know or care how the failure labels are used.
-
-match_fmf(F, LastFail, St, [H]) ->
- F(H, LastFail, St);
-match_fmf(F, LastFail, St0, [H|T]) ->
- {Fail,St1} = new_label(St0),
- {R,Aft1,St2} = F(H, Fail, St1),
- {Rs,Aft2,St3} = match_fmf(F, LastFail, St2, T),
- {R ++ [{label,Fail}] ++ Rs,sr_merge(Aft1, Aft2),St3};
-match_fmf(_, _, St, []) -> {[],void,St}.
-
-%% call_cg(Func, [Arg], [Ret], Le, Vdb, StackReg, State) ->
-%% {[Ainstr],StackReg,State}.
-%% enter_cg(Func, [Arg], Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
-%% Call and enter first put the arguments into registers and save any
-%% other registers, then clean up and compress the stack and set the
-%% frame size. Finally the actual call is made. Call then needs the
-%% return values filled in.
-
-call_cg({var,V}, As, Rs, Le, Vdb, Bef, St0) ->
- {Sis,Int} = cg_setup_call(As++[{var,V}], Bef, Le#l.i, Vdb),
- %% Put return values in registers.
- Reg = load_vars(Rs, clear_regs(Int#sr.reg)),
- %% Build complete code and final stack/register state.
- Arity = length(As),
- {Frees,Aft} = free_dead(clear_dead(Int#sr{reg=Reg}, Le#l.i, Vdb)),
- {comment({call_fun,{var,V},As}) ++ Sis ++ Frees ++ [{call_fun,Arity}],
- Aft,need_stack_frame(St0)};
-call_cg({remote,Mod,Name}, As, Rs, Le, Vdb, Bef, St0)
- when element(1, Mod) == var;
- element(1, Name) == var ->
- {Sis,Int} = cg_setup_call(As++[Mod,Name], Bef, Le#l.i, Vdb),
- %% Put return values in registers.
- Reg = load_vars(Rs, clear_regs(Int#sr.reg)),
- %% Build complete code and final stack/register state.
- Arity = length(As),
- Call = {apply,Arity},
- St = need_stack_frame(St0),
- %%{Call,St1} = build_call(Func, Arity, St0),
- {Frees,Aft} = free_dead(clear_dead(Int#sr{reg=Reg}, Le#l.i, Vdb)),
- {Sis ++ Frees ++ [Call],Aft,St};
-call_cg(Func, As, Rs, Le, Vdb, Bef, St0) ->
- {Sis,Int} = cg_setup_call(As, Bef, Le#l.i, Vdb),
- %% Put return values in registers.
- Reg = load_vars(Rs, clear_regs(Int#sr.reg)),
- %% Build complete code and final stack/register state.
- Arity = length(As),
- {Call,St1} = build_call(Func, Arity, St0),
- {Frees,Aft} = free_dead(clear_dead(Int#sr{reg=Reg}, Le#l.i, Vdb)),
- {comment({call,Func,As}) ++ Sis ++ Frees ++ Call,Aft,St1}.
-
-build_call({remote,{atom,erlang},{atom,'!'}}, 2, St0) ->
- {[send],need_stack_frame(St0)};
-build_call({remote,{atom,Mod},{atom,Name}}, Arity, St0) ->
- {[{call_ext,Arity,{extfunc,Mod,Name,Arity}}],need_stack_frame(St0)};
-build_call(Name, Arity, St0) when atom(Name) ->
- {Lbl,St1} = local_func_label(Name, Arity, need_stack_frame(St0)),
- {[{call,Arity,{f,Lbl}}],St1}.
-
-free_dead(#sr{stk=Stk0}=Aft) ->
- {Instr,Stk} = free_dead(Stk0, 0, [], []),
- {Instr,Aft#sr{stk=Stk}}.
-
-free_dead([dead|Stk], Y, Instr, StkAcc) ->
- %% Note: kill/1 is equivalent to init/1 (translated by beam_asm).
- %% We use kill/1 to help further optimisation passes.
- free_dead(Stk, Y+1, [{kill,{yy,Y}}|Instr], [free|StkAcc]);
-free_dead([Any|Stk], Y, Instr, StkAcc) ->
- free_dead(Stk, Y+1, Instr, [Any|StkAcc]);
-free_dead([], _, Instr, StkAcc) -> {Instr,reverse(StkAcc)}.
-
-enter_cg({var,V}, As, Le, Vdb, Bef, St0) ->
- {Sis,Int} = cg_setup_call(As++[{var,V}], Bef, Le#l.i, Vdb),
- %% Build complete code and final stack/register state.
- Arity = length(As),
- {comment({call_fun,{var,V},As}) ++ Sis ++ [{call_fun,Arity},return],
- clear_dead(Int#sr{reg=clear_regs(Int#sr.reg)}, Le#l.i, Vdb),
- need_stack_frame(St0)};
-enter_cg({remote,Mod,Name}=Func, As, Le, Vdb, Bef, St0)
- when element(1, Mod) == var;
- element(1, Name) == var ->
- {Sis,Int} = cg_setup_call(As++[Mod,Name], Bef, Le#l.i, Vdb),
- %% Build complete code and final stack/register state.
- Arity = length(As),
- Call = {apply_only,Arity},
- St = need_stack_frame(St0),
- {comment({enter,Func,As}) ++ Sis ++ [Call],
- clear_dead(Int#sr{reg=clear_regs(Int#sr.reg)}, Le#l.i, Vdb),
- St};
-enter_cg(Func, As, Le, Vdb, Bef, St0) ->
- {Sis,Int} = cg_setup_call(As, Bef, Le#l.i, Vdb),
- %% Build complete code and final stack/register state.
- Arity = length(As),
- {Call,St1} = build_enter(Func, Arity, St0),
- {comment({enter,Func,As}) ++ Sis ++ Call,
- clear_dead(Int#sr{reg=clear_regs(Int#sr.reg)}, Le#l.i, Vdb),
- St1}.
-
-build_enter({remote,{atom,erlang},{atom,'!'}}, 2, St0) ->
- {[send,return],need_stack_frame(St0)};
-build_enter({remote,{atom,Mod},{atom,Name}}, Arity, St0) ->
- St1 = case trap_bif(Mod, Name, Arity) of
- true -> need_stack_frame(St0);
- false -> St0
- end,
- {[{call_ext_only,Arity,{extfunc,Mod,Name,Arity}}],St1};
-build_enter(Name, Arity, St0) when is_atom(Name) ->
- {Lbl,St1} = local_func_label(Name, Arity, St0),
- {[{call_only,Arity,{f,Lbl}}],St1}.
-
-%% local_func_label(Name, Arity, State) -> {Label,State'}
-%% Get the function entry label for a local function.
-
-local_func_label(Name, Arity, St0) ->
- Key = {Name,Arity},
- case keysearch(Key, 1, St0#cg.functable) of
- {value,{Key,Label}} ->
- {Label,St0};
- false ->
- {Label,St1} = new_label(St0),
- {Label,St1#cg{functable=[{Key,Label}|St1#cg.functable]}}
- end.
-
-%% need_stack_frame(State) -> State'
-%% Make a note in the state that this function will need a stack frame.
-
-need_stack_frame(#cg{need_frame=true}=St) -> St;
-need_stack_frame(St) -> St#cg{need_frame=true}.
-
-%% trap_bif(Mod, Name, Arity) -> true|false
-%% Trap bifs that need a stack frame.
-
-trap_bif(erlang, '!', 2) -> true;
-trap_bif(erlang, link, 1) -> true;
-trap_bif(erlang, unlink, 1) -> true;
-trap_bif(erlang, monitor_node, 2) -> true;
-trap_bif(erlang, group_leader, 2) -> true;
-trap_bif(erlang, exit, 2) -> true;
-trap_bif(_, _, _) -> false.
-
-%% bif_cg(Bif, [Arg], [Ret], Le, Vdb, StackReg, State) ->
-%% {[Ainstr],StackReg,State}.
-
-bif_cg(dsetelement, [Index0,Tuple0,New0], _Rs, Le, Vdb, Bef, St0) ->
- [New,Tuple,{integer,Index1}] = cg_reg_args([New0,Tuple0,Index0], Bef),
- Index = Index1-1,
- {[{set_tuple_element,New,Tuple,Index}],
- clear_dead(Bef, Le#l.i, Vdb), St0};
-bif_cg({make_fun,Func,Arity,Index,Uniq}, As, Rs, Le, Vdb, Bef, St0) ->
- %% This behaves more like a function call.
- {Sis,Int} = cg_setup_call(As, Bef, Le#l.i, Vdb),
- Reg = load_vars(Rs, clear_regs(Int#sr.reg)),
- {FuncLbl,St1} = local_func_label(Func, Arity, St0),
- MakeFun = case St0#cg.new_funs of
- true -> {make_fun2,{f,FuncLbl},Index,Uniq,length(As)};
- false -> {make_fun,{f,FuncLbl},Uniq,length(As)}
- end,
- {comment({make_fun,{Func,Arity,Uniq},As}) ++ Sis ++
- [MakeFun],
- clear_dead(Int#sr{reg=Reg}, Le#l.i, Vdb),
- St1};
-bif_cg(Bif, As, [{var,V}], Le, Vdb, Bef, St0) ->
- Ars = cg_reg_args(As, Bef),
-
- %% If we are inside a catch, we must save everything that will
- %% be alive after the catch (because the BIF might fail and there
- %% will be a jump to the code after the catch).
- %% Currently, we are somewhat pessimistic in
- %% that we save any variable that will be live after this BIF call.
-
- {Sis,Int0} =
- case St0#cg.in_catch of
- true -> adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb);
- false -> {[],Bef}
- end,
-
- Int1 = clear_dead(Int0, Le#l.i, Vdb),
- Reg = put_reg(V, Int1#sr.reg),
- Int = Int1#sr{reg=Reg},
- Dst = fetch_reg(V, Reg),
- {Sis ++ [{bif,Bif,bif_fail(St0#cg.btype, St0#cg.bfail, length(Ars)),Ars,Dst}],
- clear_dead(Int, Le#l.i, Vdb), St0}.
-
-bif_fail(_, _, 0) -> nofail;
-bif_fail(exit, _, _) -> {f,0};
-bif_fail(fail, Fail, _) -> {f,Fail}.
-
-%% recv_loop_cg(TimeOut, ReceiveVar, ReceiveMatch, TimeOutExprs,
-%% [Ret], Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
-
-recv_loop_cg(Te, Rvar, Rm, Tes, Rs, Le, Vdb, Bef, St0) ->
- {Sis,Int0} = adjust_stack(Bef, Le#l.i, Le#l.i, Vdb),
- Int1 = Int0#sr{reg=clear_regs(Int0#sr.reg)},
- %% Get labels.
- {Rl,St1} = new_label(St0),
- {Tl,St2} = new_label(St1),
- {Bl,St3} = new_label(St2),
- St4 = St3#cg{break=Bl,recv=Rl}, %Set correct receive labels
- {Ris,Raft,St5} = cg_recv_mesg(Rvar, Rm, Tl, Int1, St4),
- {Wis,Taft,St6} = cg_recv_wait(Te, Tes, Le#l.i, Int1, St5),
- Int2 = sr_merge(Raft, Taft), %Merge stack/registers
- Reg = load_vars(Rs, Int2#sr.reg),
- {Sis ++ Ris ++ [{label,Tl}] ++ Wis ++ [{label,Bl}],
- clear_dead(Int2#sr{reg=Reg}, Le#l.i, Vdb),
- St6#cg{break=St0#cg.break,recv=St0#cg.recv}}.
-
-%% cg_recv_mesg( ) -> {[Ainstr],Aft,St}.
-
-cg_recv_mesg({var,R}, Rm, Tl, Bef, St0) ->
- Int0 = Bef#sr{reg=put_reg(R, Bef#sr.reg)},
- Ret = fetch_reg(R, Int0#sr.reg),
- %% Int1 = clear_dead(Int0, I, Rm#l.vdb),
- Int1 = Int0,
- {Mis,Int2,St1} = match_cg(Rm, none, Int1, St0),
- {[{'%live',0},{label,St1#cg.recv},{loop_rec,{f,Tl},Ret}|Mis],Int2,St1}.
-
-%% cg_recv_wait(Te, Tes, I, Vdb, Int2, St3) -> {[Ainstr],Aft,St}.
-
-cg_recv_wait({atom,infinity}, Tes, I, Bef, St0) ->
- %% We know that the 'after' body will never be executed.
- %% But to keep the stack and register information up to date,
- %% we will generate the code for the 'after' body, and then discard it.
- Int1 = clear_dead(Bef, I, Tes#l.vdb),
- {_,Int2,St1} = cg_block(Tes#l.ke, Tes#l.i, Tes#l.vdb,
- Int1#sr{reg=clear_regs(Int1#sr.reg)}, St0),
- {[{wait,{f,St1#cg.recv}}],Int2,St1};
-cg_recv_wait({integer,0}, Tes, _I, Bef, St0) ->
- {Tis,Int,St1} = cg_block(Tes#l.ke, Tes#l.i, Tes#l.vdb, Bef, St0),
- {[timeout|Tis],Int,St1};
-cg_recv_wait(Te, Tes, I, Bef, St0) ->
- Reg = cg_reg_arg(Te, Bef),
- %% Must have empty registers here! Bug if anything in registers.
- Int0 = clear_dead(Bef, I, Tes#l.vdb),
- {Tis,Int,St1} = cg_block(Tes#l.ke, Tes#l.i, Tes#l.vdb,
- Int0#sr{reg=clear_regs(Int0#sr.reg)}, St0),
- {[{wait_timeout,{f,St1#cg.recv},Reg},timeout] ++ Tis,Int,St1}.
-
-%% recv_next_cg(Le, Vdb, StackReg, St) -> {[Ainstr],StackReg,St}.
-%% Use adjust stack to clear stack, but only need it for Aft.
-
-recv_next_cg(Le, Vdb, Bef, St) ->
- {Sis,Aft} = adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb),
- {[{loop_rec_end,{f,St#cg.recv}}] ++ Sis,Aft,St}. %Joke
-
-%% try_cg(TryBlock, [BodyVar], TryBody, [ExcpVar], TryHandler, [Ret],
-%% Le, Vdb, StackReg, St) -> {[Ainstr],StackReg,St}.
-
-try_cg(Ta, Vs, Tb, Evs, Th, Rs, Le, Vdb, Bef, St0) ->
- {B,St1} = new_label(St0), %Body label
- {H,St2} = new_label(St1), %Handler label
- {E,St3} = new_label(St2), %End label
- TryTag = Ta#l.i,
- Int1 = Bef#sr{stk=put_catch(TryTag, Bef#sr.stk)},
- TryReg = fetch_stack({catch_tag,TryTag}, Int1#sr.stk),
- {Ais,Int2,St4} = cg(Ta, Vdb, Int1, St3#cg{break=B,in_catch=true}),
- Int3 = Int2#sr{stk=drop_catch(TryTag, Int2#sr.stk)},
- St5 = St4#cg{break=E,in_catch=St3#cg.in_catch},
- {Bis,Baft,St6} = cg(Tb, Vdb, Int3#sr{reg=load_vars(Vs, Int3#sr.reg)}, St5),
- {His,Haft,St7} = cg(Th, Vdb, Int3#sr{reg=load_vars(Evs, Int3#sr.reg)}, St6),
- Int4 = sr_merge(Baft, Haft), %Merge stack/registers
- Aft = Int4#sr{reg=load_vars(Rs, Int4#sr.reg)},
- {[{'try',TryReg,{f,H}}] ++ Ais ++
- [{label,B},{try_end,TryReg}] ++ Bis ++
- [{label,H},{try_case,TryReg}] ++ His ++
- [{label,E}],
- clear_dead(Aft, Le#l.i, Vdb),
- St7#cg{break=St0#cg.break}}.
-
-%% catch_cg(CatchBlock, Ret, Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
-
-catch_cg(C, {var,R}, Le, Vdb, Bef, St0) ->
- {B,St1} = new_label(St0),
- CatchTag = Le#l.i,
- Int1 = Bef#sr{stk=put_catch(CatchTag, Bef#sr.stk)},
- CatchReg = fetch_stack({catch_tag,CatchTag}, Int1#sr.stk),
- {Cis,Int2,St2} = cg_block(C, Le#l.i, Le#l.vdb, Int1,
- St1#cg{break=B,in_catch=true}),
- Aft = Int2#sr{reg=load_reg(R, 0, Int2#sr.reg),
- stk=drop_catch(CatchTag, Int2#sr.stk)},
- {[{'catch',CatchReg,{f,B}}] ++ Cis ++
- [{label,B},{catch_end,CatchReg}],
- clear_dead(Aft, Le#l.i, Vdb),
- St2#cg{break=St1#cg.break,in_catch=St1#cg.in_catch}}.
-
-%% set_cg([Var], Constr, Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
-%% We have to be careful how a 'set' works. First the structure is
-%% built, then it is filled and finally things can be cleared. The
-%% annotation must reflect this and make sure that the return
-%% variable is allocated first.
-%%
-%% put_list for constructing a cons is an atomic instruction
-%% which can safely resuse one of the source registers as target.
-%% Also binaries can reuse a source register as target.
-
-set_cg([{var,R}], {cons,Es}, Le, Vdb, Bef, St) ->
- [S1,S2] = map(fun ({var,V}) -> fetch_var(V, Bef);
- (Other) -> Other
- end, Es),
- Int0 = clear_dead(Bef, Le#l.i, Vdb),
- Int1 = Int0#sr{reg=put_reg(R, Int0#sr.reg)},
- Ret = fetch_reg(R, Int1#sr.reg),
- {[{put_list,S1,S2,Ret}], Int1, St};
-set_cg([{var,R}], {old_binary,Segs}, Le, Vdb, Bef, St) ->
- Fail = bif_fail(St#cg.btype, St#cg.bfail, 42),
- PutCode = cg_bin_put(Segs, Fail, Bef),
- Code = cg_binary_old(PutCode),
- Int0 = clear_dead(Bef, Le#l.i, Vdb),
- Aft = Int0#sr{reg=put_reg(R, Int0#sr.reg)},
- Ret = fetch_reg(R, Aft#sr.reg),
- {Code ++ [{bs_final,Fail,Ret}],Aft,St};
-set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef, #cg{in_catch=InCatch}=St) ->
- Int0 = Bef#sr{reg=put_reg(R, Bef#sr.reg)},
- Target = fetch_reg(R, Int0#sr.reg),
- Fail = bif_fail(St#cg.btype, St#cg.bfail, 42),
- Temp = find_scratch_reg(Int0#sr.reg),
- PutCode = cg_bin_put(Segs, Fail, Bef),
- {Sis,Int1} =
- case InCatch of
- true -> adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb);
- false -> {[],Int0}
- end,
- Aft = clear_dead(Int1, Le#l.i, Vdb),
- Code = cg_binary(PutCode, Target, Temp, Fail, Aft),
- {Sis++Code,Aft,St};
-set_cg([{var,R}], Con, Le, Vdb, Bef, St) ->
- %% Find a place for the return register first.
- Int = Bef#sr{reg=put_reg(R, Bef#sr.reg)},
- Ret = fetch_reg(R, Int#sr.reg),
- Ais = case Con of
- {tuple,Es} ->
- [{put_tuple,length(Es),Ret}] ++ cg_build_args(Es, Bef);
- {var,V} -> % Normally removed by kernel optimizer.
- [{move,fetch_var(V, Int),Ret}];
- {string,Str} ->
- [{put_string,length(Str),{string,Str},Ret}];
- Other ->
- [{move,Other,Ret}]
- end,
- {Ais,clear_dead(Int, Le#l.i, Vdb),St};
-set_cg([], {binary,Segs}, Le, Vdb, Bef, St) ->
- Fail = bif_fail(St#cg.btype, St#cg.bfail, 42),
- Target = find_scratch_reg(Bef#sr.reg),
- Temp = find_scratch_reg(put_reg(Target, Bef#sr.reg)),
- PutCode = cg_bin_put(Segs, Fail, Bef),
- Code = cg_binary(PutCode, Target, Temp, Fail, Bef),
- Aft = clear_dead(Bef, Le#l.i, Vdb),
- {Code,Aft,St};
-set_cg([], {old_binary,Segs}, Le, Vdb, Bef, St) ->
- Fail = bif_fail(St#cg.btype, St#cg.bfail, 42),
- PutCode = cg_bin_put(Segs, Fail, Bef),
- Ais0 = cg_binary_old(PutCode),
- Ret = find_scratch_reg(Bef#sr.reg),
- Ais = Ais0 ++ [{bs_final,Fail,Ret}],
- {Ais,clear_dead(Bef, Le#l.i, Vdb),St};
-set_cg([], _, Le, Vdb, Bef, St) ->
- %% This should have been stripped by compiler, just cleanup.
- {[],clear_dead(Bef, Le#l.i, Vdb), St}.
-
-
-%%%
-%%% Code generation for constructing binaries.
-%%%
-
-cg_binary(PutCode, Target, Temp, Fail, Bef) ->
- SzCode = cg_binary_size(PutCode, Target, Temp, Fail),
- MaxRegs = max_reg(Bef#sr.reg),
- Code = SzCode ++ [{bs_init2,Fail,Target,MaxRegs,{field_flags,[]},Target}|PutCode],
- cg_bin_opt(Code).
-
-cg_binary_size(PutCode, Target, Temp, Fail) ->
- Szs = cg_binary_size_1(PutCode, 0, []),
- cg_binary_size_expr(Szs, Target, Temp, Fail).
-
-cg_binary_size_1([{_Put,_Fail,S,U,_Flags,Src}|T], Bits, Acc) ->
- cg_binary_size_2(S, U, Src, T, Bits, Acc);
-cg_binary_size_1([], Bits, Acc) ->
- Bytes = Bits div 8,
- RemBits = Bits rem 8,
- Res = sort([{1,{integer,RemBits}},{8,{integer,Bytes}}|Acc]),
- cg_binary_size_3(Res).
-
-cg_binary_size_2({integer,N}, U, _, Next, Bits, Acc) ->
- cg_binary_size_1(Next, Bits+N*U, Acc);
-cg_binary_size_2({atom,all}, 8, E, Next, Bits, Acc) ->
- cg_binary_size_1(Next, Bits, [{8,{size,E}}|Acc]);
-cg_binary_size_2(Reg, 1, _, Next, Bits, Acc) ->
- cg_binary_size_1(Next, Bits, [{1,Reg}|Acc]);
-cg_binary_size_2(Reg, 8, _, Next, Bits, Acc) ->
- cg_binary_size_1(Next, Bits, [{8,Reg}|Acc]);
-cg_binary_size_2(Reg, U, _, Next, Bits, Acc) ->
- cg_binary_size_1(Next, Bits, [{1,{'*',Reg,U}}|Acc]).
-
-cg_binary_size_3([{_,{integer,0}}|T]) ->
- cg_binary_size_3(T);
-cg_binary_size_3([{U,S1},{U,S2}|T]) ->
- {L0,Rest} = cg_binary_size_4(T, U, []),
- L = [S1,S2|L0],
- [{U,L}|cg_binary_size_3(Rest)];
-cg_binary_size_3([{U,S}|T]) ->
- [{U,[S]}|cg_binary_size_3(T)];
-cg_binary_size_3([]) -> [].
-
-cg_binary_size_4([{U,S}|T], U, Acc) ->
- cg_binary_size_4(T, U, [S|Acc]);
-cg_binary_size_4(T, _, Acc) ->
- {Acc,T}.
-
-%% cg_binary_size_expr/4
-%% Generate code for calculating the resulting size of a binary.
-cg_binary_size_expr(Sizes, Target, Temp, Fail) ->
- cg_binary_size_expr_1(Sizes, Target, Temp, Fail,
- [{move,{integer,0},Target}]).
-
-cg_binary_size_expr_1([{1,E0}|T], Target, Temp, Fail, Acc) ->
- E1 = cg_gen_binsize(E0, Target, Temp, Fail, Acc),
- E = [{bs_bits_to_bytes,Fail,Target,Target}|E1],
- cg_binary_size_expr_1(T, Target, Temp, Fail, E);
-cg_binary_size_expr_1([{8,E0}], Target, Temp, Fail, Acc) ->
- E = cg_gen_binsize(E0, Target, Temp, Fail, Acc),
- reverse(E);
-cg_binary_size_expr_1([], _, _, _, Acc) -> reverse(Acc).
-
-cg_gen_binsize([{'*',A,B}|T], Target, Temp, Fail, Acc) ->
- cg_gen_binsize(T, Target, Temp, Fail,
- [{bs_add,Fail,[Target,A,B],Target}|Acc]);
-cg_gen_binsize([{size,B}|T], Target, Temp, Fail, Acc) ->
- cg_gen_binsize([Temp|T], Target, Temp, Fail,
- [{bif,size,Fail,[B],Temp}|Acc]);
-cg_gen_binsize([E0|T], Target, Temp, Fail, Acc) ->
- cg_gen_binsize(T, Target, Temp, Fail,
- [{bs_add,Fail,[Target,E0,1],Target}|Acc]);
-cg_gen_binsize([], _, _, _, Acc) -> Acc.
-
-%% cg_bin_opt(Code0) -> Code
-%% Optimize the size calculations for binary construction.
-
-cg_bin_opt([{move,{integer,0},D},{bs_add,_,[D,{integer,_}=S,1],Dst}|Is]) ->
- cg_bin_opt([{move,S,Dst}|Is]);
-cg_bin_opt([{move,{integer,0},D},{bs_add,Fail,[D,S,U],Dst}|Is]) ->
- cg_bin_opt([{bs_add,Fail,[{integer,0},S,U],Dst}|Is]);
-cg_bin_opt([{move,{integer,Bytes},D},{bs_init2,Fail,D,Regs0,Flags,D}|Is]) ->
- Regs = cg_bo_newregs(Regs0, D),
- cg_bin_opt([{bs_init2,Fail,Bytes,Regs,Flags,D}|Is]);
-cg_bin_opt([{move,Src,D},{bs_init2,Fail,D,Regs0,Flags,D}|Is]) ->
- Regs = cg_bo_newregs(Regs0, D),
- cg_bin_opt([{bs_init2,Fail,Src,Regs,Flags,D}|Is]);
-cg_bin_opt([{move,Src,Dst},{bs_bits_to_bytes,Fail,Dst,Dst}|Is]) ->
- cg_bin_opt([{bs_bits_to_bytes,Fail,Src,Dst}|Is]);
-cg_bin_opt([{move,Src1,Dst},{bs_add,Fail,[Dst,Src2,U],Dst}|Is]) ->
- cg_bin_opt([{bs_add,Fail,[Src1,Src2,U],Dst}|Is]);
-cg_bin_opt([{bs_bits_to_bytes,Fail,{integer,N},_}|Is0]) when N rem 8 =/= 0 ->
- case Fail of
- {f,0} ->
- Is = [{move,{atom,badarg},{x,0}},
- {call_ext_only,1,{extfunc,erlang,error,1}}|Is0],
- cg_bin_opt(Is);
- _ ->
- cg_bin_opt([{jump,Fail}|Is0])
- end;
-cg_bin_opt([I|Is]) ->
- [I|cg_bin_opt(Is)];
-cg_bin_opt([]) -> [].
-
-cg_bo_newregs(R, {x,X}) when R-1 =:= X -> R-1;
-cg_bo_newregs(R, _) -> R.
-
-%% Common for new and old binary code generation.
-
-cg_bin_put({bin_seg,S0,U,T,Fs,[E0,Next]}, Fail, Bef) ->
- S1 = case S0 of
- {var,Sv} -> fetch_var(Sv, Bef);
- _ -> S0
- end,
- E1 = case E0 of
- {var,V} -> fetch_var(V, Bef);
- Other -> Other
- end,
- Op = case T of
- integer -> bs_put_integer;
- binary -> bs_put_binary;
- float -> bs_put_float
- end,
- [{Op,Fail,S1,U,{field_flags,Fs},E1}|cg_bin_put(Next, Fail, Bef)];
-cg_bin_put(bin_end, _, _) -> [].
-
-%% Old style.
-
-cg_binary_old(PutCode) ->
- [cg_bs_init(PutCode)] ++ need_bin_buf(PutCode).
-
-cg_bs_init(Code) ->
- {Size,Fs} = foldl(fun ({_,_,{integer,N},U,_,_}, {S,Fs}) ->
- {S + N*U,Fs};
- (_, {S,_}) ->
- {S,[]}
- end, {0,[exact]}, Code),
- {bs_init,(Size+7) div 8,{field_flags,Fs}}.
-
-need_bin_buf(Code0) ->
- {Code1,F,H} = foldr(fun ({_,_,{integer,N},U,_,_}=Bs, {Code,F,H}) ->
- {[Bs|Code],F,H + N*U};
- ({_,_,_,_,_,_}=Bs, {Code,F,H}) ->
- {[Bs|need_bin_buf_need(H, F, Code)],true,0}
- end, {[],false,0}, Code0),
- need_bin_buf_need(H, F, Code1).
-
-need_bin_buf_need(0, false, Rest) -> Rest;
-need_bin_buf_need(H, _, Rest) -> [{bs_need_buf,H}|Rest].
-
-cg_build_args(As, Bef) ->
- map(fun ({var,V}) -> {put,fetch_var(V, Bef)};
- (Other) -> {put,Other}
- end, As).
-
-%% return_cg([Val], Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
-%% break_cg([Val], Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
-%% These are very simple, just put return/break values in registers
-%% from 0, then return/break. Use the call setup to clean up stack,
-%% but must clear registers to ensure sr_merge works correctly.
-
-return_cg(Rs, Le, Vdb, Bef, St) ->
- {Ms,Int} = cg_setup_call(Rs, Bef, Le#l.i, Vdb),
- {comment({return,Rs}) ++ Ms ++ [return],
- Int#sr{reg=clear_regs(Int#sr.reg)},St}.
-
-break_cg(Bs, Le, Vdb, Bef, St) ->
- {Ms,Int} = cg_setup_call(Bs, Bef, Le#l.i, Vdb),
- {comment({break,Bs}) ++ Ms ++ [{jump,{f,St#cg.break}}],
- Int#sr{reg=clear_regs(Int#sr.reg)},St}.
-
-%% cg_reg_arg(Arg0, Info) -> Arg
-%% cg_reg_args([Arg0], Info) -> [Arg]
-%% Convert argument[s] into registers. Literal values are returned unchanged.
-
-cg_reg_args(As, Bef) -> [cg_reg_arg(A, Bef) || A <- As].
-
-cg_reg_arg({var,V}, Bef) -> fetch_var(V, Bef);
-cg_reg_arg(Literal, _) -> Literal.
-
-%% cg_setup_call([Arg], Bef, Cur, Vdb) -> {[Instr],Aft}.
-%% Do the complete setup for a call/enter.
-
-cg_setup_call(As, Bef, I, Vdb) ->
- {Ms,Int0} = cg_call_args(As, Bef, I, Vdb),
- %% Have set up arguments, can now clean up, compress and save to stack.
- Int1 = Int0#sr{stk=clear_dead_stk(Int0#sr.stk, I, Vdb),res=[]},
- {Sis,Int2} = adjust_stack(Int1, I, I+1, Vdb),
- {Ms ++ Sis ++ [{'%live',length(As)}],Int2}.
-
-%% cg_call_args([Arg], SrState) -> {[Instr],SrState}.
-%% Setup the arguments to a call/enter/bif. Put the arguments into
-%% consecutive registers starting at {x,0} moving any data which
-%% needs to be saved. Return a modified SrState structure with the
-%% new register contents. N.B. the resultant register info will
-%% contain non-variable values when there are non-variable values.
-%%
-%% This routine is complicated by unsaved values in x registers.
-%% We'll move away any unsaved values that are in the registers
-%% to be overwritten by the arguments.
-
-cg_call_args(As, Bef, I, Vdb) ->
- Regs0 = load_arg_regs(Bef#sr.reg, As),
- Unsaved = unsaved_registers(Regs0, Bef#sr.stk, I, I+1, Vdb),
- {UnsavedMoves,Regs} = move_unsaved(Unsaved, Bef#sr.reg, Regs0),
- Moves0 = gen_moves(As, Bef),
- Moves = order_moves(Moves0, find_scratch_reg(Regs)),
- {UnsavedMoves ++ Moves,Bef#sr{reg=Regs}}.
-
-%% load_arg_regs([Reg], Arguments) -> [Reg]
-%% Update the register descriptor to include the arguments (from {x,0}
-%% and upwards). Values in argument register are overwritten.
-%% Values in x registers above the arguments are preserved.
-
-load_arg_regs(Regs, As) -> load_arg_regs(Regs, As, 0).
-
-load_arg_regs([_|Rs], [{var,V}|As], I) -> [{I,V}|load_arg_regs(Rs, As, I+1)];
-load_arg_regs([_|Rs], [A|As], I) -> [{I,A}|load_arg_regs(Rs, As, I+1)];
-load_arg_regs([], [{var,V}|As], I) -> [{I,V}|load_arg_regs([], As, I+1)];
-load_arg_regs([], [A|As], I) -> [{I,A}|load_arg_regs([], As, I+1)];
-load_arg_regs(Rs, [], _) -> Rs.
-
-%% Returns the variables must be saved and are currently in the
-%% x registers that are about to be overwritten by the arguments.
-
-unsaved_registers(Regs, Stk, Fb, Lf, Vdb) ->
- [V || {V,F,L} <- Vdb,
- F < Fb,
- L >= Lf,
- not on_stack(V, Stk),
- not in_reg(V, Regs)].
-
-in_reg(V, Regs) -> keymember(V, 2, Regs).
-
-%% Move away unsaved variables from the registers that are to be
-%% overwritten by the arguments.
-move_unsaved(Vs, OrigRegs, NewRegs) ->
- move_unsaved(Vs, OrigRegs, NewRegs, []).
-
-move_unsaved([V|Vs], OrigRegs, NewRegs0, Acc) ->
- NewRegs = put_reg(V, NewRegs0),
- Src = fetch_reg(V, OrigRegs),
- Dst = fetch_reg(V, NewRegs),
- move_unsaved(Vs, OrigRegs, NewRegs, [{move,Src,Dst}|Acc]);
-move_unsaved([], _, Regs, Acc) -> {Acc,Regs}.
-
-%% gen_moves(As, Sr)
-%% Generate the basic move instruction to move the arguments
-%% to their proper registers. The list will be sorted on
-%% destinations. (I.e. the move to {x,0} will be first --
-%% see the comment to order_moves/2.)
-
-gen_moves(As, Sr) -> gen_moves(As, Sr, 0, []).
-
-gen_moves([{var,V}|As], Sr, I, Acc) ->
- case fetch_var(V, Sr) of
- {x,I} -> gen_moves(As, Sr, I+1, Acc);
- Reg -> gen_moves(As, Sr, I+1, [{move,Reg,{x,I}}|Acc])
- end;
-gen_moves([A|As], Sr, I, Acc) ->
- gen_moves(As, Sr, I+1, [{move,A,{x,I}}|Acc]);
-gen_moves([], _, _, Acc) -> lists:keysort(3, Acc).
-
-%% order_moves([Move], ScratchReg) -> [Move]
-%% Orders move instruction so that source registers are not
-%% destroyed before they are used. If there are cycles
-%% (such as {move,{x,0},{x,1}}, {move,{x,1},{x,1}}),
-%% the scratch register is used to break up the cycle.
-%% If possible, the first move of the input list is placed
-%% last in the result list (to make the move to {x,0} occur
-%% just before the call to allow the Beam loader to coalesce
-%% the instructions).
-
-order_moves(Ms, Scr) -> order_moves(Ms, Scr, []).
-
-order_moves([{move,_,_}=M|Ms0], ScrReg, Acc0) ->
- {Chain,Ms} = collect_chain(Ms0, [M], ScrReg),
- Acc = reverse(Chain, Acc0),
- order_moves(Ms, ScrReg, Acc);
-order_moves([], _, Acc) -> Acc.
-
-collect_chain(Ms, Path, ScrReg) ->
- collect_chain(Ms, Path, [], ScrReg).
-
-collect_chain([{move,Src,Same}=M|Ms0], [{move,Same,_}|_]=Path, Others, ScrReg) ->
- case keysearch(Src, 3, Path) of
- {value,_} -> %We have a cycle.
- {break_up_cycle(M, Path, ScrReg),reverse(Others, Ms0)};
- false ->
- collect_chain(reverse(Others, Ms0), [M|Path], [], ScrReg)
- end;
-collect_chain([M|Ms], Path, Others, ScrReg) ->
- collect_chain(Ms, Path, [M|Others], ScrReg);
-collect_chain([], Path, Others, _) ->
- {Path,Others}.
-
-break_up_cycle({move,Src,_}=M, Path, ScrReg) ->
- [{move,ScrReg,Src},M|break_up_cycle1(Src, Path, ScrReg)].
-
-break_up_cycle1(Dst, [{move,Src,Dst}|Path], ScrReg) ->
- [{move,Src,ScrReg}|Path];
-break_up_cycle1(Dst, [M|Path], LastMove) ->
- [M|break_up_cycle1(Dst, Path, LastMove)].
-
-%% clear_dead(Sr, Until, Vdb) -> Aft.
-%% Remove all variables in Sr which have died AT ALL so far.
-
-clear_dead(Sr, Until, Vdb) ->
- Sr#sr{reg=clear_dead_reg(Sr, Until, Vdb),
- stk=clear_dead_stk(Sr#sr.stk, Until, Vdb)}.
-
-clear_dead_reg(Sr, Until, Vdb) ->
- Reg = map(fun ({I,V}) ->
- case vdb_find(V, Vdb) of
- {V,_,L} when L > Until -> {I,V};
- _ -> free %Remove anything else
- end;
- ({reserved,I,V}) -> {reserved,I,V};
- (free) -> free
- end, Sr#sr.reg),
- reserve(Sr#sr.res, Reg, Sr#sr.stk).
-
-clear_dead_stk(Stk, Until, Vdb) ->
- map(fun ({V}) ->
- case vdb_find(V, Vdb) of
- {V,_,L} when L > Until -> {V};
- _ -> dead %Remove anything else
- end;
- (free) -> free;
- (dead) -> dead
- end, Stk).
-
-%% sr_merge(Sr1, Sr2) -> Sr.
-%% Merge two stack/register states keeping the longest of both stack
-%% and register. Perform consistency check on both, elements must be
-%% the same. Allow frame size 'void' to make easy creation of
-%% "empty" frame.
-
-sr_merge(#sr{reg=R1,stk=S1,res=[]}, #sr{reg=R2,stk=S2,res=[]}) ->
- #sr{reg=longest(R1, R2),stk=longest(S1, S2),res=[]};
-sr_merge(void, S2) -> S2#sr{res=[]};
-sr_merge(S1, void) -> S1#sr{res=[]}.
-
-longest([H|T1], [H|T2]) -> [H|longest(T1, T2)];
-longest([dead|T1], [free|T2]) -> [dead|longest(T1, T2)];
-longest([free|T1], [dead|T2]) -> [dead|longest(T1, T2)];
-longest([dead|T1], []) -> [dead|T1];
-longest([], [dead|T2]) -> [dead|T2];
-longest([free|T1], []) -> [free|T1];
-longest([], [free|T2]) -> [free|T2];
-longest([], []) -> [].
-
-%% adjust_stack(Bef, FirstBefore, LastFrom, Vdb) -> {[Ainstr],Aft}.
-%% Do complete stack adjustment by compressing stack and adding
-%% variables to be saved. Try to optimise ordering on stack by
-%% having reverse order to their lifetimes.
-%%
-%% In Beam, there is a fixed stack frame and no need to do stack compression.
-
-adjust_stack(Bef, Fb, Lf, Vdb) ->
- Stk0 = Bef#sr.stk,
- {Stk1,Saves} = save_stack(Stk0, Fb, Lf, Vdb),
- {saves(Saves, Bef#sr.reg, Stk1),
- Bef#sr{stk=Stk1}}.
-
-%% save_stack(Stack, FirstBefore, LastFrom, Vdb) -> {[SaveVar],NewStack}.
-%% Save variables which are used past current point and which are not
-%% already on the stack.
-
-save_stack(Stk0, Fb, Lf, Vdb) ->
- %% New variables that are in use but not on stack.
- New = [ {V,F,L} || {V,F,L} <- Vdb,
- F < Fb,
- L >= Lf,
- not on_stack(V, Stk0) ],
- %% Add new variables that are not just dropped immediately.
- %% N.B. foldr works backwards from the end!!
- Saves = [ V || {V,_,_} <- keysort(3, New) ],
- Stk1 = foldr(fun (V, Stk) -> put_stack(V, Stk) end, Stk0, Saves),
- {Stk1,Saves}.
-
-%% saves([SaveVar], Reg, Stk) -> [{move,Reg,Stk}].
-%% Generate move instructions to save variables onto stack. The
-%% stack/reg info used is that after the new stack has been made.
-
-saves(Ss, Reg, Stk) ->
- Res = map(fun (V) ->
- {move,fetch_reg(V, Reg),fetch_stack(V, Stk)}
- end, Ss),
- Res.
-
-%% comment(C) -> ['%'{C}].
-
-%comment(C) -> [{'%',C}].
-comment(_) -> [].
-
-%% fetch_var(VarName, StkReg) -> r{R} | sp{Sp}.
-%% find_var(VarName, StkReg) -> ok{r{R} | sp{Sp}} | error.
-%% Fetch/find a variable in either the registers or on the
-%% stack. Fetch KNOWS it's there.
-
-fetch_var(V, Sr) ->
- case find_reg(V, Sr#sr.reg) of
- {ok,R} -> R;
- error -> fetch_stack(V, Sr#sr.stk)
- end.
-
-% find_var(V, Sr) ->
-% case find_reg(V, Sr#sr.reg) of
-% {ok,R} -> {ok,R};
-% error ->
-% case find_stack(V, Sr#sr.stk) of
-% {ok,S} -> {ok,S};
-% error -> error
-% end
-% end.
-
-load_vars(Vs, Regs) ->
- foldl(fun ({var,V}, Rs) -> put_reg(V, Rs) end, Regs, Vs).
-
-%% put_reg(Val, Regs) -> Regs.
-%% load_reg(Val, Reg, Regs) -> Regs.
-%% free_reg(Val, Regs) -> Regs.
-%% find_reg(Val, Regs) -> ok{r{R}} | error.
-%% fetch_reg(Val, Regs) -> r{R}.
-%% Functions to interface the registers.
-%% put_reg puts a value into a free register,
-%% load_reg loads a value into a fixed register
-%% free_reg frees a register containing a specific value.
-
-% put_regs(Vs, Rs) -> foldl(fun put_reg/2, Rs, Vs).
-
-put_reg(V, Rs) -> put_reg_1(V, Rs, 0).
-
-put_reg_1(V, [free|Rs], I) -> [{I,V}|Rs];
-put_reg_1(V, [{reserved,I,V}|Rs], I) -> [{I,V}|Rs];
-put_reg_1(V, [R|Rs], I) -> [R|put_reg_1(V, Rs, I+1)];
-put_reg_1(V, [], I) -> [{I,V}].
-
-load_reg(V, R, Rs) -> load_reg_1(V, R, Rs, 0).
-
-load_reg_1(V, I, [_|Rs], I) -> [{I,V}|Rs];
-load_reg_1(V, I, [R|Rs], C) -> [R|load_reg_1(V, I, Rs, C+1)];
-load_reg_1(V, I, [], I) -> [{I,V}];
-load_reg_1(V, I, [], C) -> [free|load_reg_1(V, I, [], C+1)].
-
-% free_reg(V, [{I,V}|Rs]) -> [free|Rs];
-% free_reg(V, [R|Rs]) -> [R|free_reg(V, Rs)];
-% free_reg(V, []) -> [].
-
-fetch_reg(V, [{I,V}|_]) -> {x,I};
-fetch_reg(V, [_|SRs]) -> fetch_reg(V, SRs).
-
-find_reg(V, [{I,V}|_]) -> {ok,{x,I}};
-find_reg(V, [_|SRs]) -> find_reg(V, SRs);
-find_reg(_, []) -> error.
-
-%% For the bit syntax, we need a scratch register if we are constructing
-%% a binary that will not be used.
-
-find_scratch_reg(Rs) -> find_scratch_reg(Rs, 0).
-
-find_scratch_reg([free|_], I) -> {x,I};
-find_scratch_reg([_|Rs], I) -> find_scratch_reg(Rs, I+1);
-find_scratch_reg([], I) -> {x,I}.
-
-%%copy_reg(Val, R, Regs) -> load_reg(Val, R, Regs).
-%%move_reg(Val, R, Regs) -> load_reg(Val, R, free_reg(Val, Regs)).
-
-%%clear_regs(Regs) -> map(fun (R) -> free end, Regs).
-clear_regs(_) -> [].
-
-max_reg(Regs) ->
- foldl(fun ({I,_}, _) -> I;
- (_, Max) -> Max end,
- -1, Regs) + 1.
-
-%% put_stack(Val, [{Val}]) -> [{Val}].
-%% fetch_stack(Var, Stk) -> sp{S}.
-%% find_stack(Var, Stk) -> ok{sp{S}} | error.
-%% Functions to interface the stack.
-
-put_stack(Val, []) -> [{Val}];
-put_stack(Val, [dead|Stk]) -> [{Val}|Stk];
-put_stack(Val, [free|Stk]) -> [{Val}|Stk];
-put_stack(Val, [NotFree|Stk]) -> [NotFree|put_stack(Val, Stk)].
-
-put_stack_carefully(Val, Stk0) ->
- case catch put_stack_carefully1(Val, Stk0) of
- error -> error;
- Stk1 when list(Stk1) -> Stk1
- end.
-
-put_stack_carefully1(_, []) -> throw(error);
-put_stack_carefully1(Val, [dead|Stk]) -> [{Val}|Stk];
-put_stack_carefully1(Val, [free|Stk]) -> [{Val}|Stk];
-put_stack_carefully1(Val, [NotFree|Stk]) ->
- [NotFree|put_stack_carefully1(Val, Stk)].
-
-fetch_stack(Var, Stk) -> fetch_stack(Var, Stk, 0).
-
-fetch_stack(V, [{V}|_], I) -> {yy,I};
-fetch_stack(V, [_|Stk], I) -> fetch_stack(V, Stk, I+1).
-
-% find_stack(Var, Stk) -> find_stack(Var, Stk, 0).
-
-% find_stack(V, [{V}|Stk], I) -> {ok,{yy,I}};
-% find_stack(V, [O|Stk], I) -> find_stack(V, Stk, I+1);
-% find_stack(V, [], I) -> error.
-
-on_stack(V, Stk) -> keymember(V, 1, Stk).
-
-%% put_catch(CatchTag, Stack) -> Stack'
-%% drop_catch(CatchTag, Stack) -> Stack'
-%% Special interface for putting and removing catch tags, to ensure that
-%% catches nest properly. Also used for try tags.
-
-put_catch(Tag, Stk0) -> put_catch(Tag, reverse(Stk0), []).
-
-put_catch(Tag, [], Stk) ->
- put_stack({catch_tag,Tag}, Stk);
-put_catch(Tag, [{{catch_tag,_}}|_]=RevStk, Stk) ->
- reverse(RevStk, put_stack({catch_tag,Tag}, Stk));
-put_catch(Tag, [Other|Stk], Acc) ->
- put_catch(Tag, Stk, [Other|Acc]).
-
-drop_catch(Tag, [{{catch_tag,Tag}}|Stk]) -> [free|Stk];
-drop_catch(Tag, [Other|Stk]) -> [Other|drop_catch(Tag, Stk)].
-
-%%%
-%%% Finish the code generation for the bit syntax matching.
-%%%
-
-bs_function({function,Name,Arity,CLabel,Asm0}=Func) ->
- case bs_needed(Asm0, 0, false, []) of
- {false,[]} -> Func;
- {true,Dict} ->
- Asm = bs_replace(Asm0, Dict, []),
- {function,Name,Arity,CLabel,Asm}
- end.
-
-%%%
-%%% Pass 1: Found out which bs_restore's that are needed. For now we assume
-%%% that a bs_restore is needed unless it is directly preceeded by a bs_save.
-%%%
-
-bs_needed([{bs_save,Name},{bs_restore,Name}|T], N, _BsUsed, Dict) ->
- bs_needed(T, N, true, Dict);
-bs_needed([{bs_save,_Name}|T], N, _BsUsed, Dict) ->
- bs_needed(T, N, true, Dict);
-bs_needed([{bs_restore,Name}|T], N, _BsUsed, Dict) ->
- case keysearch(Name, 1, Dict) of
- {value,{Name,_}} -> bs_needed(T, N, true, Dict);
- false -> bs_needed(T, N+1, true, [{Name,N}|Dict])
- end;
-bs_needed([{bs_init,_,_}|T], N, _, Dict) ->
- bs_needed(T, N, true, Dict);
-bs_needed([{bs_init2,_,_,_,_,_}|T], N, _, Dict) ->
- bs_needed(T, N, true, Dict);
-bs_needed([{bs_start_match,_,_}|T], N, _, Dict) ->
- bs_needed(T, N, true, Dict);
-bs_needed([_|T], N, BsUsed, Dict) ->
- bs_needed(T, N, BsUsed, Dict);
-bs_needed([], _, BsUsed, Dict) -> {BsUsed,Dict}.
-
-%%%
-%%% Pass 2: Only needed if there were some bs_* instructions found.
-%%%
-%%% Remove any bs_save with a name that never were found to be restored
-%%% in the first pass.
-%%%
-
-bs_replace([{bs_save,Name}=Save,{bs_restore,Name}|T], Dict, Acc) ->
- bs_replace([Save|T], Dict, Acc);
-bs_replace([{bs_save,Name}|T], Dict, Acc) ->
- case keysearch(Name, 1, Dict) of
- {value,{Name,N}} ->
- bs_replace(T, Dict, [{bs_save,N}|Acc]);
- false ->
- bs_replace(T, Dict, Acc)
- end;
-bs_replace([{bs_restore,Name}|T], Dict, Acc) ->
- case keysearch(Name, 1, Dict) of
- {value,{Name,N}} ->
- bs_replace(T, Dict, [{bs_restore,N}|Acc]);
- false ->
- bs_replace(T, Dict, Acc)
- end;
-bs_replace([{bs_init2,Fail,Bytes,Regs,Flags,Dst}|T0], Dict, Acc) ->
- case bs_find_test_heap(T0) of
- none ->
- bs_replace(T0, Dict, [{bs_init2,Fail,Bytes,0,Regs,Flags,Dst}|Acc]);
- {T,Words} ->
- bs_replace(T, Dict, [{bs_init2,Fail,Bytes,Words,Regs,Flags,Dst}|Acc])
- end;
-bs_replace([H|T], Dict, Acc) ->
- bs_replace(T, Dict, [H|Acc]);
-bs_replace([], _, Acc) -> reverse(Acc).
-
-bs_find_test_heap(Is) ->
- bs_find_test_heap_1(Is, []).
-
-bs_find_test_heap_1([{bs_put_integer,_,_,_,_,_}=I|Is], Acc) ->
- bs_find_test_heap_1(Is, [I|Acc]);
-bs_find_test_heap_1([{bs_put_float,_,_,_,_,_}=I|Is], Acc) ->
- bs_find_test_heap_1(Is, [I|Acc]);
-bs_find_test_heap_1([{bs_put_binary,_,_,_,_,_}=I|Is], Acc) ->
- bs_find_test_heap_1(Is, [I|Acc]);
-bs_find_test_heap_1([{test_heap,Words,_}|Is], Acc) ->
- {reverse(Acc, Is),Words};
-bs_find_test_heap_1(_, _) -> none.
-
-%% new_label(St) -> {L,St}.
-
-new_label(St) ->
- L = St#cg.lcount,
- {L,St#cg{lcount=L+1}}.
-
-flatmapfoldl(F, Accu0, [Hd|Tail]) ->
- {R,Accu1} = F(Hd, Accu0),
- {Rs,Accu2} = flatmapfoldl(F, Accu1, Tail),
- {R++Rs,Accu2};
-flatmapfoldl(_, Accu, []) -> {[],Accu}.
-
-flatmapfoldr(F, Accu0, [Hd|Tail]) ->
- {Rs,Accu1} = flatmapfoldr(F, Accu0, Tail),
- {R,Accu2} = F(Hd, Accu1),
- {R++Rs,Accu2};
-flatmapfoldr(_, Accu, []) -> {[],Accu}.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_core.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_core.erl
deleted file mode 100644
index b561182932..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_core.erl
+++ /dev/null
@@ -1,1320 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: v3_core.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
-%%
-%% Purpose : Transform normal Erlang to Core Erlang
-
-%% At this stage all preprocessing has been done. All that is left are
-%% "pure" Erlang functions.
-%%
-%% Core transformation is done in three stages:
-%%
-%% 1. Flatten expressions into an internal core form without doing
-%% matching.
-%%
-%% 2. Step "forwards" over the icore code annotating each "top-level"
-%% thing with variable usage. Detect bound variables in matching
-%% and replace with explicit guard test. Annotate "internal-core"
-%% expressions with variables they use and create. Convert matches
-%% to cases when not pure assignments.
-%%
-%% 3. Step "backwards" over icore code using variable usage
-%% annotations to change implicit exported variables to explicit
-%% returns.
-%%
-%% To ensure the evaluation order we ensure that all arguments are
-%% safe. A "safe" is basically a core_lib simple with VERY restricted
-%% binaries.
-%%
-%% We have to be very careful with matches as these create variables.
-%% While we try not to flatten things more than necessary we must make
-%% sure that all matches are at the top level. For this we use the
-%% type "novars" which are non-match expressions. Cases and receives
-%% can also create problems due to exports variables so they are not
-%% "novars" either. I.e. a novars will not export variables.
-%%
-%% Annotations in the #iset, #iletrec, and all other internal records
-%% is kept in a record, #a, not in a list as in proper core. This is
-%% easier and faster and creates no problems as we have complete control
-%% over all annotations.
-%%
-%% On output, the annotation for most Core Erlang terms will contain
-%% the source line number. A few terms will be marked with the atom
-%% atom 'compiler_generated', to indicate that the compiler has generated
-%% them and that no warning should be generated if they are optimized
-%% away.
-%%
-%%
-%% In this translation:
-%%
-%% call ops are safes
-%% call arguments are safes
-%% match arguments are novars
-%% case arguments are novars
-%% receive timeouts are novars
-%% let/set arguments are expressions
-%% fun is not a safe
-
--module(v3_core).
-
--export([module/2,format_error/1]).
-
--import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,splitwith/2]).
--import(ordsets, [add_element/2,del_element/2,is_element/2,
- union/1,union/2,intersection/2,subtract/2]).
-
--include("core_parse.hrl").
-
--record(a, {us=[],ns=[],anno=[]}). %Internal annotation
-
-%% Internal core expressions and help functions.
-%% N.B. annotations fields in place as normal Core expressions.
-
--record(iset, {anno=#a{},var,arg}).
--record(iletrec, {anno=#a{},defs,body}).
--record(imatch, {anno=#a{},pat,guard=[],arg,fc}).
--record(icase, {anno=#a{},args,clauses,fc}).
--record(iclause, {anno=#a{},pats,pguard=[],guard,body}).
--record(ifun, {anno=#a{},id,vars,clauses,fc}).
--record(iapply, {anno=#a{},op,args}).
--record(icall, {anno=#a{},module,name,args}).
--record(iprimop, {anno=#a{},name,args}).
--record(itry, {anno=#a{},args,vars,body,evars,handler}).
--record(icatch, {anno=#a{},body}).
--record(ireceive1, {anno=#a{},clauses}).
--record(ireceive2, {anno=#a{},clauses,timeout,action}).
--record(iprotect, {anno=#a{},body}).
--record(ibinary, {anno=#a{},segments}). %Not used in patterns.
-
--record(core, {vcount=0, %Variable counter
- fcount=0, %Function counter
- ws=[]}). %Warnings.
-
-module({Mod,Exp,Forms}, _Opts) ->
- Cexp = map(fun ({N,A}) -> #c_fname{id=N,arity=A} end, Exp),
- {Kfs,As,Ws} = foldr(fun form/2, {[],[],[]}, Forms),
- {ok,#c_module{name=#c_atom{val=Mod},exports=Cexp,attrs=As,defs=Kfs},Ws}.
-
-form({function,_,_,_,_}=F0, {Fs,As,Ws0}) ->
- {F,Ws} = function(F0, Ws0),
- {[F|Fs],As,Ws};
-form({attribute,_,_,_}=F, {Fs,As,Ws}) ->
- {Fs,[attribute(F)|As],Ws}.
-
-attribute({attribute,_,Name,Val}) ->
- #c_def{name=core_lib:make_literal(Name),
- val=core_lib:make_literal(Val)}.
-
-function({function,_,Name,Arity,Cs0}, Ws0) ->
- %%ok = io:fwrite("~p - ", [{Name,Arity}]),
- St0 = #core{vcount=0,ws=Ws0},
- {B0,St1} = body(Cs0, Arity, St0),
- %%ok = io:fwrite("1", []),
- %%ok = io:fwrite("~w:~p~n", [?LINE,B0]),
- {B1,St2} = ubody(B0, St1),
- %%ok = io:fwrite("2", []),
- %%ok = io:fwrite("~w:~p~n", [?LINE,B1]),
- {B2,#core{ws=Ws}} = cbody(B1, St2),
- %%ok = io:fwrite("3~n", []),
- {#c_def{name=#c_fname{id=Name,arity=Arity},val=B2},Ws}.
-
-body(Cs0, Arity, St0) ->
- Anno = [element(2, hd(Cs0))],
- {Args,St1} = new_vars(Anno, Arity, St0),
- {Cs1,St2} = clauses(Cs0, St1),
- {Ps,St3} = new_vars(Arity, St2), %Need new variables here
- Fc = fail_clause(Ps, #c_tuple{es=[#c_atom{val=function_clause}|Ps]}),
- {#ifun{anno=#a{anno=Anno},id=[],vars=Args,clauses=Cs1,fc=Fc},St3}.
-
-%% clause(Clause, State) -> {Cclause,State} | noclause.
-%% clauses([Clause], State) -> {[Cclause],State}.
-%% Convert clauses. Trap bad pattern aliases and remove clause from
-%% clause list.
-
-clauses([C0|Cs0], St0) ->
- case clause(C0, St0) of
- {noclause,St} -> clauses(Cs0, St);
- {C,St1} ->
- {Cs,St2} = clauses(Cs0, St1),
- {[C|Cs],St2}
- end;
-clauses([], St) -> {[],St}.
-
-clause({clause,Lc,H0,G0,B0}, St0) ->
- case catch head(H0) of
- {'EXIT',_}=Exit -> exit(Exit); %Propagate error
- nomatch ->
- St = add_warning(Lc, nomatch, St0),
- {noclause,St}; %Bad pattern
- H1 ->
- {G1,St1} = guard(G0, St0),
- {B1,St2} = exprs(B0, St1),
- {#iclause{anno=#a{anno=[Lc]},pats=H1,guard=G1,body=B1},St2}
- end.
-
-%% head([P]) -> [P].
-
-head(Ps) -> pattern_list(Ps).
-
-%% guard([Expr], State) -> {[Cexpr],State}.
-%% Build an explict and/or tree of guard alternatives, then traverse
-%% top-level and/or tree and "protect" inner tests.
-
-guard([], St) -> {[],St};
-guard(Gs0, St) ->
- Gs = foldr(fun (Gt0, Rhs) ->
- Gt1 = guard_tests(Gt0),
- L = element(2, Gt1),
- {op,L,'or',Gt1,Rhs}
- end, guard_tests(last(Gs0)), first(Gs0)),
- gexpr_top(Gs, St).
-
-guard_tests([]) -> [];
-guard_tests(Gs) ->
- L = element(2, hd(Gs)),
- {protect,L,foldr(fun (G, Rhs) -> {op,L,'and',G,Rhs} end, last(Gs), first(Gs))}.
-
-%% gexpr_top(Expr, State) -> {Cexpr,State}.
-%% Generate an internal core expression of a guard test. Explicitly
-%% handle outer boolean expressions and "protect" inner tests in a
-%% reasonably smart way.
-
-gexpr_top(E0, St0) ->
- {E1,Eps0,Bools,St1} = gexpr(E0, [], St0),
- {E,Eps,St} = force_booleans(Bools, E1, Eps0, St1),
- {Eps++[E],St}.
-
-%% gexpr(Expr, Bools, State) -> {Cexpr,[PreExp],Bools,State}.
-%% Generate an internal core expression of a guard test.
-
-gexpr({protect,Line,Arg}, Bools0, St0) ->
- case gexpr(Arg, [], St0) of
- {E0,[],Bools,St1} ->
- {E,Eps,St} = force_booleans(Bools, E0, [], St1),
- {E,Eps,Bools0,St};
- {E0,Eps0,Bools,St1} ->
- {E,Eps,St} = force_booleans(Bools, E0, Eps0, St1),
- {#iprotect{anno=#a{anno=[Line]},body=Eps++[E]},[],Bools0,St}
- end;
-gexpr({op,Line,Op,L,R}=Call, Bools0, St0) ->
- case erl_internal:bool_op(Op, 2) of
- true ->
- {Le,Lps,Bools1,St1} = gexpr(L, Bools0, St0),
- {Ll,Llps,St2} = force_safe(Le, St1),
- {Re,Rps,Bools,St3} = gexpr(R, Bools1, St2),
- {Rl,Rlps,St4} = force_safe(Re, St3),
- Anno = [Line],
- {#icall{anno=#a{anno=Anno}, %Must have an #a{}
- module=#c_atom{anno=Anno,val=erlang},name=#c_atom{anno=Anno,val=Op},
- args=[Ll,Rl]},Lps ++ Llps ++ Rps ++ Rlps,Bools,St4};
- false ->
- gexpr_test(Call, Bools0, St0)
- end;
-gexpr({op,Line,Op,A}=Call, Bools0, St0) ->
- case erl_internal:bool_op(Op, 1) of
- true ->
- {Ae,Aps,Bools,St1} = gexpr(A, Bools0, St0),
- {Al,Alps,St2} = force_safe(Ae, St1),
- Anno = [Line],
- {#icall{anno=#a{anno=Anno}, %Must have an #a{}
- module=#c_atom{anno=Anno,val=erlang},name=#c_atom{anno=Anno,val=Op},
- args=[Al]},Aps ++ Alps,Bools,St2};
- false ->
- gexpr_test(Call, Bools0, St0)
- end;
-gexpr(E0, Bools, St0) ->
- gexpr_test(E0, Bools, St0).
-
-%% gexpr_test(Expr, Bools, State) -> {Cexpr,[PreExp],Bools,State}.
-%% Generate a guard test. At this stage we must be sure that we have
-%% a proper boolean value here so wrap things with an true test if we
-%% don't know, i.e. if it is not a comparison or a type test.
-
-gexpr_test({atom,L,true}, Bools, St0) ->
- {#c_atom{anno=[L],val=true},[],Bools,St0};
-gexpr_test({atom,L,false}, Bools, St0) ->
- {#c_atom{anno=[L],val=false},[],Bools,St0};
-gexpr_test(E0, Bools0, St0) ->
- {E1,Eps0,St1} = expr(E0, St0),
- %% Generate "top-level" test and argument calls.
- case E1 of
- #icall{anno=Anno,module=#c_atom{val=erlang},name=#c_atom{val=N},args=As} ->
- Ar = length(As),
- case erl_internal:type_test(N, Ar) orelse
- erl_internal:comp_op(N, Ar) orelse
- (N == internal_is_record andalso Ar == 3) of
- true -> {E1,Eps0,Bools0,St1};
- false ->
- Lanno = Anno#a.anno,
- {New,St2} = new_var(Lanno, St1),
- Bools = [New|Bools0],
- {#icall{anno=Anno, %Must have an #a{}
- module=#c_atom{anno=Lanno,val=erlang},
- name=#c_atom{anno=Lanno,val='=:='},
- args=[New,#c_atom{anno=Lanno,val=true}]},
- Eps0 ++ [#iset{anno=Anno,var=New,arg=E1}],Bools,St2}
- end;
- _ ->
- Anno = get_ianno(E1),
- Lanno = get_lineno_anno(E1),
- case core_lib:is_simple(E1) of
- true ->
- Bools = [E1|Bools0],
- {#icall{anno=Anno, %Must have an #a{}
- module=#c_atom{anno=Lanno,val=erlang},
- name=#c_atom{anno=Lanno,val='=:='},
- args=[E1,#c_atom{anno=Lanno,val=true}]},Eps0,Bools,St1};
- false ->
- {New,St2} = new_var(Lanno, St1),
- Bools = [New|Bools0],
- {#icall{anno=Anno, %Must have an #a{}
- module=#c_atom{anno=Lanno,val=erlang},
- name=#c_atom{anno=Lanno,val='=:='},
- args=[New,#c_atom{anno=Lanno,val=true}]},
- Eps0 ++ [#iset{anno=Anno,var=New,arg=E1}],Bools,St2}
- end
- end.
-
-force_booleans([], E, Eps, St) ->
- {E,Eps,St};
-force_booleans([V|Vs], E0, Eps0, St0) ->
- {E1,Eps1,St1} = force_safe(E0, St0),
- Lanno = element(2, V),
- Anno = #a{anno=Lanno},
- Call = #icall{anno=Anno,module=#c_atom{anno=Lanno,val=erlang},
- name=#c_atom{anno=Lanno,val=is_boolean},
- args=[V]},
- {New,St} = new_var(Lanno, St1),
- Iset = #iset{anno=Anno,var=New,arg=Call},
- Eps = Eps0 ++ Eps1 ++ [Iset],
- E = #icall{anno=Anno,
- module=#c_atom{anno=Lanno,val=erlang},name=#c_atom{anno=Lanno,val='and'},
- args=[E1,New]},
- force_booleans(Vs, E, Eps, St).
-
-%% exprs([Expr], State) -> {[Cexpr],State}.
-%% Flatten top-level exprs.
-
-exprs([E0|Es0], St0) ->
- {E1,Eps,St1} = expr(E0, St0),
- {Es1,St2} = exprs(Es0, St1),
- {Eps ++ [E1] ++ Es1,St2};
-exprs([], St) -> {[],St}.
-
-%% expr(Expr, State) -> {Cexpr,[PreExp],State}.
-%% Generate an internal core expression.
-
-expr({var,L,V}, St) -> {#c_var{anno=[L],name=V},[],St};
-expr({char,L,C}, St) -> {#c_char{anno=[L],val=C},[],St};
-expr({integer,L,I}, St) -> {#c_int{anno=[L],val=I},[],St};
-expr({float,L,F}, St) -> {#c_float{anno=[L],val=F},[],St};
-expr({atom,L,A}, St) -> {#c_atom{anno=[L],val=A},[],St};
-expr({nil,L}, St) -> {#c_nil{anno=[L]},[],St};
-expr({string,L,S}, St) -> {#c_string{anno=[L],val=S},[],St};
-expr({cons,L,H0,T0}, St0) ->
- {H1,Hps,St1} = safe(H0, St0),
- {T1,Tps,St2} = safe(T0, St1),
- {#c_cons{anno=[L],hd=H1,tl=T1},Hps ++ Tps,St2};
-expr({lc,L,E,Qs}, St) ->
- lc_tq(L, E, Qs, {nil,L}, St);
-expr({tuple,L,Es0}, St0) ->
- {Es1,Eps,St1} = safe_list(Es0, St0),
- {#c_tuple{anno=[L],es=Es1},Eps,St1};
-expr({bin,L,Es0}, St0) ->
- {Es1,Eps,St1} = expr_bin(Es0, St0),
- {#ibinary{anno=#a{anno=[L]},segments=Es1},Eps,St1};
-expr({block,_,Es0}, St0) ->
- %% Inline the block directly.
- {Es1,St1} = exprs(first(Es0), St0),
- {E1,Eps,St2} = expr(last(Es0), St1),
- {E1,Es1 ++ Eps,St2};
-expr({'if',L,Cs0}, St0) ->
- {Cs1,St1} = clauses(Cs0, St0),
- Fc = fail_clause([], #c_atom{val=if_clause}),
- {#icase{anno=#a{anno=[L]},args=[],clauses=Cs1,fc=Fc},[],St1};
-expr({'case',L,E0,Cs0}, St0) ->
- {E1,Eps,St1} = novars(E0, St0),
- {Cs1,St2} = clauses(Cs0, St1),
- {Fpat,St3} = new_var(St2),
- Fc = fail_clause([Fpat], #c_tuple{es=[#c_atom{val=case_clause},Fpat]}),
- {#icase{anno=#a{anno=[L]},args=[E1],clauses=Cs1,fc=Fc},Eps,St3};
-expr({'receive',L,Cs0}, St0) ->
- {Cs1,St1} = clauses(Cs0, St0),
- {#ireceive1{anno=#a{anno=[L]},clauses=Cs1}, [], St1};
-expr({'receive',L,Cs0,Te0,Tes0}, St0) ->
- {Te1,Teps,St1} = novars(Te0, St0),
- {Tes1,St2} = exprs(Tes0, St1),
- {Cs1,St3} = clauses(Cs0, St2),
- {#ireceive2{anno=#a{anno=[L]},
- clauses=Cs1,timeout=Te1,action=Tes1},Teps,St3};
-expr({'try',L,Es0,[],Ecs,[]}, St0) ->
- %% 'try ... catch ... end'
- {Es1,St1} = exprs(Es0, St0),
- {V,St2} = new_var(St1), %This name should be arbitrary
- {Evs,Hs,St3} = try_exception(Ecs, St2),
- {#itry{anno=#a{anno=[L]},args=Es1,vars=[V],body=[V],
- evars=Evs,handler=Hs},
- [],St3};
-expr({'try',L,Es0,Cs0,Ecs,[]}, St0) ->
- %% 'try ... of ... catch ... end'
- {Es1,St1} = exprs(Es0, St0),
- {V,St2} = new_var(St1), %This name should be arbitrary
- {Cs1,St3} = clauses(Cs0, St2),
- {Fpat,St4} = new_var(St3),
- Fc = fail_clause([Fpat], #c_tuple{es=[#c_atom{val=try_clause},Fpat]}),
- {Evs,Hs,St5} = try_exception(Ecs, St4),
- {#itry{anno=#a{anno=[L]},args=Es1,
- vars=[V],body=[#icase{anno=#a{},args=[V],clauses=Cs1,fc=Fc}],
- evars=Evs,handler=Hs},
- [],St5};
-expr({'try',L,Es0,[],[],As0}, St0) ->
- %% 'try ... after ... end'
- {Es1,St1} = exprs(Es0, St0),
- {As1,St2} = exprs(As0, St1),
- {Evs,Hs,St3} = try_after(As1,St2),
- {V,St4} = new_var(St3), % (must not exist in As1)
- %% TODO: this duplicates the 'after'-code; should lift to function.
- {#itry{anno=#a{anno=[L]},args=Es1,vars=[V],body=As1++[V],
- evars=Evs,handler=Hs},
- [],St4};
-expr({'try',L,Es,Cs,Ecs,As}, St0) ->
- %% 'try ... [of ...] [catch ...] after ... end'
- expr({'try',L,[{'try',L,Es,Cs,Ecs,[]}],[],[],As}, St0);
-expr({'catch',L,E0}, St0) ->
- {E1,Eps,St1} = expr(E0, St0),
- {#icatch{anno=#a{anno=[L]},body=Eps ++ [E1]},[],St1};
-expr({'fun',L,{function,F,A},{_,_,_}=Id}, St) ->
- {#c_fname{anno=[L,{id,Id}],id=F,arity=A},[],St};
-expr({'fun',L,{clauses,Cs},Id}, St) ->
- fun_tq(Id, Cs, L, St);
-expr({call,L0,{remote,_,{atom,_,erlang},{atom,_,is_record}},[_,_,_]=As}, St)
- when L0 < 0 ->
- %% Compiler-generated erlang:is_record/3 should be converted to
- %% erlang:internal_is_record/3.
- L = -L0,
- expr({call,L,{remote,L,{atom,L,erlang},{atom,L,internal_is_record}},As}, St);
-expr({call,L,{remote,_,M,F},As0}, St0) ->
- {[M1,F1|As1],Aps,St1} = safe_list([M,F|As0], St0),
- {#icall{anno=#a{anno=[L]},module=M1,name=F1,args=As1},Aps,St1};
-expr({call,Lc,{atom,Lf,F},As0}, St0) ->
- {As1,Aps,St1} = safe_list(As0, St0),
- Op = #c_fname{anno=[Lf],id=F,arity=length(As1)},
- {#iapply{anno=#a{anno=[Lc]},op=Op,args=As1},Aps,St1};
-expr({call,L,FunExp,As0}, St0) ->
- {Fun,Fps,St1} = safe(FunExp, St0),
- {As1,Aps,St2} = safe_list(As0, St1),
- {#iapply{anno=#a{anno=[L]},op=Fun,args=As1},Fps ++ Aps,St2};
-expr({match,L,P0,E0}, St0) ->
- %% First fold matches together to create aliases.
- {P1,E1} = fold_match(E0, P0),
- {E2,Eps,St1} = novars(E1, St0),
- P2 = (catch pattern(P1)),
- {Fpat,St2} = new_var(St1),
- Fc = fail_clause([Fpat], #c_tuple{es=[#c_atom{val=badmatch},Fpat]}),
- case P2 of
- {'EXIT',_}=Exit -> exit(Exit); %Propagate error
- nomatch ->
- St = add_warning(L, nomatch, St2),
- {#icase{anno=#a{anno=[L]},
- args=[E2],clauses=[],fc=Fc},Eps,St};
- _Other ->
- {#imatch{anno=#a{anno=[L]},pat=P2,arg=E2,fc=Fc},Eps,St2}
- end;
-expr({op,_,'++',{lc,Llc,E,Qs},L2}, St) ->
- %% Optimise this here because of the list comprehension algorithm.
- lc_tq(Llc, E, Qs, L2, St);
-expr({op,L,Op,A0}, St0) ->
- {A1,Aps,St1} = safe(A0, St0),
- LineAnno = [L],
- {#icall{anno=#a{anno=LineAnno}, %Must have an #a{}
- module=#c_atom{anno=LineAnno,val=erlang},
- name=#c_atom{anno=LineAnno,val=Op},args=[A1]},Aps,St1};
-expr({op,L,Op,L0,R0}, St0) ->
- {As,Aps,St1} = safe_list([L0,R0], St0),
- LineAnno = [L],
- {#icall{anno=#a{anno=LineAnno}, %Must have an #a{}
- module=#c_atom{anno=LineAnno,val=erlang},
- name=#c_atom{anno=LineAnno,val=Op},args=As},Aps,St1}.
-
-%% try_exception([ExcpClause], St) -> {[ExcpVar],Handler,St}.
-
-try_exception(Ecs0, St0) ->
- %% Note that Tag is not needed for rethrow - it is already in Info.
- {Evs,St1} = new_vars(3, St0), % Tag, Value, Info
- {Ecs1,St2} = clauses(Ecs0, St1),
- [_,Value,Info] = Evs,
- Ec = #iclause{anno=#a{anno=[compiler_generated]},
- pats=[#c_tuple{es=Evs}],guard=[#c_atom{val=true}],
- body=[#iprimop{anno=#a{}, %Must have an #a{}
- name=#c_atom{val=raise},
- args=[Info,Value]}]},
- Hs = [#icase{anno=#a{},args=[#c_tuple{es=Evs}],clauses=Ecs1,fc=Ec}],
- {Evs,Hs,St2}.
-
-try_after(As, St0) ->
- %% See above.
- {Evs,St1} = new_vars(3, St0), % Tag, Value, Info
- [_,Value,Info] = Evs,
- B = As ++ [#iprimop{anno=#a{}, %Must have an #a{}
- name=#c_atom{val=raise},
- args=[Info,Value]}],
- Ec = #iclause{anno=#a{anno=[compiler_generated]},
- pats=[#c_tuple{es=Evs}],guard=[#c_atom{val=true}],
- body=B},
- Hs = [#icase{anno=#a{},args=[#c_tuple{es=Evs}],clauses=[],fc=Ec}],
- {Evs,Hs,St1}.
-
-%% expr_bin([ArgExpr], St) -> {[Arg],[PreExpr],St}.
-%% Flatten the arguments of a bin. Do this straight left to right!
-
-expr_bin(Es, St) ->
- foldr(fun (E, {Ces,Esp,St0}) ->
- {Ce,Ep,St1} = bitstr(E, St0),
- {[Ce|Ces],Ep ++ Esp,St1}
- end, {[],[],St}, Es).
-
-bitstr({bin_element,_,E0,Size0,[Type,{unit,Unit}|Flags]}, St0) ->
- {E1,Eps,St1} = safe(E0, St0),
- {Size1,Eps2,St2} = safe(Size0, St1),
- {#c_bitstr{val=E1,size=Size1,
- unit=core_lib:make_literal(Unit),
- type=core_lib:make_literal(Type),
- flags=core_lib:make_literal(Flags)},
- Eps ++ Eps2,St2}.
-
-%% fun_tq(Id, [Clauses], Line, State) -> {Fun,[PreExp],State}.
-
-fun_tq(Id, Cs0, L, St0) ->
- {Cs1,St1} = clauses(Cs0, St0),
- Arity = length((hd(Cs1))#iclause.pats),
- {Args,St2} = new_vars(Arity, St1),
- {Ps,St3} = new_vars(Arity, St2), %Need new variables here
- Fc = fail_clause(Ps, #c_tuple{es=[#c_atom{val=function_clause}|Ps]}),
- Fun = #ifun{anno=#a{anno=[L]},
- id=[{id,Id}], %We KNOW!
- vars=Args,clauses=Cs1,fc=Fc},
- {Fun,[],St3}.
-
-%% lc_tq(Line, Exp, [Qualifier], More, State) -> {LetRec,[PreExp],State}.
-%% This TQ from Simon PJ pp 127-138.
-%% This gets a bit messy as we must transform all directly here. We
-%% recognise guard tests and try to fold them together and join to a
-%% preceding generators, this should give us better and more compact
-%% code.
-%% More could be transformed before calling lc_tq.
-
-lc_tq(Line, E, [{generate,Lg,P,G}|Qs0], More, St0) ->
- {Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0),
- {Name,St1} = new_fun_name("lc", St0),
- {Head,St2} = new_var(St1),
- {Tname,St3} = new_var_name(St2),
- LA = [Line],
- LAnno = #a{anno=LA},
- Tail = #c_var{anno=LA,name=Tname},
- {Arg,St4} = new_var(St3),
- NewMore = {call,Lg,{atom,Lg,Name},[{var,Lg,Tname}]},
- {Guardc,St5} = lc_guard_tests(Gs, St4), %These are always flat!
- {Lc,Lps,St6} = lc_tq(Line, E, Qs1, NewMore, St5),
- {Mc,Mps,St7} = expr(More, St6),
- {Nc,Nps,St8} = expr(NewMore, St7),
- case catch pattern(P) of
- {'EXIT',_}=Exit ->
- St9 = St8,
- Pc = nomatch,
- exit(Exit); %Propagate error
- nomatch ->
- St9 = add_warning(Line, nomatch, St8),
- Pc = nomatch;
- Pc ->
- St9 = St8
- end,
- {Gc,Gps,St10} = safe(G, St9), %Will be a function argument!
- Fc = fail_clause([Arg], #c_tuple{anno=LA,
- es=[#c_atom{val=function_clause},Arg]}),
- Cs0 = [#iclause{anno=#a{anno=[compiler_generated|LA]},
- pats=[#c_cons{anno=LA,hd=Head,tl=Tail}],
- guard=[],
- body=Nps ++ [Nc]},
- #iclause{anno=LAnno,
- pats=[#c_nil{anno=LA}],guard=[],
- body=Mps ++ [Mc]}],
- Cs = case Pc of
- nomatch -> Cs0;
- _ ->
- [#iclause{anno=LAnno,
- pats=[#c_cons{anno=LA,hd=Pc,tl=Tail}],
- guard=Guardc,
- body=Lps ++ [Lc]}|Cs0]
- end,
- Fun = #ifun{anno=LAnno,id=[],vars=[Arg],clauses=Cs,fc=Fc},
- {#iletrec{anno=LAnno,defs=[{Name,Fun}],
- body=Gps ++ [#iapply{anno=LAnno,
- op=#c_fname{anno=LA,id=Name,arity=1},
- args=[Gc]}]},
- [],St10};
-lc_tq(Line, E, [Fil0|Qs0], More, St0) ->
- %% Special case sequences guard tests.
- LA = [Line],
- LAnno = #a{anno=LA},
- case is_guard_test(Fil0) of
- true ->
- {Gs0,Qs1} = splitwith(fun is_guard_test/1, Qs0),
- {Lc,Lps,St1} = lc_tq(Line, E, Qs1, More, St0),
- {Mc,Mps,St2} = expr(More, St1),
- {Gs,St3} = lc_guard_tests([Fil0|Gs0], St2), %These are always flat!
- {#icase{anno=LAnno,
- args=[],
- clauses=[#iclause{anno=LAnno,pats=[],
- guard=Gs,body=Lps ++ [Lc]}],
- fc=#iclause{anno=LAnno,pats=[],guard=[],body=Mps ++ [Mc]}},
- [],St3};
- false ->
- {Lc,Lps,St1} = lc_tq(Line, E, Qs0, More, St0),
- {Mc,Mps,St2} = expr(More, St1),
- {Fpat,St3} = new_var(St2),
- Fc = fail_clause([Fpat], #c_tuple{es=[#c_atom{val=case_clause},Fpat]}),
- %% Do a novars little optimisation here.
- case Fil0 of
- {op,_,'not',Fil1} ->
- {Filc,Fps,St4} = novars(Fil1, St3),
- {#icase{anno=LAnno,
- args=[Filc],
- clauses=[#iclause{anno=LAnno,
- pats=[#c_atom{anno=LA,val=true}],
- guard=[],
- body=Mps ++ [Mc]},
- #iclause{anno=LAnno,
- pats=[#c_atom{anno=LA,val=false}],
- guard=[],
- body=Lps ++ [Lc]}],
- fc=Fc},
- Fps,St4};
- _Other ->
- {Filc,Fps,St4} = novars(Fil0, St3),
- {#icase{anno=LAnno,
- args=[Filc],
- clauses=[#iclause{anno=LAnno,
- pats=[#c_atom{anno=LA,val=true}],
- guard=[],
- body=Lps ++ [Lc]},
- #iclause{anno=LAnno,
- pats=[#c_atom{anno=LA,val=false}],
- guard=[],
- body=Mps ++ [Mc]}],
- fc=Fc},
- Fps,St4}
- end
- end;
-lc_tq(Line, E, [], More, St) ->
- expr({cons,Line,E,More}, St).
-
-lc_guard_tests([], St) -> {[],St};
-lc_guard_tests(Gs0, St) ->
- Gs = guard_tests(Gs0),
- gexpr_top(Gs, St).
-
-%% is_guard_test(Expression) -> true | false.
-%% Test if a general expression is a guard test. Use erl_lint here
-%% as it now allows sys_pre_expand transformed source.
-
-is_guard_test(E) -> erl_lint:is_guard_test(E).
-
-%% novars(Expr, State) -> {Novars,[PreExpr],State}.
-%% Generate a novars expression, basically a call or a safe. At this
-%% level we do not need to do a deep check.
-
-novars(E0, St0) ->
- {E1,Eps,St1} = expr(E0, St0),
- {Se,Sps,St2} = force_novars(E1, St1),
- {Se,Eps ++ Sps,St2}.
-
-force_novars(#iapply{}=App, St) -> {App,[],St};
-force_novars(#icall{}=Call, St) -> {Call,[],St};
-force_novars(#iprimop{}=Prim, St) -> {Prim,[],St};
-force_novars(#ifun{}=Fun, St) -> {Fun,[],St}; %These are novars too
-force_novars(#ibinary{}=Bin, St) -> {Bin,[],St};
-force_novars(Ce, St) ->
- force_safe(Ce, St).
-
-%% safe(Expr, State) -> {Safe,[PreExpr],State}.
-%% Generate an internal safe expression. These are simples without
-%% binaries which can fail. At this level we do not need to do a
-%% deep check. Must do special things with matches here.
-
-safe(E0, St0) ->
- {E1,Eps,St1} = expr(E0, St0),
- {Se,Sps,St2} = force_safe(E1, St1),
- {Se,Eps ++ Sps,St2}.
-
-safe_list(Es, St) ->
- foldr(fun (E, {Ces,Esp,St0}) ->
- {Ce,Ep,St1} = safe(E, St0),
- {[Ce|Ces],Ep ++ Esp,St1}
- end, {[],[],St}, Es).
-
-force_safe(#imatch{anno=Anno,pat=P,arg=E,fc=Fc}, St0) ->
- {Le,Lps,St1} = force_safe(E, St0),
- {Le,Lps ++ [#imatch{anno=Anno,pat=P,arg=Le,fc=Fc}],St1};
-force_safe(Ce, St0) ->
- case is_safe(Ce) of
- true -> {Ce,[],St0};
- false ->
- {V,St1} = new_var(St0),
- {V,[#iset{var=V,arg=Ce}],St1}
- end.
-
-is_safe(#c_cons{}) -> true;
-is_safe(#c_tuple{}) -> true;
-is_safe(#c_var{}) -> true;
-is_safe(E) -> core_lib:is_atomic(E).
-
-%%% %% variable(Expr, State) -> {Variable,[PreExpr],State}.
-%%% %% force_variable(Expr, State) -> {Variable,[PreExpr],State}.
-%%% %% Generate a variable.
-
-%%% variable(E0, St0) ->
-%%% {E1,Eps,St1} = expr(E0, St0),
-%%% {V,Vps,St2} = force_variable(E1, St1),
-%%% {V,Eps ++ Vps,St2}.
-
-%%% force_variable(#c_var{}=Var, St) -> {Var,[],St};
-%%% force_variable(Ce, St0) ->
-%%% {V,St1} = new_var(St0),
-%%% {V,[#iset{var=V,arg=Ce}],St1}.
-
-%%% %% atomic(Expr, State) -> {Atomic,[PreExpr],State}.
-%%% %% force_atomic(Expr, State) -> {Atomic,[PreExpr],State}.
-
-%%% atomic(E0, St0) ->
-%%% {E1,Eps,St1} = expr(E0, St0),
-%%% {A,Aps,St2} = force_atomic(E1, St1),
-%%% {A,Eps ++ Aps,St2}.
-
-%%% force_atomic(Ce, St0) ->
-%%% case core_lib:is_atomic(Ce) of
-%%% true -> {Ce,[],St0};
-%%% false ->
-%%% {V,St1} = new_var(St0),
-%%% {V,[#iset{var=V,arg=Ce}],St1}
-%%% end.
-
-%% fold_match(MatchExpr, Pat) -> {MatchPat,Expr}.
-%% Fold nested matches into one match with aliased patterns.
-
-fold_match({match,L,P0,E0}, P) ->
- {P1,E1} = fold_match(E0, P),
- {{match,L,P0,P1},E1};
-fold_match(E, P) -> {P,E}.
-
-%% pattern(Pattern) -> CorePat.
-%% Transform a pattern by removing line numbers. We also normalise
-%% aliases in patterns to standard form, {alias,Pat,[Var]}.
-
-pattern({var,L,V}) -> #c_var{anno=[L],name=V};
-pattern({char,L,C}) -> #c_char{anno=[L],val=C};
-pattern({integer,L,I}) -> #c_int{anno=[L],val=I};
-pattern({float,L,F}) -> #c_float{anno=[L],val=F};
-pattern({atom,L,A}) -> #c_atom{anno=[L],val=A};
-pattern({string,L,S}) -> #c_string{anno=[L],val=S};
-pattern({nil,L}) -> #c_nil{anno=[L]};
-pattern({cons,L,H,T}) ->
- #c_cons{anno=[L],hd=pattern(H),tl=pattern(T)};
-pattern({tuple,L,Ps}) ->
- #c_tuple{anno=[L],es=pattern_list(Ps)};
-pattern({bin,L,Ps}) ->
- %% We don't create a #ibinary record here, since there is
- %% no need to hold any used/new annoations in a pattern.
- #c_binary{anno=[L],segments=pat_bin(Ps)};
-pattern({match,_,P1,P2}) ->
- pat_alias(pattern(P1), pattern(P2)).
-
-%% bin_pattern_list([BinElement]) -> [BinSeg].
-
-pat_bin(Ps) -> map(fun pat_segment/1, Ps).
-
-pat_segment({bin_element,_,Term,Size,[Type,{unit,Unit}|Flags]}) ->
- #c_bitstr{val=pattern(Term),size=pattern(Size),
- unit=core_lib:make_literal(Unit),
- type=core_lib:make_literal(Type),
- flags=core_lib:make_literal(Flags)}.
-
-%% pat_alias(CorePat, CorePat) -> AliasPat.
-%% Normalise aliases. Trap bad aliases by throwing 'nomatch'.
-
-pat_alias(#c_var{name=V1}, P2) -> #c_alias{var=#c_var{name=V1},pat=P2};
-pat_alias(P1, #c_var{name=V2}) -> #c_alias{var=#c_var{name=V2},pat=P1};
-pat_alias(#c_cons{}=Cons, #c_string{anno=A,val=[H|T]}=S) ->
- pat_alias(Cons, #c_cons{anno=A,hd=#c_char{anno=A,val=H},
- tl=S#c_string{val=T}});
-pat_alias(#c_string{anno=A,val=[H|T]}=S, #c_cons{}=Cons) ->
- pat_alias(#c_cons{anno=A,hd=#c_char{anno=A,val=H},
- tl=S#c_string{val=T}}, Cons);
-pat_alias(#c_nil{}=Nil, #c_string{val=[]}) ->
- Nil;
-pat_alias(#c_string{val=[]}, #c_nil{}=Nil) ->
- Nil;
-pat_alias(#c_cons{anno=A,hd=H1,tl=T1}, #c_cons{hd=H2,tl=T2}) ->
- #c_cons{anno=A,hd=pat_alias(H1, H2),tl=pat_alias(T1, T2)};
-pat_alias(#c_tuple{es=Es1}, #c_tuple{es=Es2}) ->
- #c_tuple{es=pat_alias_list(Es1, Es2)};
-pat_alias(#c_char{val=C}=Char, #c_int{val=C}) ->
- Char;
-pat_alias(#c_int{val=C}, #c_char{val=C}=Char) ->
- Char;
-pat_alias(#c_alias{var=V1,pat=P1},
- #c_alias{var=V2,pat=P2}) ->
- if V1 == V2 -> pat_alias(P1, P2);
- true -> #c_alias{var=V1,pat=#c_alias{var=V2,pat=pat_alias(P1, P2)}}
- end;
-pat_alias(#c_alias{var=V1,pat=P1}, P2) ->
- #c_alias{var=V1,pat=pat_alias(P1, P2)};
-pat_alias(P1, #c_alias{var=V2,pat=P2}) ->
- #c_alias{var=V2,pat=pat_alias(P1, P2)};
-pat_alias(P, P) -> P;
-pat_alias(_, _) -> throw(nomatch).
-
-%% pat_alias_list([A1], [A2]) -> [A].
-
-pat_alias_list([A1|A1s], [A2|A2s]) ->
- [pat_alias(A1, A2)|pat_alias_list(A1s, A2s)];
-pat_alias_list([], []) -> [];
-pat_alias_list(_, _) -> throw(nomatch).
-
-%% pattern_list([P]) -> [P].
-
-pattern_list(Ps) -> map(fun pattern/1, Ps).
-
-%% first([A]) -> [A].
-%% last([A]) -> A.
-
-first([_]) -> [];
-first([H|T]) -> [H|first(T)].
-
-last([L]) -> L;
-last([_|T]) -> last(T).
-
-%% make_vars([Name]) -> [{Var,Name}].
-
-make_vars(Vs) -> [ #c_var{name=V} || V <- Vs ].
-
-%% new_fun_name(Type, State) -> {FunName,State}.
-
-new_fun_name(Type, #core{fcount=C}=St) ->
- {list_to_atom(Type ++ "$^" ++ integer_to_list(C)),St#core{fcount=C+1}}.
-
-%% new_var_name(State) -> {VarName,State}.
-
-new_var_name(#core{vcount=C}=St) ->
- {list_to_atom("cor" ++ integer_to_list(C)),St#core{vcount=C + 1}}.
-
-%% new_var(State) -> {{var,Name},State}.
-%% new_var(LineAnno, State) -> {{var,Name},State}.
-
-new_var(St) ->
- new_var([], St).
-
-new_var(Anno, St0) ->
- {New,St} = new_var_name(St0),
- {#c_var{anno=Anno,name=New},St}.
-
-%% new_vars(Count, State) -> {[Var],State}.
-%% new_vars(Anno, Count, State) -> {[Var],State}.
-%% Make Count new variables.
-
-new_vars(N, St) -> new_vars_1(N, [], St, []).
-new_vars(Anno, N, St) -> new_vars_1(N, Anno, St, []).
-
-new_vars_1(N, Anno, St0, Vs) when N > 0 ->
- {V,St1} = new_var(Anno, St0),
- new_vars_1(N-1, Anno, St1, [V|Vs]);
-new_vars_1(0, _, St, Vs) -> {Vs,St}.
-
-fail_clause(Pats, A) ->
- #iclause{anno=#a{anno=[compiler_generated]},
- pats=Pats,guard=[],
- body=[#iprimop{anno=#a{},name=#c_atom{val=match_fail},args=[A]}]}.
-
-ubody(B, St) -> uexpr(B, [], St).
-
-%% uclauses([Lclause], [KnownVar], State) -> {[Lclause],State}.
-
-uclauses(Lcs, Ks, St0) ->
- mapfoldl(fun (Lc, St) -> uclause(Lc, Ks, St) end, St0, Lcs).
-
-%% uclause(Lclause, [KnownVar], State) -> {Lclause,State}.
-
-uclause(Cl0, Ks, St0) ->
- {Cl1,_Pvs,Used,New,St1} = uclause(Cl0, Ks, Ks, St0),
- A0 = get_ianno(Cl1),
- A = A0#a{us=Used,ns=New},
- {Cl1#iclause{anno=A},St1}.
-
-uclause(#iclause{anno=Anno,pats=Ps0,guard=G0,body=B0}, Pks, Ks0, St0) ->
- {Ps1,Pg,Pvs,Pus,St1} = upattern_list(Ps0, Pks, St0),
- Pu = union(Pus, intersection(Pvs, Ks0)),
- Pn = subtract(Pvs, Pu),
- Ks1 = union(Pn, Ks0),
- {G1,St2} = uguard(Pg, G0, Ks1, St1),
- Gu = used_in_any(G1),
- Gn = new_in_any(G1),
- Ks2 = union(Gn, Ks1),
- {B1,St3} = uexprs(B0, Ks2, St2),
- Used = intersection(union([Pu,Gu,used_in_any(B1)]), Ks0),
- New = union([Pn,Gn,new_in_any(B1)]),
- {#iclause{anno=Anno,pats=Ps1,guard=G1,body=B1},Pvs,Used,New,St3}.
-
-%% uguard([Test], [Kexpr], [KnownVar], State) -> {[Kexpr],State}.
-%% Build a guard expression list by folding in the equality tests.
-
-uguard([], [], _, St) -> {[],St};
-uguard(Pg, [], Ks, St) ->
- %% No guard, so fold together equality tests.
- uguard(first(Pg), [last(Pg)], Ks, St);
-uguard(Pg, Gs0, Ks, St0) ->
- %% Gs0 must contain at least one element here.
- {Gs3,St5} = foldr(fun (T, {Gs1,St1}) ->
- {L,St2} = new_var(St1),
- {R,St3} = new_var(St2),
- {[#iset{var=L,arg=T}] ++ first(Gs1) ++
- [#iset{var=R,arg=last(Gs1)},
- #icall{anno=#a{}, %Must have an #a{}
- module=#c_atom{val=erlang},
- name=#c_atom{val='and'},
- args=[L,R]}],
- St3}
- end, {Gs0,St0}, Pg),
- %%ok = io:fwrite("core ~w: ~p~n", [?LINE,Gs3]),
- uexprs(Gs3, Ks, St5).
-
-%% uexprs([Kexpr], [KnownVar], State) -> {[Kexpr],State}.
-
-uexprs([#imatch{anno=A,pat=P0,arg=Arg,fc=Fc}|Les], Ks, St0) ->
- %% Optimise for simple set of unbound variable.
- case upattern(P0, Ks, St0) of
- {#c_var{},[],_Pvs,_Pus,_} ->
- %% Throw our work away and just set to iset.
- uexprs([#iset{var=P0,arg=Arg}|Les], Ks, St0);
- _Other ->
- %% Throw our work away and set to icase.
- if
- Les == [] ->
- %% Need to explicitly return match "value", make
- %% safe for efficiency.
- {La,Lps,St1} = force_safe(Arg, St0),
- Mc = #iclause{anno=A,pats=[P0],guard=[],body=[La]},
- uexprs(Lps ++ [#icase{anno=A,
- args=[La],clauses=[Mc],fc=Fc}], Ks, St1);
- true ->
- Mc = #iclause{anno=A,pats=[P0],guard=[],body=Les},
- uexprs([#icase{anno=A,args=[Arg],
- clauses=[Mc],fc=Fc}], Ks, St0)
- end
- end;
-uexprs([Le0|Les0], Ks, St0) ->
- {Le1,St1} = uexpr(Le0, Ks, St0),
- {Les1,St2} = uexprs(Les0, union((core_lib:get_anno(Le1))#a.ns, Ks), St1),
- {[Le1|Les1],St2};
-uexprs([], _, St) -> {[],St}.
-
-uexpr(#iset{anno=A,var=V,arg=A0}, Ks, St0) ->
- {A1,St1} = uexpr(A0, Ks, St0),
- {#iset{anno=A#a{us=del_element(V#c_var.name, (core_lib:get_anno(A1))#a.us),
- ns=add_element(V#c_var.name, (core_lib:get_anno(A1))#a.ns)},
- var=V,arg=A1},St1};
-%% imatch done in uexprs.
-uexpr(#iletrec{anno=A,defs=Fs0,body=B0}, Ks, St0) ->
- %%ok = io:fwrite("~w: ~p~n", [?LINE,{Fs0,B0}]),
- {Fs1,St1} = mapfoldl(fun ({Name,F0}, St0) ->
- {F1,St1} = uexpr(F0, Ks, St0),
- {{Name,F1},St1}
- end, St0, Fs0),
- {B1,St2} = uexprs(B0, Ks, St1),
- Used = used_in_any(map(fun ({_,F}) -> F end, Fs1) ++ B1),
- {#iletrec{anno=A#a{us=Used,ns=[]},defs=Fs1,body=B1},St2};
-uexpr(#icase{anno=A,args=As0,clauses=Cs0,fc=Fc0}, Ks, St0) ->
- %% As0 will never generate new variables.
- {As1,St1} = uexpr_list(As0, Ks, St0),
- {Cs1,St2} = uclauses(Cs0, Ks, St1),
- {Fc1,St3} = uclause(Fc0, Ks, St2),
- Used = union(used_in_any(As1), used_in_any(Cs1)),
- New = new_in_all(Cs1),
- {#icase{anno=A#a{us=Used,ns=New},args=As1,clauses=Cs1,fc=Fc1},St3};
-uexpr(#ifun{anno=A,id=Id,vars=As,clauses=Cs0,fc=Fc0}, Ks0, St0) ->
- Avs = lit_list_vars(As),
- Ks1 = union(Avs, Ks0),
- {Cs1,St1} = ufun_clauses(Cs0, Ks1, St0),
- {Fc1,St2} = ufun_clause(Fc0, Ks1, St1),
- Used = subtract(intersection(used_in_any(Cs1), Ks0), Avs),
- {#ifun{anno=A#a{us=Used,ns=[]},id=Id,vars=As,clauses=Cs1,fc=Fc1},St2};
-uexpr(#iapply{anno=A,op=Op,args=As}, _, St) ->
- Used = union(lit_vars(Op), lit_list_vars(As)),
- {#iapply{anno=A#a{us=Used},op=Op,args=As},St};
-uexpr(#iprimop{anno=A,name=Name,args=As}, _, St) ->
- Used = lit_list_vars(As),
- {#iprimop{anno=A#a{us=Used},name=Name,args=As},St};
-uexpr(#icall{anno=A,module=Mod,name=Name,args=As}, _, St) ->
- Used = union([lit_vars(Mod),lit_vars(Name),lit_list_vars(As)]),
- {#icall{anno=A#a{us=Used},module=Mod,name=Name,args=As},St};
-uexpr(#itry{anno=A,args=As0,vars=Vs,body=Bs0,evars=Evs,handler=Hs0}, Ks, St0) ->
- %% Note that we export only from body and exception.
- {As1,St1} = uexprs(As0, Ks, St0),
- {Bs1,St2} = uexprs(Bs0, Ks, St1),
- {Hs1,St3} = uexprs(Hs0, Ks, St2),
- Used = intersection(used_in_any(Bs1++Hs1++As1), Ks),
- New = new_in_all(Bs1++Hs1),
- {#itry{anno=A#a{us=Used,ns=New},
- args=As1,vars=Vs,body=Bs1,evars=Evs,handler=Hs1},St3};
-uexpr(#icatch{anno=A,body=Es0}, Ks, St0) ->
- {Es1,St1} = uexprs(Es0, Ks, St0),
- {#icatch{anno=A#a{us=used_in_any(Es1)},body=Es1},St1};
-uexpr(#ireceive1{anno=A,clauses=Cs0}, Ks, St0) ->
- {Cs1,St1} = uclauses(Cs0, Ks, St0),
- {#ireceive1{anno=A#a{us=used_in_any(Cs1),ns=new_in_all(Cs1)},
- clauses=Cs1},St1};
-uexpr(#ireceive2{anno=A,clauses=Cs0,timeout=Te0,action=Tes0}, Ks, St0) ->
- %% Te0 will never generate new variables.
- {Te1,St1} = uexpr(Te0, Ks, St0),
- {Cs1,St2} = uclauses(Cs0, Ks, St1),
- {Tes1,St3} = uexprs(Tes0, Ks, St2),
- Used = union([used_in_any(Cs1),used_in_any(Tes1),
- (core_lib:get_anno(Te1))#a.us]),
- New = case Cs1 of
- [] -> new_in_any(Tes1);
- _ -> intersection(new_in_all(Cs1), new_in_any(Tes1))
- end,
- {#ireceive2{anno=A#a{us=Used,ns=New},
- clauses=Cs1,timeout=Te1,action=Tes1},St3};
-uexpr(#iprotect{anno=A,body=Es0}, Ks, St0) ->
- {Es1,St1} = uexprs(Es0, Ks, St0),
- Used = used_in_any(Es1),
- {#iprotect{anno=A#a{us=Used},body=Es1},St1}; %No new variables escape!
-uexpr(#ibinary{anno=A,segments=Ss}, _, St) ->
- Used = bitstr_vars(Ss),
- {#ibinary{anno=A#a{us=Used},segments=Ss},St};
-uexpr(Lit, _, St) ->
- true = core_lib:is_simple(Lit), %Sanity check!
- Vs = lit_vars(Lit),
- Anno = core_lib:get_anno(Lit),
- {core_lib:set_anno(Lit, #a{us=Vs,anno=Anno}),St}.
-
-uexpr_list(Les0, Ks, St0) ->
- mapfoldl(fun (Le, St) -> uexpr(Le, Ks, St) end, St0, Les0).
-
-%% ufun_clauses([Lclause], [KnownVar], State) -> {[Lclause],State}.
-
-ufun_clauses(Lcs, Ks, St0) ->
- mapfoldl(fun (Lc, St) -> ufun_clause(Lc, Ks, St) end, St0, Lcs).
-
-%% ufun_clause(Lclause, [KnownVar], State) -> {Lclause,State}.
-
-ufun_clause(Cl0, Ks, St0) ->
- {Cl1,Pvs,Used,_,St1} = uclause(Cl0, [], Ks, St0),
- A0 = get_ianno(Cl1),
- A = A0#a{us=subtract(intersection(Used, Ks), Pvs),ns=[]},
- {Cl1#iclause{anno=A},St1}.
-
-%% upattern(Pat, [KnownVar], State) ->
-%% {Pat,[GuardTest],[NewVar],[UsedVar],State}.
-
-upattern(#c_var{name='_'}, _, St0) ->
- {New,St1} = new_var_name(St0),
- {#c_var{name=New},[],[New],[],St1};
-upattern(#c_var{name=V}=Var, Ks, St0) ->
- case is_element(V, Ks) of
- true ->
- {N,St1} = new_var_name(St0),
- New = #c_var{name=N},
- Test = #icall{anno=#a{us=add_element(N, [V])},
- module=#c_atom{val=erlang},
- name=#c_atom{val='=:='},
- args=[New,Var]},
- %% Test doesn't need protecting.
- {New,[Test],[N],[],St1};
- false -> {Var,[],[V],[],St0}
- end;
-upattern(#c_cons{hd=H0,tl=T0}=Cons, Ks, St0) ->
- {H1,Hg,Hv,Hu,St1} = upattern(H0, Ks, St0),
- {T1,Tg,Tv,Tu,St2} = upattern(T0, union(Hv, Ks), St1),
- {Cons#c_cons{hd=H1,tl=T1},Hg ++ Tg,union(Hv, Tv),union(Hu, Tu),St2};
-upattern(#c_tuple{es=Es0}=Tuple, Ks, St0) ->
- {Es1,Esg,Esv,Eus,St1} = upattern_list(Es0, Ks, St0),
- {Tuple#c_tuple{es=Es1},Esg,Esv,Eus,St1};
-upattern(#c_binary{segments=Es0}=Bin, Ks, St0) ->
- {Es1,Esg,Esv,Eus,St1} = upat_bin(Es0, Ks, St0),
- {Bin#c_binary{segments=Es1},Esg,Esv,Eus,St1};
-upattern(#c_alias{var=V0,pat=P0}=Alias, Ks, St0) ->
- {V1,Vg,Vv,Vu,St1} = upattern(V0, Ks, St0),
- {P1,Pg,Pv,Pu,St2} = upattern(P0, union(Vv, Ks), St1),
- {Alias#c_alias{var=V1,pat=P1},Vg ++ Pg,union(Vv, Pv),union(Vu, Pu),St2};
-upattern(Other, _, St) -> {Other,[],[],[],St}. %Constants
-
-%% upattern_list([Pat], [KnownVar], State) ->
-%% {[Pat],[GuardTest],[NewVar],[UsedVar],State}.
-
-upattern_list([P0|Ps0], Ks, St0) ->
- {P1,Pg,Pv,Pu,St1} = upattern(P0, Ks, St0),
- {Ps1,Psg,Psv,Psu,St2} = upattern_list(Ps0, union(Pv, Ks), St1),
- {[P1|Ps1],Pg ++ Psg,union(Pv, Psv),union(Pu, Psu),St2};
-upattern_list([], _, St) -> {[],[],[],[],St}.
-
-%% upat_bin([Pat], [KnownVar], State) ->
-%% {[Pat],[GuardTest],[NewVar],[UsedVar],State}.
-upat_bin(Es0, Ks, St0) ->
- upat_bin(Es0, Ks, [], St0).
-
-%% upat_bin([Pat], [KnownVar], [LocalVar], State) ->
-%% {[Pat],[GuardTest],[NewVar],[UsedVar],State}.
-upat_bin([P0|Ps0], Ks, Bs, St0) ->
- {P1,Pg,Pv,Pu,Bs1,St1} = upat_element(P0, Ks, Bs, St0),
- {Ps1,Psg,Psv,Psu,St2} = upat_bin(Ps0, union(Pv, Ks), Bs1, St1),
- {[P1|Ps1],Pg ++ Psg,union(Pv, Psv),union(Pu, Psu),St2};
-upat_bin([], _, _, St) -> {[],[],[],[],St}.
-
-
-%% upat_element(Segment, [KnownVar], [LocalVar], State) ->
-%% {Segment,[GuardTest],[NewVar],[UsedVar],[LocalVar],State}
-upat_element(#c_bitstr{val=H0,size=Sz}=Seg, Ks, Bs, St0) ->
- {H1,Hg,Hv,[],St1} = upattern(H0, Ks, St0),
- Bs1 = case H0 of
- #c_var{name=Hname} ->
- case H1 of
- #c_var{name=Hname} ->
- Bs;
- #c_var{name=Other} ->
- [{Hname, Other}|Bs]
- end;
- _ ->
- Bs
- end,
- {Sz1, Us} = case Sz of
- #c_var{name=Vname} ->
- rename_bitstr_size(Vname, Bs);
- _Other -> {Sz, []}
- end,
- {Seg#c_bitstr{val=H1, size=Sz1},Hg,Hv,Us,Bs1,St1}.
-
-rename_bitstr_size(V, [{V, N}|_]) ->
- New = #c_var{name=N},
- {New, [N]};
-rename_bitstr_size(V, [_|Rest]) ->
- rename_bitstr_size(V, Rest);
-rename_bitstr_size(V, []) ->
- Old = #c_var{name=V},
- {Old, [V]}.
-
-used_in_any(Les) ->
- foldl(fun (Le, Ns) -> union((core_lib:get_anno(Le))#a.us, Ns) end,
- [], Les).
-
-new_in_any(Les) ->
- foldl(fun (Le, Ns) -> union((core_lib:get_anno(Le))#a.ns, Ns) end,
- [], Les).
-
-new_in_all([Le|Les]) ->
- foldl(fun (L, Ns) -> intersection((core_lib:get_anno(L))#a.ns, Ns) end,
- (core_lib:get_anno(Le))#a.ns, Les);
-new_in_all([]) -> [].
-
-%% The AfterVars are the variables which are used afterwards. We need
-%% this to work out which variables are actually exported and used
-%% from case/receive. In subblocks/clauses the AfterVars of the block
-%% are just the exported variables.
-
-cbody(B0, St0) ->
- {B1,_,_,St1} = cexpr(B0, [], St0),
- {B1,St1}.
-
-%% cclause(Lclause, [AfterVar], State) -> {Cclause,State}.
-%% The AfterVars are the exported variables.
-
-cclause(#iclause{anno=#a{anno=Anno},pats=Ps,guard=G0,body=B0}, Exp, St0) ->
- {B1,_Us1,St1} = cexprs(B0, Exp, St0),
- {G1,St2} = cguard(G0, St1),
- {#c_clause{anno=Anno,pats=Ps,guard=G1,body=B1},St2}.
-
-cclauses(Lcs, Es, St0) ->
- mapfoldl(fun (Lc, St) -> cclause(Lc, Es, St) end, St0, Lcs).
-
-cguard([], St) -> {#c_atom{val=true},St};
-cguard(Gs, St0) ->
- {G,_,St1} = cexprs(Gs, [], St0),
- {G,St1}.
-
-%% cexprs([Lexpr], [AfterVar], State) -> {Cexpr,[AfterVar],State}.
-%% Must be sneaky here at the last expr when combining exports for the
-%% whole sequence and exports for that expr.
-
-cexprs([#iset{var=#c_var{name=Name}=Var}=Iset], As, St) ->
- %% Make return value explicit, and make Var true top level.
- cexprs([Iset,Var#c_var{anno=#a{us=[Name]}}], As, St);
-cexprs([Le], As, St0) ->
- {Ce,Es,Us,St1} = cexpr(Le, As, St0),
- Exp = make_vars(As), %The export variables
- if
- Es == [] -> {core_lib:make_values([Ce|Exp]),union(Us, As),St1};
- true ->
- {R,St2} = new_var(St1),
- {#c_let{anno=get_lineno_anno(Ce),
- vars=[R|make_vars(Es)],arg=Ce,
- body=core_lib:make_values([R|Exp])},
- union(Us, As),St2}
- end;
-cexprs([#iset{anno=#a{anno=A},var=V,arg=A0}|Les], As0, St0) ->
- {Ces,As1,St1} = cexprs(Les, As0, St0),
- {A1,Es,Us,St2} = cexpr(A0, As1, St1),
- {#c_let{anno=A,vars=[V|make_vars(Es)],arg=A1,body=Ces},
- union(Us, As1),St2};
-cexprs([Le|Les], As0, St0) ->
- {Ces,As1,St1} = cexprs(Les, As0, St0),
- {Ce,Es,Us,St2} = cexpr(Le, As1, St1),
- if
- Es == [] ->
- {#c_seq{arg=Ce,body=Ces},union(Us, As1),St2};
- true ->
- {R,St3} = new_var(St2),
- {#c_let{vars=[R|make_vars(Es)],arg=Ce,body=Ces},
- union(Us, As1),St3}
- end.
-
-%% cexpr(Lexpr, [AfterVar], State) -> {Cexpr,[ExpVar],[UsedVar],State}.
-
-cexpr(#iletrec{anno=A,defs=Fs0,body=B0}, As, St0) ->
- {Fs1,{_,St1}} = mapfoldl(fun ({Name,F0}, {Used,St0}) ->
- {F1,[],Us,St1} = cexpr(F0, [], St0),
- {#c_def{name=#c_fname{id=Name,arity=1},
- val=F1},
- {union(Us, Used),St1}}
- end, {[],St0}, Fs0),
- Exp = intersection(A#a.ns, As),
- {B1,_Us,St2} = cexprs(B0, Exp, St1),
- {#c_letrec{anno=A#a.anno,defs=Fs1,body=B1},Exp,A#a.us,St2};
-cexpr(#icase{anno=A,args=Largs,clauses=Lcs,fc=Lfc}, As, St0) ->
- Exp = intersection(A#a.ns, As), %Exports
- {Cargs,St1} = foldr(fun (La, {Cas,Sta}) ->
- {Ca,[],_Us1,Stb} = cexpr(La, As, Sta),
- {[Ca|Cas],Stb}
- end, {[],St0}, Largs),
- {Ccs,St2} = cclauses(Lcs, Exp, St1),
- {Cfc,St3} = cclause(Lfc, [], St2), %Never exports
- {#c_case{anno=A#a.anno,
- arg=core_lib:make_values(Cargs),clauses=Ccs ++ [Cfc]},
- Exp,A#a.us,St3};
-cexpr(#ireceive1{anno=A,clauses=Lcs}, As, St0) ->
- Exp = intersection(A#a.ns, As), %Exports
- {Ccs,St1} = cclauses(Lcs, Exp, St0),
- {#c_receive{anno=A#a.anno,
- clauses=Ccs,
- timeout=#c_atom{val=infinity},action=#c_atom{val=true}},
- Exp,A#a.us,St1};
-cexpr(#ireceive2{anno=A,clauses=Lcs,timeout=Lto,action=Les}, As, St0) ->
- Exp = intersection(A#a.ns, As), %Exports
- {Cto,[],_Us1,St1} = cexpr(Lto, As, St0),
- {Ccs,St2} = cclauses(Lcs, Exp, St1),
- {Ces,_Us2,St3} = cexprs(Les, Exp, St2),
- {#c_receive{anno=A#a.anno,
- clauses=Ccs,timeout=Cto,action=Ces},
- Exp,A#a.us,St3};
-cexpr(#itry{anno=A,args=La,vars=Vs,body=Lb,evars=Evs,handler=Lh}, As, St0) ->
- Exp = intersection(A#a.ns, As), %Exports
- {Ca,_Us1,St1} = cexprs(La, [], St0),
- {Cb,_Us2,St2} = cexprs(Lb, Exp, St1),
- {Ch,_Us3,St3} = cexprs(Lh, Exp, St2),
- {#c_try{anno=A#a.anno,arg=Ca,vars=Vs,body=Cb,evars=Evs,handler=Ch},
- Exp,A#a.us,St3};
-cexpr(#icatch{anno=A,body=Les}, _As, St0) ->
- {Ces,_Us1,St1} = cexprs(Les, [], St0), %Never export!
- {#c_catch{body=Ces},[],A#a.us,St1};
-cexpr(#ifun{anno=A,id=Id,vars=Args,clauses=Lcs,fc=Lfc}, _As, St0) ->
- {Ccs,St1} = cclauses(Lcs, [], St0), %NEVER export!
- {Cfc,St2} = cclause(Lfc, [], St1),
- Anno = A#a.anno,
- {#c_fun{anno=Id++Anno,vars=Args,
- body=#c_case{anno=Anno,
- arg=core_lib:set_anno(core_lib:make_values(Args), Anno),
- clauses=Ccs ++ [Cfc]}},
- [],A#a.us,St2};
-cexpr(#iapply{anno=A,op=Op,args=Args}, _As, St) ->
- {#c_apply{anno=A#a.anno,op=Op,args=Args},[],A#a.us,St};
-cexpr(#icall{anno=A,module=Mod,name=Name,args=Args}, _As, St) ->
- {#c_call{anno=A#a.anno,module=Mod,name=Name,args=Args},[],A#a.us,St};
-cexpr(#iprimop{anno=A,name=Name,args=Args}, _As, St) ->
- {#c_primop{anno=A#a.anno,name=Name,args=Args},[],A#a.us,St};
-cexpr(#iprotect{anno=A,body=Es}, _As, St0) ->
- {Ce,_,St1} = cexprs(Es, [], St0),
- V = #c_var{name='Try'}, %The names are arbitrary
- Vs = [#c_var{name='T'},#c_var{name='R'}],
- {#c_try{anno=A#a.anno,arg=Ce,vars=[V],body=V,
- evars=Vs,handler=#c_atom{val=false}},
- [],A#a.us,St1};
-cexpr(#ibinary{anno=#a{anno=Anno,us=Us},segments=Segs}, _As, St) ->
- {#c_binary{anno=Anno,segments=Segs},[],Us,St};
-cexpr(Lit, _As, St) ->
- true = core_lib:is_simple(Lit), %Sanity check!
- Anno = core_lib:get_anno(Lit),
- Vs = Anno#a.us,
- %%Vs = lit_vars(Lit),
- {core_lib:set_anno(Lit, Anno#a.anno),[],Vs,St}.
-
-%% lit_vars(Literal) -> [Var].
-
-lit_vars(Lit) -> lit_vars(Lit, []).
-
-lit_vars(#c_cons{hd=H,tl=T}, Vs) -> lit_vars(H, lit_vars(T, Vs));
-lit_vars(#c_tuple{es=Es}, Vs) -> lit_list_vars(Es, Vs);
-lit_vars(#c_var{name=V}, Vs) -> add_element(V, Vs);
-lit_vars(_, Vs) -> Vs. %These are atomic
-
-% lit_bin_vars(Segs, Vs) ->
-% foldl(fun (#c_bitstr{val=V,size=S}, Vs0) ->
-% lit_vars(V, lit_vars(S, Vs0))
-% end, Vs, Segs).
-
-lit_list_vars(Ls) -> lit_list_vars(Ls, []).
-
-lit_list_vars(Ls, Vs) ->
- foldl(fun (L, Vs0) -> lit_vars(L, Vs0) end, Vs, Ls).
-
-bitstr_vars(Segs) ->
- bitstr_vars(Segs, []).
-
-bitstr_vars(Segs, Vs) ->
- foldl(fun (#c_bitstr{val=V,size=S}, Vs0) ->
- lit_vars(V, lit_vars(S, Vs0))
- end, Vs, Segs).
-
-get_ianno(Ce) ->
- case core_lib:get_anno(Ce) of
- #a{}=A -> A;
- A when is_list(A) -> #a{anno=A}
- end.
-
-get_lineno_anno(Ce) ->
- case core_lib:get_anno(Ce) of
- #a{anno=A} -> A;
- A when is_list(A) -> A
- end.
-
-
-%%%
-%%% Handling of warnings.
-%%%
-
-format_error(nomatch) -> "pattern cannot possibly match".
-
-add_warning(Line, Term, #core{ws=Ws}=St) when Line >= 0 ->
- St#core{ws=[{Line,?MODULE,Term}|Ws]};
-add_warning(_, _, St) -> St.
-
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel.erl
deleted file mode 100644
index 2d600fabc4..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel.erl
+++ /dev/null
@@ -1,1568 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: v3_kernel.erl,v 1.3 2010/03/04 13:54:20 maria Exp $
-%%
-%% Purpose : Transform Core Erlang to Kernel Erlang
-
-%% Kernel erlang is like Core Erlang with a few significant
-%% differences:
-%%
-%% 1. It is flat! There are no nested calls or sub-blocks.
-%%
-%% 2. All variables are unique in a function. There is no scoping, or
-%% rather the scope is the whole function.
-%%
-%% 3. Pattern matching (in cases and receives) has been compiled.
-%%
-%% 4. The annotations contain variable usages. Seeing we have to work
-%% this out anyway for funs we might as well pass it on for free to
-%% later passes.
-%%
-%% 5. All remote-calls are to statically named m:f/a. Meta-calls are
-%% passed via erlang:apply/3.
-%%
-%% The translation is done in two passes:
-%%
-%% 1. Basic translation, translate variable/function names, flatten
-%% completely, pattern matching compilation.
-%%
-%% 2. Fun-lifting (lambda-lifting), variable usage annotation and
-%% last-call handling.
-%%
-%% All new Kexprs are created in the first pass, they are just
-%% annotated in the second.
-%%
-%% Functions and BIFs
-%%
-%% Functions are "call"ed or "enter"ed if it is a last call, their
-%% return values may be ignored. BIFs are things which are known to
-%% be internal by the compiler and can only be called, their return
-%% values cannot be ignored.
-%%
-%% Letrec's are handled rather naively. All the functions in one
-%% letrec are handled as one block to find the free variables. While
-%% this is not optimal it reflects how letrec's often are used. We
-%% don't have to worry about variable shadowing and nested letrec's as
-%% this is handled in the variable/function name translation. There
-%% is a little bit of trickery to ensure letrec transformations fit
-%% into the scheme of things.
-%%
-%% To ensure unique variable names we use a variable substitution
-%% table and keep the set of all defined variables. The nested
-%% scoping of Core means that we must also nest the substitution
-%% tables, but the defined set must be passed through to match the
-%% flat structure of Kernel and to make sure variables with the same
-%% name from different scopes get different substitutions.
-%%
-%% We also use these substitutions to handle the variable renaming
-%% necessary in pattern matching compilation.
-%%
-%% The pattern matching compilation assumes that the values of
-%% different types don't overlap. This means that as there is no
-%% character type yet in the machine all characters must be converted
-%% to integers!
-
--module(v3_kernel).
-
--export([module/2,format_error/1]).
-
--import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,splitwith/2,
- member/2,reverse/1,reverse/2]).
--import(ordsets, [add_element/2,del_element/2,union/2,union/1,subtract/2]).
-
--include("core_parse.hrl").
--include("v3_kernel.hrl").
-
-%% These are not defined in v3_kernel.hrl.
-get_kanno(Kthing) -> element(2, Kthing).
-set_kanno(Kthing, Anno) -> setelement(2, Kthing, Anno).
-
-%% Internal kernel expressions and help functions.
-%% N.B. the annotation field is ALWAYS the first field!
-
--record(ivalues, {anno=[],args}).
--record(ifun, {anno=[],vars,body}).
--record(iset, {anno=[],vars,arg,body}).
--record(iletrec, {anno=[],defs}).
--record(ialias, {anno=[],vars,pat}).
--record(iclause, {anno=[],sub,pats,guard,body}).
--record(ireceive_accept, {anno=[],arg}).
--record(ireceive_next, {anno=[],arg}).
-
-%% State record for kernel translator.
--record(kern, {func, %Current function
- vcount=0, %Variable counter
- fcount=0, %Fun counter
- ds=[], %Defined variables
- funs=[], %Fun functions
- free=[], %Free variables
- ws=[], %Warnings.
- extinstr=false}). %Generate extended instructions
-
-module(#c_module{anno=A,name=M,exports=Es,attrs=As,defs=Fs}, Options) ->
- ExtInstr = not member(no_new_apply, Options),
- {Kfs,St} = mapfoldl(fun function/2, #kern{extinstr=ExtInstr}, Fs),
- Kes = map(fun (#c_fname{id=N,arity=Ar}) -> {N,Ar} end, Es),
- Kas = map(fun (#c_def{name=#c_atom{val=N},val=V}) ->
- {N,core_lib:literal_value(V)} end, As),
- {ok,#k_mdef{anno=A,name=M#c_atom.val,exports=Kes,attributes=Kas,
- body=Kfs ++ St#kern.funs},St#kern.ws}.
-
-function(#c_def{anno=Af,name=#c_fname{id=F,arity=Arity},val=Body}, St0) ->
- %%ok = io:fwrite("kern: ~p~n", [{F,Arity}]),
- St1 = St0#kern{func={F,Arity},vcount=0,fcount=0,ds=sets:new()},
- {#ifun{anno=Ab,vars=Kvs,body=B0},[],St2} = expr(Body, new_sub(), St1),
- {B1,_,St3} = ubody(B0, return, St2),
- %%B1 = B0, St3 = St2, %Null second pass
- {#k_fdef{anno=#k{us=[],ns=[],a=Af ++ Ab},
- func=F,arity=Arity,vars=Kvs,body=B1},St3}.
-
-%% body(Cexpr, Sub, State) -> {Kexpr,[PreKepxr],State}.
-%% Do the main sequence of a body. A body ends in an atomic value or
-%% values. Must check if vector first so do expr.
-
-body(#c_values{anno=A,es=Ces}, Sub, St0) ->
- %% Do this here even if only in bodies.
- {Kes,Pe,St1} = atomic_list(Ces, Sub, St0),
- %%{Kes,Pe,St1} = expr_list(Ces, Sub, St0),
- {#ivalues{anno=A,args=Kes},Pe,St1};
-body(#ireceive_next{anno=A}, _, St) ->
- {#k_receive_next{anno=A},[],St};
-body(Ce, Sub, St0) ->
- expr(Ce, Sub, St0).
-
-%% guard(Cexpr, Sub, State) -> {Kexpr,State}.
-%% We handle guards almost as bodies. The only special thing we
-%% must do is to make the final Kexpr a #k_test{}.
-%% Also, we wrap the entire guard in a try/catch which is
-%% not strictly needed, but makes sure that every 'bif' instruction
-%% will get a proper failure label.
-
-guard(G0, Sub, St0) ->
- {G1,St1} = wrap_guard(G0, St0),
- {Ge0,Pre,St2} = expr(G1, Sub, St1),
- {Ge,St} = gexpr_test(Ge0, St2),
- {pre_seq(Pre, Ge),St}.
-
-%% Wrap the entire guard in a try/catch if needed.
-
-wrap_guard(#c_try{}=Try, St) -> {Try,St};
-wrap_guard(Core, St0) ->
- {VarName,St} = new_var_name(St0),
- Var = #c_var{name=VarName},
- Try = #c_try{arg=Core,vars=[Var],body=Var,evars=[],handler=#c_atom{val=false}},
- {Try,St}.
-
-%% gexpr_test(Kexpr, State) -> {Kexpr,State}.
-%% Builds the final boolean test from the last Kexpr in a guard test.
-%% Must enter try blocks and isets and find the last Kexpr in them.
-%% This must end in a recognised BEAM test!
-
-gexpr_test(#k_bif{anno=A,op=#k_remote{mod=#k_atom{val=erlang},
- name=#k_atom{val=is_boolean},arity=1}=Op,
- args=Kargs}, St) ->
- %% XXX Remove this clause in R11. For bootstrap purposes, we must
- %% recognize erlang:is_boolean/1 here.
- {#k_test{anno=A,op=Op,args=Kargs},St};
-gexpr_test(#k_bif{anno=A,op=#k_remote{mod=#k_atom{val=erlang},
- name=#k_atom{val=internal_is_record},arity=3}=Op,
- args=Kargs}, St) ->
- {#k_test{anno=A,op=Op,args=Kargs},St};
-gexpr_test(#k_bif{anno=A,op=#k_remote{mod=#k_atom{val=erlang},
- name=#k_atom{val=F},arity=Ar}=Op,
- args=Kargs}=Ke, St) ->
- %% Either convert to test if ok, or add test.
- %% At this stage, erlang:float/1 is not a type test. (It should
- %% have been converted to erlang:is_float/1.)
- case erl_internal:new_type_test(F, Ar) orelse
- erl_internal:comp_op(F, Ar) of
- true -> {#k_test{anno=A,op=Op,args=Kargs},St};
- false -> gexpr_test_add(Ke, St) %Add equality test
- end;
-gexpr_test(#k_try{arg=B0,vars=[#k_var{name=X}],body=#k_var{name=X},
- handler=#k_atom{val=false}}=Try, St0) ->
- {B,St} = gexpr_test(B0, St0),
- %%ok = io:fwrite("~w: ~p~n", [?LINE,{B0,B}]),
- {Try#k_try{arg=B},St};
-gexpr_test(#iset{body=B0}=Iset, St0) ->
- {B1,St1} = gexpr_test(B0, St0),
- {Iset#iset{body=B1},St1};
-gexpr_test(Ke, St) -> gexpr_test_add(Ke, St). %Add equality test
-
-gexpr_test_add(Ke, St0) ->
- Test = #k_remote{mod=#k_atom{val='erlang'},
- name=#k_atom{val='=:='},
- arity=2},
- {Ae,Ap,St1} = force_atomic(Ke, St0),
- {pre_seq(Ap, #k_test{anno=get_kanno(Ke),
- op=Test,args=[Ae,#k_atom{val='true'}]}),St1}.
-
-%% expr(Cexpr, Sub, State) -> {Kexpr,[PreKexpr],State}.
-%% Convert a Core expression, flattening it at the same time.
-
-expr(#c_var{anno=A,name=V}, Sub, St) ->
- {#k_var{anno=A,name=get_vsub(V, Sub)},[],St};
-expr(#c_char{anno=A,val=C}, _Sub, St) ->
- {#k_int{anno=A,val=C},[],St}; %Convert to integers!
-expr(#c_int{anno=A,val=I}, _Sub, St) ->
- {#k_int{anno=A,val=I},[],St};
-expr(#c_float{anno=A,val=F}, _Sub, St) ->
- {#k_float{anno=A,val=F},[],St};
-expr(#c_atom{anno=A,val=At}, _Sub, St) ->
- {#k_atom{anno=A,val=At},[],St};
-expr(#c_string{anno=A,val=S}, _Sub, St) ->
- {#k_string{anno=A,val=S},[],St};
-expr(#c_nil{anno=A}, _Sub, St) ->
- {#k_nil{anno=A},[],St};
-expr(#c_cons{anno=A,hd=Ch,tl=Ct}, Sub, St0) ->
- %% Do cons in two steps, first the expressions left to right, then
- %% any remaining literals right to left.
- {Kh0,Hp0,St1} = expr(Ch, Sub, St0),
- {Kt0,Tp0,St2} = expr(Ct, Sub, St1),
- {Kt1,Tp1,St3} = force_atomic(Kt0, St2),
- {Kh1,Hp1,St4} = force_atomic(Kh0, St3),
- {#k_cons{anno=A,hd=Kh1,tl=Kt1},Hp0 ++ Tp0 ++ Tp1 ++ Hp1,St4};
-expr(#c_tuple{anno=A,es=Ces}, Sub, St0) ->
- {Kes,Ep,St1} = atomic_list(Ces, Sub, St0),
- {#k_tuple{anno=A,es=Kes},Ep,St1};
-expr(#c_binary{anno=A,segments=Cv}, Sub, St0) ->
- case catch atomic_bin(Cv, Sub, St0, 0) of
- {'EXIT',R} -> exit(R);
- bad_element_size ->
- Erl = #c_atom{val=erlang},
- Name = #c_atom{val=error},
- Args = [#c_atom{val=badarg}],
- Fault = #c_call{module=Erl,name=Name,args=Args},
- expr(Fault, Sub, St0);
- {Kv,Ep,St1} ->
- {#k_binary{anno=A,segs=Kv},Ep,St1}
- end;
-expr(#c_fname{anno=A,arity=Ar}=Fname, Sub, St) ->
- %% A local in an expression.
- %% For now, these are wrapped into a fun by reverse
- %% etha-conversion, but really, there should be exactly one
- %% such "lambda function" for each escaping local name,
- %% instead of one for each occurrence as done now.
- Vs = [#c_var{name=list_to_atom("V" ++ integer_to_list(V))} ||
- V <- integers(1, Ar)],
- Fun = #c_fun{anno=A,vars=Vs,body=#c_apply{op=Fname,args=Vs}},
- expr(Fun, Sub, St);
-expr(#c_fun{anno=A,vars=Cvs,body=Cb}, Sub0, St0) ->
- {Kvs,Sub1,St1} = pattern_list(Cvs, Sub0, St0),
- %%ok = io:fwrite("~w: ~p~n", [?LINE,{{Cvs,Sub0,St0},{Kvs,Sub1,St1}}]),
- {Kb,Pb,St2} = body(Cb, Sub1, St1),
- {#ifun{anno=A,vars=Kvs,body=pre_seq(Pb, Kb)},[],St2};
-expr(#c_seq{arg=Ca,body=Cb}, Sub, St0) ->
- {Ka,Pa,St1} = body(Ca, Sub, St0),
- case is_exit_expr(Ka) of
- true -> {Ka,Pa,St1};
- false ->
- {Kb,Pb,St2} = body(Cb, Sub, St1),
- {Kb,Pa ++ [Ka] ++ Pb,St2}
- end;
-expr(#c_let{anno=A,vars=Cvs,arg=Ca,body=Cb}, Sub0, St0) ->
- %%ok = io:fwrite("~w: ~p~n", [?LINE,{Cvs,Sub0,St0}]),
- {Ka,Pa,St1} = body(Ca, Sub0, St0),
- case is_exit_expr(Ka) of
- true -> {Ka,Pa,St1};
- false ->
- {Kps,Sub1,St2} = pattern_list(Cvs, Sub0, St1),
- %%ok = io:fwrite("~w: ~p~n", [?LINE,{Kps,Sub1,St1,St2}]),
- %% Break known multiple values into separate sets.
- Sets = case Ka of
- #ivalues{args=Kas} ->
- foldr2(fun (V, Val, Sb) ->
- [#iset{vars=[V],arg=Val}|Sb] end,
- [], Kps, Kas);
- _Other ->
- [#iset{anno=A,vars=Kps,arg=Ka}]
- end,
- {Kb,Pb,St3} = body(Cb, Sub1, St2),
- {Kb,Pa ++ Sets ++ Pb,St3}
- end;
-expr(#c_letrec{anno=A,defs=Cfs,body=Cb}, Sub0, St0) ->
- %% Make new function names and store substitution.
- {Fs0,{Sub1,St1}} =
- mapfoldl(fun (#c_def{name=#c_fname{id=F,arity=Ar},val=B}, {Sub,St0}) ->
- {N,St1} = new_fun_name(atom_to_list(F)
- ++ "/" ++
- integer_to_list(Ar),
- St0),
- {{N,B},{set_fsub(F, Ar, N, Sub),St1}}
- end, {Sub0,St0}, Cfs),
- %% Run translation on functions and body.
- {Fs1,St2} = mapfoldl(fun ({N,Fd0}, St1) ->
- {Fd1,[],St2} = expr(Fd0, Sub1, St1),
- Fd = set_kanno(Fd1, A),
- {{N,Fd},St2}
- end, St1, Fs0),
- {Kb,Pb,St3} = body(Cb, Sub1, St2),
- {Kb,[#iletrec{anno=A,defs=Fs1}|Pb],St3};
-expr(#c_case{arg=Ca,clauses=Ccs}, Sub, St0) ->
- {Ka,Pa,St1} = body(Ca, Sub, St0), %This is a body!
- {Kvs,Pv,St2} = match_vars(Ka, St1), %Must have variables here!
- {Km,St3} = kmatch(Kvs, Ccs, Sub, St2),
- Match = flatten_seq(build_match(Kvs, Km)),
- {last(Match),Pa ++ Pv ++ first(Match),St3};
-expr(#c_receive{anno=A,clauses=Ccs0,timeout=Ce,action=Ca}, Sub, St0) ->
- {Ke,Pe,St1} = atomic_lit(Ce, Sub, St0), %Force this to be atomic!
- {Rvar,St2} = new_var(St1),
- %% Need to massage accept clauses and add reject clause before matching.
- Ccs1 = map(fun (#c_clause{anno=Banno,body=B0}=C) ->
- B1 = #c_seq{arg=#ireceive_accept{anno=A},body=B0},
- C#c_clause{anno=Banno,body=B1}
- end, Ccs0),
- {Mpat,St3} = new_var_name(St2),
- Rc = #c_clause{anno=[compiler_generated|A],
- pats=[#c_var{name=Mpat}],guard=#c_atom{anno=A,val=true},
- body=#ireceive_next{anno=A}},
- {Km,St4} = kmatch([Rvar], Ccs1 ++ [Rc], Sub, add_var_def(Rvar, St3)),
- {Ka,Pa,St5} = body(Ca, Sub, St4),
- {#k_receive{anno=A,var=Rvar,body=Km,timeout=Ke,action=pre_seq(Pa, Ka)},
- Pe,St5};
-expr(#c_apply{anno=A,op=Cop,args=Cargs}, Sub, St) ->
- c_apply(A, Cop, Cargs, Sub, St);
-expr(#c_call{anno=A,module=M0,name=F0,args=Cargs}, Sub, St0) ->
- {[M1,F1|Kargs],Ap,St1} = atomic_list([M0,F0|Cargs], Sub, St0),
- Ar = length(Cargs),
- case {M1,F1} of
- {#k_atom{val=Ma},#k_atom{val=Fa}} ->
- Call = case is_remote_bif(Ma, Fa, Ar) of
- true ->
- #k_bif{anno=A,
- op=#k_remote{mod=M1,name=F1,arity=Ar},
- args=Kargs};
- false ->
- #k_call{anno=A,
- op=#k_remote{mod=M1,name=F1,arity=Ar},
- args=Kargs}
- end,
- {Call,Ap,St1};
- _Other when St0#kern.extinstr == false -> %Old explicit apply
- Call = #c_call{anno=A,
- module=#c_atom{val=erlang},
- name=#c_atom{val=apply},
- args=[M0,F0,make_list(Cargs)]},
- expr(Call, Sub, St0);
- _Other -> %New instruction in R10.
- Call = #k_call{anno=A,
- op=#k_remote{mod=M1,name=F1,arity=Ar},
- args=Kargs},
- {Call,Ap,St1}
- end;
-expr(#c_primop{anno=A,name=#c_atom{val=match_fail},args=Cargs}, Sub, St0) ->
- %% This special case will disappear.
- {Kargs,Ap,St1} = atomic_list(Cargs, Sub, St0),
- Ar = length(Cargs),
- Call = #k_call{anno=A,op=#k_internal{name=match_fail,arity=Ar},args=Kargs},
- {Call,Ap,St1};
-expr(#c_primop{anno=A,name=#c_atom{val=N},args=Cargs}, Sub, St0) ->
- {Kargs,Ap,St1} = atomic_list(Cargs, Sub, St0),
- Ar = length(Cargs),
- {#k_bif{anno=A,op=#k_internal{name=N,arity=Ar},args=Kargs},Ap,St1};
-expr(#c_try{anno=A,arg=Ca,vars=Cvs,body=Cb,evars=Evs,handler=Ch}, Sub0, St0) ->
- %% The normal try expression. The body and exception handler
- %% variables behave as let variables.
- {Ka,Pa,St1} = body(Ca, Sub0, St0),
- {Kcvs,Sub1,St2} = pattern_list(Cvs, Sub0, St1),
- {Kb,Pb,St3} = body(Cb, Sub1, St2),
- {Kevs,Sub2,St4} = pattern_list(Evs, Sub0, St3),
- {Kh,Ph,St5} = body(Ch, Sub2, St4),
- {#k_try{anno=A,arg=pre_seq(Pa, Ka),
- vars=Kcvs,body=pre_seq(Pb, Kb),
- evars=Kevs,handler=pre_seq(Ph, Kh)},[],St5};
-expr(#c_catch{anno=A,body=Cb}, Sub, St0) ->
- {Kb,Pb,St1} = body(Cb, Sub, St0),
- {#k_catch{anno=A,body=pre_seq(Pb, Kb)},[],St1};
-%% Handle internal expressions.
-expr(#ireceive_accept{anno=A}, _Sub, St) -> {#k_receive_accept{anno=A},[],St}.
-
-%% expr_list([Cexpr], Sub, State) -> {[Kexpr],[PreKexpr],State}.
-
-% expr_list(Ces, Sub, St) ->
-% foldr(fun (Ce, {Kes,Esp,St0}) ->
-% {Ke,Ep,St1} = expr(Ce, Sub, St0),
-% {[Ke|Kes],Ep ++ Esp,St1}
-% end, {[],[],St}, Ces).
-
-%% match_vars(Kexpr, State) -> {[Kvar],[PreKexpr],State}.
-%% Force return from body into a list of variables.
-
-match_vars(#ivalues{args=As}, St) ->
- foldr(fun (Ka, {Vs,Vsp,St0}) ->
- {V,Vp,St1} = force_variable(Ka, St0),
- {[V|Vs],Vp ++ Vsp,St1}
- end, {[],[],St}, As);
-match_vars(Ka, St0) ->
- {V,Vp,St1} = force_variable(Ka, St0),
- {[V],Vp,St1}.
-
-%% c_apply(A, Op, [Carg], Sub, State) -> {Kexpr,[PreKexpr],State}.
-%% Transform application, detect which are guaranteed to be bifs.
-
-c_apply(A, #c_fname{anno=Ra,id=F0,arity=Ar}, Cargs, Sub, St0) ->
- {Kargs,Ap,St1} = atomic_list(Cargs, Sub, St0),
- F1 = get_fsub(F0, Ar, Sub), %Has it been rewritten
- {#k_call{anno=A,op=#k_local{anno=Ra,name=F1,arity=Ar},args=Kargs},
- Ap,St1};
-c_apply(A, Cop, Cargs, Sub, St0) ->
- {Kop,Op,St1} = variable(Cop, Sub, St0),
- {Kargs,Ap,St2} = atomic_list(Cargs, Sub, St1),
- {#k_call{anno=A,op=Kop,args=Kargs},Op ++ Ap,St2}.
-
-flatten_seq(#iset{anno=A,vars=Vs,arg=Arg,body=B}) ->
- [#iset{anno=A,vars=Vs,arg=Arg}|flatten_seq(B)];
-flatten_seq(Ke) -> [Ke].
-
-pre_seq([#iset{anno=A,vars=Vs,arg=Arg,body=B}|Ps], K) ->
- B = undefined, %Assertion.
- #iset{anno=A,vars=Vs,arg=Arg,body=pre_seq(Ps, K)};
-pre_seq([P|Ps], K) ->
- #iset{vars=[],arg=P,body=pre_seq(Ps, K)};
-pre_seq([], K) -> K.
-
-%% atomic_lit(Cexpr, Sub, State) -> {Katomic,[PreKexpr],State}.
-%% Convert a Core expression making sure the result is an atomic
-%% literal.
-
-atomic_lit(Ce, Sub, St0) ->
- {Ke,Kp,St1} = expr(Ce, Sub, St0),
- {Ka,Ap,St2} = force_atomic(Ke, St1),
- {Ka,Kp ++ Ap,St2}.
-
-force_atomic(Ke, St0) ->
- case is_atomic(Ke) of
- true -> {Ke,[],St0};
- false ->
- {V,St1} = new_var(St0),
- {V,[#iset{vars=[V],arg=Ke}],St1}
- end.
-
-% force_atomic_list(Kes, St) ->
-% foldr(fun (Ka, {As,Asp,St0}) ->
-% {A,Ap,St1} = force_atomic(Ka, St0),
-% {[A|As],Ap ++ Asp,St1}
-% end, {[],[],St}, Kes).
-
-atomic_bin([#c_bitstr{anno=A,val=E0,size=S0,unit=U,type=T,flags=Fs}|Es0],
- Sub, St0, B0) ->
- {E,Ap1,St1} = atomic_lit(E0, Sub, St0),
- {S1,Ap2,St2} = atomic_lit(S0, Sub, St1),
- validate_bin_element_size(S1),
- U0 = core_lib:literal_value(U),
- Fs0 = core_lib:literal_value(Fs),
- {B1,Fs1} = aligned(B0, S1, U0, Fs0),
- {Es,Ap3,St3} = atomic_bin(Es0, Sub, St2, B1),
- {#k_bin_seg{anno=A,size=S1,
- unit=U0,
- type=core_lib:literal_value(T),
- flags=Fs1,
- seg=E,next=Es},
- Ap1++Ap2++Ap3,St3};
-atomic_bin([], _Sub, St, _Bits) -> {#k_bin_end{},[],St}.
-
-validate_bin_element_size(#k_var{}) -> ok;
-validate_bin_element_size(#k_int{val=V}) when V >= 0 -> ok;
-validate_bin_element_size(#k_atom{val=all}) -> ok;
-validate_bin_element_size(_) -> throw(bad_element_size).
-
-%% atomic_list([Cexpr], Sub, State) -> {[Kexpr],[PreKexpr],State}.
-
-atomic_list(Ces, Sub, St) ->
- foldr(fun (Ce, {Kes,Esp,St0}) ->
- {Ke,Ep,St1} = atomic_lit(Ce, Sub, St0),
- {[Ke|Kes],Ep ++ Esp,St1}
- end, {[],[],St}, Ces).
-
-%% is_atomic(Kexpr) -> boolean().
-%% Is a Kexpr atomic? Strings are NOT considered atomic!
-
-is_atomic(#k_int{}) -> true;
-is_atomic(#k_float{}) -> true;
-is_atomic(#k_atom{}) -> true;
-%%is_atomic(#k_char{}) -> true; %No characters
-%%is_atomic(#k_string{}) -> true;
-is_atomic(#k_nil{}) -> true;
-is_atomic(#k_var{}) -> true;
-is_atomic(_) -> false.
-
-%% variable(Cexpr, Sub, State) -> {Kvar,[PreKexpr],State}.
-%% Convert a Core expression making sure the result is a variable.
-
-variable(Ce, Sub, St0) ->
- {Ke,Kp,St1} = expr(Ce, Sub, St0),
- {Kv,Vp,St2} = force_variable(Ke, St1),
- {Kv,Kp ++ Vp,St2}.
-
-force_variable(#k_var{}=Ke, St) -> {Ke,[],St};
-force_variable(Ke, St0) ->
- {V,St1} = new_var(St0),
- {V,[#iset{vars=[V],arg=Ke}],St1}.
-
-%% pattern(Cpat, Sub, State) -> {Kpat,Sub,State}.
-%% Convert patterns. Variables shadow so rename variables that are
-%% already defined.
-
-pattern(#c_var{anno=A,name=V}, Sub, St0) ->
- case sets:is_element(V, St0#kern.ds) of
- true ->
- {New,St1} = new_var_name(St0),
- {#k_var{anno=A,name=New},
- set_vsub(V, New, Sub),
- St1#kern{ds=sets:add_element(New, St1#kern.ds)}};
- false ->
- {#k_var{anno=A,name=V},Sub,
- St0#kern{ds=sets:add_element(V, St0#kern.ds)}}
- end;
-pattern(#c_char{anno=A,val=C}, Sub, St) ->
- {#k_int{anno=A,val=C},Sub,St}; %Convert to integers!
-pattern(#c_int{anno=A,val=I}, Sub, St) ->
- {#k_int{anno=A,val=I},Sub,St};
-pattern(#c_float{anno=A,val=F}, Sub, St) ->
- {#k_float{anno=A,val=F},Sub,St};
-pattern(#c_atom{anno=A,val=At}, Sub, St) ->
- {#k_atom{anno=A,val=At},Sub,St};
-pattern(#c_string{val=S}, Sub, St) ->
- L = foldr(fun (C, T) -> #k_cons{hd=#k_int{val=C},tl=T} end,
- #k_nil{}, S),
- {L,Sub,St};
-pattern(#c_nil{anno=A}, Sub, St) ->
- {#k_nil{anno=A},Sub,St};
-pattern(#c_cons{anno=A,hd=Ch,tl=Ct}, Sub0, St0) ->
- {Kh,Sub1,St1} = pattern(Ch, Sub0, St0),
- {Kt,Sub2,St2} = pattern(Ct, Sub1, St1),
- {#k_cons{anno=A,hd=Kh,tl=Kt},Sub2,St2};
-pattern(#c_tuple{anno=A,es=Ces}, Sub0, St0) ->
- {Kes,Sub1,St1} = pattern_list(Ces, Sub0, St0),
- {#k_tuple{anno=A,es=Kes},Sub1,St1};
-pattern(#c_binary{anno=A,segments=Cv}, Sub0, St0) ->
- {Kv,Sub1,St1} = pattern_bin(Cv, Sub0, St0),
- {#k_binary{anno=A,segs=Kv},Sub1,St1};
-pattern(#c_alias{anno=A,var=Cv,pat=Cp}, Sub0, St0) ->
- {Cvs,Cpat} = flatten_alias(Cp),
- {Kvs,Sub1,St1} = pattern_list([Cv|Cvs], Sub0, St0),
- {Kpat,Sub2,St2} = pattern(Cpat, Sub1, St1),
- {#ialias{anno=A,vars=Kvs,pat=Kpat},Sub2,St2}.
-
-flatten_alias(#c_alias{var=V,pat=P}) ->
- {Vs,Pat} = flatten_alias(P),
- {[V|Vs],Pat};
-flatten_alias(Pat) -> {[],Pat}.
-
-pattern_bin(Es, Sub, St) -> pattern_bin(Es, Sub, St, 0).
-
-pattern_bin([#c_bitstr{anno=A,val=E0,size=S0,unit=U,type=T,flags=Fs}|Es0],
- Sub0, St0, B0) ->
- {S1,[],St1} = expr(S0, Sub0, St0),
- U0 = core_lib:literal_value(U),
- Fs0 = core_lib:literal_value(Fs),
- %%ok= io:fwrite("~w: ~p~n", [?LINE,{B0,S1,U0,Fs0}]),
- {B1,Fs1} = aligned(B0, S1, U0, Fs0),
- {E,Sub1,St2} = pattern(E0, Sub0, St1),
- {Es,Sub2,St3} = pattern_bin(Es0, Sub1, St2, B1),
- {#k_bin_seg{anno=A,size=S1,
- unit=U0,
- type=core_lib:literal_value(T),
- flags=Fs1,
- seg=E,next=Es},
- Sub2,St3};
-pattern_bin([], Sub, St, _Bits) -> {#k_bin_end{},Sub,St}.
-
-%% pattern_list([Cexpr], Sub, State) -> {[Kexpr],Sub,State}.
-
-pattern_list(Ces, Sub, St) ->
- foldr(fun (Ce, {Kes,Sub0,St0}) ->
- {Ke,Sub1,St1} = pattern(Ce, Sub0, St0),
- {[Ke|Kes],Sub1,St1}
- end, {[],Sub,St}, Ces).
-
-%% new_sub() -> Subs.
-%% set_vsub(Name, Sub, Subs) -> Subs.
-%% subst_vsub(Name, Sub, Subs) -> Subs.
-%% get_vsub(Name, Subs) -> SubName.
-%% Add/get substitute Sub for Name to VarSub. Use orddict so we know
-%% the format is a list {Name,Sub} pairs. When adding a new
-%% substitute we fold substitute chains so we never have to search
-%% more than once.
-
-new_sub() -> orddict:new().
-
-get_vsub(V, Vsub) ->
- case orddict:find(V, Vsub) of
- {ok,Val} -> Val;
- error -> V
- end.
-
-set_vsub(V, S, Vsub) ->
- orddict:store(V, S, Vsub).
-
-subst_vsub(V, S, Vsub0) ->
- %% Fold chained substitutions.
- Vsub1 = orddict:map(fun (_, V1) when V1 =:= V -> S;
- (_, V1) -> V1
- end, Vsub0),
- orddict:store(V, S, Vsub1).
-
-get_fsub(F, A, Fsub) ->
- case orddict:find({F,A}, Fsub) of
- {ok,Val} -> Val;
- error -> F
- end.
-
-set_fsub(F, A, S, Fsub) ->
- orddict:store({F,A}, S, Fsub).
-
-new_fun_name(St) ->
- new_fun_name("anonymous", St).
-
-%% new_fun_name(Type, State) -> {FunName,State}.
-
-new_fun_name(Type, #kern{func={F,Arity},fcount=C}=St) ->
- Name = "-" ++ atom_to_list(F) ++ "/" ++ integer_to_list(Arity) ++
- "-" ++ Type ++ "-" ++ integer_to_list(C) ++ "-",
- {list_to_atom(Name),St#kern{fcount=C+1}}.
-
-%% new_var_name(State) -> {VarName,State}.
-
-new_var_name(#kern{vcount=C}=St) ->
- {list_to_atom("ker" ++ integer_to_list(C)),St#kern{vcount=C+1}}.
-
-%% new_var(State) -> {#k_var{},State}.
-
-new_var(St0) ->
- {New,St1} = new_var_name(St0),
- {#k_var{name=New},St1}.
-
-%% new_vars(Count, State) -> {[#k_var{}],State}.
-%% Make Count new variables.
-
-new_vars(N, St) -> new_vars(N, St, []).
-
-new_vars(N, St0, Vs) when N > 0 ->
- {V,St1} = new_var(St0),
- new_vars(N-1, St1, [V|Vs]);
-new_vars(0, St, Vs) -> {Vs,St}.
-
-make_vars(Vs) -> [ #k_var{name=V} || V <- Vs ].
-
-add_var_def(V, St) ->
- St#kern{ds=sets:add_element(V#k_var.name, St#kern.ds)}.
-
-%%add_vars_def(Vs, St) ->
-%% Ds = foldl(fun (#k_var{name=V}, Ds) -> add_element(V, Ds) end,
-%% St#kern.ds, Vs),
-%% St#kern{ds=Ds}.
-
-%% is_remote_bif(Mod, Name, Arity) -> true | false.
-%% Test if function is really a BIF.
-
-is_remote_bif(erlang, is_boolean, 1) ->
- %% XXX Remove this clause in R11. For bootstrap purposes, we must
- %% recognize erlang:is_boolean/1 here.
- true;
-is_remote_bif(erlang, internal_is_record, 3) -> true;
-is_remote_bif(erlang, get, 1) -> true;
-is_remote_bif(erlang, N, A) ->
- case erl_internal:guard_bif(N, A) of
- true -> true;
- false ->
- case erl_internal:type_test(N, A) of
- true -> true;
- false ->
- case catch erl_internal:op_type(N, A) of
- arith -> true;
- bool -> true;
- comp -> true;
- _Other -> false %List, send or not an op
- end
- end
- end;
-is_remote_bif(_, _, _) -> false.
-
-%% bif_vals(Name, Arity) -> integer().
-%% bif_vals(Mod, Name, Arity) -> integer().
-%% Determine how many return values a BIF has. Provision for BIFs to
-%% return multiple values. Only used in bodies where a BIF may be
-%% called for effect only.
-
-bif_vals(dsetelement, 3) -> 0;
-bif_vals(_, _) -> 1.
-
-bif_vals(_, _, _) -> 1.
-
-%% foldr2(Fun, Acc, List1, List2) -> Acc.
-%% Fold over two lists.
-
-foldr2(Fun, Acc0, [E1|L1], [E2|L2]) ->
- Acc1 = Fun(E1, E2, Acc0),
- foldr2(Fun, Acc1, L1, L2);
-foldr2(_, Acc, [], []) -> Acc.
-
-%% first([A]) -> [A].
-%% last([A]) -> A.
-
-last([L]) -> L;
-last([_|T]) -> last(T).
-
-first([_]) -> [];
-first([H|T]) -> [H|first(T)].
-
-%% This code implements the algorithm for an optimizing compiler for
-%% pattern matching given "The Implementation of Functional
-%% Programming Languages" by Simon Peyton Jones. The code is much
-%% longer as the meaning of constructors is different from the book.
-%%
-%% In Erlang many constructors can have different values, e.g. 'atom'
-%% or 'integer', whereas in the original algorithm thse would be
-%% different constructors. Our view makes it easier in later passes to
-%% handle indexing over each type.
-%%
-%% Patterns are complicated by having alias variables. The form of a
-%% pattern is Pat | {alias,Pat,[AliasVar]}. This is hidden by access
-%% functions to pattern arguments but the code must be aware of it.
-%%
-%% The compilation proceeds in two steps:
-%%
-%% 1. The patterns in the clauses to converted to lists of kernel
-%% patterns. The Core clause is now hybrid, this is easier to work
-%% with. Remove clauses with trivially false guards, this simplifies
-%% later passes. Add local defined vars and variable subs to each
-%% clause for later use.
-%%
-%% 2. The pattern matching is optimised. Variable substitutions are
-%% added to the VarSub structure and new variables are made visible.
-%% The guard and body are then converted to Kernel form.
-
-%% kmatch([Var], [Clause], Sub, State) -> {Kexpr,[PreExpr],State}.
-
-kmatch(Us, Ccs, Sub, St0) ->
- {Cs,St1} = match_pre(Ccs, Sub, St0), %Convert clauses
- %%Def = kernel_match_error, %The strict case
- %% This should be a kernel expression from the first pass.
- Def = #k_call{anno=[compiler_generated],
- op=#k_remote{mod=#k_atom{val=erlang},
- name=#k_atom{val=exit},
- arity=1},
- args=[#k_atom{val=kernel_match_error}]},
- {Km,St2} = match(Us, Cs, Def, St1), %Do the match.
- {Km,St2}.
-
-%% match_pre([Cclause], Sub, State) -> {[Clause],State}.
-%% Must be careful not to generate new substitutions here now!
-%% Remove clauses with trivially false guards which will never
-%% succeed.
-
-match_pre(Cs, Sub0, St) ->
- foldr(fun (#c_clause{anno=A,pats=Ps,guard=G,body=B}, {Cs0,St0}) ->
- case is_false_guard(G) of
- true -> {Cs0,St0};
- false ->
- {Kps,Sub1,St1} = pattern_list(Ps, Sub0, St0),
- {[#iclause{anno=A,sub=Sub1,pats=Kps,guard=G,body=B}|
- Cs0],St1}
- end
- end, {[],St}, Cs).
-
-%% match([Var], [Clause], Default, State) -> {MatchExpr,State}.
-
-match([U|Us], Cs, Def, St0) ->
- %%ok = io:format("match ~p~n", [Cs]),
- Pcss = partition(Cs),
- foldr(fun (Pcs, {D,St}) -> match_varcon([U|Us], Pcs, D, St) end,
- {Def,St0}, Pcss);
-match([], Cs, Def, St) ->
- match_guard(Cs, Def, St).
-
-%% match_guard([Clause], Default, State) -> {IfExpr,State}.
-%% Build a guard to handle guards. A guard *ALWAYS* fails if no
-%% clause matches, there will be a surrounding 'alt' to catch the
-%% failure. Drop redundant cases, i.e. those after a true guard.
-
-match_guard(Cs0, Def0, St0) ->
- {Cs1,Def1,St1} = match_guard_1(Cs0, Def0, St0),
- {build_alt(build_guard(Cs1), Def1),St1}.
-
-match_guard_1([#iclause{anno=A,sub=Sub,guard=G,body=B}|Cs0], Def0, St0) ->
- case is_true_guard(G) of
- true ->
- %% The true clause body becomes the default.
- {Kb,Pb,St1} = body(B, Sub, St0),
- Line = get_line(A),
- St2 = maybe_add_warning(Cs0, Line, St1),
- St = maybe_add_warning(Def0, Line, St2),
- {[],pre_seq(Pb, Kb),St};
- false ->
- {Kg,St1} = guard(G, Sub, St0),
- {Kb,Pb,St2} = body(B, Sub, St1),
- {Cs1,Def1,St3} = match_guard_1(Cs0, Def0, St2),
- {[#k_guard_clause{guard=Kg,body=pre_seq(Pb, Kb)}|Cs1],
- Def1,St3}
- end;
-match_guard_1([], Def, St) -> {[],Def,St}.
-
-maybe_add_warning([C|_], Line, St) ->
- maybe_add_warning(C, Line, St);
-maybe_add_warning([], _Line, St) -> St;
-maybe_add_warning(fail, _Line, St) -> St;
-maybe_add_warning(Ke, MatchLine, St) ->
- case get_kanno(Ke) of
- [compiler_generated|_] -> St;
- Anno ->
- Line = get_line(Anno),
- Warn = case MatchLine of
- none -> nomatch_shadow;
- _ -> {nomatch_shadow,MatchLine}
- end,
- add_warning(Line, Warn, St)
- end.
-
-get_line([Line|_]) when is_integer(Line) -> Line;
-get_line([_|T]) -> get_line(T);
-get_line([]) -> none.
-
-
-%% is_true_guard(Guard) -> boolean().
-%% is_false_guard(Guard) -> boolean().
-%% Test if a guard is either trivially true/false. This has probably
-%% already been optimised away, but what the heck!
-
-is_true_guard(G) -> guard_value(G) == true.
-is_false_guard(G) -> guard_value(G) == false.
-
-%% guard_value(Guard) -> true | false | unknown.
-
-guard_value(#c_atom{val=true}) -> true;
-guard_value(#c_atom{val=false}) -> false;
-guard_value(#c_call{module=#c_atom{val=erlang},
- name=#c_atom{val='not'},
- args=[A]}) ->
- case guard_value(A) of
- true -> false;
- false -> true;
- unknown -> unknown
- end;
-guard_value(#c_call{module=#c_atom{val=erlang},
- name=#c_atom{val='and'},
- args=[Ca,Cb]}) ->
- case guard_value(Ca) of
- true -> guard_value(Cb);
- false -> false;
- unknown ->
- case guard_value(Cb) of
- false -> false;
- _Other -> unknown
- end
- end;
-guard_value(#c_call{module=#c_atom{val=erlang},
- name=#c_atom{val='or'},
- args=[Ca,Cb]}) ->
- case guard_value(Ca) of
- true -> true;
- false -> guard_value(Cb);
- unknown ->
- case guard_value(Cb) of
- true -> true;
- _Other -> unknown
- end
- end;
-guard_value(#c_try{arg=E,vars=[#c_var{name=X}],body=#c_var{name=X},
- handler=#c_atom{val=false}}) ->
- guard_value(E);
-guard_value(_) -> unknown.
-
-%% partition([Clause]) -> [[Clause]].
-%% Partition a list of clauses into groups which either contain
-%% clauses with a variable first argument, or with a "constructor".
-
-partition([C1|Cs]) ->
- V1 = is_var_clause(C1),
- {More,Rest} = splitwith(fun (C) -> is_var_clause(C) == V1 end, Cs),
- [[C1|More]|partition(Rest)];
-partition([]) -> [].
-
-%% match_varcon([Var], [Clause], Def, [Var], Sub, State) ->
-%% {MatchExpr,State}.
-
-match_varcon(Us, [C|_]=Cs, Def, St) ->
- case is_var_clause(C) of
- true -> match_var(Us, Cs, Def, St);
- false -> match_con(Us, Cs, Def, St)
- end.
-
-%% match_var([Var], [Clause], Def, State) -> {MatchExpr,State}.
-%% Build a call to "select" from a list of clauses all containing a
-%% variable as the first argument. We must rename the variable in
-%% each clause to be the match variable as these clause will share
-%% this variable and may have different names for it. Rename aliases
-%% as well.
-
-match_var([U|Us], Cs0, Def, St) ->
- Cs1 = map(fun (#iclause{sub=Sub0,pats=[Arg|As]}=C) ->
- Vs = [arg_arg(Arg)|arg_alias(Arg)],
- Sub1 = foldl(fun (#k_var{name=V}, Acc) ->
- subst_vsub(V, U#k_var.name, Acc)
- end, Sub0, Vs),
- C#iclause{sub=Sub1,pats=As}
- end, Cs0),
- match(Us, Cs1, Def, St).
-
-%% match_con(Variables, [Clause], Default, State) -> {SelectExpr,State}.
-%% Build call to "select" from a list of clauses all containing a
-%% constructor/constant as first argument. Group the constructors
-%% according to type, the order is really irrelevant but tries to be
-%% smart.
-
-match_con([U|Us], Cs, Def, St0) ->
- %% Extract clauses for different constructors (types).
- %%ok = io:format("match_con ~p~n", [Cs]),
- Ttcs = [ {T,Tcs} || T <- [k_cons,k_tuple,k_atom,k_float,k_int,k_nil,
- k_binary,k_bin_end],
- begin Tcs = select(T, Cs),
- Tcs /= []
- end ] ++ select_bin_con(Cs),
- %%ok = io:format("ttcs = ~p~n", [Ttcs]),
- {Scs,St1} =
- mapfoldl(fun ({T,Tcs}, St) ->
- {[S|_]=Sc,S1} = match_value([U|Us], T, Tcs, fail, St),
- %%ok = io:format("match_con type2 ~p~n", [T]),
- Anno = get_kanno(S),
- {#k_type_clause{anno=Anno,type=T,values=Sc},S1} end,
- St0, Ttcs),
- {build_alt_1st_no_fail(build_select(U, Scs), Def),St1}.
-
-%% select_bin_con([Clause]) -> [{Type,[Clause]}].
-%% Extract clauses for the k_bin_seg constructor. As k_bin_seg
-%% matching can overlap, the k_bin_seg constructors cannot be
-%% reordered, only grouped.
-
-select_bin_con(Cs0) ->
- Cs1 = lists:filter(fun (C) ->
- clause_con(C) == k_bin_seg
- end, Cs0),
- select_bin_con_1(Cs1).
-
-select_bin_con_1([C1|Cs]) ->
- Con = clause_con(C1),
- {More,Rest} = splitwith(fun (C) -> clause_con(C) == Con end, Cs),
- [{Con,[C1|More]}|select_bin_con_1(Rest)];
-select_bin_con_1([]) -> [].
-
-%% select(Con, [Clause]) -> [Clause].
-
-select(T, Cs) -> [ C || C <- Cs, clause_con(C) == T ].
-
-%% match_value([Var], Con, [Clause], Default, State) -> {SelectExpr,State}.
-%% At this point all the clauses have the same constructor, we must
-%% now separate them according to value.
-
-match_value(_, _, [], _, St) -> {[],St};
-match_value(Us, T, Cs0, Def, St0) ->
- Css = group_value(T, Cs0),
- %%ok = io:format("match_value ~p ~p~n", [T, Css]),
- {Css1,St1} = mapfoldl(fun (Cs, St) ->
- match_clause(Us, Cs, Def, St) end,
- St0, Css),
- {Css1,St1}.
- %%{#k_select_val{type=T,var=hd(Us),clauses=Css1},St1}.
-
-%% group_value([Clause]) -> [[Clause]].
-%% Group clauses according to value. Here we know that
-%% 1. Some types are singled valued
-%% 2. The clauses in bin_segs cannot be reordered only grouped
-%% 3. Other types are disjoint and can be reordered
-
-group_value(k_cons, Cs) -> [Cs]; %These are single valued
-group_value(k_nil, Cs) -> [Cs];
-group_value(k_binary, Cs) -> [Cs];
-group_value(k_bin_end, Cs) -> [Cs];
-group_value(k_bin_seg, Cs) ->
- group_bin_seg(Cs);
-group_value(_, Cs) ->
- %% group_value(Cs).
- Cd = foldl(fun (C, Gcs0) -> dict:append(clause_val(C), C, Gcs0) end,
- dict:new(), Cs),
- dict:fold(fun (_, Vcs, Css) -> [Vcs|Css] end, [], Cd).
-
-group_bin_seg([C1|Cs]) ->
- V1 = clause_val(C1),
- {More,Rest} = splitwith(fun (C) -> clause_val(C) == V1 end, Cs),
- [[C1|More]|group_bin_seg(Rest)];
-group_bin_seg([]) -> [].
-
-%% Profiling shows that this quadratic implementation account for a big amount
-%% of the execution time if there are many values.
-% group_value([C|Cs]) ->
-% V = clause_val(C),
-% Same = [ Cv || Cv <- Cs, clause_val(Cv) == V ], %Same value
-% Rest = [ Cv || Cv <- Cs, clause_val(Cv) /= V ], % and all the rest
-% [[C|Same]|group_value(Rest)];
-% group_value([]) -> [].
-
-%% match_clause([Var], [Clause], Default, State) -> {Clause,State}.
-%% At this point all the clauses have the same "value". Build one
-%% select clause for this value and continue matching. Rename
-%% aliases as well.
-
-match_clause([U|Us], [C|_]=Cs0, Def, St0) ->
- Anno = get_kanno(C),
- {Match0,Vs,St1} = get_match(get_con(Cs0), St0),
- Match = sub_size_var(Match0, Cs0),
- {Cs1,St2} = new_clauses(Cs0, U, St1),
- {B,St3} = match(Vs ++ Us, Cs1, Def, St2),
- {#k_val_clause{anno=Anno,val=Match,body=B},St3}.
-
-sub_size_var(#k_bin_seg{size=#k_var{name=Name}=Kvar}=BinSeg, [#iclause{sub=Sub}|_]) ->
- BinSeg#k_bin_seg{size=Kvar#k_var{name=get_vsub(Name, Sub)}};
-sub_size_var(K, _) -> K.
-
-get_con([C|_]) -> arg_arg(clause_arg(C)). %Get the constructor
-
-get_match(#k_cons{}, St0) ->
- {[H,T],St1} = new_vars(2, St0),
- {#k_cons{hd=H,tl=T},[H,T],St1};
-get_match(#k_binary{}, St0) ->
- {[V]=Mes,St1} = new_vars(1, St0),
- {#k_binary{segs=V},Mes,St1};
-get_match(#k_bin_seg{}=Seg, St0) ->
- {[S,N]=Mes,St1} = new_vars(2, St0),
- {Seg#k_bin_seg{seg=S,next=N},Mes,St1};
-get_match(#k_tuple{es=Es}, St0) ->
- {Mes,St1} = new_vars(length(Es), St0),
- {#k_tuple{es=Mes},Mes,St1};
-get_match(M, St) ->
- {M,[],St}.
-
-new_clauses(Cs0, U, St) ->
- Cs1 = map(fun (#iclause{sub=Sub0,pats=[Arg|As]}=C) ->
- Head = case arg_arg(Arg) of
- #k_cons{hd=H,tl=T} -> [H,T|As];
- #k_tuple{es=Es} -> Es ++ As;
- #k_binary{segs=E} -> [E|As];
- #k_bin_seg{seg=S,next=N} ->
- [S,N|As];
- _Other -> As
- end,
- Vs = arg_alias(Arg),
- Sub1 = foldl(fun (#k_var{name=V}, Acc) ->
- subst_vsub(V, U#k_var.name, Acc)
- end, Sub0, Vs),
- C#iclause{sub=Sub1,pats=Head}
- end, Cs0),
- {Cs1,St}.
-
-%% build_guard([GuardClause]) -> GuardExpr.
-
-build_guard([]) -> fail;
-build_guard(Cs) -> #k_guard{clauses=Cs}.
-
-%% build_select(Var, [ConClause]) -> SelectExpr.
-
-build_select(V, [Tc|_]=Tcs) ->
- Anno = get_kanno(Tc),
- #k_select{anno=Anno,var=V,types=Tcs}.
-
-%% build_alt(First, Then) -> AltExpr.
-%% Build an alt, attempt some simple optimisation.
-
-build_alt(fail, Then) -> Then;
-build_alt(First,Then) -> build_alt_1st_no_fail(First, Then).
-
-build_alt_1st_no_fail(First, fail) -> First;
-build_alt_1st_no_fail(First, Then) -> #k_alt{first=First,then=Then}.
-
-%% build_match([MatchVar], MatchExpr) -> Kexpr.
-%% Build a match expr if there is a match.
-
-build_match(Us, #k_alt{}=Km) -> #k_match{vars=Us,body=Km};
-build_match(Us, #k_select{}=Km) -> #k_match{vars=Us,body=Km};
-build_match(Us, #k_guard{}=Km) -> #k_match{vars=Us,body=Km};
-build_match(_, Km) -> Km.
-
-%% clause_arg(Clause) -> FirstArg.
-%% clause_con(Clause) -> Constructor.
-%% clause_val(Clause) -> Value.
-%% is_var_clause(Clause) -> boolean().
-
-clause_arg(#iclause{pats=[Arg|_]}) -> Arg.
-
-clause_con(C) -> arg_con(clause_arg(C)).
-
-clause_val(C) -> arg_val(clause_arg(C)).
-
-is_var_clause(C) -> clause_con(C) == k_var.
-
-%% arg_arg(Arg) -> Arg.
-%% arg_alias(Arg) -> Aliases.
-%% arg_con(Arg) -> Constructor.
-%% arg_val(Arg) -> Value.
-%% These are the basic functions for obtaining fields in an argument.
-
-arg_arg(#ialias{pat=Con}) -> Con;
-arg_arg(Con) -> Con.
-
-arg_alias(#ialias{vars=As}) -> As;
-arg_alias(_Con) -> [].
-
-arg_con(Arg) ->
- case arg_arg(Arg) of
- #k_int{} -> k_int;
- #k_float{} -> k_float;
- #k_atom{} -> k_atom;
- #k_nil{} -> k_nil;
- #k_cons{} -> k_cons;
- #k_tuple{} -> k_tuple;
- #k_binary{} -> k_binary;
- #k_bin_end{} -> k_bin_end;
- #k_bin_seg{} -> k_bin_seg;
- #k_var{} -> k_var
- end.
-
-arg_val(Arg) ->
- case arg_arg(Arg) of
- #k_int{val=I} -> I;
- #k_float{val=F} -> F;
- #k_atom{val=A} -> A;
- #k_nil{} -> 0;
- #k_cons{} -> 2;
- #k_tuple{es=Es} -> length(Es);
- #k_bin_seg{size=S,unit=U,type=T,flags=Fs} ->
- {set_kanno(S, []),U,T,Fs};
- #k_bin_end{} -> 0;
- #k_binary{} -> 0
- end.
-
-%% ubody(Expr, Break, State) -> {Expr,[UsedVar],State}.
-%% Tag the body sequence with its used variables. These bodies
-%% either end with a #k_break{}, or with #k_return{} or an expression
-%% which itself can return, #k_enter{}, #k_match{} ... .
-
-ubody(#iset{vars=[],arg=#iletrec{}=Let,body=B0}, Br, St0) ->
- %% An iletrec{} should never be last.
- St1 = iletrec_funs(Let, St0),
- ubody(B0, Br, St1);
-ubody(#iset{anno=A,vars=Vs,arg=E0,body=B0}, Br, St0) ->
- {E1,Eu,St1} = uexpr(E0, {break,Vs}, St0),
- {B1,Bu,St2} = ubody(B0, Br, St1),
- Ns = lit_list_vars(Vs),
- Used = union(Eu, subtract(Bu, Ns)), %Used external vars
- {#k_seq{anno=#k{us=Used,ns=Ns,a=A},arg=E1,body=B1},Used,St2};
-ubody(#ivalues{anno=A,args=As}, return, St) ->
- Au = lit_list_vars(As),
- {#k_return{anno=#k{us=Au,ns=[],a=A},args=As},Au,St};
-ubody(#ivalues{anno=A,args=As}, {break,_Vbs}, St) ->
- Au = lit_list_vars(As),
- {#k_break{anno=#k{us=Au,ns=[],a=A},args=As},Au,St};
-ubody(E, return, St0) ->
- %% Enterable expressions need no trailing return.
- case is_enter_expr(E) of
- true -> uexpr(E, return, St0);
- false ->
- {Ea,Pa,St1} = force_atomic(E, St0),
- ubody(pre_seq(Pa, #ivalues{args=[Ea]}), return, St1)
- end;
-ubody(E, {break,Rs}, St0) ->
- %%ok = io:fwrite("ubody ~w:~p~n", [?LINE,{E,Br}]),
- %% Exiting expressions need no trailing break.
- case is_exit_expr(E) of
- true -> uexpr(E, return, St0);
- false ->
- {Ea,Pa,St1} = force_atomic(E, St0),
- ubody(pre_seq(Pa, #ivalues{args=[Ea]}), {break,Rs}, St1)
- end.
-
-iletrec_funs(#iletrec{defs=Fs}, St0) ->
- %% Use union of all free variables.
- %% First just work out free variables for all functions.
- Free = foldl(fun ({_,#ifun{vars=Vs,body=Fb0}}, Free0) ->
- {_,Fbu,_} = ubody(Fb0, return, St0),
- Ns = lit_list_vars(Vs),
- Free1 = subtract(Fbu, Ns),
- union(Free1, Free0)
- end, [], Fs),
- FreeVs = make_vars(Free),
- %% Add this free info to State.
- St1 = foldl(fun ({N,#ifun{vars=Vs}}, Lst) ->
- store_free(N, length(Vs), FreeVs, Lst)
- end, St0, Fs),
- %% Now regenerate local functions to use free variable information.
- St2 = foldl(fun ({N,#ifun{anno=Fa,vars=Vs,body=Fb0}}, Lst0) ->
- {Fb1,_,Lst1} = ubody(Fb0, return, Lst0),
- Arity = length(Vs) + length(FreeVs),
- Fun = #k_fdef{anno=#k{us=[],ns=[],a=Fa},
- func=N,arity=Arity,
- vars=Vs ++ FreeVs,body=Fb1},
- Lst1#kern{funs=[Fun|Lst1#kern.funs]}
- end, St1, Fs),
- St2.
-
-%% is_exit_expr(Kexpr) -> boolean().
-%% Test whether Kexpr always exits and never returns.
-
-is_exit_expr(#k_call{op=#k_remote{mod=erlang,name=throw,arity=1}}) -> true;
-is_exit_expr(#k_call{op=#k_remote{mod=erlang,name=exit,arity=1}}) -> true;
-is_exit_expr(#k_call{op=#k_remote{mod=erlang,name=error,arity=1}}) -> true;
-is_exit_expr(#k_call{op=#k_remote{mod=erlang,name=error,arity=2}}) -> true;
-is_exit_expr(#k_call{op=#k_remote{mod=erlang,name=fault,arity=1}}) -> true;
-is_exit_expr(#k_call{op=#k_remote{mod=erlang,name=fault,arity=2}}) -> true;
-is_exit_expr(#k_call{op=#k_internal{name=match_fail,arity=1}}) -> true;
-is_exit_expr(#k_bif{op=#k_internal{name=rethrow,arity=2}}) -> true;
-is_exit_expr(#k_receive_next{}) -> true;
-is_exit_expr(_) -> false.
-
-%% is_enter_expr(Kexpr) -> boolean().
-%% Test whether Kexpr is "enterable", i.e. can handle return from
-%% within itself without extra #k_return{}.
-
-is_enter_expr(#k_call{}) -> true;
-is_enter_expr(#k_match{}) -> true;
-is_enter_expr(#k_receive{}) -> true;
-is_enter_expr(#k_receive_next{}) -> true;
-%%is_enter_expr(#k_try{}) -> true; %Soon
-is_enter_expr(_) -> false.
-
-%% uguard(Expr, State) -> {Expr,[UsedVar],State}.
-%% Tag the guard sequence with its used variables.
-
-uguard(#k_try{anno=A,arg=B0,vars=[#k_var{name=X}],body=#k_var{name=X},
- handler=#k_atom{val=false}}=Try, St0) ->
- {B1,Bu,St1} = uguard(B0, St0),
- {Try#k_try{anno=#k{us=Bu,ns=[],a=A},arg=B1},Bu,St1};
-uguard(T, St) ->
- %%ok = io:fwrite("~w: ~p~n", [?LINE,T]),
- uguard_test(T, St).
-
-%% uguard_test(Expr, State) -> {Test,[UsedVar],State}.
-%% At this stage tests are just expressions which don't return any
-%% values.
-
-uguard_test(T, St) -> uguard_expr(T, [], St).
-
-uguard_expr(#iset{anno=A,vars=Vs,arg=E0,body=B0}, Rs, St0) ->
- Ns = lit_list_vars(Vs),
- {E1,Eu,St1} = uguard_expr(E0, Vs, St0),
- {B1,Bu,St2} = uguard_expr(B0, Rs, St1),
- Used = union(Eu, subtract(Bu, Ns)),
- {#k_seq{anno=#k{us=Used,ns=Ns,a=A},arg=E1,body=B1},Used,St2};
-uguard_expr(#k_try{anno=A,arg=B0,vars=[#k_var{name=X}],body=#k_var{name=X},
- handler=#k_atom{val=false}}=Try, Rs, St0) ->
- {B1,Bu,St1} = uguard_expr(B0, Rs, St0),
- {Try#k_try{anno=#k{us=Bu,ns=lit_list_vars(Rs),a=A},arg=B1,ret=Rs},
- Bu,St1};
-uguard_expr(#k_test{anno=A,op=Op,args=As}=Test, Rs, St) ->
- [] = Rs, %Sanity check
- Used = union(op_vars(Op), lit_list_vars(As)),
- {Test#k_test{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A}},
- Used,St};
-uguard_expr(#k_bif{anno=A,op=Op,args=As}=Bif, Rs, St) ->
- Used = union(op_vars(Op), lit_list_vars(As)),
- {Bif#k_bif{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A},ret=Rs},
- Used,St};
-uguard_expr(#ivalues{anno=A,args=As}, Rs, St) ->
- Sets = foldr2(fun (V, Arg, Rhs) ->
- #iset{anno=A,vars=[V],arg=Arg,body=Rhs}
- end, #k_atom{val=true}, Rs, As),
- uguard_expr(Sets, [], St);
-uguard_expr(#k_match{anno=A,vars=Vs,body=B0}, Rs, St0) ->
- %% Experimental support for andalso/orelse in guards.
- Br = case Rs of
- [] -> return;
- _ -> {break,Rs}
- end,
- {B1,Bu,St1} = umatch(B0, Br, St0),
- {#k_match{anno=#k{us=Bu,ns=lit_list_vars(Rs),a=A},
- vars=Vs,body=B1,ret=Rs},Bu,St1};
-uguard_expr(Lit, Rs, St) ->
- %% Transform literals to puts here.
- Used = lit_vars(Lit),
- {#k_put{anno=#k{us=Used,ns=lit_list_vars(Rs),a=get_kanno(Lit)},
- arg=Lit,ret=Rs},Used,St}.
-
-%% uexpr(Expr, Break, State) -> {Expr,[UsedVar],State}.
-%% Tag an expression with its used variables.
-%% Break = return | {break,[RetVar]}.
-
-uexpr(#k_call{anno=A,op=#k_local{name=F,arity=Ar}=Op,args=As0}=Call, Br, St) ->
- Free = get_free(F, Ar, St),
- As1 = As0 ++ Free, %Add free variables LAST!
- Used = lit_list_vars(As1),
- {case Br of
- {break,Rs} ->
- Call#k_call{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A},
- op=Op#k_local{arity=Ar + length(Free)},
- args=As1,ret=Rs};
- return ->
- #k_enter{anno=#k{us=Used,ns=[],a=A},
- op=Op#k_local{arity=Ar + length(Free)},
- args=As1}
- end,Used,St};
-uexpr(#k_call{anno=A,op=Op,args=As}=Call, {break,Rs}, St) ->
- Used = union(op_vars(Op), lit_list_vars(As)),
- {Call#k_call{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A},ret=Rs},
- Used,St};
-uexpr(#k_call{anno=A,op=Op,args=As}, return, St) ->
- Used = union(op_vars(Op), lit_list_vars(As)),
- {#k_enter{anno=#k{us=Used,ns=[],a=A},op=Op,args=As},
- Used,St};
-uexpr(#k_bif{anno=A,op=Op,args=As}=Bif, {break,Rs}, St0) ->
- Used = union(op_vars(Op), lit_list_vars(As)),
- {Brs,St1} = bif_returns(Op, Rs, St0),
- {Bif#k_bif{anno=#k{us=Used,ns=lit_list_vars(Brs),a=A},ret=Brs},
- Used,St1};
-uexpr(#k_match{anno=A,vars=Vs,body=B0}, Br, St0) ->
- Rs = break_rets(Br),
- {B1,Bu,St1} = umatch(B0, Br, St0),
- {#k_match{anno=#k{us=Bu,ns=lit_list_vars(Rs),a=A},
- vars=Vs,body=B1,ret=Rs},Bu,St1};
-uexpr(#k_receive{anno=A,var=V,body=B0,timeout=T,action=A0}, Br, St0) ->
- Rs = break_rets(Br),
- Tu = lit_vars(T), %Timeout is atomic
- {B1,Bu,St1} = umatch(B0, Br, St0),
- {A1,Au,St2} = ubody(A0, Br, St1),
- Used = del_element(V#k_var.name, union(Bu, union(Tu, Au))),
- {#k_receive{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A},
- var=V,body=B1,timeout=T,action=A1,ret=Rs},
- Used,St2};
-uexpr(#k_receive_accept{anno=A}, _, St) ->
- {#k_receive_accept{anno=#k{us=[],ns=[],a=A}},[],St};
-uexpr(#k_receive_next{anno=A}, _, St) ->
- {#k_receive_next{anno=#k{us=[],ns=[],a=A}},[],St};
-uexpr(#k_try{anno=A,arg=A0,vars=Vs,body=B0,evars=Evs,handler=H0},
- {break,Rs0}, St0) ->
- {Avs,St1} = new_vars(length(Vs), St0), %Need dummy names here
- {A1,Au,St2} = ubody(A0, {break,Avs}, St1), %Must break to clean up here!
- {B1,Bu,St3} = ubody(B0, {break,Rs0}, St2),
- {H1,Hu,St4} = ubody(H0, {break,Rs0}, St3),
- %% Guarantee ONE return variable.
- NumNew = if
- Rs0 =:= [] -> 1;
- true -> 0
- end,
- {Ns,St5} = new_vars(NumNew, St4),
- Rs1 = Rs0 ++ Ns,
- Used = union([Au,subtract(Bu, lit_list_vars(Vs)),
- subtract(Hu, lit_list_vars(Evs))]),
- {#k_try{anno=#k{us=Used,ns=lit_list_vars(Rs1),a=A},
- arg=A1,vars=Vs,body=B1,evars=Evs,handler=H1,ret=Rs1},
- Used,St5};
-uexpr(#k_catch{anno=A,body=B0}, {break,Rs0}, St0) ->
- {Rb,St1} = new_var(St0),
- {B1,Bu,St2} = ubody(B0, {break,[Rb]}, St1),
- %% Guarantee ONE return variable.
- {Ns,St3} = new_vars(1 - length(Rs0), St2),
- Rs1 = Rs0 ++ Ns,
- {#k_catch{anno=#k{us=Bu,ns=lit_list_vars(Rs1),a=A},body=B1,ret=Rs1},Bu,St3};
-uexpr(#ifun{anno=A,vars=Vs,body=B0}=IFun, {break,Rs}, St0) ->
- {B1,Bu,St1} = ubody(B0, return, St0), %Return out of new function
- Ns = lit_list_vars(Vs),
- Free = subtract(Bu, Ns), %Free variables in fun
- Fvs = make_vars(Free),
- Arity = length(Vs) + length(Free),
- {{Index,Uniq,Fname}, St3} =
- case lists:keysearch(id, 1, A) of
- {value,{id,Id}} ->
- {Id, St1};
- false ->
- %% No id annotation. Must invent one.
- I = St1#kern.fcount,
- U = erlang:hash(IFun, (1 bsl 27)-1),
- {N, St2} = new_fun_name(St1),
- {{I,U,N}, St2}
- end,
- Fun = #k_fdef{anno=#k{us=[],ns=[],a=A},func=Fname,arity=Arity,
- vars=Vs ++ Fvs,body=B1},
- {#k_bif{anno=#k{us=Free,ns=lit_list_vars(Rs),a=A},
- op=#k_internal{name=make_fun,arity=length(Free)+3},
- args=[#k_atom{val=Fname},#k_int{val=Arity},
- #k_int{val=Index},#k_int{val=Uniq}|Fvs],
- ret=Rs},
-% {#k_call{anno=#k{us=Free,ns=lit_list_vars(Rs),a=A},
-% op=#k_internal{name=make_fun,arity=length(Free)+3},
-% args=[#k_atom{val=Fname},#k_int{val=Arity},
-% #k_int{val=Index},#k_int{val=Uniq}|Fvs],
-% ret=Rs},
- Free,St3#kern{funs=[Fun|St3#kern.funs]}};
-uexpr(Lit, {break,Rs}, St) ->
- %% Transform literals to puts here.
- %%ok = io:fwrite("uexpr ~w:~p~n", [?LINE,Lit]),
- Used = lit_vars(Lit),
- {#k_put{anno=#k{us=Used,ns=lit_list_vars(Rs),a=get_kanno(Lit)},
- arg=Lit,ret=Rs},Used,St}.
-
-%% get_free(Name, Arity, State) -> [Free].
-%% store_free(Name, Arity, [Free], State) -> State.
-
-get_free(F, A, St) ->
- case orddict:find({F,A}, St#kern.free) of
- {ok,Val} -> Val;
- error -> []
- end.
-
-store_free(F, A, Free, St) ->
- St#kern{free=orddict:store({F,A}, Free, St#kern.free)}.
-
-break_rets({break,Rs}) -> Rs;
-break_rets(return) -> [].
-
-%% bif_returns(Op, [Ret], State) -> {[Ret],State}.
-
-bif_returns(#k_remote{mod=M,name=N,arity=Ar}, Rs, St0) ->
- %%ok = io:fwrite("uexpr ~w:~p~n", [?LINE,{M,N,Ar,Rs}]),
- {Ns,St1} = new_vars(bif_vals(M, N, Ar) - length(Rs), St0),
- {Rs ++ Ns,St1};
-bif_returns(#k_internal{name=N,arity=Ar}, Rs, St0) ->
- %%ok = io:fwrite("uexpr ~w:~p~n", [?LINE,{N,Ar,Rs}]),
- {Ns,St1} = new_vars(bif_vals(N, Ar) - length(Rs), St0),
- {Rs ++ Ns,St1}.
-
-%% umatch(Match, Break, State) -> {Match,[UsedVar],State}.
-%% Tag a match expression with its used variables.
-
-umatch(#k_alt{anno=A,first=F0,then=T0}, Br, St0) ->
- {F1,Fu,St1} = umatch(F0, Br, St0),
- {T1,Tu,St2} = umatch(T0, Br, St1),
- Used = union(Fu, Tu),
- {#k_alt{anno=#k{us=Used,ns=[],a=A},first=F1,then=T1},
- Used,St2};
-umatch(#k_select{anno=A,var=V,types=Ts0}, Br, St0) ->
- {Ts1,Tus,St1} = umatch_list(Ts0, Br, St0),
- Used = add_element(V#k_var.name, Tus),
- {#k_select{anno=#k{us=Used,ns=[],a=A},var=V,types=Ts1},Used,St1};
-umatch(#k_type_clause{anno=A,type=T,values=Vs0}, Br, St0) ->
- {Vs1,Vus,St1} = umatch_list(Vs0, Br, St0),
- {#k_type_clause{anno=#k{us=Vus,ns=[],a=A},type=T,values=Vs1},Vus,St1};
-umatch(#k_val_clause{anno=A,val=P,body=B0}, Br, St0) ->
- {U0,Ps} = pat_vars(P),
- {B1,Bu,St1} = umatch(B0, Br, St0),
- Used = union(U0, subtract(Bu, Ps)),
- {#k_val_clause{anno=#k{us=Used,ns=[],a=A},val=P,body=B1},
- Used,St1};
-umatch(#k_guard{anno=A,clauses=Gs0}, Br, St0) ->
- {Gs1,Gus,St1} = umatch_list(Gs0, Br, St0),
- {#k_guard{anno=#k{us=Gus,ns=[],a=A},clauses=Gs1},Gus,St1};
-umatch(#k_guard_clause{anno=A,guard=G0,body=B0}, Br, St0) ->
- %%ok = io:fwrite("~w: ~p~n", [?LINE,G0]),
- {G1,Gu,St1} = uguard(G0, St0),
- %%ok = io:fwrite("~w: ~p~n", [?LINE,G1]),
- {B1,Bu,St2} = umatch(B0, Br, St1),
- Used = union(Gu, Bu),
- {#k_guard_clause{anno=#k{us=Used,ns=[],a=A},guard=G1,body=B1},Used,St2};
-umatch(B0, Br, St0) -> ubody(B0, Br, St0).
-
-umatch_list(Ms0, Br, St) ->
- foldr(fun (M0, {Ms1,Us,Sta}) ->
- {M1,Mu,Stb} = umatch(M0, Br, Sta),
- {[M1|Ms1],union(Mu, Us),Stb}
- end, {[],[],St}, Ms0).
-
-%% op_vars(Op) -> [VarName].
-
-op_vars(#k_local{}) -> [];
-op_vars(#k_remote{mod=Mod,name=Name}) ->
- ordsets:from_list([V || #k_var{name=V} <- [Mod,Name]]);
-op_vars(#k_internal{}) -> [];
-op_vars(Atomic) -> lit_vars(Atomic).
-
-%% lit_vars(Literal) -> [VarName].
-%% Return the variables in a literal.
-
-lit_vars(#k_var{name=N}) -> [N];
-lit_vars(#k_int{}) -> [];
-lit_vars(#k_float{}) -> [];
-lit_vars(#k_atom{}) -> [];
-%%lit_vars(#k_char{}) -> [];
-lit_vars(#k_string{}) -> [];
-lit_vars(#k_nil{}) -> [];
-lit_vars(#k_cons{hd=H,tl=T}) ->
- union(lit_vars(H), lit_vars(T));
-lit_vars(#k_binary{segs=V}) -> lit_vars(V);
-lit_vars(#k_bin_end{}) -> [];
-lit_vars(#k_bin_seg{size=Size,seg=S,next=N}) ->
- union(lit_vars(Size), union(lit_vars(S), lit_vars(N)));
-lit_vars(#k_tuple{es=Es}) ->
- lit_list_vars(Es).
-
-lit_list_vars(Ps) ->
- foldl(fun (P, Vs) -> union(lit_vars(P), Vs) end, [], Ps).
-
-%% pat_vars(Pattern) -> {[UsedVarName],[NewVarName]}.
-%% Return variables in a pattern. All variables are new variables
-%% except those in the size field of binary segments.
-
-pat_vars(#k_var{name=N}) -> {[],[N]};
-%%pat_vars(#k_char{}) -> {[],[]};
-pat_vars(#k_int{}) -> {[],[]};
-pat_vars(#k_float{}) -> {[],[]};
-pat_vars(#k_atom{}) -> {[],[]};
-pat_vars(#k_string{}) -> {[],[]};
-pat_vars(#k_nil{}) -> {[],[]};
-pat_vars(#k_cons{hd=H,tl=T}) ->
- pat_list_vars([H,T]);
-pat_vars(#k_binary{segs=V}) ->
- pat_vars(V);
-pat_vars(#k_bin_seg{size=Size,seg=S,next=N}) ->
- {U1,New} = pat_list_vars([S,N]),
- {[],U2} = pat_vars(Size),
- {union(U1, U2),New};
-pat_vars(#k_bin_end{}) -> {[],[]};
-pat_vars(#k_tuple{es=Es}) ->
- pat_list_vars(Es).
-
-pat_list_vars(Ps) ->
- foldl(fun (P, {Used0,New0}) ->
- {Used,New} = pat_vars(P),
- {union(Used0, Used),union(New0, New)} end,
- {[],[]}, Ps).
-
-%% aligned(Bits, Size, Unit, Flags) -> {Size,Flags}
-%% Add 'aligned' to the flags if the current field is aligned.
-%% Number of bits correct modulo 8.
-
-aligned(B, S, U, Fs) when B rem 8 =:= 0 ->
- {incr_bits(B, S, U),[aligned|Fs]};
-aligned(B, S, U, Fs) ->
- {incr_bits(B, S, U),Fs}.
-
-incr_bits(B, #k_int{val=S}, U) when integer(B) -> B + S*U;
-incr_bits(_, #k_atom{val=all}, _) -> 0; %Always aligned
-incr_bits(B, _, 8) -> B;
-incr_bits(_, _, _) -> unknown.
-
-make_list(Es) ->
- foldr(fun (E, Acc) -> #c_cons{hd=E,tl=Acc} end, #c_nil{}, Es).
-
-%% List of integers in interval [N,M]. Empty list if N > M.
-
-integers(N, M) when N =< M ->
- [N|integers(N + 1, M)];
-integers(_, _) -> [].
-
-%%%
-%%% Handling of warnings.
-%%%
-
-format_error({nomatch_shadow,Line}) ->
- M = io_lib:format("this clause cannot match because a previous clause at line ~p "
- "always matches", [Line]),
- lists:flatten(M);
-format_error(nomatch_shadow) ->
- "this clause cannot match because a previous clause always matches".
-
-add_warning(none, Term, #kern{ws=Ws}=St) ->
- St#kern{ws=[{?MODULE,Term}|Ws]};
-add_warning(Line, Term, #kern{ws=Ws}=St) when Line >= 0 ->
- St#kern{ws=[{Line,?MODULE,Term}|Ws]};
-add_warning(_, _, St) -> St.
-
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel.hrl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel.hrl
deleted file mode 100644
index 822a9e34e1..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel.hrl
+++ /dev/null
@@ -1,77 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: v3_kernel.hrl,v 1.1 2008/12/17 09:53:43 mikpe Exp $
-%%
-
-%% Purpose : Kernel Erlang as records.
-
-%% It would be nice to incorporate some generic functions as well but
-%% this could make including this file difficult.
-%% N.B. the annotation field is ALWAYS the first field!
-
-%% Kernel annotation record.
--record(k, {us, %Used variables
- ns, %New variables
- a}). %Core annotation
-
-%% Literals
-%% NO CHARACTERS YET.
-%%-record(k_char, {anno=[],val}).
--record(k_int, {anno=[],val}).
--record(k_float, {anno=[],val}).
--record(k_atom, {anno=[],val}).
--record(k_string, {anno=[],val}).
--record(k_nil, {anno=[]}).
-
--record(k_tuple, {anno=[],es}).
--record(k_cons, {anno=[],hd,tl}).
--record(k_binary, {anno=[],segs}).
--record(k_bin_seg, {anno=[],size,unit,type,flags,seg,next}).
--record(k_bin_end, {anno=[]}).
--record(k_var, {anno=[],name}).
-
--record(k_local, {anno=[],name,arity}).
--record(k_remote, {anno=[],mod,name,arity}).
--record(k_internal, {anno=[],name,arity}).
-
--record(k_mdef, {anno=[],name,exports,attributes,body}).
--record(k_fdef, {anno=[],func,arity,vars,body}).
-
--record(k_seq, {anno=[],arg,body}).
--record(k_put, {anno=[],arg,ret=[]}).
--record(k_bif, {anno=[],op,args,ret=[]}).
--record(k_test, {anno=[],op,args}).
--record(k_call, {anno=[],op,args,ret=[]}).
--record(k_enter, {anno=[],op,args}).
--record(k_receive, {anno=[],var,body,timeout,action,ret=[]}).
--record(k_receive_accept, {anno=[]}).
--record(k_receive_next, {anno=[]}).
--record(k_try, {anno=[],arg,vars,body,evars,handler,ret=[]}).
--record(k_catch, {anno=[],body,ret=[]}).
-
--record(k_match, {anno=[],vars,body,ret=[]}).
--record(k_alt, {anno=[],first,then}).
--record(k_select, {anno=[],var,types}).
--record(k_type_clause, {anno=[],type,values}).
--record(k_val_clause, {anno=[],val,body}).
--record(k_guard, {anno=[],clauses}).
--record(k_guard_clause, {anno=[],guard,body}).
-
--record(k_break, {anno=[],args=[]}).
--record(k_return, {anno=[],args=[]}).
-
-%%k_get_anno(Thing) -> element(2, Thing).
-%%k_set_anno(Thing, Anno) -> setelement(2, Thing, Anno).
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel_pp.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel_pp.erl
deleted file mode 100644
index 92ff173834..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel_pp.erl
+++ /dev/null
@@ -1,444 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: v3_kernel_pp.erl,v 1.1 2008/12/17 09:53:43 mikpe Exp $
-%%
-%% Purpose : Kernel Erlang (naive) prettyprinter
-
--module(v3_kernel_pp).
-
--include("v3_kernel.hrl").
-
--export([format/1]).
-
-%% These are "internal" structures in sys_kernel which are here for
-%% debugging purposes.
--record(iset, {anno=[],vars,arg,body}).
--record(ifun, {anno=[],vars,body}).
-
-%% ====================================================================== %%
-%% format(Node) -> Text
-%% Node = coreErlang()
-%% Text = string() | [Text]
-%%
-%% Prettyprint-formats (naively) an abstract Core Erlang syntax
-%% tree.
-
--record(ctxt, {indent = 0,
- item_indent = 2,
- body_indent = 2,
- tab_width = 8}).
-
-canno(Cthing) -> element(2, Cthing).
-
-format(Node) -> format(Node, #ctxt{}).
-
-format(Node, Ctxt) ->
- case canno(Node) of
- [] ->
- format_1(Node, Ctxt);
- List ->
- format_anno(List, Ctxt, fun (Ctxt1) -> format_1(Node, Ctxt1) end)
- end.
-
-format_anno(Anno, Ctxt, ObjFun) ->
- Ctxt1 = ctxt_bump_indent(Ctxt, 2),
- ["( ",
- ObjFun(Ctxt1),
- nl_indent(Ctxt1),
- "-| ",io_lib:write(Anno),
- " )"].
-
-%% format_1(Kexpr, Context) -> string().
-
-format_1(#k_atom{val=A}, _Ctxt) -> core_atom(A);
-%%format_1(#k_char{val=C}, _Ctxt) -> io_lib:write_char(C);
-format_1(#k_float{val=F}, _Ctxt) -> float_to_list(F);
-format_1(#k_int{val=I}, _Ctxt) -> integer_to_list(I);
-format_1(#k_nil{}, _Ctxt) -> "[]";
-format_1(#k_string{val=S}, _Ctxt) -> io_lib:write_string(S);
-format_1(#k_var{name=V}, _Ctxt) ->
- if atom(V) ->
- case atom_to_list(V) of
- [$_|Cs] -> "_X" ++ Cs;
- [C|Cs] when C >= $A, C =< $Z -> [C|Cs];
- Cs -> [$_|Cs]
- end;
- integer(V) -> [$_|integer_to_list(V)]
- end;
-format_1(#k_cons{hd=H,tl=T}, Ctxt) ->
- Txt = ["["|format(H, ctxt_bump_indent(Ctxt, 1))],
- [Txt|format_list_tail(T, ctxt_bump_indent(Ctxt, width(Txt, Ctxt)))];
-format_1(#k_tuple{es=Es}, Ctxt) ->
- [${,
- format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
- $}
- ];
-format_1(#k_binary{segs=S}, Ctxt) ->
- ["#<",format(S, ctxt_bump_indent(Ctxt, 2)),">#"];
-format_1(#k_bin_seg{}=S, Ctxt) ->
- [format_bin_seg_1(S, Ctxt),
- format_bin_seg(S#k_bin_seg.next, ctxt_bump_indent(Ctxt, 2))];
-format_1(#k_bin_end{}, _Ctxt) -> "#<>#";
-format_1(#k_local{name=N,arity=A}, Ctxt) ->
- "local " ++ format_fa_pair({N,A}, Ctxt);
-format_1(#k_remote{mod=M,name=N,arity=A}, _Ctxt) ->
- %% This is for our internal translator.
- io_lib:format("remote ~s:~s/~w", [format(M),format(N),A]);
-format_1(#k_internal{name=N,arity=A}, Ctxt) ->
- "internal " ++ format_fa_pair({N,A}, Ctxt);
-format_1(#k_seq{arg=A,body=B}, Ctxt) ->
- Ctxt1 = ctxt_bump_indent(Ctxt, 2),
- ["do",
- nl_indent(Ctxt1),
- format(A, Ctxt1),
- nl_indent(Ctxt),
- "then",
- nl_indent(Ctxt)
- | format(B, Ctxt)
- ];
-format_1(#k_match{vars=Vs,body=Bs,ret=Rs}, Ctxt) ->
- Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent),
- ["match ",
- format_hseq(Vs, ",", ctxt_bump_indent(Ctxt, 6), fun format/2),
- nl_indent(Ctxt1),
- format(Bs, Ctxt1),
- nl_indent(Ctxt),
- "end",
- format_ret(Rs, Ctxt1)
- ];
-format_1(#k_alt{first=O,then=T}, Ctxt) ->
- Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent),
- ["alt",
- nl_indent(Ctxt1),
- format(O, Ctxt1),
- nl_indent(Ctxt1),
- format(T, Ctxt1)];
-format_1(#k_select{var=V,types=Cs}, Ctxt) ->
- Ctxt1 = ctxt_bump_indent(Ctxt, 2),
- ["select ",
- format(V, Ctxt),
- nl_indent(Ctxt1),
- format_vseq(Cs, "", "", Ctxt1, fun format/2)
- ];
-format_1(#k_type_clause{type=T,values=Cs}, Ctxt) ->
- Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
- ["type ",
- io_lib:write(T),
- nl_indent(Ctxt1),
- format_vseq(Cs, "", "", Ctxt1, fun format/2)
- ];
-format_1(#k_val_clause{val=Val,body=B}, Ctxt) ->
- Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
- [format(Val, Ctxt),
- " ->",
- nl_indent(Ctxt1)
- | format(B, Ctxt1)
- ];
-format_1(#k_guard{clauses=Gs}, Ctxt) ->
- Ctxt1 = ctxt_bump_indent(Ctxt, 5),
- ["when ",
- nl_indent(Ctxt1),
- format_vseq(Gs, "", "", Ctxt1, fun format/2)];
-format_1(#k_guard_clause{guard=G,body=B}, Ctxt) ->
- Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
- [format(G, Ctxt),
- nl_indent(Ctxt),
- "->",
- nl_indent(Ctxt1)
- | format(B, Ctxt1)
- ];
-format_1(#k_call{op=Op,args=As,ret=Rs}, Ctxt) ->
- Txt = ["call (",format(Op, ctxt_bump_indent(Ctxt, 6)),$)],
- Ctxt1 = ctxt_bump_indent(Ctxt, 2),
- [Txt,format_args(As, Ctxt1),
- format_ret(Rs, Ctxt1)
- ];
-format_1(#k_enter{op=Op,args=As}, Ctxt) ->
- Txt = ["enter (",format(Op, ctxt_bump_indent(Ctxt, 7)),$)],
- Ctxt1 = ctxt_bump_indent(Ctxt, 2),
- [Txt,format_args(As, Ctxt1)];
-format_1(#k_bif{op=Op,args=As,ret=Rs}, Ctxt) ->
- Txt = ["bif (",format(Op, ctxt_bump_indent(Ctxt, 5)),$)],
- Ctxt1 = ctxt_bump_indent(Ctxt, 2),
- [Txt,format_args(As, Ctxt1),
- format_ret(Rs, Ctxt1)
- ];
-format_1(#k_test{op=Op,args=As}, Ctxt) ->
- Txt = ["test (",format(Op, ctxt_bump_indent(Ctxt, 6)),$)],
- Ctxt1 = ctxt_bump_indent(Ctxt, 2),
- [Txt,format_args(As, Ctxt1)];
-format_1(#k_put{arg=A,ret=Rs}, Ctxt) ->
- [format(A, Ctxt),
- format_ret(Rs, ctxt_bump_indent(Ctxt, 1))
- ];
-format_1(#k_try{arg=A,vars=Vs,body=B,evars=Evs,handler=H,ret=Rs}, Ctxt) ->
- Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
- ["try",
- nl_indent(Ctxt1),
- format(A, Ctxt1),
- nl_indent(Ctxt),
- "of ",
- format_hseq(Vs, ", ", ctxt_bump_indent(Ctxt, 3), fun format/2),
- nl_indent(Ctxt1),
- format(B, Ctxt1),
- nl_indent(Ctxt),
- "catch ",
- format_hseq(Evs, ", ", ctxt_bump_indent(Ctxt, 6), fun format/2),
- nl_indent(Ctxt1),
- format(H, Ctxt1),
- nl_indent(Ctxt),
- "end",
- format_ret(Rs, Ctxt1)
- ];
-format_1(#k_catch{body=B,ret=Rs}, Ctxt) ->
- Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
- ["catch",
- nl_indent(Ctxt1),
- format(B, Ctxt1),
- nl_indent(Ctxt),
- "end",
- format_ret(Rs, Ctxt1)
- ];
-format_1(#k_receive{var=V,body=B,timeout=T,action=A,ret=Rs}, Ctxt) ->
- Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent),
- ["receive ",
- format(V, Ctxt),
- nl_indent(Ctxt1),
- format(B, Ctxt1),
- nl_indent(Ctxt),
- "after ",
- format(T, ctxt_bump_indent(Ctxt, 6)),
- " ->",
- nl_indent(Ctxt1),
- format(A, Ctxt1),
- nl_indent(Ctxt),
- "end",
- format_ret(Rs, Ctxt1)
- ];
-format_1(#k_receive_accept{}, _Ctxt) -> "receive_accept";
-format_1(#k_receive_next{}, _Ctxt) -> "receive_next";
-format_1(#k_break{args=As}, Ctxt) ->
- ["<",
- format_hseq(As, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
- ">"
- ];
-format_1(#k_return{args=As}, Ctxt) ->
- ["<<",
- format_hseq(As, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
- ">>"
- ];
-format_1(#k_fdef{func=F,arity=A,vars=Vs,body=B}, Ctxt) ->
- Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
- ["fdef ",
- format_fa_pair({F,A}, ctxt_bump_indent(Ctxt, 5)),
- format_args(Vs, ctxt_bump_indent(Ctxt, 14)),
- " =",
- nl_indent(Ctxt1),
- format(B, Ctxt1)
- ];
-format_1(#k_mdef{name=N,exports=Es,attributes=As,body=B}, Ctxt) ->
- ["module ",
- format(#k_atom{val=N}, ctxt_bump_indent(Ctxt, 7)),
- nl_indent(Ctxt),
- "export [",
- format_vseq(Es,
- "", ",",
- ctxt_bump_indent(Ctxt, 8),
- fun format_fa_pair/2),
- "]",
- nl_indent(Ctxt),
- "attributes [",
- format_vseq(As,
- "", ",",
- ctxt_bump_indent(Ctxt, 12),
- fun format_attribute/2),
- "]",
- nl_indent(Ctxt),
- format_vseq(B,
- "", "",
- Ctxt,
- fun format/2),
- nl_indent(Ctxt)
- | "end"
- ];
-%% Internal sys_kernel structures.
-format_1(#iset{vars=Vs,arg=A,body=B}, Ctxt) ->
- Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
- ["set <",
- format_hseq(Vs, ", ", ctxt_bump_indent(Ctxt, 5), fun format/2),
- "> =",
- nl_indent(Ctxt1),
- format(A, Ctxt1),
- nl_indent(Ctxt),
- "in "
- | format(B, ctxt_bump_indent(Ctxt, 2))
- ];
-format_1(#ifun{vars=Vs,body=B}, Ctxt) ->
- Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
- ["fun ",
- format_args(Vs, ctxt_bump_indent(Ctxt, 4)),
- " ->",
- nl_indent(Ctxt1)
- | format(B, Ctxt1)
- ];
-format_1(Type, _Ctxt) ->
- ["** Unsupported type: ",
- io_lib:write(Type)
- | " **"
- ].
-
-%% format_ret([RetVar], Context) -> Txt.
-%% Format the return vars of kexpr.
-
-format_ret(Rs, Ctxt) ->
- [" >> ",
- "<",
- format_hseq(Rs, ",", ctxt_bump_indent(Ctxt, 5), fun format/2),
- ">"].
-
-%% format_args([Arg], Context) -> Txt.
-%% Format arguments.
-
-format_args(As, Ctxt) ->
- [$(,format_hseq(As, ", ", ctxt_bump_indent(Ctxt, 1), fun format/2),$)].
-
-%% format_hseq([Thing], Separator, Context, Fun) -> Txt.
-%% Format a sequence horizontally.
-
-format_hseq([H], _Sep, Ctxt, Fun) ->
- Fun(H, Ctxt);
-format_hseq([H|T], Sep, Ctxt, Fun) ->
- Txt = [Fun(H, Ctxt)|Sep],
- Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt, Ctxt)),
- [Txt|format_hseq(T, Sep, Ctxt1, Fun)];
-format_hseq([], _, _, _) -> "".
-
-%% format_vseq([Thing], LinePrefix, LineSuffix, Context, Fun) -> Txt.
-%% Format a sequence vertically.
-
-format_vseq([H], _Pre, _Suf, Ctxt, Fun) ->
- Fun(H, Ctxt);
-format_vseq([H|T], Pre, Suf, Ctxt, Fun) ->
- [Fun(H, Ctxt),Suf,nl_indent(Ctxt),Pre|
- format_vseq(T, Pre, Suf, Ctxt, Fun)];
-format_vseq([], _, _, _, _) -> "".
-
-format_fa_pair({F,A}, _Ctxt) -> [core_atom(F),$/,integer_to_list(A)].
-
-%% format_attribute({Name,Val}, Context) -> Txt.
-
-format_attribute({Name,Val}, Ctxt) when list(Val) ->
- Txt = format(#k_atom{val=Name}, Ctxt),
- Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt,Ctxt)+4),
- [Txt," = ",
- $[,format_vseq(Val, "", ",", Ctxt1,
- fun (A, _C) -> io_lib:write(A) end),$]
- ];
-format_attribute({Name,Val}, Ctxt) ->
- Txt = format(#k_atom{val=Name}, Ctxt),
- [Txt," = ",io_lib:write(Val)].
-
-format_list_tail(#k_nil{anno=[]}, _Ctxt) -> "]";
-format_list_tail(#k_cons{anno=[],hd=H,tl=T}, Ctxt) ->
- Txt = [$,|format(H, Ctxt)],
- Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt, Ctxt)),
- [Txt|format_list_tail(T, Ctxt1)];
-format_list_tail(Tail, Ctxt) ->
- ["|",format(Tail, ctxt_bump_indent(Ctxt, 1)), "]"].
-
-format_bin_seg(#k_bin_end{anno=[]}, _Ctxt) -> "";
-format_bin_seg(#k_bin_seg{anno=[],next=N}=Seg, Ctxt) ->
- Txt = [$,|format_bin_seg_1(Seg, Ctxt)],
- [Txt|format_bin_seg(N, ctxt_bump_indent(Ctxt, width(Txt, Ctxt)))];
-format_bin_seg(Seg, Ctxt) ->
- ["|",format(Seg, ctxt_bump_indent(Ctxt, 2))].
-
-format_bin_seg_1(#k_bin_seg{size=S,unit=U,type=T,flags=Fs,seg=Seg}, Ctxt) ->
- [format(Seg, Ctxt),
- ":",format(S, Ctxt),"*",io_lib:write(U),
- ":",io_lib:write(T),
- lists:map(fun (F) -> [$-,io_lib:write(F)] end, Fs)
- ].
-
-% format_bin_elements(#k_binary_cons{hd=H,tl=T,size=S,info=I}, Ctxt) ->
-% A = canno(T),
-% Fe = fun (Eh, Es, Ei, Ct) ->
-% [format(Eh, Ct),":",format(Es, Ct),"/",io_lib:write(Ei)]
-% end,
-% case T of
-% #k_zero_binary{} when A == [] ->
-% Fe(H, S, I, Ctxt);
-% #k_binary_cons{} when A == [] ->
-% Txt = [Fe(H, S, I, Ctxt)|","],
-% Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt, Ctxt)),
-% [Txt|format_bin_elements(T, Ctxt1)];
-% _ ->
-% Txt = [Fe(H, S, I, Ctxt)|"|"],
-% [Txt|format(T, ctxt_bump_indent(Ctxt, width(Txt, Ctxt)))]
-% end.
-
-indent(Ctxt) -> indent(Ctxt#ctxt.indent, Ctxt).
-
-indent(N, _Ctxt) when N =< 0 -> "";
-indent(N, Ctxt) ->
- T = Ctxt#ctxt.tab_width,
- string:chars($\t, N div T, string:chars($\s, N rem T)).
-
-nl_indent(Ctxt) -> [$\n|indent(Ctxt)].
-
-
-unindent(T, Ctxt) ->
- unindent(T, Ctxt#ctxt.indent, Ctxt, []).
-
-unindent(T, N, _Ctxt, C) when N =< 0 ->
- [T|C];
-unindent([$\s|T], N, Ctxt, C) ->
- unindent(T, N - 1, Ctxt, C);
-unindent([$\t|T], N, Ctxt, C) ->
- Tab = Ctxt#ctxt.tab_width,
- if N >= Tab ->
- unindent(T, N - Tab, Ctxt, C);
- true ->
- unindent([string:chars($\s, Tab - N)|T], 0, Ctxt, C)
- end;
-unindent([L|T], N, Ctxt, C) when list(L) ->
- unindent(L, N, Ctxt, [T|C]);
-unindent([H|T], _N, _Ctxt, C) ->
- [H|[T|C]];
-unindent([], N, Ctxt, [H|T]) ->
- unindent(H, N, Ctxt, T);
-unindent([], _, _, []) -> [].
-
-
-width(Txt, Ctxt) ->
- width(Txt, 0, Ctxt, []).
-
-width([$\t|T], A, Ctxt, C) ->
- width(T, A + Ctxt#ctxt.tab_width, Ctxt, C);
-width([$\n|T], _A, Ctxt, C) ->
- width(unindent([T|C], Ctxt), Ctxt);
-width([H|T], A, Ctxt, C) when list(H) ->
- width(H, A, Ctxt, [T|C]);
-width([_|T], A, Ctxt, C) ->
- width(T, A + 1, Ctxt, C);
-width([], A, Ctxt, [H|T]) ->
- width(H, A, Ctxt, T);
-width([], A, _, []) -> A.
-
-ctxt_bump_indent(Ctxt, Dx) ->
- Ctxt#ctxt{indent=Ctxt#ctxt.indent + Dx}.
-
-core_atom(A) -> io_lib:write_string(atom_to_list(A), $').
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_life.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_life.erl
deleted file mode 100644
index ff210d83f5..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_life.erl
+++ /dev/null
@@ -1,448 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: v3_life.erl,v 1.2 2010/03/04 13:54:20 maria Exp $
-%%
-%% Purpose : Convert annotated kernel expressions to annotated beam format.
-
-%% This module creates beam format annotated with variable lifetime
-%% information. Each thing is given an index and for each variable we
-%% store the first and last index for its occurrence. The variable
-%% database, VDB, attached to each thing is only relevant internally
-%% for that thing.
-%%
-%% For nested things like matches the numbering continues locally and
-%% the VDB for that thing refers to the variable usage within that
-%% thing. Variables which live through a such a thing are internally
-%% given a very large last index. Internally the indexes continue
-%% after the index of that thing. This creates no problems as the
-%% internal variable info never escapes and externally we only see
-%% variable which are alive both before or after.
-%%
-%% This means that variables never "escape" from a thing and the only
-%% way to get values from a thing is to "return" them, with 'break' or
-%% 'return'. Externally these values become the return values of the
-%% thing. This is no real limitation as most nested things have
-%% multiple threads so working out a common best variable usage is
-%% difficult.
-
--module(v3_life).
-
--export([module/2]).
-
--export([vdb_find/2]).
-
--import(lists, [map/2,foldl/3]).
--import(ordsets, [add_element/2,intersection/2,union/2,union/1]).
-
--include("v3_kernel.hrl").
--include("v3_life.hrl").
-
-%% These are not defined in v3_kernel.hrl.
-get_kanno(Kthing) -> element(2, Kthing).
-%%set_kanno(Kthing, Anno) -> setelement(2, Kthing, Anno).
-
-module(#k_mdef{name=M,exports=Es,attributes=As,body=Fs0}, Opts) ->
- put(?MODULE, Opts),
- Fs1 = map(fun function/1, Fs0),
- erase(?MODULE),
- {ok,{M,Es,As,Fs1}}.
-
-%% function(Kfunc) -> Func.
-
-function(#k_fdef{func=F,arity=Ar,vars=Vs,body=Kb}) ->
- %%ok = io:fwrite("life ~w: ~p~n", [?LINE,{F,Ar}]),
- As = var_list(Vs),
- Vdb0 = foldl(fun ({var,N}, Vdb) -> new_var(N, 0, Vdb) end, [], As),
- %% Force a top-level match!
- B0 = case Kb of
- #k_match{} -> Kb;
- _ ->
- Ka = get_kanno(Kb),
- #k_match{anno=#k{us=Ka#k.us,ns=[],a=Ka#k.a},
- vars=Vs,body=Kb,ret=[]}
- end,
- {B1,_,Vdb1} = body(B0, 1, Vdb0),
- {function,F,Ar,As,B1,Vdb1}.
-
-%% body(Kbody, I, Vdb) -> {[Expr],MaxI,Vdb}.
-%% Handle a body, need special cases for transforming match_fails.
-%% We KNOW that they only occur last in a body.
-
-body(#k_seq{arg=#k_put{anno=Pa,arg=Arg,ret=[R]},
- body=#k_enter{anno=Ea,op=#k_internal{name=match_fail,arity=1},
- args=[R]}},
- I, Vdb0) ->
- Vdb1 = use_vars(Pa#k.us, I, Vdb0), %All used here
- {[match_fail(Arg, I, Pa#k.a ++ Ea#k.a)],I,Vdb1};
-body(#k_enter{anno=Ea,op=#k_internal{name=match_fail,arity=1},args=[Arg]},
- I, Vdb0) ->
- Vdb1 = use_vars(Ea#k.us, I, Vdb0),
- {[match_fail(Arg, I, Ea#k.a)],I,Vdb1};
-body(#k_seq{arg=Ke,body=Kb}, I, Vdb0) ->
- %%ok = io:fwrite("life ~w:~p~n", [?LINE,{Ke,I,Vdb0}]),
- A = get_kanno(Ke),
- Vdb1 = use_vars(A#k.us, I, new_vars(A#k.ns, I, Vdb0)),
- {Es,MaxI,Vdb2} = body(Kb, I+1, Vdb1),
- E = expr(Ke, I, Vdb2),
- {[E|Es],MaxI,Vdb2};
-body(Ke, I, Vdb0) ->
- %%ok = io:fwrite("life ~w:~p~n", [?LINE,{Ke,I,Vdb0}]),
- A = get_kanno(Ke),
- Vdb1 = use_vars(A#k.us, I, new_vars(A#k.ns, I, Vdb0)),
- E = expr(Ke, I, Vdb1),
- {[E],I,Vdb1}.
-
-%% guard(Kguard, I, Vdb) -> Guard.
-
-guard(#k_try{anno=A,arg=Ts,vars=[#k_var{name=X}],body=#k_var{name=X},
- handler=#k_atom{val=false},ret=Rs}, I, Vdb) ->
- %% Lock variables that are alive before try and used afterwards.
- %% Don't lock variables that are only used inside the try expression.
- Pdb0 = vdb_sub(I, I+1, Vdb),
- {T,MaxI,Pdb1} = guard_body(Ts, I+1, Pdb0),
- Pdb2 = use_vars(A#k.ns, MaxI+1, Pdb1), %Save "return" values
- #l{ke={protected,T,var_list(Rs)},i=I,a=A#k.a,vdb=Pdb2};
-guard(#k_seq{}=G, I, Vdb0) ->
- {Es,_,Vdb1} = guard_body(G, I, Vdb0),
- #l{ke={block,Es},i=I,vdb=Vdb1,a=[]};
-guard(G, I, Vdb) -> guard_expr(G, I, Vdb).
-
-%% guard_body(Kbody, I, Vdb) -> {[Expr],MaxI,Vdb}.
-
-guard_body(#k_seq{arg=Ke,body=Kb}, I, Vdb0) ->
- A = get_kanno(Ke),
- Vdb1 = use_vars(A#k.us, I, new_vars(A#k.ns, I, Vdb0)),
- {Es,MaxI,Vdb2} = guard_body(Kb, I+1, Vdb1),
- E = guard_expr(Ke, I, Vdb2),
- {[E|Es],MaxI,Vdb2};
-guard_body(Ke, I, Vdb0) ->
- A = get_kanno(Ke),
- Vdb1 = use_vars(A#k.us, I, new_vars(A#k.ns, I, Vdb0)),
- E = guard_expr(Ke, I, Vdb1),
- {[E],I,Vdb1}.
-
-%% guard_expr(Call, I, Vdb) -> Expr
-
-guard_expr(#k_test{anno=A,op=Op,args=As}, I, _Vdb) ->
- #l{ke={test,test_op(Op),atomic_list(As)},i=I,a=A#k.a};
-guard_expr(#k_bif{anno=A,op=Op,args=As,ret=Rs}, I, _Vdb) ->
- #l{ke={bif,bif_op(Op),atomic_list(As),var_list(Rs)},i=I,a=A#k.a};
-guard_expr(#k_put{anno=A,arg=Arg,ret=Rs}, I, _Vdb) ->
- #l{ke={set,var_list(Rs),literal(Arg)},i=I,a=A#k.a};
-guard_expr(#k_match{anno=A,body=Kb,ret=Rs}, I, Vdb) ->
- %% Experimental support for andalso/orelse in guards.
- %% Work out imported variables which need to be locked.
- Mdb = vdb_sub(I, I+1, Vdb),
- M = match(Kb, A#k.us, I+1, Mdb),
- #l{ke={match,M,var_list(Rs)},i=I,vdb=use_vars(A#k.us, I+1, Mdb),a=A#k.a};
-guard_expr(G, I, Vdb) -> guard(G, I, Vdb).
-
-%% expr(Kexpr, I, Vdb) -> Expr.
-
-expr(#k_call{anno=A,op=Op,args=As,ret=Rs}, I, _Vdb) ->
- #l{ke={call,call_op(Op),atomic_list(As),var_list(Rs)},i=I,a=A#k.a};
-expr(#k_enter{anno=A,op=Op,args=As}, I, _Vdb) ->
- #l{ke={enter,call_op(Op),atomic_list(As)},i=I,a=A#k.a};
-expr(#k_bif{anno=A,op=Op,args=As,ret=Rs}, I, _Vdb) ->
- Bif = k_bif(A, Op, As, Rs),
- #l{ke=Bif,i=I,a=A#k.a};
-expr(#k_match{anno=A,body=Kb,ret=Rs}, I, Vdb) ->
- %% Work out imported variables which need to be locked.
- Mdb = vdb_sub(I, I+1, Vdb),
- M = match(Kb, A#k.us, I+1, Mdb),
- #l{ke={match,M,var_list(Rs)},i=I,vdb=use_vars(A#k.us, I+1, Mdb),a=A#k.a};
-expr(#k_try{anno=A,arg=Ka,vars=Vs,body=Kb,evars=Evs,handler=Kh,ret=Rs}, I, Vdb) ->
- %% Lock variables that are alive before the catch and used afterwards.
- %% Don't lock variables that are only used inside the try.
- Tdb0 = vdb_sub(I, I+1, Vdb),
- %% This is the tricky bit. Lock variables in Arg that are used in
- %% the body and handler. Add try tag 'variable'.
- Ab = get_kanno(Kb),
- Ah = get_kanno(Kh),
- Tdb1 = use_vars(Ab#k.us, I+3, use_vars(Ah#k.us, I+3, Tdb0)),
- Tdb2 = vdb_sub(I, I+2, Tdb1),
- Vnames = fun (Kvar) -> Kvar#k_var.name end, %Get the variable names
- {Aes,_,Adb} = body(Ka, I+2, add_var({catch_tag,I+1}, I+1, 1000000, Tdb2)),
- {Bes,_,Bdb} = body(Kb, I+4, new_vars(map(Vnames, Vs), I+3, Tdb2)),
- {Hes,_,Hdb} = body(Kh, I+4, new_vars(map(Vnames, Evs), I+3, Tdb2)),
- #l{ke={'try',#l{ke={block,Aes},i=I+1,vdb=Adb,a=[]},
- var_list(Vs),#l{ke={block,Bes},i=I+3,vdb=Bdb,a=[]},
- var_list(Evs),#l{ke={block,Hes},i=I+3,vdb=Hdb,a=[]},
- var_list(Rs)},
- i=I,vdb=Tdb1,a=A#k.a};
-expr(#k_catch{anno=A,body=Kb,ret=[R]}, I, Vdb) ->
- %% Lock variables that are alive before the catch and used afterwards.
- %% Don't lock variables that are only used inside the catch.
- %% Add catch tag 'variable'.
- Cdb0 = vdb_sub(I, I+1, Vdb),
- {Es,_,Cdb1} = body(Kb, I+1, add_var({catch_tag,I}, I, 1000000, Cdb0)),
- #l{ke={'catch',Es,variable(R)},i=I,vdb=Cdb1,a=A#k.a};
-expr(#k_receive{anno=A,var=V,body=Kb,timeout=T,action=Ka,ret=Rs}, I, Vdb) ->
- %% Work out imported variables which need to be locked.
- Rdb = vdb_sub(I, I+1, Vdb),
- M = match(Kb, add_element(V#k_var.name, A#k.us), I+1,
- new_var(V#k_var.name, I, Rdb)),
- {Tes,_,Adb} = body(Ka, I+1, Rdb),
- #l{ke={receive_loop,atomic_lit(T),variable(V),M,
- #l{ke=Tes,i=I+1,vdb=Adb,a=[]},var_list(Rs)},
- i=I,vdb=use_vars(A#k.us, I+1, Vdb),a=A#k.a};
-expr(#k_receive_accept{anno=A}, I, _Vdb) ->
- #l{ke=receive_accept,i=I,a=A#k.a};
-expr(#k_receive_next{anno=A}, I, _Vdb) ->
- #l{ke=receive_next,i=I,a=A#k.a};
-expr(#k_put{anno=A,arg=Arg,ret=Rs}, I, _Vdb) ->
- #l{ke={set,var_list(Rs),literal(Arg)},i=I,a=A#k.a};
-expr(#k_break{anno=A,args=As}, I, _Vdb) ->
- #l{ke={break,atomic_list(As)},i=I,a=A#k.a};
-expr(#k_return{anno=A,args=As}, I, _Vdb) ->
- #l{ke={return,atomic_list(As)},i=I,a=A#k.a}.
-
-%% call_op(Op) -> Op.
-%% bif_op(Op) -> Op.
-%% test_op(Op) -> Op.
-%% Do any necessary name translations here to munge into beam format.
-
-call_op(#k_local{name=N}) -> N;
-call_op(#k_remote{mod=M,name=N}) -> {remote,atomic_lit(M),atomic_lit(N)};
-call_op(Other) -> variable(Other).
-
-bif_op(#k_remote{mod=#k_atom{val=erlang},name=#k_atom{val=N}}) -> N;
-bif_op(#k_internal{name=N}) -> N.
-
-test_op(#k_remote{mod=#k_atom{val=erlang},name=#k_atom{val=N}}) -> N.
-
-%% k_bif(Anno, Op, [Arg], [Ret]) -> Expr.
-%% Build bifs, do special handling of internal some calls.
-
-k_bif(_A, #k_internal{name=dsetelement,arity=3}, As, []) ->
- {bif,dsetelement,atomic_list(As),[]};
-k_bif(_A, #k_internal{name=make_fun},
- [#k_atom{val=Fun},#k_int{val=Arity},
- #k_int{val=Index},#k_int{val=Uniq}|Free],
- Rs) ->
- {bif,{make_fun,Fun,Arity,Index,Uniq},var_list(Free),var_list(Rs)};
-k_bif(_A, Op, As, Rs) ->
- %% The general case.
- {bif,bif_op(Op),atomic_list(As),var_list(Rs)}.
-
-%% match(Kexpr, [LockVar], I, Vdb) -> Expr.
-%% Convert match tree to old format.
-
-match(#k_alt{anno=A,first=Kf,then=Kt}, Ls, I, Vdb0) ->
- Vdb1 = use_vars(union(A#k.us, Ls), I, Vdb0),
- F = match(Kf, Ls, I+1, Vdb1),
- T = match(Kt, Ls, I+1, Vdb1),
- #l{ke={alt,F,T},i=I,vdb=Vdb1,a=A#k.a};
-match(#k_select{anno=A,var=V,types=Kts}, Ls0, I, Vdb0) ->
- Ls1 = add_element(V#k_var.name, Ls0),
- Vdb1 = use_vars(union(A#k.us, Ls1), I, Vdb0),
- Ts = map(fun (Tc) -> type_clause(Tc, Ls1, I+1, Vdb1) end, Kts),
- #l{ke={select,literal(V),Ts},i=I,vdb=Vdb1,a=A#k.a};
-match(#k_guard{anno=A,clauses=Kcs}, Ls, I, Vdb0) ->
- Vdb1 = use_vars(union(A#k.us, Ls), I, Vdb0),
- Cs = map(fun (G) -> guard_clause(G, Ls, I+1, Vdb1) end, Kcs),
- #l{ke={guard,Cs},i=I,vdb=Vdb1,a=A#k.a};
-match(Other, Ls, I, Vdb0) ->
- Vdb1 = use_vars(Ls, I, Vdb0),
- {B,_,Vdb2} = body(Other, I+1, Vdb1),
- #l{ke={block,B},i=I,vdb=Vdb2,a=[]}.
-
-type_clause(#k_type_clause{anno=A,type=T,values=Kvs}, Ls, I, Vdb0) ->
- %%ok = io:format("life ~w: ~p~n", [?LINE,{T,Kvs}]),
- Vdb1 = use_vars(union(A#k.us, Ls), I+1, Vdb0),
- Vs = map(fun (Vc) -> val_clause(Vc, Ls, I+1, Vdb1) end, Kvs),
- #l{ke={type_clause,type(T),Vs},i=I,vdb=Vdb1,a=A#k.a}.
-
-val_clause(#k_val_clause{anno=A,val=V,body=Kb}, Ls0, I, Vdb0) ->
- {_Used,New} = match_pat_vars(V),
- %% Not clear yet how Used should be used.
- Bus = (get_kanno(Kb))#k.us,
- %%ok = io:format("Ls0 = ~p, Used=~p\n New=~p, Bus=~p\n", [Ls0,Used,New,Bus]),
- Ls1 = union(intersection(New, Bus), Ls0), %Lock for safety
- Vdb1 = use_vars(union(A#k.us, Ls1), I+1, new_vars(New, I, Vdb0)),
- B = match(Kb, Ls1, I+1, Vdb1),
- #l{ke={val_clause,literal(V),B},i=I,vdb=use_vars(Bus, I+1, Vdb1),a=A#k.a}.
-
-guard_clause(#k_guard_clause{anno=A,guard=Kg,body=Kb}, Ls, I, Vdb0) ->
- Vdb1 = use_vars(union(A#k.us, Ls), I+2, Vdb0),
- Gdb = vdb_sub(I+1, I+2, Vdb1),
- G = guard(Kg, I+1, Gdb),
- B = match(Kb, Ls, I+2, Vdb1),
- #l{ke={guard_clause,G,B},
- i=I,vdb=use_vars((get_kanno(Kg))#k.us, I+2, Vdb1),
- a=A#k.a}.
-
-%% match_fail(FailValue, I, Anno) -> Expr.
-%% Generate the correct match_fail instruction. N.B. there is no
-%% generic case for when the fail value has been created elsewhere.
-
-match_fail(#k_tuple{es=[#k_atom{val=function_clause}|As]}, I, A) ->
- #l{ke={match_fail,{function_clause,literal_list(As)}},i=I,a=A};
-match_fail(#k_tuple{es=[#k_atom{val=badmatch},Val]}, I, A) ->
- #l{ke={match_fail,{badmatch,literal(Val)}},i=I,a=A};
-match_fail(#k_tuple{es=[#k_atom{val=case_clause},Val]}, I, A) ->
- #l{ke={match_fail,{case_clause,literal(Val)}},i=I,a=A};
-match_fail(#k_atom{val=if_clause}, I, A) ->
- #l{ke={match_fail,if_clause},i=I,a=A};
-match_fail(#k_tuple{es=[#k_atom{val=try_clause},Val]}, I, A) ->
- #l{ke={match_fail,{try_clause,literal(Val)}},i=I,a=A}.
-
-%% type(Ktype) -> Type.
-
-type(k_int) -> integer;
-type(k_char) -> integer; %Hhhmmm???
-type(k_float) -> float;
-type(k_atom) -> atom;
-type(k_nil) -> nil;
-type(k_cons) -> cons;
-type(k_tuple) -> tuple;
-type(k_binary) -> binary;
-type(k_bin_seg) -> bin_seg;
-type(k_bin_end) -> bin_end.
-
-%% variable(Klit) -> Lit.
-%% var_list([Klit]) -> [Lit].
-
-variable(#k_var{name=N}) -> {var,N}.
-
-var_list(Ks) -> map(fun variable/1, Ks).
-
-%% atomic_lit(Klit) -> Lit.
-%% atomic_list([Klit]) -> [Lit].
-
-atomic_lit(#k_var{name=N}) -> {var,N};
-atomic_lit(#k_int{val=I}) -> {integer,I};
-atomic_lit(#k_float{val=F}) -> {float,F};
-atomic_lit(#k_atom{val=N}) -> {atom,N};
-%%atomic_lit(#k_char{val=C}) -> {char,C};
-%%atomic_lit(#k_string{val=S}) -> {string,S};
-atomic_lit(#k_nil{}) -> nil.
-
-atomic_list(Ks) -> map(fun atomic_lit/1, Ks).
-
-%% literal(Klit) -> Lit.
-%% literal_list([Klit]) -> [Lit].
-
-literal(#k_var{name=N}) -> {var,N};
-literal(#k_int{val=I}) -> {integer,I};
-literal(#k_float{val=F}) -> {float,F};
-literal(#k_atom{val=N}) -> {atom,N};
-%%literal(#k_char{val=C}) -> {char,C};
-literal(#k_string{val=S}) -> {string,S};
-literal(#k_nil{}) -> nil;
-literal(#k_cons{hd=H,tl=T}) ->
- {cons,[literal(H),literal(T)]};
-literal(#k_binary{segs=V}) ->
- case proplists:get_bool(no_new_binaries, get(?MODULE)) of
- true ->
- {old_binary,literal(V)};
- false ->
- {binary,literal(V)}
- end;
-literal(#k_bin_seg{size=S,unit=U,type=T,flags=Fs,seg=Seg,next=N}) ->
- {bin_seg,literal(S),U,T,Fs,[literal(Seg),literal(N)]};
-literal(#k_bin_end{}) -> bin_end;
-literal(#k_tuple{es=Es}) ->
- {tuple,literal_list(Es)}.
-
-literal_list(Ks) -> map(fun literal/1, Ks).
-
-%% match_pat_vars(Pattern) -> {[UsedVarName],[NewVarName]}.
-
-match_pat_vars(#k_var{name=N}) -> {[],[N]};
-match_pat_vars(#k_int{}) -> {[],[]};
-match_pat_vars(#k_float{}) -> {[],[]};
-match_pat_vars(#k_atom{}) -> {[],[]};
-%%match_pat_vars(#k_char{}) -> {[],[]};
-match_pat_vars(#k_string{}) -> {[],[]};
-match_pat_vars(#k_nil{}) -> {[],[]};
-match_pat_vars(#k_cons{hd=H,tl=T}) ->
- match_pat_list_vars([H,T]);
-match_pat_vars(#k_binary{segs=V}) ->
- match_pat_vars(V);
-match_pat_vars(#k_bin_seg{size=S,seg=Seg,next=N}) ->
- {U1,New1} = match_pat_vars(Seg),
- {U2,New2} = match_pat_vars(N),
- {[],U3} = match_pat_vars(S),
- {union([U1,U2,U3]),union(New1, New2)};
-match_pat_vars(#k_bin_end{}) -> {[],[]};
-match_pat_vars(#k_tuple{es=Es}) ->
- match_pat_list_vars(Es).
-
-match_pat_list_vars(Ps) ->
- foldl(fun (P, {Used0,New0}) ->
- {Used,New} = match_pat_vars(P),
- {union(Used0, Used),union(New0, New)} end,
- {[],[]}, Ps).
-
-%% new_var(VarName, I, Vdb) -> Vdb.
-%% new_vars([VarName], I, Vdb) -> Vdb.
-%% use_var(VarName, I, Vdb) -> Vdb.
-%% use_vars([VarName], I, Vdb) -> Vdb.
-%% add_var(VarName, F, L, Vdb) -> Vdb.
-
-new_var(V, I, Vdb) ->
- case vdb_find(V, Vdb) of
- {V,F,L} when I < F -> vdb_store(V, I, L, Vdb);
- {V,_,_} -> Vdb;
- error -> vdb_store(V, I, I, Vdb)
- end.
-
-new_vars(Vs, I, Vdb0) ->
- foldl(fun (V, Vdb) -> new_var(V, I, Vdb) end, Vdb0, Vs).
-
-use_var(V, I, Vdb) ->
- case vdb_find(V, Vdb) of
- {V,F,L} when I > L -> vdb_store(V, F, I, Vdb);
- {V,_,_} -> Vdb;
- error -> vdb_store(V, I, I, Vdb)
- end.
-
-use_vars(Vs, I, Vdb0) ->
- foldl(fun (V, Vdb) -> use_var(V, I, Vdb) end, Vdb0, Vs).
-
-add_var(V, F, L, Vdb) ->
- use_var(V, L, new_var(V, F, Vdb)).
-
-vdb_find(V, Vdb) ->
- %% Peformance note: Profiling shows that this function accounts for
- %% a lot of the execution time when huge constants terms are built.
- %% Using the BIF lists:keysearch/3 is a lot faster than the
- %% original Erlang version.
- case lists:keysearch(V, 1, Vdb) of
- {value,Vd} -> Vd;
- false -> error
- end.
-
-%vdb_find(V, [{V1,F,L}=Vd|Vdb]) when V < V1 -> error;
-%vdb_find(V, [{V1,F,L}=Vd|Vdb]) when V == V1 -> Vd;
-%vdb_find(V, [{V1,F,L}=Vd|Vdb]) when V > V1 -> vdb_find(V, Vdb);
-%vdb_find(V, []) -> error.
-
-vdb_store(V, F, L, [{V1,_,_}=Vd|Vdb]) when V > V1 ->
- [Vd|vdb_store(V, F, L, Vdb)];
-vdb_store(V, F, L, [{V1,_,_}=Vd|Vdb]) when V < V1 -> [{V,F,L},Vd|Vdb];
-vdb_store(V, F, L, [{_V1,_,_}|Vdb]) -> [{V,F,L}|Vdb]; %V == V1
-vdb_store(V, F, L, []) -> [{V,F,L}].
-
-%% vdb_sub(Min, Max, Vdb) -> Vdb.
-%% Extract variables which are used before and after Min. Lock
-%% variables alive after Max.
-
-vdb_sub(Min, Max, Vdb) ->
- [ if L >= Max -> {V,F,1000000};
- true -> Vd
- end || {V,F,L}=Vd <- Vdb, F < Min, L >= Min ].
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_life.hrl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_life.hrl
deleted file mode 100644
index 95adcfcfd8..0000000000
--- a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_life.hrl
+++ /dev/null
@@ -1,25 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: v3_life.hrl,v 1.1 2008/12/17 09:53:43 mikpe Exp $
-%%
-%% This record contains variable life-time annotation for a
-%% kernel expression. Added by v3_life, used by v3_codegen.
-
--record(l, {ke, %Kernel expression
- i=0, %Op number
- vdb=[], %Variable database
- a}). %Core annotation
-
diff --git a/lib/dialyzer/test/options2_SUITE_data/dialyzer_options b/lib/dialyzer/test/options2_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..5db2e50d23
--- /dev/null
+++ b/lib/dialyzer/test/options2_SUITE_data/dialyzer_options
@@ -0,0 +1 @@
+{dialyzer_options, [{defines, [{'vsn', 4}]}, {warnings, [no_return]}]}.
diff --git a/lib/dialyzer/test/options2_SUITE_data/results/kernel b/lib/dialyzer/test/options2_SUITE_data/results/kernel
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/lib/dialyzer/test/options2_SUITE_data/src/kernel/global.erl b/lib/dialyzer/test/options2_SUITE_data/src/kernel/global.erl
new file mode 100644
index 0000000000..4778a39a3c
--- /dev/null
+++ b/lib/dialyzer/test/options2_SUITE_data/src/kernel/global.erl
@@ -0,0 +1,1999 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: global.erl,v 1.4 2009/09/17 09:46:19 kostis Exp $
+%%
+-module(global).
+-behaviour(gen_server).
+
+%% A Global register that allows the global registration of pid's and
+%% name's, that dynamically keeps up to date with the entire network.
+%% global can operate in two modes; in a fully connected network, or
+%% in a non-fully connected network. In the latter case, the name
+%% registration mechanism won't work.
+%%
+
+%% External exports
+-export([start/0, start_link/0, stop/0, sync/0, sync/1,
+ safe_whereis_name/1, whereis_name/1, register_name/2, register_name/3,
+ register_name_external/2, register_name_external/3, unregister_name_external/1,
+ re_register_name/2, re_register_name/3,
+ unregister_name/1, registered_names/0, send/2, node_disconnected/1,
+ set_lock/1, set_lock/2, set_lock/3,
+ del_lock/1, del_lock/2,
+ trans/2, trans/3, trans/4,
+ random_exit_name/3, random_notify_name/3, notify_all_name/3, cnode/3]).
+
+%% Internal exports
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
+ code_change/3, timer/2, sync_init/2, init_locker/5, resolve_it/4,
+ init_the_locker/1]).
+
+-export([info/0]).
+
+
+%-define(PRINT(X), erlang:display(X)).
+-define(PRINT(X), true).
+
+%-define(P2(X), erlang:display(X)).
+%-define(P2(X), erlang:display({cs(),X})).
+-define(P2(X), true).
+
+%-define(P1(X), erlang:display(X)).
+-define(P1(X), true).
+
+%-define(P(X), erlang:display(X)).
+-define(P(X), true).
+
+%-define(FORMAT(S, A), format(S, A)).
+-define(FORMAT(S, A), ok).
+
+%%% In certain places in the server, calling io:format hangs everything,
+%%% so we'd better use erlang:display/1.
+% format(S, A) ->
+% erlang:display({format, cs(), S, A}),
+% % io:format(S, A),
+% ok.
+
+% cs() ->
+% {Big, Small, Tiny} = now(),
+% (Small rem 100) * 100 + (Tiny div 10000).
+
+%% Some notes on the internal structure:
+%% One invariant is that the list of locker processes is keyed; i.e.,
+%% there is only one process per neighboring node.
+%% When an item has been stored in the process dictionary, it is not
+%% necessarily cleared when not in use anymore. In other words, it's
+%% not an error if there is already an item there when one is to be
+%% stored.
+
+
+%% This is the protocol version
+%% Vsn 1 is the original protocol.
+%% Vsn 2 is enhanced with code to take care of registration of names from
+%% non erlang nodes, e.g. c-nodes.
+%% Vsn 3 is enhanced with a tag in the synch messages to distinguish
+%% different synch sessions from each other, see OTP-2766.
+%% Note: This requires also that the ticket OTP-2928 is fixed on the nodes
+%% running vsn 1 or 2; if such nodes will coexist with vsn 3 nodes.
+%% Vsn 4 uses a single, permanent, locker process, but works like vsn 3
+%% when communicating with vsn 3 nodes.
+
+%% -define(vsn, 4). %% Now given in options
+
+%%-----------------------------------------------------------------
+%% connect_all = boolean() - true if we are supposed to set up a
+%% fully connected net
+%% known = [Node] - all nodes known to us
+%% synced = [Node] - all nodes that have the same names as us
+%% lockers = [{Node, MyLockerPid}] - the pid of the locker
+%% process for each Node
+%% syncers = [pid()] - all current syncers processes
+%% node_name = atom() - our node name (can change if distribution
+%% is started/stopped dynamically)
+%%
+%% In addition to these, we keep info about messages arrived in
+%% the process dictionary:
+%% {pre_connect, Node} = {Vsn, InitMsg} - init_connect msgs that
+%% arrived before nodeup
+%% {wait_lock, Node} = {exchange, NameList} | lock_is_set
+%% - see comment below (handle_cast)
+%% {save_ops, Node} = [operation()] - save the ops between
+%% exchange and resolved
+%% {prot_vsn, Node} = Vsn - the exchange protocol version
+%% {sync_tag_my, Node} = My tag, used at synchronization with Node
+%% {sync_tag_his, Node} = The Node's tag, used at synchronization
+%%-----------------------------------------------------------------
+-record(state, {connect_all, known = [], synced = [],
+ lockers = [], syncers = [], node_name = node(),
+ the_locker, the_deleter}).
+
+start() -> gen_server:start({local, global_name_server}, global, [], []).
+start_link() -> gen_server:start_link({local, global_name_server},global,[],[]).
+stop() -> gen_server:call(global_name_server, stop, infinity).
+
+sync() ->
+ case check_sync_nodes() of
+ {error, Error} ->
+ {error, Error};
+ SyncNodes ->
+ gen_server:call(global_name_server, {sync, SyncNodes}, infinity)
+ end.
+sync(Nodes) ->
+ case check_sync_nodes(Nodes) of
+ {error, Error} ->
+ {error, Error};
+ SyncNodes ->
+ gen_server:call(global_name_server, {sync, SyncNodes}, infinity)
+ end.
+
+
+send(Name, Msg) ->
+ case whereis_name(Name) of
+ Pid when pid(Pid) ->
+ Pid ! Msg,
+ Pid;
+ undefined ->
+ exit({badarg, {Name, Msg}})
+ end.
+
+%% See OTP-3737. (safe_whereis_name/1 is in fact not used anywhere in OTP.)
+whereis_name(Name) ->
+ where(Name).
+
+safe_whereis_name(Name) ->
+ gen_server:call(global_name_server, {whereis, Name}, infinity).
+
+
+node_disconnected(Node) ->
+ global_name_server ! {nodedown, Node}.
+
+
+%%-----------------------------------------------------------------
+%% Method = function(Name, Pid1, Pid2) -> Pid | Pid2 | none
+%% Method is called if a name conflict is detected when two nodes
+%% are connecting to each other. It is supposed to return one of
+%% the Pids or 'none'. If a pid is returned, that pid is
+%% registered as Name on all nodes. If 'none' is returned, the
+%% Name is unregistered on all nodes. If anything else is returned,
+%% the Name is unregistered as well.
+%% Method is called once at one of the nodes where the processes reside
+%% only. If different Methods are used for the same name, it is
+%% undefined which one of them is used.
+%% Method is blocking, i.e. when it is called, no calls to whereis/
+%% send is let through until it has returned.
+%%-----------------------------------------------------------------
+register_name(Name, Pid) when pid(Pid) ->
+ register_name(Name, Pid, {global, random_exit_name}).
+register_name(Name, Pid, Method) when pid(Pid) ->
+ trans_all_known(fun(Nodes) ->
+ case where(Name) of
+ undefined ->
+ gen_server:multi_call(Nodes,
+ global_name_server,
+ {register, Name, Pid, Method}),
+ yes;
+ _Pid -> no
+ end
+ end).
+
+unregister_name(Name) ->
+ case where(Name) of
+ undefined ->
+ ok;
+ _ ->
+ trans_all_known(fun(Nodes) ->
+ gen_server:multi_call(Nodes,
+ global_name_server,
+ {unregister, Name}),
+ ok
+ end)
+ end.
+
+re_register_name(Name, Pid) when pid(Pid) ->
+ re_register_name(Name, Pid, {global, random_exit_name}).
+re_register_name(Name, Pid, Method) when pid(Pid) ->
+ trans_all_known(fun(Nodes) ->
+ gen_server:multi_call(Nodes,
+ global_name_server,
+ {register, Name, Pid, Method}),
+ yes
+ end).
+
+%% Returns all globally registered names
+registered_names() -> lists:map(fun({Name, _Pid, _Method}) -> Name end,
+ ets:tab2list(global_names)).
+
+%%-----------------------------------------------------------------
+%% An external node (i.e not an erlang node) (un)registers a name.
+%% If the registered Pid crashes the name is to be removed from global.
+%% If the external node crashes the name is to be removed from global.
+%% If the erlang node which registers the name crashes the name is also to be
+%% removed, because the registered process is not supervised any more,
+%% (i.e there is no link to the registered Pid).
+%%-----------------------------------------------------------------
+register_name_external(Name, Pid) when pid(Pid) ->
+ register_name_external(Name, Pid, {global, random_exit_name}).
+register_name_external(Name, Pid, Method) when pid(Pid) ->
+ trans_all_known(fun(Nodes) ->
+ case where(Name) of
+ undefined ->
+ gen_server:multi_call(Nodes,
+ global_name_server,
+ {register, Name, Pid, Method}),
+ gen_server:multi_call(Nodes,
+ global_name_server,
+ {register_ext, Name, Pid, node()}),
+ yes;
+ _Pid -> no
+ end
+ end).
+
+
+
+
+unregister_name_external(Name) ->
+ case where(Name) of
+ undefined ->
+ ok;
+ _ ->
+ trans_all_known(fun(Nodes) ->
+ gen_server:multi_call(Nodes,
+ global_name_server,
+ {unregister, Name}),
+ gen_server:multi_call(Nodes,
+ global_name_server,
+ {unregister_ext, Name}),
+ ok
+ end)
+ end.
+
+
+
+
+
+%%-----------------------------------------------------------------
+%% Args: Id = id()
+%% Nodes = [node()]
+%% id() = {ResourceId, LockRequesterId}
+%% Retries = infinity | int() > 0
+%% Purpose: Sets a lock on the specified nodes (or all nodes if
+%% none are specified) on ResourceId for LockRequesterId. If there
+%% already exists a lock on ResourceId for another owner
+%% than LockRequesterId, false is returned, otherwise true.
+%% Returns: boolean()
+%%-----------------------------------------------------------------
+set_lock(Id) ->
+ set_lock(Id, [node() | nodes()], infinity, 1).
+set_lock(Id, Nodes) ->
+ set_lock(Id, Nodes, infinity, 1).
+set_lock(Id, Nodes, Retries) when Retries > 0 ->
+ set_lock(Id, Nodes, Retries, 1);
+set_lock(Id, Nodes, infinity) ->
+ set_lock(Id, Nodes, infinity, 1).
+set_lock(_Id, _Nodes, 0, _) -> false;
+set_lock({ResourceId, LockRequesterId}, Nodes, Retries, Times) ->
+ Id = {ResourceId, LockRequesterId},
+ Msg = {set_lock, Id},
+ {Replies, _} =
+ gen_server:multi_call(Nodes, global_name_server, Msg),
+ ?P2({set_lock, node(), self(), {ResourceId, LockRequesterId},
+ Nodes, Retries, Times, Replies, catch erlang:error(kaka)}),
+ ?P({set_lock, node(), ResourceId,
+ {LockRequesterId, node(LockRequesterId)}}),
+ case check_replies(Replies, Id, Nodes) of
+ true -> ?P({set_lock_true, node(), ResourceId}),
+ true;
+ false ->
+ random_sleep(Times),
+ set_lock(Id, Nodes, dec(Retries), Times+1);
+ N when integer(N) ->
+ ?P({sleeping, N}),
+ timer:sleep(N*500),
+ set_lock(Id, Nodes, Retries, Times);
+ Pid when pid(Pid) ->
+ ?P({waiting_for, Pid}),
+ Ref = erlang:monitor(process, Pid),
+ receive
+ {'DOWN', Ref, process, Pid, _Reason} ->
+ ?P({waited_for, Pid, _Reason}),
+ set_lock(Id, Nodes, Retries, Times)
+ end
+ end.
+
+check_replies([{_Node, true} | T], Id, Nodes) ->
+ check_replies(T, Id, Nodes);
+check_replies([{_Node, Status} | _T], Id, Nodes) ->
+ gen_server:multi_call(Nodes, global_name_server, {del_lock, Id}),
+ Status;
+check_replies([], _Id, _Nodes) ->
+ true.
+
+del_lock(Id) ->
+ del_lock(Id, [node() | nodes()]).
+del_lock({ResourceId, LockRequesterId}, Nodes) ->
+ Id = {ResourceId, LockRequesterId},
+ ?P2({del_lock, node(), self(), ResourceId, LockRequesterId, Nodes}),
+ gen_server:multi_call(Nodes, global_name_server, {del_lock, Id}),
+ true.
+
+%%-----------------------------------------------------------------
+%% Args: Id = id()
+%% Fun = fun() | {M,F}
+%% Nodes = [node()]
+%% Retries = infinity | int() > 0
+%% Purpose: Sets a lock on Id (as set_lock), and evaluates
+%% Res = Fun() on success.
+%% Returns: Res | aborted (note, if Retries is infinity, the
+%% transaction won't abort)
+%%-----------------------------------------------------------------
+trans(Id, Fun) -> trans(Id, Fun, [node() | nodes()], infinity).
+trans(Id, Fun, Nodes) -> trans(Id, Fun, Nodes, infinity).
+trans(_Id, _Fun, _Nodes, 0) -> aborted;
+trans(Id, Fun, Nodes, Retries) ->
+ case set_lock(Id, Nodes, Retries) of
+ true ->
+ case catch Fun() of
+ {'EXIT', R} ->
+ del_lock(Id, Nodes),
+ exit(R);
+ Res ->
+ del_lock(Id, Nodes),
+ Res
+ end;
+ false ->
+ aborted
+ end.
+
+%%% Similar to trans(Id, Fun), but always uses global's own lock,
+%%% on all nodes known to global, making sure that no new nodes have
+%%% become known while we got the list of known nodes.
+trans_all_known(F) ->
+ Id = {global, self()},
+ Nodes = [node() | gen_server:call(global_name_server, get_known)],
+ case set_lock(Id, Nodes) of
+ true ->
+ Nodes2 = [node() | gen_server:call(global_name_server, get_known)],
+ case Nodes2 -- Nodes of
+ [] ->
+ case catch F(Nodes2) of
+ {'EXIT', R} ->
+ del_lock(Id, Nodes2),
+ exit(R);
+ Res ->
+ del_lock(Id, Nodes2),
+ Res
+ end;
+ _ ->
+ del_lock(Id, Nodes),
+ trans_all_known(F)
+ end;
+ false ->
+ aborted
+ end.
+
+info() ->
+ gen_server:call(global_name_server, info).
+
+%%%-----------------------------------------------------------------
+%%% Call-back functions from gen_server
+%%%-----------------------------------------------------------------
+init([]) ->
+ process_flag(trap_exit, true),
+ ets:new(global_locks, [set, named_table, protected]),
+ ets:new(global_names, [set, named_table, protected]),
+ ets:new(global_names_ext, [set, named_table, protected]),
+
+ %% multi
+ S = #state{the_locker = start_the_locker(self()),
+ the_deleter = start_the_deleter(self())},
+
+ case init:get_argument(connect_all) of
+ {ok, [["false"]]} ->
+ {ok, S#state{connect_all = false}};
+ _ ->
+ {ok, S#state{connect_all = true}}
+ end.
+
+%%-----------------------------------------------------------------
+%% Connection algorithm
+%% ====================
+%% This alg solves the problem with partitioned nets as well.
+%%
+%% The main idea in the alg is that when two nodes connect, they
+%% try to set a lock in their own partition (i.e. all nodes already
+%% known to them). When the lock is set in each partition, these
+%% two nodes send each other a list with all registered names in
+%% resp partition(*). If no conflict is found, the name tables are
+%% just updated. If a conflict is found, a resolve function is
+%% called once for each conflict. The result of the resolving
+%% is sent to the other node. When the names are exchanged, all
+%% other nodes in each partition are informed of the other nodes,
+%% and they ping each other to form a fully connected net.
+%%
+%% Here's the flow:
+%% Suppose nodes A and B connect, and C is connected to A.
+%%
+%% Node A
+%% ------
+%% << {nodeup, B}
+%% [spawn locker]
+%% B ! {init_connect, MyLocker}
+%% << {init_connect, MyLocker}
+%% [The lockers try to set the lock]
+%% << {lock_is_set, B}
+%% [Now, lock is set in both partitions]
+%% B ! {exchange, Names}
+%% << {exchange, Names}
+%% [solve conflict]
+%% B ! {resolved, Resolved}
+%% << {resolved, Resolved}
+%% C ! {new_nodes, Resolved, [B]}
+%%
+%% Node C
+%% ------
+%% << {new_nodes, ResolvedOps, NewNodes}
+%% [insert Ops]
+%% ping(NewNodes)
+%% << {nodeup, B}
+%%
+%%
+%% Several things can disturb this picture.
+%%
+%% First, the got_names message may arrive *before* the nodeup
+%% message, due to delay in net_kernel and an optimisation in the
+%% emulator. We handle this by keeping track of these messages in the
+%% pre_connect and lockers variables in our state.
+%%
+%% The most common situation is when a new node connects to an
+%% existing net. In this case there's no need to set the lock on
+%% all nodes in the net, as we know that there won't be any conflict.
+%% This is optimised by sending {first_contact, Node} instead of got_names.
+%% This implies that first_contact may arrive before nodeup as well.
+%%
+%% Of course we must handle that some node goes down during the
+%% connection.
+%%
+%% (*) When this information is being exchanged, no one is allowed
+%% to change the global register table. All calls to register etc
+%% are protected by a lock. If a registered process dies
+%% during this phase, the deregistration is done as soon as possible
+%% on each node (i.e. when the info about the process has arrived).
+%%-----------------------------------------------------------------
+%% Messages in the protocol
+%% ========================
+%% 1. Between connecting nodes (gen_server:casts)
+%% {init_connect, Vsn, Node, InitMsg}
+%% InitMsg = {locker, LockerPid}
+%% {exchange, Node, ListOfNames}
+%% {resolved, Node, Ops, Known}
+%% Known = list of nodes in Node's partition
+%% 2. Between lockers on connecting nodes (!s)
+%% {his_locker, Pid} (from our global)
+%% lockers link to each other
+%% {lock, Bool} loop until both lockers have lock = true,
+%% then send to global {lock_is_set, Node}
+%% 3. From connecting node to other nodes in the partition
+%% {new_nodes, Node, Ops, NewNodes}
+%% 4. sync protocol
+%% {in_sync, Node, IsKnown}
+%% - sent by each node to all new nodes
+%%-----------------------------------------------------------------
+
+handle_call({whereis, Name}, From, S) ->
+ do_whereis(Name, From),
+ {noreply, S};
+
+handle_call({register, Name, Pid, Method}, _From, S) ->
+ ?P2({register, node(), Name}),
+ ins_name(Name, Pid, Method),
+ {reply, yes, S};
+
+handle_call({unregister, Name}, _From, S) ->
+ case ets:lookup(global_names, Name) of
+ [{_, Pid, _}] ->
+ ?P2({unregister, node(), Name, Pid, node(Pid)}),
+ ets:delete(global_names, Name),
+ dounlink(Pid);
+ _ -> ok
+ end,
+ {reply, ok, S};
+
+handle_call({register_ext, Name, Pid, RegNode}, _F, S) ->
+ ins_name_ext(Name, Pid, RegNode),
+ {reply, yes, S};
+
+handle_call({unregister_ext, Name}, _From, S) ->
+ ets:delete(global_names_ext, Name),
+ {reply, ok, S};
+
+
+handle_call({set_lock, Lock}, {Pid, _Tag}, S) ->
+ Reply = handle_set_lock(Lock, Pid),
+ {reply, Reply, S};
+
+handle_call({del_lock, Lock}, {Pid, _Tag}, S) ->
+ handle_del_lock(Lock, Pid),
+ {reply, true, S};
+
+handle_call(get_known, _From, S) ->
+ {reply, S#state.known, S};
+
+%% R7 may call us?
+handle_call(get_known_v2, _From, S) ->
+ {reply, S#state.known, S};
+
+handle_call({sync, Nodes}, From, S) ->
+ %% If we have several global groups, this won't work, since we will
+ %% do start_sync on a nonempty list of nodes even if the system
+ %% is quiet.
+ Pid = start_sync(lists:delete(node(), Nodes) -- S#state.synced, From),
+ {noreply, S#state{syncers = [Pid | S#state.syncers]}};
+
+handle_call(get_protocol_version, _From, S) ->
+ {reply, ?vsn, S};
+
+handle_call(get_names_ext, _From, S) ->
+ {reply, get_names_ext(), S};
+
+handle_call(info, _From, S) ->
+ {reply, S, S};
+
+handle_call(stop, _From, S) ->
+ {stop, normal, stopped, S}.
+
+
+%%=======================================================================================
+%% init_connect
+%%
+%% Vsn 1 is the original protocol.
+%% Vsn 2 is enhanced with code to take care of registration of names from
+%% non erlang nodes, e.g. c-nodes.
+%% Vsn 3 is enhanced with a tag in the synch messages to distinguish
+%% different synch sessions from each other, see OTP-2766.
+%% Note: This requires also that the ticket OTP-2928 is fixed on the nodes
+%% running vsn 1 or 2; if such nodes will coexist with vsn 3 nodes.
+%%=======================================================================================
+handle_cast({init_connect, Vsn, Node, InitMsg}, S) ->
+ ?FORMAT("~p #### init_connect Vsn ~p, Node ~p, InitMsg ~p~n",[node(), Vsn, Node, InitMsg]),
+ case Vsn of
+ %% It is always the responsibility of newer versions to understand
+ %% older versions of the protocol.
+ {HisVsn, HisTag} when HisVsn > ?vsn ->
+ init_connect(?vsn, Node, InitMsg, HisTag, S#state.lockers, S);
+ {HisVsn, HisTag} ->
+ init_connect(HisVsn, Node, InitMsg, HisTag, S#state.lockers, S);
+ %% To be future compatible
+ Tuple when tuple(Tuple) ->
+ List = tuple_to_list(Tuple),
+ [_HisVsn, HisTag | _] = List,
+ %% use own version handling if his is newer.
+ init_connect(?vsn, Node, InitMsg, HisTag, S#state.lockers, S);
+ _ when Vsn < 3 ->
+ init_connect(Vsn, Node, InitMsg, undef, S#state.lockers, S);
+ _ ->
+ Txt = io_lib:format("Illegal global protocol version ~p Node: ~p",[Vsn, Node]),
+ error_logger:info_report(lists:flatten(Txt))
+ end,
+ {noreply, S};
+
+%%=======================================================================================
+%% lock_is_set
+%%
+%% Ok, the lock is now set on both partitions. Send our names to other node.
+%%=======================================================================================
+handle_cast({lock_is_set, Node, MyTag}, S) ->
+ ?FORMAT("~p #### lock_is_set Node ~p~n",[node(), Node]),
+ Sync_tag_my = get({sync_tag_my, Node}),
+ PVsn = get({prot_vsn, Node}),
+ ?P2({lock_is_set, node(), Node, {MyTag, PVsn}, Sync_tag_my}),
+ case {MyTag, PVsn} of
+ {Sync_tag_my, undefined} ->
+ %% Patch for otp-2728, the connection to the Node is flipping up and down
+ %% the messages from the 'older' sync tries can disturb the 'new' sync try
+ %% therefor all messages are discarded if the protocol vsn is not defined.
+ Txt = io_lib:format("undefined global protocol version Node: ~p",[Node]),
+ error_logger:info_report(lists:flatten(Txt)),
+ {noreply, S};
+ {Sync_tag_my, _} ->
+ %% Check that the Node is still not known
+ case lists:member(Node, S#state.known) of
+ false ->
+ ?P2({lset, node(), Node, false}),
+ lock_is_set(Node, S#state.known),
+ {noreply, S};
+ true ->
+ ?P2({lset, node(), Node, true}),
+ erase({wait_lock, Node}),
+ NewS = cancel_locker(Node, S),
+ {noreply, NewS}
+ end;
+ _ ->
+ ?P2({lset, illegal, node(), Node}),
+ %% Illegal tag, delete the locker.
+ erase({wait_lock, Node}),
+ NewS = cancel_locker(Node, S),
+ {noreply, NewS}
+ end;
+
+%%=======================================================================================
+%% exchange
+%%
+%% Here the names are checked to detect name clashes.
+%%=======================================================================================
+%% Vsn 3 of the protocol
+handle_cast({exchange, Node, NameList, NameExtList, MyTag}, S) ->
+ ?FORMAT("~p #### handle_cast 3 lock_is_set exchange ~p~n",
+ [node(),{Node, NameList, NameExtList, MyTag}]),
+ Sync_tag_my = get({sync_tag_my, Node}),
+ PVsn = get({prot_vsn, Node}),
+ case {MyTag, PVsn} of
+ {Sync_tag_my, undefined} ->
+ %% Patch for otp-2728, the connection to the Node is flipping up and down
+ %% the messages from the 'older' sync tries can disturb the 'new' sync try
+ %% therefor all messages are discarded if the protocol vsn is not defined.
+ Txt = lists:flatten(io_lib:format(
+ "undefined global protocol version Node: ~p",[Node])),
+ error_logger:info_report(Txt),
+ {noreply, S};
+ {Sync_tag_my, _} ->
+ exchange(PVsn, Node, {NameList, NameExtList}, S#state.known),
+ {noreply, S};
+ _ ->
+ %% Illegal tag, delete the locker.
+ erase({wait_lock, Node}),
+ NewS = cancel_locker(Node, S),
+ {noreply, NewS}
+ end;
+
+
+
+%%=======================================================================================
+%% resolved
+%%
+%% Here the name clashes are resolved.
+%%=======================================================================================
+%% Vsn 3 of the protocol
+handle_cast({resolved, Node, Resolved, HisKnown, _HisKnown_v2, Names_ext, MyTag}, S) ->
+ ?FORMAT("~p #### 2 resolved ~p~n",[node(),{Node, Resolved, HisKnown, Names_ext}]),
+ Sync_tag_my = get({sync_tag_my, Node}),
+ PVsn = get({prot_vsn, Node}),
+ case {MyTag, PVsn} of
+ {Sync_tag_my, undefined} ->
+ %% Patch for otp-2728, the connection to the Node is flipping up and down
+ %% the messages from the 'older' sync tries can disturb the 'new' sync try
+ %% therefor all messages are discarded if the protocol vsn is not defined.
+ Txt = lists:flatten(io_lib:format(
+ "undefined global protocol version Node: ~p",[Node])),
+ error_logger:info_report(Txt),
+ {noreply, S};
+ {Sync_tag_my, _} ->
+ NewS = resolved(Node, Resolved, {HisKnown, HisKnown}, Names_ext, S),
+ {noreply, NewS};
+ _ ->
+ %% Illegal tag, delete the locker.
+ erase({wait_lock, Node}),
+ NewS = cancel_locker(Node, S),
+ {noreply, NewS}
+ end;
+
+
+
+
+
+
+%%=======================================================================================
+%% new_nodes
+%%
+%% We get to know the other node's known nodes.
+%%=======================================================================================
+%% Vsn 2 and 3 of the protocol
+handle_cast({new_nodes, _Node, Ops, Names_ext, Nodes, _Nodes_v2}, S) ->
+ ?P2({new_nodes, node(), Nodes}),
+ ?FORMAT("~p #### 2 new_nodes ~p~n",[node(),{Ops, Names_ext, Nodes}]),
+ NewS = new_nodes(Ops, Names_ext, Nodes, S),
+ {noreply, NewS};
+
+
+
+
+%%=======================================================================================
+%% in_sync
+%%
+%% We are in sync with this node (from the other node's known world).
+%%=======================================================================================
+handle_cast({in_sync, Node, IsKnown}, S) ->
+ ?FORMAT("~p #### in_sync ~p~n",[node(),{Node, IsKnown}]),
+ lists:foreach(fun(Pid) -> Pid ! {synced, [Node]} end, S#state.syncers),
+ %% moved up:
+ NewS = cancel_locker(Node, S),
+ erase({wait_lock, Node}),
+ erase({pre_connect, Node}),
+ erase({sync_tag_my, Node}),
+ erase({sync_tag_his, Node}),
+ NKnown = case lists:member(Node, Known = NewS#state.known) of
+ false when IsKnown == true ->
+ gen_server:cast({global_name_server, Node},
+ {in_sync, node(), false}),
+ [Node | Known];
+ _ ->
+ Known
+ end,
+ NSynced = case lists:member(Node, Synced = NewS#state.synced) of
+ true -> Synced;
+ false -> [Node | Synced]
+ end,
+ {noreply, NewS#state{known = NKnown, synced = NSynced}};
+
+
+
+
+%% Called when Pid on other node crashed
+handle_cast({async_del_name, Name, Pid}, S) ->
+ ?P2({async_del_name, node(), Name, Pid, node(Pid)}),
+ case ets:lookup(global_names, Name) of
+ [{Name, Pid, _}] ->
+ ets:delete(global_names, Name),
+ dounlink(Pid);
+ _ -> ok
+ end,
+ ets:delete(global_names_ext, Name),
+ {noreply, S};
+
+handle_cast({async_del_lock, _ResourceId, Pid}, S) ->
+ del_locks2(ets:tab2list(global_locks), Pid),
+% ets:match_delete(global_locks, {ResourceId, '_', Pid}),
+ {noreply, S}.
+
+
+handle_info({'EXIT', Deleter, _Reason}=Exit, #state{the_deleter=Deleter}=S) ->
+ {stop, {deleter_died,Exit}, S#state{the_deleter=undefined}};
+handle_info({'EXIT', Pid, _Reason}, #state{the_deleter=Deleter}=S)
+ when pid(Pid) ->
+ ?P2({global, exit, node(), Pid, node(Pid)}),
+ check_exit(Deleter, Pid),
+ Syncers = lists:delete(Pid, S#state.syncers),
+ Lockers = lists:keydelete(Pid, 2, S#state.lockers),
+ ?PRINT({exit, Pid, lockers, node(), S#state.lockers}),
+ {noreply, S#state{syncers = Syncers, lockers = Lockers}};
+
+handle_info({nodedown, Node}, S) when Node == S#state.node_name ->
+ %% Somebody stopped the distribution dynamically - change
+ %% references to old node name (Node) to new node name ('nonode@nohost')
+ {noreply, change_our_node_name(node(), S)};
+
+handle_info({nodedown, Node}, S) ->
+ ?FORMAT("~p #### nodedown 1 ####### Node ~p",[node(),Node]),
+ %% moved up:
+ do_node_down(Node),
+ #state{known = Known, synced = Syncs} = S,
+ NewS = cancel_locker(Node, S),
+
+ erase({wait_lock, Node}),
+ erase({save_ops, Node}),
+ erase({pre_connect, Node}),
+ erase({prot_vsn, Node}),
+ erase({sync_tag_my, Node}),
+ erase({sync_tag_his, Node}),
+ {noreply, NewS#state{known = lists:delete(Node, Known),
+ synced = lists:delete(Node, Syncs)}};
+
+
+
+handle_info({nodeup, Node}, S) when Node == node() ->
+ ?FORMAT("~p #### nodeup S ####### Node ~p~n",[node(), Node]),
+ %% Somebody started the distribution dynamically - change
+ %% references to old node name ('nonode@nohost') to Node.
+ {noreply, change_our_node_name(Node, S)};
+
+handle_info({nodeup, Node}, S) when S#state.connect_all == true ->
+ ?FORMAT("~p #### nodeup 1 ####### Node ~p",[node(),Node]),
+ IsKnown = lists:member(Node, S#state.known) or
+ %% This one is only for double nodeups (shouldn't occur!)
+ lists:keymember(Node, 1, S#state.lockers),
+ case IsKnown of
+ true ->
+ {noreply, S};
+ false ->
+ %% now() is used as a tag to separate different sycnh sessions
+ %% from each others. Global could be confused at bursty nodeups
+ %% because it couldn't separate the messages between the different
+ %% synch sessions started by a nodeup.
+ MyTag = now(),
+ resend_pre_connect(Node),
+
+ %% multi
+ S#state.the_locker ! {nodeup, Node, S#state.known, MyTag, self()},
+
+ Pid = start_locker(Node, S#state.known, MyTag, self(), S#state.the_locker),
+ Ls = S#state.lockers,
+ InitC = {init_connect, {?vsn, MyTag}, node(), {locker, Pid, S#state.known}},
+ ?P2({putting, MyTag}),
+ put({sync_tag_my, Node}, MyTag),
+ gen_server:cast({global_name_server, Node}, InitC),
+ {noreply, S#state{lockers = [{Node, Pid} | Ls]}}
+ end;
+
+
+%% This message is only to test otp-2766 Global may be confused at bursty
+%% nodeup/nodedowns. It's a copy of the complex part of the handling of
+%% the 'nodeup' message.
+handle_info({test_vsn_tag_nodeup, Node}, S) when S#state.connect_all == true,
+ Node == node() ->
+ {noreply, S};
+handle_info({test_vsn_tag_nodeup, Node}, S) when S#state.connect_all == true ->
+ ?FORMAT("~p #### test_nodeup 1 ####### Node ~p~n",[node(), Node]),
+ MyTag = now(),
+ resend_pre_connect(Node),
+ S#state.the_locker ! {nodeup, Node, S#state.known, MyTag, self()},
+ Pid = start_locker(Node, S#state.known, MyTag, self(), S#state.the_locker),
+ Ls = S#state.lockers,
+ InitC = {init_connect, {?vsn, MyTag}, node(), {locker, Pid, S#state.known}},
+ put({sync_tag_my, Node}, MyTag),
+ gen_server:cast({global_name_server, Node}, InitC),
+ ?PRINT({lockers, node(), Ls}),
+ {noreply, S#state{lockers = [{Node, Pid} | Ls]}};
+
+
+handle_info({whereis, Name, From}, S) ->
+ do_whereis(Name, From),
+ {noreply, S};
+
+handle_info(known, S) ->
+ io:format(">>>> ~p~n",[S#state.known]),
+ {noreply, S};
+
+handle_info(_, S) ->
+ {noreply, S}.
+
+
+
+
+%%=======================================================================================
+%%=======================================================================================
+%%=============================== Internal Functions ====================================
+%%=======================================================================================
+%%=======================================================================================
+
+
+
+%%=======================================================================================
+%% Another node wants to synchronize its registered names with us.
+%% Start a locker process. Both nodes must have a lock before they are
+%% allowed to continue.
+%%=======================================================================================
+init_connect(Vsn, Node, InitMsg, HisTag, Lockers, S) ->
+ ?P2({init_connect, node(), Node}),
+ ?FORMAT("~p #### init_connect Vsn, Node, InitMsg ~p~n",[node(),{Vsn, Node, InitMsg}]),
+ %% It is always the responsibility of newer versions to understand
+ %% older versions of the protocol.
+ put({prot_vsn, Node}, Vsn),
+ put({sync_tag_his, Node}, HisTag),
+ if
+ Vsn =< 3 ->
+ case lists:keysearch(Node, 1, Lockers) of
+ {value, {_Node, MyLocker}} ->
+ %% We both have lockers; let them set the lock
+ case InitMsg of
+ {locker, HisLocker, HisKnown} -> %% current version
+ ?PRINT({init_connect1, node(), self(), Node,
+ MyLocker, HisLocker}),
+ MyLocker ! {his_locker, HisLocker, HisKnown};
+
+ {locker, _HisLocker, HisKnown, HisTheLocker} -> %% multi
+ ?PRINT({init_connect1, node(), self(), Node,
+ MyLocker, _HisLocker}),
+ S#state.the_locker ! {his_the_locker, HisTheLocker,
+ HisKnown, S#state.known}
+ end;
+ false ->
+ ?PRINT({init_connect11, node(), self(), Node}),
+ put({pre_connect, Node}, {Vsn, InitMsg, HisTag})
+ end;
+ true -> % Vsn > 3
+ ?P2(vsn4),
+ case lists:keysearch(Node, 1, Lockers) of
+ {value, {_Node, _MyLocker}} ->
+ %% We both have lockers; let them set the lock
+ case InitMsg of
+ {locker, HisLocker, HisKnown} -> %% current version
+ ?PRINT({init_connect1, node(), self(), Node,
+ _MyLocker, HisLocker}),
+ HisLocker ! {his_locker_new, S#state.the_locker,
+ {HisKnown, S#state.known}};
+
+ {locker, _HisLocker, HisKnown, HisTheLocker} -> %% multi
+ ?PRINT({init_connect1, node(), self(), Node,
+ _MyLocker, _HisLocker}),
+ S#state.the_locker ! {his_the_locker, HisTheLocker,
+ HisKnown, S#state.known}
+ end;
+ false ->
+ ?PRINT({init_connect11, node(), self(), Node}),
+ put({pre_connect, Node}, {Vsn, InitMsg, HisTag})
+ end
+ end.
+
+
+
+%%=======================================================================================
+%% In the simple case, we'll get lock_is_set before we get exchange,
+%% but we may get exchange before we get lock_is_set from our locker.
+%% If that's the case, we'll have to remember the exchange info, and
+%% handle it when we get the lock_is_set. We do this by using the
+%% process dictionary - when the lock_is_set msg is received, we store
+%% this info. When exchange is received, we can check the dictionary
+%% if the lock_is_set has been received. If not, we store info about
+%% the exchange instead. In the lock_is_set we must first check if
+%% exchange info is stored, in that case we take care of it.
+%%=======================================================================================
+lock_is_set(Node, Known) ->
+ ?FORMAT("~p #### lock_is_set ~p~n",[node(),{Node, Node, Known}]),
+ PVsn = get({prot_vsn, Node}),
+ case PVsn of
+ _ -> % 3 and higher
+ gen_server:cast({global_name_server, Node},
+ {exchange, node(), get_names(), get_names_ext(),
+ get({sync_tag_his, Node})})
+ end,
+ %% If both have the lock, continue with exchange
+ case get({wait_lock, Node}) of
+ {exchange, NameList, NameExtList} ->
+ %% vsn 2, 3
+ put({wait_lock, Node}, lock_is_set),
+ exchange(PVsn, Node, {NameList, NameExtList}, Known);
+ undefined ->
+ put({wait_lock, Node}, lock_is_set)
+ end.
+
+
+
+%%=======================================================================================
+%% exchange
+%%=======================================================================================
+%% Vsn 3 and higher of the protocol
+exchange(_Vsn, Node, {NameList, NameExtList}, Known) ->
+ ?FORMAT("~p #### 3 lock_is_set exchange ~p~n",[node(),{Node, NameList, NameExtList}]),
+ case erase({wait_lock, Node}) of
+ lock_is_set ->
+ {Ops, Resolved} = exchange_names(NameList, Node, [], []),
+ put({save_ops, Node}, Ops),
+ gen_server:cast({global_name_server, Node},
+ {resolved, node(), Resolved, Known,
+ Known, get_names_ext(), get({sync_tag_his, Node})});
+ undefined ->
+ put({wait_lock, Node}, {exchange, NameList, NameExtList})
+ end.
+
+
+
+
+
+resolved(Node, Resolved, {HisKnown, _HisKnown_v2}, Names_ext, S) ->
+ ?P2({resolved, node(), Node, S#state.known}),
+ ?FORMAT("~p #### 2 resolved ~p~n",[node(),{Node, Resolved, HisKnown, Names_ext}]),
+ erase({prot_vsn, Node}),
+ Ops = erase({save_ops, Node}) ++ Resolved,
+ Known = S#state.known,
+ Synced = S#state.synced,
+ NewNodes = [Node | HisKnown],
+ do_ops(Ops),
+ do_ops_ext(Ops,Names_ext),
+ gen_server:abcast(Known, global_name_server,
+ {new_nodes, node(), Ops, Names_ext, NewNodes, NewNodes}),
+ %% I am synced with Node, but not with HisKnown yet
+ lists:foreach(fun(Pid) -> Pid ! {synced, [Node]} end, S#state.syncers),
+ gen_server:abcast(HisKnown, global_name_server, {in_sync, node(), true}),
+ NewS = lists:foldl(fun(Node1, S1) -> cancel_locker(Node1, S1) end,
+ S,
+ NewNodes),
+ %% See (*) below... we're node b in that description
+ NewKnown = Known ++ (NewNodes -- Known),
+ NewS#state{known = NewKnown, synced = [Node | Synced]}.
+
+
+
+
+new_nodes(Ops, Names_ext, Nodes, S) ->
+ ?FORMAT("~p #### 2 new_nodes ~p~n",[node(),{Ops, Names_ext, Nodes}]),
+ do_ops(Ops),
+ do_ops_ext(Ops,Names_ext),
+ Known = S#state.known,
+ %% (*) This one requires some thought...
+ %% We're node a, other nodes b and c:
+ %% The problem is that {in_sync, a} may arrive before {resolved, [a]} to
+ %% b from c, leading to b sending {new_nodes, [a]} to us (node a).
+ %% Therefore, we make sure we never get duplicates in Known.
+ NewNodes = lists:delete(node(), Nodes -- Known),
+ gen_server:abcast(NewNodes, global_name_server, {in_sync, node(), true}),
+ S#state{known = Known ++ NewNodes}.
+
+
+
+
+
+do_whereis(Name, From) ->
+ case is_lock_set(global) of
+ false ->
+ gen_server:reply(From, where(Name));
+ true ->
+ send_again({whereis, Name, From})
+ end.
+
+terminate(_Reason, _S) ->
+ ets:delete(global_names),
+ ets:delete(global_names_ext),
+ ets:delete(global_locks).
+
+code_change(_OldVsn, S, _Extra) ->
+ {ok, S}.
+
+%% Resend init_connect to ourselves.
+resend_pre_connect(Node) ->
+ case erase({pre_connect, Node}) of
+% {Vsn, InitMsg, undef} ->
+% %% Vsn 1 & 2
+% ?PRINT({resend_pre_connect2, node(), self(), Node}),
+% gen_server:cast(self(), {init_connect, Vsn, Node, InitMsg});
+ {Vsn, InitMsg, HisTag} ->
+ %% Vsn 3
+ ?PRINT({resend_pre_connect3, node(), self(), Node}),
+ gen_server:cast(self(), {init_connect, {Vsn, HisTag}, Node, InitMsg});
+ _ ->
+ ?PRINT({resend_pre_connect0, node(), self(), Node}),
+ ok
+ end.
+
+ins_name(Name, Pid, Method) ->
+ case ets:lookup(global_names, Name) of
+ [{Name, Pid2, _}] ->
+ dounlink(Pid2);
+ [] ->
+ ok
+ end,
+ dolink(Pid),
+ ets:insert(global_names, {Name, Pid, Method}).
+
+ins_name_ext(Name, Pid, RegNode) ->
+ case ets:lookup(global_names_ext, Name) of
+ [{Name, Pid2, _}] ->
+ dounlink(Pid2);
+ [] ->
+ ok
+ end,
+ dolink_ext(Pid, RegNode),
+ ets:insert(global_names_ext, {Name, Pid, RegNode}).
+
+where(Name) ->
+ case ets:lookup(global_names, Name) of
+ [{_, Pid, _}] -> Pid;
+ [] -> undefined
+ end.
+
+handle_set_lock({ResourceId, LockRequesterId}, Pid) ->
+ case ets:lookup(global_locks, ResourceId) of
+ [{ResourceId, LockRequesterId, Pids}] ->
+ case lists:member(Pid, Pids) of
+ true ->
+ true;
+ false ->
+ dolink(Pid),
+ ets:insert(global_locks, {ResourceId, LockRequesterId, [Pid | Pids]}),
+ true
+ end;
+ [{ResourceId, _LockRequesterId2, _Pid2}] ->
+ case ResourceId of
+ global ->
+ ?P({before,
+ LockRequesterId,
+ _LockRequesterId2,
+ S#state.lockers}),
+ false;
+ _ ->
+ false
+ end;
+ [] ->
+ dolink(Pid),
+ ets:insert(global_locks, {ResourceId, LockRequesterId, [Pid]}),
+ true
+ end.
+
+is_lock_set(ResourceId) ->
+ case ets:lookup(global_locks, ResourceId) of
+ [_Lock] -> true;
+ [] -> false
+ end.
+
+handle_del_lock({ResourceId, LockRequesterId}, Pid) ->
+ case ets:lookup(global_locks, ResourceId) of
+ [{ResourceId, LockRequesterId, Pids}] when [Pid] == Pids ->
+ ets:delete(global_locks, ResourceId),
+ dounlink(Pid);
+ [{ResourceId, LockRequesterId, Pids}] ->
+ NewPids = lists:delete(Pid, Pids),
+ ets:insert(global_locks, {ResourceId, LockRequesterId, NewPids}),
+ dounlink(Pid);
+ _ -> ok
+ end.
+
+do_ops(Ops) ->
+ lists:foreach(fun({insert, Item}) -> ets:insert(global_names, Item);
+ ({delete, Name}) ->
+ case ets:lookup(global_names, Name) of
+ [{Name, Pid, _}] ->
+ ?P2({do_ops_delete, node(), Name, Pid, node(Pid)}),
+ ets:delete(global_names, Name),
+ dounlink(Pid);
+ [] ->
+ ok
+ end
+ end, Ops).
+
+%% If a new name, then it must be checked if it is an external name
+%% If delete a name it is always deleted from global_names_ext
+do_ops_ext(Ops, Names_ext) ->
+ lists:foreach(fun({insert, {Name, Pid, _Method}}) ->
+ case lists:keysearch(Name, 1, Names_ext) of
+ {value, {Name, Pid, RegNode}} ->
+ ets:insert(global_names_ext, {Name, Pid, RegNode});
+ _ ->
+ ok
+ end;
+ ({delete, Name}) ->
+ ets:delete(global_names_ext, Name)
+ end, Ops).
+
+%%-----------------------------------------------------------------
+%% A locker is a process spawned by global_name_server when a
+%% nodeup is received from a new node. Its purpose is to try to
+%% set a lock in our partition, i.e. on all nodes known to us.
+%% When the lock is set, it tells global about it, and keeps
+%% the lock set. global sends a cancel message to the locker when
+%% the partitions are connected.
+
+%% Versions: at version 2, the messages exchanged between the lockers
+%% include the known nodes (see OTP-3576). There is no way of knowing
+%% the version number of the other side's locker when sending a message
+%% to it, so we send both version 1 and 2, and flush the version 1 if
+%% we receive version 2.
+%%
+%% Due to a mistake, an intermediate version of the new locking protocol
+%% (using 3-tuples) went out in R7, which only understands itself. This patch
+%% to R7 handles all kinds, which means sending all, and flush the ones we
+%% don't want. (It will remain difficult to make a future version of the
+%% protocol communicate with this one.)
+%%
+%%-----------------------------------------------------------------
+%% (Version 2 in patched R7. No named version in R6 and older - let's call that
+%% version 1.)
+-define(locker_vsn, 2).
+
+%%% multi
+
+-record(multi, {known, others = []}).
+
+start_the_locker(Global) ->
+ spawn_link(?MODULE, init_the_locker, [Global]).
+
+%init_the_locker(Global) ->
+% ok;
+init_the_locker(Global) ->
+ process_flag(trap_exit, true), %needed?
+ loop_the_locker(Global, #multi{}),
+ erlang:error(locker_exited).
+
+remove_node(_Node, []) ->
+ [];
+remove_node(Node, [{Node, _HisTheLocker, _HisKnown, _MyTag} | Rest]) ->
+ Rest;
+remove_node(Node, [E | Rest]) ->
+ [E | remove_node(Node, Rest)].
+
+find_node_tag(_Node, []) ->
+ false;
+find_node_tag(Node, [{Node, _HisTheLocker, _HisKnown, MyTag} | _Rest]) ->
+ {true, MyTag};
+find_node_tag(Node, [_E | Rest]) ->
+ find_node_tag(Node, Rest).
+
+loop_the_locker(Global, S) ->
+ ?P2({others, node(), S#multi.others}),
+% Known = S#multi.known,
+ Timeout = case S#multi.others of
+ [] ->
+ infinity;
+ _ ->
+ 0
+ end,
+ receive
+% {nodeup, Node, Known, Tag, P} ->
+% ?P2({the_locker, nodeup, time(), node(), nodeup, Node, Tag}),
+% loop_the_locker(Global, S);
+ {his_the_locker, HisTheLocker, HisKnown, MyKnown} ->
+ ?P2({his_the_locker, time(), node(), HisTheLocker,
+ node(HisTheLocker)}),
+ receive
+ {nodeup, Node, _Known, MyTag, _P} when node(HisTheLocker) == Node ->
+ ?P2({the_locker, nodeup, node(), Node,
+ node(HisTheLocker), MyTag,
+ process_info(self(), messages)}),
+ Others = S#multi.others,
+ loop_the_locker(Global,
+ S#multi{known=MyKnown,
+ others=[{node(HisTheLocker), HisTheLocker, HisKnown, MyTag} | Others]});
+ {cancel, Node, _Tag} when node(HisTheLocker) == Node ->
+ loop_the_locker(Global, S)
+ after 60000 ->
+ ?P2({nodeupnevercame, node(), node(HisTheLocker)}),
+ error_logger:error_msg("global: nodeup never came ~w ~w~n",
+ [node(), node(HisTheLocker)]),
+ loop_the_locker(Global, S)
+ end;
+ {cancel, Node, undefined} ->
+ ?P2({the_locker, cancel1, undefined, node(), Node}),
+%% If we actually cancel something when a cancel message with the tag
+%% 'undefined' arrives, we may be acting on an old nodedown, to cancel
+%% a new nodeup, so we can't do that.
+% receive
+% {nodeup, Node, _Known, _MyTag, _P} ->
+% ?P2({the_locker, cancelnodeup1, node(), Node}),
+% ok
+% after 0 ->
+% ok
+% end,
+% Others = remove_node(Node, S#multi.others),
+% loop_the_locker(Global, S#multi{others = Others});
+ loop_the_locker(Global, S);
+ {cancel, Node, Tag} ->
+ ?P2({the_locker, cancel1, Tag, node(), Node}),
+ receive
+ {nodeup, Node, _Known, Tag, _P} ->
+ ?P2({the_locker, cancelnodeup2, node(), Node}),
+ ok
+ after 0 ->
+ ok
+ end,
+ Others = remove_node(Node, S#multi.others),
+ loop_the_locker(Global, S#multi{others = Others});
+ {lock_set, _Pid, false, _} ->
+ ?P2({the_locker, spurious, node(), node(_Pid)}),
+ loop_the_locker(Global, S);
+ {lock_set, Pid, true, HisKnown} ->
+ Node = node(Pid),
+ ?P2({the_locker, spontaneous, node(), Node}),
+
+ NewKnown = gen_server:call(global_name_server, get_known),
+
+ Others =
+ case find_node_tag(Node, S#multi.others) of
+ {true, MyTag} ->
+
+ BothsKnown = HisKnown -- (HisKnown -- NewKnown),
+ Known1 = if
+ node() < Node ->
+ [node() | NewKnown];
+ true ->
+ [node() | NewKnown] -- BothsKnown
+ end,
+
+ ?P2({lock1, node()}),
+ LockId = {global, self()},
+ IsLockSet = set_lock(LockId, Known1, 1),
+ Pid ! {lock_set, self(), IsLockSet, NewKnown},
+ ?P2({the_locker, spontaneous, node(), Node, IsLockSet}),
+ case IsLockSet of
+ true ->
+ gen_server:cast(global_name_server,
+ {lock_is_set, Node, MyTag}),
+ ?P1({lock_sync_done, time(), node(),
+ {Pid, node(Pid)}, self()}),
+ %% Wait for global to tell us to remove lock.
+ receive
+ {cancel, Node, _Tag} ->
+ %% All conflicts are resolved,
+ %% remove lock.
+ ?PRINT({node(), self(), locked1}),
+ del_lock(LockId, Known1);
+ {'EXIT', Pid, _} ->
+ ?PRINT({node(), self(), locked2}),
+ %% Other node died;
+ %% remove lock and ignore him.
+ del_lock(LockId, Known1),
+ link(Global)
+ end,
+ remove_node(Node, S#multi.others);
+ false ->
+ S#multi.others
+ end;
+ false ->
+ ?P2({the_locker, spontaneous, node(), Node, not_there}),
+ Pid ! {lock_set, self(), false, NewKnown},
+ S#multi.others
+ end,
+ loop_the_locker(Global, S#multi{others = Others});
+ Other when element(1, Other) /= nodeup ->
+ ?P2({the_locker, other_msg, Other}),
+ loop_the_locker(Global, S)
+ after Timeout ->
+ NewKnown = gen_server:call(global_name_server, get_known),
+ [{Node, HisTheLocker, HisKnown, MyTag} | Rest] = S#multi.others,
+ BothsKnown = HisKnown -- (HisKnown -- NewKnown),
+ Known1 = if
+ node() < Node ->
+ [node() | NewKnown];
+ true ->
+ [node() | NewKnown] -- BothsKnown
+ end,
+ ?P2({picking, node(), Node}),
+ case lists:member(Node, NewKnown) of
+ false ->
+ LockId = {global, self()},
+ ?P2({lock2, node()}),
+ IsLockSet = set_lock(LockId, Known1, 1),
+ Others =
+ case IsLockSet of
+ true ->
+ HisTheLocker ! {lock_set, self(),
+ IsLockSet, NewKnown},
+ %% OTP-4902
+ lock_set_loop(Global, S,
+ Node, MyTag, Rest,
+ Known1,
+ LockId);
+ false ->
+ ?P2({the_locker, not_locked, node(),
+ Node}),
+ S#multi.others
+ end,
+ loop_the_locker(Global, S#multi{known=NewKnown,
+ others = Others});
+ true ->
+ ?P2({is_known, node(), Node}),
+ loop_the_locker(Global, S#multi{known=NewKnown,
+ others = Rest})
+ end
+ end.
+
+lock_set_loop(Global, S, Node, MyTag, Rest, Known1, LockId) ->
+ receive
+ {lock_set, P, true, _} when node(P) == Node ->
+ ?P2({the_locker, both_set, node(), Node}),
+
+ %% do sync
+ gen_server:cast(global_name_server, {lock_is_set, Node, MyTag}),
+ ?P1({lock_sync_done, time(), node(), {Pid, node(Pid)}, self()}),
+
+ %% Wait for global to tell us to remove lock.
+ receive
+ {cancel, Node, _} ->
+ %% All conflicts are resolved, remove lock.
+ ?PRINT({node(), self(), locked1}),
+ del_lock(LockId, Known1);
+ {'EXIT', _Pid, _} ->
+ ?PRINT({node(), self(), locked2}),
+ %% Other node died; remove lock and ignore him.
+ del_lock(LockId, Known1),
+ link(Global)
+ end,
+ Rest;
+ {lock_set, P, false, _} when node(P) == Node ->
+ ?P2({the_locker, not_both_set, node(), Node}),
+ del_lock(LockId, Known1),
+ S#multi.others;
+ {cancel, Node, _} ->
+ ?P2({the_locker, cancel2, node(), Node}),
+ del_lock(LockId, Known1),
+ remove_node(Node, S#multi.others);
+ {'EXIT', _, _} ->
+ ?P2({the_locker, exit, node(), Node}),
+ del_lock(LockId, Known1),
+ S#multi.others
+
+ after
+ %% OTP-4902
+ %% A cyclic deadlock could occur in rare cases where three or
+ %% more nodes waited for a reply from each other.
+ %% Therefore, reject lock_set attempts in this state from
+ %% nodes < this node (its enough if at least one node in
+ %% the cycle rejects and thus breaks the deadlock)
+ 5000 ->
+ reject_lock_set(),
+ lock_set_loop(Global, S, Node, MyTag, Rest, Known1, LockId)
+ end.
+
+reject_lock_set() ->
+ receive
+ {lock_set, P, true, _} when node(P) < node() ->
+ P ! {lock_set, self(), false, []},
+ reject_lock_set()
+ after
+ 0 ->
+ true
+ end.
+
+start_locker(Node, Known, MyTag, Global, TheLocker) ->
+ %% No link here! The del_lock call would delete the link anyway.
+ %% global_name_server has control of these processes anyway...
+ %% When the locker process exits due to being sent the 'cancel' message
+ %% by the server, the server then removes it from its tables.
+ %% When the locker terminates due to other reasons, the server must
+ %% be told, so we make a link to it just before exiting.
+ spawn(?MODULE, init_locker, [Node, Known, MyTag, Global, TheLocker]).
+
+init_locker(Node, Known, MyTag, Global, TheLocker) ->
+ process_flag(trap_exit, true),
+ ?PRINT({init_locker, node(), self(), Node}),
+ ?P1({init_locker, time(), node(), self(), Node}),
+ receive
+ {his_locker, Pid, HisKnown} ->
+ ?PRINT({init_locker, node(), self(), his_locker, Node}),
+ link(Pid),
+ %% If two nodes in a group of nodes first disconnect
+ %% and then reconnect, this causes global to deadlock.
+ %% This because both of the reconnecting nodes
+ %% tries to set lock on the other nodes in the group.
+ %% This is solved by letting only one of the reconneting nodes set the lock.
+ BothsKnown = HisKnown -- (HisKnown -- Known),
+ ?P({loop_locker1, node(), {Pid, node(Pid)}}),
+ Res = loop_locker(Node, Pid, Known, 1, MyTag, BothsKnown, Global),
+ ?P({loop_locker2, node(), {Pid, node(Pid)}}),
+ Res;
+ {his_locker_new, HisTheLocker, {Known1, Known2}} ->
+ %% slide into the vsn 4 stuff
+ ?P2({his_locker_new, node()}),
+ HisTheLocker ! {his_the_locker, TheLocker, Known1, Known2},
+ exit(normal);
+ cancel ->
+ ?PRINT({init_locker, node(), self(), cancel, Node}),
+ exit(normal)
+ end.
+
+loop_locker(Node, Pid, Known0, Try, MyTag, BothsKnown, Global) ->
+ Known = if
+ node() < Node ->
+ [node() | Known0];
+ true ->
+ [node() | Known0] -- BothsKnown
+ end,
+
+ ?PRINT({locking, node(), self(), Known}),
+ LockId = {global, self()},
+ ?P2({lock3, node()}),
+ IsLockSet = set_lock(LockId, Known, 1),
+ ?P({loop_locker, IsLockSet,
+ node(), {Pid, node(Pid)}, self(), Try}),
+ ?P1({loop_locker, time(), IsLockSet,
+ node(), {Pid, node(Pid)}, self(), Try}),
+ ?PRINT({locking1, node(), self(), Known, IsLockSet}),
+ %% Tell other node that we managed to get the lock.
+ Pid ! {lock, ?locker_vsn, IsLockSet, Known},
+ Pid ! {lock, IsLockSet, Known},
+ Pid ! {lock, IsLockSet},
+ %% Wait for other node's result.
+ receive
+ %% R7 patched and later
+ {lock, _LockerVsn, true, _} when IsLockSet == true ->
+ receive
+ {lock, _} ->
+ ok
+ end,
+ receive
+ {lock, _, _} ->
+ ok
+ end,
+ ?PRINT({node(), self(), locked}),
+ %% Now we got the lock in both partitions. Tell
+ %% global, and let him resolve name conflict.
+ ?P1({lock_sync, time(), node(), {Pid, node(Pid)}, self()}),
+ gen_server:cast(global_name_server, {lock_is_set, Node, MyTag}),
+ ?P1({lock_sync_done, time(), node(), {Pid, node(Pid)}, self()}),
+ %% Wait for global to tell us to remove lock.
+ receive
+ cancel ->
+ %% All conflicts are resolved, remove lock.
+ ?PRINT({node(), self(), locked1}),
+ del_lock(LockId, Known);
+ {'EXIT', Pid, _} ->
+ ?PRINT({node(), self(), locked2}),
+ %% Other node died; remove lock and ignore him.
+ del_lock(LockId, Known),
+ link(Global)
+ end;
+ {lock, _LockerVsn, _, HisKnown} ->
+ receive
+ {lock, _} ->
+ ok
+ end,
+ receive
+ {lock, _, _} ->
+ ok
+ end,
+ %% Some of us failed to get the lock; try again
+ ?PRINT({node(), self(), locked0}),
+ d_lock(IsLockSet, LockId, Known),
+ try_again_locker(Node, Pid, Try, MyTag, HisKnown, Global);
+ %% R7 unpatched
+ {lock, true, _} when IsLockSet == true ->
+ ?PRINT({node(), self(), locked}),
+ %% Now we got the lock in both partitions. Tell
+ %% global, and let him resolve name conflict.
+ gen_server:cast(global_name_server, {lock_is_set, Node, MyTag}),
+ %% Wait for global to tell us to remove lock.
+ receive
+ cancel ->
+ %% All conflicts are resolved, remove lock.
+ ?PRINT({node(), self(), locked1}),
+ del_lock(LockId, Known);
+ {'EXIT', Pid, _} ->
+ ?PRINT({node(), self(), locked2}),
+ %% Other node died; remove lock and ignore him.
+ del_lock(LockId, Known),
+ link(Global)
+ end;
+ {lock, _, HisKnown} ->
+ %% Some of us failed to get the lock; try again
+ ?PRINT({node(), self(), locked0}),
+ d_lock(IsLockSet, LockId, Known),
+ try_again_locker(Node, Pid, Try, MyTag, HisKnown, Global);
+ %% R6 and earlier
+ {lock, true} when IsLockSet == true ->
+ ?PRINT({node(), self(), locked}),
+ %% Now we got the lock in both partitions. Tell
+ %% global, and let him resolve name conflict.
+ gen_server:cast(global_name_server, {lock_is_set, Node, MyTag}),
+ %% Wait for global to tell us to remove lock.
+ receive
+ cancel ->
+ %% All conflicts are resolved, remove lock.
+ ?PRINT({node(), self(), locked1}),
+ del_lock(LockId, Known);
+ {'EXIT', Pid, _} ->
+ ?PRINT({node(), self(), locked2}),
+ %% Other node died; remove lock and ignore him.
+ del_lock(LockId, Known),
+ link(Global)
+ end;
+ {lock, _} ->
+ %% Some of us failed to get the lock; try again
+ ?PRINT({node(), self(), locked0}),
+ d_lock(IsLockSet, LockId, Known),
+ try_again_locker(Node, Pid, Try, MyTag, BothsKnown, Global);
+ {'EXIT', Pid, _} ->
+ %% Other node died; remove lock and ignore him.
+ ?PRINT({node(), self(), locked7}),
+ d_lock(IsLockSet, LockId, Known),
+ link(Global);
+ cancel ->
+ ?PRINT({node(), self(), locked8}),
+ d_lock(IsLockSet, LockId, Known)
+ end.
+
+d_lock(true, LockId, Known) -> del_lock(LockId, Known);
+d_lock(false, _, _) -> ok.
+
+try_again_locker(Node, Pid, Try, MyTag, HisKnown, Global) ->
+ ?PRINT({try_again, node(), self(), Node, Pid, Known, Try, MyTag}),
+ ?P1({try_again, time(), node(), self(), Node, Pid, Known, Try, MyTag}),
+ random_sleep(Try),
+ ?P1({try_again2, time(), node(), self(), Node, Pid, Known, Try, MyTag}),
+ NewKnown = gen_server:call(global_name_server, get_known),
+ case lists:member(Node, NewKnown) of
+ false ->
+ BothsKnown1 = HisKnown -- (HisKnown -- NewKnown),
+ ?PRINT({node(), self(), Node, again, notknown}),
+ ?PRINT({bothknown, BothsKnown, BothsKnown1}),
+ loop_locker(Node, Pid, NewKnown, Try+1, MyTag,
+ BothsKnown1, Global);
+ true ->
+ ?PRINT({node(), self(), Node, again, known}),
+ link(Global),
+ %% Node is already handled, we are ready.
+ ok
+ end.
+
+cancel_locker(Node, S) ->
+ %% multi
+ ?P2({cancel, node(), Node, get({sync_tag_my, Node})}),
+ S#state.the_locker ! {cancel, Node, get({sync_tag_my, Node})},
+
+ Lockers = S#state.lockers,
+ case lists:keysearch(Node, 1, Lockers) of
+ {value, {_, Pid}} ->
+ Pid ! cancel,
+ ?PRINT({cancel, Node, lockers, node(), Lockers}),
+ S#state{lockers = lists:keydelete(Node, 1, Lockers)};
+ _ ->
+ S
+ end.
+
+%% A node sent us his names. When a name clash is found, the resolve
+%% function is called from the smaller node => all resolve funcs are called
+%% from the same partition.
+exchange_names([{Name, Pid, Method} |Tail], Node, Ops, Res) ->
+ case ets:lookup(global_names, Name) of
+ [{Name, Pid, _}] ->
+ exchange_names(Tail, Node, Ops, Res);
+ [{Name, Pid2, Method2}] when node() < Node ->
+ %% Name clash! Add the result of resolving to Res(olved).
+ %% We know that node(Pid) /= node(), so we don't
+ %% need to link/unlink to Pid.
+ Node2 = node(Pid2), %%&&&&&& check external node???
+ case rpc:call(Node2, ?MODULE, resolve_it,
+ [Method2, Name, Pid, Pid2]) of
+ Pid ->
+ dounlink(Pid2),
+ ets:insert(global_names, {Name, Pid, Method}),
+ Op = {insert, {Name, Pid, Method}},
+ exchange_names(Tail, Node, [Op | Ops], [Op | Res]);
+ Pid2 ->
+ Op = {insert, {Name, Pid2, Method2}},
+ exchange_names(Tail, Node, Ops, [Op | Res]);
+ none ->
+ dounlink(Pid2),
+ ?P2({unregister, node(), Name, Pid2, node(Pid2)}),
+ ets:delete(global_names, Name),
+ Op = {delete, Name},
+ exchange_names(Tail, Node, [Op | Ops], [Op | Res]);
+ {badrpc, Badrpc} ->
+ error_logger:info_msg("global: badrpc ~w received when "
+ "conflicting name ~w was found",
+ [Badrpc, Name]),
+ dounlink(Pid2),
+ ets:insert(global_names, {Name, Pid, Method}),
+ Op = {insert, {Name, Pid, Method}},
+ exchange_names(Tail, Node, [Op | Ops], [Op | Res]);
+ Else ->
+ error_logger:info_msg("global: Resolve method ~w for "
+ "conflicting name ~w returned ~w~n",
+ [Method, Name, Else]),
+ dounlink(Pid2),
+ ets:delete(global_names, Name),
+ Op = {delete, Name},
+ exchange_names(Tail, Node, [Op | Ops], [Op | Res])
+ end;
+ [{Name, _Pid2, _}] ->
+ %% The other node will solve the conflict.
+ exchange_names(Tail, Node, Ops, Res);
+ _ ->
+ %% Entirely new name.
+ ets:insert(global_names, {Name, Pid, Method}),
+ exchange_names(Tail, Node,
+ [{insert, {Name, Pid, Method}} | Ops], Res)
+ end;
+exchange_names([], _, Ops, Res) ->
+ {Ops, Res}.
+
+resolve_it(Method, Name, Pid1, Pid2) ->
+ catch Method(Name, Pid1, Pid2).
+
+minmax(P1,P2) ->
+ if node(P1) < node(P2) -> {P1, P2}; true -> {P2, P1} end.
+
+random_exit_name(Name, Pid, Pid2) ->
+ {Min, Max} = minmax(Pid, Pid2),
+ error_logger:info_msg("global: Name conflict terminating ~w~n",
+ [{Name, Max}]),
+ exit(Max, kill),
+ Min.
+
+random_notify_name(Name, Pid, Pid2) ->
+ {Min, Max} = minmax(Pid, Pid2),
+ Max ! {global_name_conflict, Name},
+ Min.
+
+notify_all_name(Name, Pid, Pid2) ->
+ Pid ! {global_name_conflict, Name, Pid2},
+ Pid2 ! {global_name_conflict, Name, Pid},
+ none.
+
+cnode(Name, Pid, Pid2) ->
+ {Min, Max} = minmax(Pid, Pid2),
+ error_logger:info_msg("global: Name conflict terminating ~w~n",
+ [{Name, Max}]),
+ Max ! {global_name_conflict, Name},
+ Min.
+
+%% Only link to pids on our own node
+dolink(Pid) when node(Pid) == node() ->
+ link(Pid);
+dolink(_) -> ok.
+
+%% Only link to pids on our own node
+dolink_ext(Pid, RegNode) when RegNode == node() -> link(Pid);
+dolink_ext(_, _) -> ok.
+
+dounlink(Pid) when node(Pid) == node() ->
+ case ets:match(global_names, {'_', Pid, '_'}) of
+ [] ->
+ case is_pid_used(Pid) of
+ false ->
+ unlink(Pid);
+ true -> ok
+ end;
+ _ -> ok
+ end;
+dounlink(_Pid) ->
+ ok.
+
+is_pid_used(Pid) ->
+ is_pid_used(ets:tab2list(global_locks), Pid).
+
+is_pid_used([], _Pid) ->
+ false;
+is_pid_used([{_ResourceId, _LockReqId, Pids} | Tail], Pid) ->
+ case lists:member(Pid, Pids) of
+ true ->
+ true;
+ false ->
+ is_pid_used(Tail, Pid)
+ end.
+
+
+
+%% check_exit/3 removes the Pid from affected tables.
+%% This function needs to abcast the thingie since only the local
+%% server is linked to the registered process (or the owner of the
+%% lock). All the other servers rely on the nodedown mechanism.
+check_exit(Deleter, Pid) ->
+ del_names(Deleter, Pid, ets:tab2list(global_names)),
+ del_locks(ets:tab2list(global_locks), Pid).
+
+del_names(Deleter, Pid, [{Name, Pid, _Method} | Tail]) ->
+ %% First, delete the Pid from the local ets; then send to other nodes
+ ets:delete(global_names, Name),
+ ets:delete(global_names_ext, Name),
+ dounlink(Pid),
+ Deleter ! {delete_name,self(),Name,Pid},
+ del_names(Deleter, Pid, Tail);
+del_names(Deleter, Pid, [_|T]) ->
+ del_names(Deleter, Pid, T);
+del_names(_Deleter, _Pid, []) -> done.
+
+del_locks([{ResourceId, LockReqId, Pids} | Tail], Pid) ->
+ case {lists:member(Pid, Pids), Pids} of
+ {true, [Pid]} ->
+ ets:delete(global_locks, ResourceId),
+ gen_server:abcast(nodes(), global_name_server,
+ {async_del_lock, ResourceId, Pid});
+ {true, _} ->
+ NewPids = lists:delete(Pid, Pids),
+ ets:insert(global_locks, {ResourceId, LockReqId, NewPids}),
+ gen_server:abcast(nodes(), global_name_server,
+ {async_del_lock, ResourceId, Pid});
+ _ ->
+ continue
+ end,
+ del_locks(Tail, Pid);
+del_locks([], _Pid) -> done.
+
+del_locks2([{ResourceId, LockReqId, Pids} | Tail], Pid) ->
+ case {lists:member(Pid, Pids), Pids} of
+ {true, [Pid]} ->
+ ets:delete(global_locks, ResourceId);
+ {true, _} ->
+ NewPids = lists:delete(Pid, Pids),
+ ets:insert(global_locks, {ResourceId, LockReqId, NewPids});
+ _ ->
+ continue
+ end,
+ del_locks2(Tail, Pid);
+del_locks2([], _Pid) ->
+ done.
+
+
+
+%% Unregister all Name/Pid pairs such that node(Pid) == Node
+%% and delete all locks where node(Pid) == Node
+do_node_down(Node) ->
+ do_node_down_names(Node, ets:tab2list(global_names)),
+ do_node_down_names_ext(Node, ets:tab2list(global_names_ext)),
+ do_node_down_locks(Node, ets:tab2list(global_locks)).
+
+do_node_down_names(Node, [{Name, Pid, _Method} | T]) when node(Pid) == Node ->
+ ets:delete(global_names, Name),
+ do_node_down_names(Node, T);
+do_node_down_names(Node, [_|T]) ->
+ do_node_down_names(Node, T);
+do_node_down_names(_, []) -> ok.
+
+%%remove all external names registered on the crashed node
+do_node_down_names_ext(Node, [{Name, _Pid, Node} | T]) ->
+ ets:delete(global_names, Name),
+ ets:delete(global_names_ext, Name),
+ do_node_down_names_ext(Node, T);
+do_node_down_names_ext(Node, [_|T]) ->
+ do_node_down_names_ext(Node, T);
+do_node_down_names_ext(_, []) -> ok.
+
+do_node_down_locks(Node, [{ResourceId, LockReqId, Pids} | T]) ->
+ case do_node_down_locks2(Pids, Node) of
+ [] ->
+ continue;
+ RemovePids ->
+ case Pids -- RemovePids of
+ [] ->
+ ets:delete(global_locks, ResourceId);
+ NewPids ->
+ ets:insert(global_locks, {ResourceId, LockReqId, NewPids})
+ end
+ end,
+ do_node_down_locks(Node, T);
+do_node_down_locks(Node, [_|T]) ->
+ do_node_down_locks(Node, T);
+do_node_down_locks(_, []) -> done.
+
+
+do_node_down_locks2(Pids, Node) ->
+ do_node_down_locks2(Pids, Node, []).
+
+do_node_down_locks2([], _Node, Res) ->
+ Res;
+do_node_down_locks2([Pid | Pids], Node, Res) when node(Pid) == Node ->
+ do_node_down_locks2(Pids, Node, [Pid | Res]);
+do_node_down_locks2([_ | Pids], Node, Res) ->
+ do_node_down_locks2(Pids, Node, Res).
+
+
+get_names() ->
+ ets:tab2list(global_names).
+
+get_names_ext() ->
+ ets:tab2list(global_names_ext).
+
+random_sleep(Times) ->
+ case (Times rem 10) of
+ 0 -> erase(random_seed);
+ _ -> ok
+ end,
+ case get(random_seed) of
+ undefined ->
+ {A1, A2, A3} = now(),
+ random:seed(A1, A2, A3 + erlang:phash(node(), 100000));
+ _ -> ok
+ end,
+ %% First time 1/4 seconds, then doubling each time up to 8 seconds max.
+ Tmax = if Times > 5 -> 8000;
+ true -> ((1 bsl Times) * 1000) div 8
+ end,
+ T = random:uniform(Tmax),
+ ?P({random_sleep, node(), self(), Times, T}),
+ receive after T -> ok end.
+
+dec(infinity) -> infinity;
+dec(N) -> N-1.
+
+send_again(Msg) ->
+ spawn_link(?MODULE, timer, [self(), Msg]).
+
+timer(Pid, Msg) ->
+ random_sleep(5),
+ Pid ! Msg.
+
+change_our_node_name(NewNode, S) ->
+ S#state{node_name = NewNode}.
+
+
+%%-----------------------------------------------------------------
+%% Each sync process corresponds to one call to sync. Each such
+%% process asks the global_name_server on all Nodes if it is in sync
+%% with Nodes. If not, that (other) node spawns a syncer process that
+%% waits for global to get in sync with all Nodes. When it is in
+%% sync, the syncer process tells the original sync process about it.
+%%-----------------------------------------------------------------
+start_sync(Nodes, From) ->
+ spawn_link(?MODULE, sync_init, [Nodes, From]).
+
+sync_init(Nodes, From) ->
+ lists:foreach(fun(Node) -> monitor_node(Node, true) end, Nodes),
+ sync_loop(Nodes, From).
+
+sync_loop([], From) ->
+ gen_server:reply(From, ok);
+sync_loop(Nodes, From) ->
+ receive
+ {nodedown, Node} ->
+ monitor_node(Node, false),
+ sync_loop(lists:delete(Node, Nodes), From);
+ {synced, SNodes} ->
+ lists:foreach(fun(N) -> monitor_node(N, false) end, SNodes),
+ sync_loop(Nodes -- SNodes, From)
+ end.
+
+
+%%%====================================================================================
+%%% Get the current global_groups definition
+%%%====================================================================================
+check_sync_nodes() ->
+ case get_own_nodes() of
+ {ok, all} ->
+ nodes();
+ {ok, NodesNG} ->
+ %% global_groups parameter is defined, we are not allowed to sync
+ %% with nodes not in our own global group.
+ (nodes() -- (nodes() -- NodesNG));
+ {error, Error} ->
+ {error, Error}
+ end.
+
+check_sync_nodes(SyncNodes) ->
+ case get_own_nodes() of
+ {ok, all} ->
+ SyncNodes;
+ {ok, NodesNG} ->
+ %% global_groups parameter is defined, we are not allowed to sync
+ %% with nodes not in our own global group.
+ OwnNodeGroup = (nodes() -- (nodes() -- NodesNG)),
+ IllegalSyncNodes = (SyncNodes -- [node() | OwnNodeGroup]),
+ case IllegalSyncNodes of
+ [] -> SyncNodes;
+ _ -> {error, {"Trying to sync nodes not defined in the own global group",
+ IllegalSyncNodes}}
+ end;
+ {error, Error} ->
+ {error, Error}
+ end.
+
+get_own_nodes() ->
+ case global_group:get_own_nodes_with_errors() of
+ {error, Error} ->
+ {error, {"global_groups definition error", Error}};
+ OkTup ->
+ OkTup
+ end.
+
+
+%%-----------------------------------------------------------------
+%% The deleter process is a satellite process to global_name_server
+%% that does background batch deleting of names when a process
+%% that had globally registered names dies. It is started by and
+%% linked to global_name_server.
+%%-----------------------------------------------------------------
+
+start_the_deleter(Global) ->
+ spawn_link(
+ fun () ->
+ loop_the_deleter(Global)
+ end).
+
+loop_the_deleter(Global) ->
+ Deletions = collect_deletions(Global, []),
+ trans({global, self()},
+ fun() ->
+ lists:map(
+ fun ({Name,Pid}) ->
+ ?P2({delete_name2, Name, Pid, nodes()}),
+ gen_server:abcast(nodes(), global_name_server,
+ {async_del_name, Name, Pid})
+ end, Deletions)
+ end,
+ nodes()),
+ loop_the_deleter(Global).
+
+collect_deletions(Global, Deletions) ->
+ receive
+ {delete_name,Global,Name,Pid} ->
+ ?P2({delete_name, node(), self(), Name, Pid, nodes()}),
+ collect_deletions(Global, [{Name,Pid}|Deletions]);
+ Other ->
+ error_logger:error_msg("The global_name_server deleter process "
+ "received an unexpected message:\n~p\n",
+ [Other]),
+ collect_deletions(Global, Deletions)
+ after case Deletions of
+ [] -> infinity;
+ _ -> 0
+ end ->
+ lists:reverse(Deletions)
+ end.
diff --git a/lib/dialyzer/test/options2_tests_SUITE_data/dialyzer_options b/lib/dialyzer/test/options2_tests_SUITE_data/dialyzer_options
deleted file mode 100644
index 5db2e50d23..0000000000
--- a/lib/dialyzer/test/options2_tests_SUITE_data/dialyzer_options
+++ /dev/null
@@ -1 +0,0 @@
-{dialyzer_options, [{defines, [{'vsn', 4}]}, {warnings, [no_return]}]}.
diff --git a/lib/dialyzer/test/options2_tests_SUITE_data/results/kernel b/lib/dialyzer/test/options2_tests_SUITE_data/results/kernel
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/lib/dialyzer/test/options2_tests_SUITE_data/src/kernel/global.erl b/lib/dialyzer/test/options2_tests_SUITE_data/src/kernel/global.erl
deleted file mode 100644
index 1f0e01d074..0000000000
--- a/lib/dialyzer/test/options2_tests_SUITE_data/src/kernel/global.erl
+++ /dev/null
@@ -1,1999 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: global.erl,v 1.4 2009/09/17 09:46:19 kostis Exp $
-%%
--module(global).
--behaviour(gen_server).
-
-%% A Global register that allows the global registration of pid's and
-%% name's, that dynamically keeps up to date with the entire network.
-%% global can operate in two modes; in a fully connected network, or
-%% in a non-fully connected network. In the latter case, the name
-%% registration mechanism won't work.
-%%
-
-%% External exports
--export([start/0, start_link/0, stop/0, sync/0, sync/1,
- safe_whereis_name/1, whereis_name/1, register_name/2, register_name/3,
- register_name_external/2, register_name_external/3, unregister_name_external/1,
- re_register_name/2, re_register_name/3,
- unregister_name/1, registered_names/0, send/2, node_disconnected/1,
- set_lock/1, set_lock/2, set_lock/3,
- del_lock/1, del_lock/2,
- trans/2, trans/3, trans/4,
- random_exit_name/3, random_notify_name/3, notify_all_name/3, cnode/3]).
-
-%% Internal exports
--export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
- code_change/3, timer/2, sync_init/2, init_locker/5, resolve_it/4,
- init_the_locker/1]).
-
--export([info/0]).
-
-
-%-define(PRINT(X), erlang:display(X)).
--define(PRINT(X), true).
-
-%-define(P2(X), erlang:display(X)).
-%-define(P2(X), erlang:display({cs(),X})).
--define(P2(X), true).
-
-%-define(P1(X), erlang:display(X)).
--define(P1(X), true).
-
-%-define(P(X), erlang:display(X)).
--define(P(X), true).
-
-%-define(FORMAT(S, A), format(S, A)).
--define(FORMAT(S, A), ok).
-
-%%% In certain places in the server, calling io:format hangs everything,
-%%% so we'd better use erlang:display/1.
-% format(S, A) ->
-% erlang:display({format, cs(), S, A}),
-% % io:format(S, A),
-% ok.
-
-% cs() ->
-% {Big, Small, Tiny} = now(),
-% (Small rem 100) * 100 + (Tiny div 10000).
-
-%% Some notes on the internal structure:
-%% One invariant is that the list of locker processes is keyed; i.e.,
-%% there is only one process per neighboring node.
-%% When an item has been stored in the process dictionary, it is not
-%% necessarily cleared when not in use anymore. In other words, it's
-%% not an error if there is already an item there when one is to be
-%% stored.
-
-
-%% This is the protocol version
-%% Vsn 1 is the original protocol.
-%% Vsn 2 is enhanced with code to take care of registration of names from
-%% non erlang nodes, e.g. c-nodes.
-%% Vsn 3 is enhanced with a tag in the synch messages to distinguish
-%% different synch sessions from each other, see OTP-2766.
-%% Note: This requires also that the ticket OTP-2928 is fixed on the nodes
-%% running vsn 1 or 2; if such nodes will coexist with vsn 3 nodes.
-%% Vsn 4 uses a single, permanent, locker process, but works like vsn 3
-%% when communicating with vsn 3 nodes.
-
-%% -define(vsn, 4). %% Now given in options
-
-%%-----------------------------------------------------------------
-%% connect_all = boolean() - true if we are supposed to set up a
-%% fully connected net
-%% known = [Node] - all nodes known to us
-%% synced = [Node] - all nodes that have the same names as us
-%% lockers = [{Node, MyLockerPid}] - the pid of the locker
-%% process for each Node
-%% syncers = [pid()] - all current syncers processes
-%% node_name = atom() - our node name (can change if distribution
-%% is started/stopped dynamically)
-%%
-%% In addition to these, we keep info about messages arrived in
-%% the process dictionary:
-%% {pre_connect, Node} = {Vsn, InitMsg} - init_connect msgs that
-%% arrived before nodeup
-%% {wait_lock, Node} = {exchange, NameList} | lock_is_set
-%% - see comment below (handle_cast)
-%% {save_ops, Node} = [operation()] - save the ops between
-%% exchange and resolved
-%% {prot_vsn, Node} = Vsn - the exchange protocol version
-%% {sync_tag_my, Node} = My tag, used at synchronization with Node
-%% {sync_tag_his, Node} = The Node's tag, used at synchronization
-%%-----------------------------------------------------------------
--record(state, {connect_all, known = [], synced = [],
- lockers = [], syncers = [], node_name = node(),
- the_locker, the_deleter}).
-
-start() -> gen_server:start({local, global_name_server}, global, [], []).
-start_link() -> gen_server:start_link({local, global_name_server},global,[],[]).
-stop() -> gen_server:call(global_name_server, stop, infinity).
-
-sync() ->
- case check_sync_nodes() of
- {error, Error} ->
- {error, Error};
- SyncNodes ->
- gen_server:call(global_name_server, {sync, SyncNodes}, infinity)
- end.
-sync(Nodes) ->
- case check_sync_nodes(Nodes) of
- {error, Error} ->
- {error, Error};
- SyncNodes ->
- gen_server:call(global_name_server, {sync, SyncNodes}, infinity)
- end.
-
-
-send(Name, Msg) ->
- case whereis_name(Name) of
- Pid when pid(Pid) ->
- Pid ! Msg,
- Pid;
- undefined ->
- exit({badarg, {Name, Msg}})
- end.
-
-%% See OTP-3737. (safe_whereis_name/1 is in fact not used anywhere in OTP.)
-whereis_name(Name) ->
- where(Name).
-
-safe_whereis_name(Name) ->
- gen_server:call(global_name_server, {whereis, Name}, infinity).
-
-
-node_disconnected(Node) ->
- global_name_server ! {nodedown, Node}.
-
-
-%%-----------------------------------------------------------------
-%% Method = function(Name, Pid1, Pid2) -> Pid | Pid2 | none
-%% Method is called if a name conflict is detected when two nodes
-%% are connecting to each other. It is supposed to return one of
-%% the Pids or 'none'. If a pid is returned, that pid is
-%% registered as Name on all nodes. If 'none' is returned, the
-%% Name is unregistered on all nodes. If anything else is returned,
-%% the Name is unregistered as well.
-%% Method is called once at one of the nodes where the processes reside
-%% only. If different Methods are used for the same name, it is
-%% undefined which one of them is used.
-%% Method is blocking, i.e. when it is called, no calls to whereis/
-%% send is let through until it has returned.
-%%-----------------------------------------------------------------
-register_name(Name, Pid) when pid(Pid) ->
- register_name(Name, Pid, {global, random_exit_name}).
-register_name(Name, Pid, Method) when pid(Pid) ->
- trans_all_known(fun(Nodes) ->
- case where(Name) of
- undefined ->
- gen_server:multi_call(Nodes,
- global_name_server,
- {register, Name, Pid, Method}),
- yes;
- _Pid -> no
- end
- end).
-
-unregister_name(Name) ->
- case where(Name) of
- undefined ->
- ok;
- _ ->
- trans_all_known(fun(Nodes) ->
- gen_server:multi_call(Nodes,
- global_name_server,
- {unregister, Name}),
- ok
- end)
- end.
-
-re_register_name(Name, Pid) when pid(Pid) ->
- re_register_name(Name, Pid, {global, random_exit_name}).
-re_register_name(Name, Pid, Method) when pid(Pid) ->
- trans_all_known(fun(Nodes) ->
- gen_server:multi_call(Nodes,
- global_name_server,
- {register, Name, Pid, Method}),
- yes
- end).
-
-%% Returns all globally registered names
-registered_names() -> lists:map(fun({Name, _Pid, _Method}) -> Name end,
- ets:tab2list(global_names)).
-
-%%-----------------------------------------------------------------
-%% An external node (i.e not an erlang node) (un)registers a name.
-%% If the registered Pid crashes the name is to be removed from global.
-%% If the external node crashes the name is to be removed from global.
-%% If the erlang node which registers the name crashes the name is also to be
-%% removed, because the registered process is not supervised any more,
-%% (i.e there is no link to the registered Pid).
-%%-----------------------------------------------------------------
-register_name_external(Name, Pid) when pid(Pid) ->
- register_name_external(Name, Pid, {global, random_exit_name}).
-register_name_external(Name, Pid, Method) when pid(Pid) ->
- trans_all_known(fun(Nodes) ->
- case where(Name) of
- undefined ->
- gen_server:multi_call(Nodes,
- global_name_server,
- {register, Name, Pid, Method}),
- gen_server:multi_call(Nodes,
- global_name_server,
- {register_ext, Name, Pid, node()}),
- yes;
- _Pid -> no
- end
- end).
-
-
-
-
-unregister_name_external(Name) ->
- case where(Name) of
- undefined ->
- ok;
- _ ->
- trans_all_known(fun(Nodes) ->
- gen_server:multi_call(Nodes,
- global_name_server,
- {unregister, Name}),
- gen_server:multi_call(Nodes,
- global_name_server,
- {unregister_ext, Name}),
- ok
- end)
- end.
-
-
-
-
-
-%%-----------------------------------------------------------------
-%% Args: Id = id()
-%% Nodes = [node()]
-%% id() = {ResourceId, LockRequesterId}
-%% Retries = infinity | int() > 0
-%% Purpose: Sets a lock on the specified nodes (or all nodes if
-%% none are specified) on ResourceId for LockRequesterId. If there
-%% already exists a lock on ResourceId for another owner
-%% than LockRequesterId, false is returned, otherwise true.
-%% Returns: boolean()
-%%-----------------------------------------------------------------
-set_lock(Id) ->
- set_lock(Id, [node() | nodes()], infinity, 1).
-set_lock(Id, Nodes) ->
- set_lock(Id, Nodes, infinity, 1).
-set_lock(Id, Nodes, Retries) when Retries > 0 ->
- set_lock(Id, Nodes, Retries, 1);
-set_lock(Id, Nodes, infinity) ->
- set_lock(Id, Nodes, infinity, 1).
-set_lock(_Id, _Nodes, 0, _) -> false;
-set_lock({ResourceId, LockRequesterId}, Nodes, Retries, Times) ->
- Id = {ResourceId, LockRequesterId},
- Msg = {set_lock, Id},
- {Replies, _} =
- gen_server:multi_call(Nodes, global_name_server, Msg),
- ?P2({set_lock, node(), self(), {ResourceId, LockRequesterId},
- Nodes, Retries, Times, Replies, catch erlang:error(kaka)}),
- ?P({set_lock, node(), ResourceId,
- {LockRequesterId, node(LockRequesterId)}}),
- case check_replies(Replies, Id, Nodes) of
- true -> ?P({set_lock_true, node(), ResourceId}),
- true;
- false ->
- random_sleep(Times),
- set_lock(Id, Nodes, dec(Retries), Times+1);
- N when integer(N) ->
- ?P({sleeping, N}),
- timer:sleep(N*500),
- set_lock(Id, Nodes, Retries, Times);
- Pid when pid(Pid) ->
- ?P({waiting_for, Pid}),
- Ref = erlang:monitor(process, Pid),
- receive
- {'DOWN', Ref, process, Pid, _Reason} ->
- ?P({waited_for, Pid, _Reason}),
- set_lock(Id, Nodes, Retries, Times)
- end
- end.
-
-check_replies([{_Node, true} | T], Id, Nodes) ->
- check_replies(T, Id, Nodes);
-check_replies([{_Node, Status} | _T], Id, Nodes) ->
- gen_server:multi_call(Nodes, global_name_server, {del_lock, Id}),
- Status;
-check_replies([], _Id, _Nodes) ->
- true.
-
-del_lock(Id) ->
- del_lock(Id, [node() | nodes()]).
-del_lock({ResourceId, LockRequesterId}, Nodes) ->
- Id = {ResourceId, LockRequesterId},
- ?P2({del_lock, node(), self(), ResourceId, LockRequesterId, Nodes}),
- gen_server:multi_call(Nodes, global_name_server, {del_lock, Id}),
- true.
-
-%%-----------------------------------------------------------------
-%% Args: Id = id()
-%% Fun = fun() | {M,F}
-%% Nodes = [node()]
-%% Retries = infinity | int() > 0
-%% Purpose: Sets a lock on Id (as set_lock), and evaluates
-%% Res = Fun() on success.
-%% Returns: Res | aborted (note, if Retries is infinity, the
-%% transaction won't abort)
-%%-----------------------------------------------------------------
-trans(Id, Fun) -> trans(Id, Fun, [node() | nodes()], infinity).
-trans(Id, Fun, Nodes) -> trans(Id, Fun, Nodes, infinity).
-trans(_Id, _Fun, _Nodes, 0) -> aborted;
-trans(Id, Fun, Nodes, Retries) ->
- case set_lock(Id, Nodes, Retries) of
- true ->
- case catch Fun() of
- {'EXIT', R} ->
- del_lock(Id, Nodes),
- exit(R);
- Res ->
- del_lock(Id, Nodes),
- Res
- end;
- false ->
- aborted
- end.
-
-%%% Similar to trans(Id, Fun), but always uses global's own lock,
-%%% on all nodes known to global, making sure that no new nodes have
-%%% become known while we got the list of known nodes.
-trans_all_known(F) ->
- Id = {global, self()},
- Nodes = [node() | gen_server:call(global_name_server, get_known)],
- case set_lock(Id, Nodes) of
- true ->
- Nodes2 = [node() | gen_server:call(global_name_server, get_known)],
- case Nodes2 -- Nodes of
- [] ->
- case catch F(Nodes2) of
- {'EXIT', R} ->
- del_lock(Id, Nodes2),
- exit(R);
- Res ->
- del_lock(Id, Nodes2),
- Res
- end;
- _ ->
- del_lock(Id, Nodes),
- trans_all_known(F)
- end;
- false ->
- aborted
- end.
-
-info() ->
- gen_server:call(global_name_server, info).
-
-%%%-----------------------------------------------------------------
-%%% Call-back functions from gen_server
-%%%-----------------------------------------------------------------
-init([]) ->
- process_flag(trap_exit, true),
- ets:new(global_locks, [set, named_table, protected]),
- ets:new(global_names, [set, named_table, protected]),
- ets:new(global_names_ext, [set, named_table, protected]),
-
- %% multi
- S = #state{the_locker = start_the_locker(self()),
- the_deleter = start_the_deleter(self())},
-
- case init:get_argument(connect_all) of
- {ok, [["false"]]} ->
- {ok, S#state{connect_all = false}};
- _ ->
- {ok, S#state{connect_all = true}}
- end.
-
-%%-----------------------------------------------------------------
-%% Connection algorithm
-%% ====================
-%% This alg solves the problem with partitioned nets as well.
-%%
-%% The main idea in the alg is that when two nodes connect, they
-%% try to set a lock in their own partition (i.e. all nodes already
-%% known to them). When the lock is set in each partition, these
-%% two nodes send each other a list with all registered names in
-%% resp partition(*). If no conflict is found, the name tables are
-%% just updated. If a conflict is found, a resolve function is
-%% called once for each conflict. The result of the resolving
-%% is sent to the other node. When the names are exchanged, all
-%% other nodes in each partition are informed of the other nodes,
-%% and they ping each other to form a fully connected net.
-%%
-%% Here's the flow:
-%% Suppose nodes A and B connect, and C is connected to A.
-%%
-%% Node A
-%% ------
-%% << {nodeup, B}
-%% [spawn locker]
-%% B ! {init_connect, MyLocker}
-%% << {init_connect, MyLocker}
-%% [The lockers try to set the lock]
-%% << {lock_is_set, B}
-%% [Now, lock is set in both partitions]
-%% B ! {exchange, Names}
-%% << {exchange, Names}
-%% [solve conflict]
-%% B ! {resolved, Resolved}
-%% << {resolved, Resolved}
-%% C ! {new_nodes, Resolved, [B]}
-%%
-%% Node C
-%% ------
-%% << {new_nodes, ResolvedOps, NewNodes}
-%% [insert Ops]
-%% ping(NewNodes)
-%% << {nodeup, B}
-%%
-%%
-%% Several things can disturb this picture.
-%%
-%% First, the got_names message may arrive *before* the nodeup
-%% message, due to delay in net_kernel and an optimisation in the
-%% emulator. We handle this by keeping track of these messages in the
-%% pre_connect and lockers variables in our state.
-%%
-%% The most common situation is when a new node connects to an
-%% existing net. In this case there's no need to set the lock on
-%% all nodes in the net, as we know that there won't be any conflict.
-%% This is optimised by sending {first_contact, Node} instead of got_names.
-%% This implies that first_contact may arrive before nodeup as well.
-%%
-%% Of course we must handle that some node goes down during the
-%% connection.
-%%
-%% (*) When this information is being exchanged, no one is allowed
-%% to change the global register table. All calls to register etc
-%% are protected by a lock. If a registered process dies
-%% during this phase, the deregistration is done as soon as possible
-%% on each node (i.e. when the info about the process has arrived).
-%%-----------------------------------------------------------------
-%% Messages in the protocol
-%% ========================
-%% 1. Between connecting nodes (gen_server:casts)
-%% {init_connect, Vsn, Node, InitMsg}
-%% InitMsg = {locker, LockerPid}
-%% {exchange, Node, ListOfNames}
-%% {resolved, Node, Ops, Known}
-%% Known = list of nodes in Node's partition
-%% 2. Between lockers on connecting nodes (!s)
-%% {his_locker, Pid} (from our global)
-%% lockers link to each other
-%% {lock, Bool} loop until both lockers have lock = true,
-%% then send to global {lock_is_set, Node}
-%% 3. From connecting node to other nodes in the partition
-%% {new_nodes, Node, Ops, NewNodes}
-%% 4. sync protocol
-%% {in_sync, Node, IsKnown}
-%% - sent by each node to all new nodes
-%%-----------------------------------------------------------------
-
-handle_call({whereis, Name}, From, S) ->
- do_whereis(Name, From),
- {noreply, S};
-
-handle_call({register, Name, Pid, Method}, _From, S) ->
- ?P2({register, node(), Name}),
- ins_name(Name, Pid, Method),
- {reply, yes, S};
-
-handle_call({unregister, Name}, _From, S) ->
- case ets:lookup(global_names, Name) of
- [{_, Pid, _}] ->
- ?P2({unregister, node(), Name, Pid, node(Pid)}),
- ets:delete(global_names, Name),
- dounlink(Pid);
- _ -> ok
- end,
- {reply, ok, S};
-
-handle_call({register_ext, Name, Pid, RegNode}, _F, S) ->
- ins_name_ext(Name, Pid, RegNode),
- {reply, yes, S};
-
-handle_call({unregister_ext, Name}, _From, S) ->
- ets:delete(global_names_ext, Name),
- {reply, ok, S};
-
-
-handle_call({set_lock, Lock}, {Pid, _Tag}, S) ->
- Reply = handle_set_lock(Lock, Pid),
- {reply, Reply, S};
-
-handle_call({del_lock, Lock}, {Pid, _Tag}, S) ->
- handle_del_lock(Lock, Pid),
- {reply, true, S};
-
-handle_call(get_known, _From, S) ->
- {reply, S#state.known, S};
-
-%% R7 may call us?
-handle_call(get_known_v2, _From, S) ->
- {reply, S#state.known, S};
-
-handle_call({sync, Nodes}, From, S) ->
- %% If we have several global groups, this won't work, since we will
- %% do start_sync on a nonempty list of nodes even if the system
- %% is quiet.
- Pid = start_sync(lists:delete(node(), Nodes) -- S#state.synced, From),
- {noreply, S#state{syncers = [Pid | S#state.syncers]}};
-
-handle_call(get_protocol_version, _From, S) ->
- {reply, ?vsn, S};
-
-handle_call(get_names_ext, _From, S) ->
- {reply, get_names_ext(), S};
-
-handle_call(info, _From, S) ->
- {reply, S, S};
-
-handle_call(stop, _From, S) ->
- {stop, normal, stopped, S}.
-
-
-%%=======================================================================================
-%% init_connect
-%%
-%% Vsn 1 is the original protocol.
-%% Vsn 2 is enhanced with code to take care of registration of names from
-%% non erlang nodes, e.g. c-nodes.
-%% Vsn 3 is enhanced with a tag in the synch messages to distinguish
-%% different synch sessions from each other, see OTP-2766.
-%% Note: This requires also that the ticket OTP-2928 is fixed on the nodes
-%% running vsn 1 or 2; if such nodes will coexist with vsn 3 nodes.
-%%=======================================================================================
-handle_cast({init_connect, Vsn, Node, InitMsg}, S) ->
- ?FORMAT("~p #### init_connect Vsn ~p, Node ~p, InitMsg ~p~n",[node(), Vsn, Node, InitMsg]),
- case Vsn of
- %% It is always the responsibility of newer versions to understand
- %% older versions of the protocol.
- {HisVsn, HisTag} when HisVsn > ?vsn ->
- init_connect(?vsn, Node, InitMsg, HisTag, S#state.lockers, S);
- {HisVsn, HisTag} ->
- init_connect(HisVsn, Node, InitMsg, HisTag, S#state.lockers, S);
- %% To be future compatible
- Tuple when tuple(Tuple) ->
- List = tuple_to_list(Tuple),
- [_HisVsn, HisTag | _] = List,
- %% use own version handling if his is newer.
- init_connect(?vsn, Node, InitMsg, HisTag, S#state.lockers, S);
- _ when Vsn < 3 ->
- init_connect(Vsn, Node, InitMsg, undef, S#state.lockers, S);
- _ ->
- Txt = io_lib:format("Illegal global protocol version ~p Node: ~p",[Vsn, Node]),
- error_logger:info_report(lists:flatten(Txt))
- end,
- {noreply, S};
-
-%%=======================================================================================
-%% lock_is_set
-%%
-%% Ok, the lock is now set on both partitions. Send our names to other node.
-%%=======================================================================================
-handle_cast({lock_is_set, Node, MyTag}, S) ->
- ?FORMAT("~p #### lock_is_set Node ~p~n",[node(), Node]),
- Sync_tag_my = get({sync_tag_my, Node}),
- PVsn = get({prot_vsn, Node}),
- ?P2({lock_is_set, node(), Node, {MyTag, PVsn}, Sync_tag_my}),
- case {MyTag, PVsn} of
- {Sync_tag_my, undefined} ->
- %% Patch for otp-2728, the connection to the Node is flipping up and down
- %% the messages from the 'older' sync tries can disturb the 'new' sync try
- %% therefor all messages are discarded if the protocol vsn is not defined.
- Txt = io_lib:format("undefined global protocol version Node: ~p",[Node]),
- error_logger:info_report(lists:flatten(Txt)),
- {noreply, S};
- {Sync_tag_my, _} ->
- %% Check that the Node is still not known
- case lists:member(Node, S#state.known) of
- false ->
- ?P2({lset, node(), Node, false}),
- lock_is_set(Node, S#state.known),
- {noreply, S};
- true ->
- ?P2({lset, node(), Node, true}),
- erase({wait_lock, Node}),
- NewS = cancel_locker(Node, S),
- {noreply, NewS}
- end;
- _ ->
- ?P2({lset, illegal, node(), Node}),
- %% Illegal tag, delete the locker.
- erase({wait_lock, Node}),
- NewS = cancel_locker(Node, S),
- {noreply, NewS}
- end;
-
-%%=======================================================================================
-%% exchange
-%%
-%% Here the names are checked to detect name clashes.
-%%=======================================================================================
-%% Vsn 3 of the protocol
-handle_cast({exchange, Node, NameList, NameExtList, MyTag}, S) ->
- ?FORMAT("~p #### handle_cast 3 lock_is_set exchange ~p~n",
- [node(),{Node, NameList, NameExtList, MyTag}]),
- Sync_tag_my = get({sync_tag_my, Node}),
- PVsn = get({prot_vsn, Node}),
- case {MyTag, PVsn} of
- {Sync_tag_my, undefined} ->
- %% Patch for otp-2728, the connection to the Node is flipping up and down
- %% the messages from the 'older' sync tries can disturb the 'new' sync try
- %% therefor all messages are discarded if the protocol vsn is not defined.
- Txt = lists:flatten(io_lib:format(
- "undefined global protocol version Node: ~p",[Node])),
- error_logger:info_report(Txt),
- {noreply, S};
- {Sync_tag_my, _} ->
- exchange(PVsn, Node, {NameList, NameExtList}, S#state.known),
- {noreply, S};
- _ ->
- %% Illegal tag, delete the locker.
- erase({wait_lock, Node}),
- NewS = cancel_locker(Node, S),
- {noreply, NewS}
- end;
-
-
-
-%%=======================================================================================
-%% resolved
-%%
-%% Here the name clashes are resolved.
-%%=======================================================================================
-%% Vsn 3 of the protocol
-handle_cast({resolved, Node, Resolved, HisKnown, _HisKnown_v2, Names_ext, MyTag}, S) ->
- ?FORMAT("~p #### 2 resolved ~p~n",[node(),{Node, Resolved, HisKnown, Names_ext}]),
- Sync_tag_my = get({sync_tag_my, Node}),
- PVsn = get({prot_vsn, Node}),
- case {MyTag, PVsn} of
- {Sync_tag_my, undefined} ->
- %% Patch for otp-2728, the connection to the Node is flipping up and down
- %% the messages from the 'older' sync tries can disturb the 'new' sync try
- %% therefor all messages are discarded if the protocol vsn is not defined.
- Txt = lists:flatten(io_lib:format(
- "undefined global protocol version Node: ~p",[Node])),
- error_logger:info_report(Txt),
- {noreply, S};
- {Sync_tag_my, _} ->
- NewS = resolved(Node, Resolved, {HisKnown, HisKnown}, Names_ext, S),
- {noreply, NewS};
- _ ->
- %% Illegal tag, delete the locker.
- erase({wait_lock, Node}),
- NewS = cancel_locker(Node, S),
- {noreply, NewS}
- end;
-
-
-
-
-
-
-%%=======================================================================================
-%% new_nodes
-%%
-%% We get to know the other node's known nodes.
-%%=======================================================================================
-%% Vsn 2 and 3 of the protocol
-handle_cast({new_nodes, _Node, Ops, Names_ext, Nodes, _Nodes_v2}, S) ->
- ?P2({new_nodes, node(), Nodes}),
- ?FORMAT("~p #### 2 new_nodes ~p~n",[node(),{Ops, Names_ext, Nodes}]),
- NewS = new_nodes(Ops, Names_ext, Nodes, S),
- {noreply, NewS};
-
-
-
-
-%%=======================================================================================
-%% in_sync
-%%
-%% We are in sync with this node (from the other node's known world).
-%%=======================================================================================
-handle_cast({in_sync, Node, IsKnown}, S) ->
- ?FORMAT("~p #### in_sync ~p~n",[node(),{Node, IsKnown}]),
- lists:foreach(fun(Pid) -> Pid ! {synced, [Node]} end, S#state.syncers),
- %% moved up:
- NewS = cancel_locker(Node, S),
- erase({wait_lock, Node}),
- erase({pre_connect, Node}),
- erase({sync_tag_my, Node}),
- erase({sync_tag_his, Node}),
- NKnown = case lists:member(Node, Known = NewS#state.known) of
- false when IsKnown == true ->
- gen_server:cast({global_name_server, Node},
- {in_sync, node(), false}),
- [Node | Known];
- _ ->
- Known
- end,
- NSynced = case lists:member(Node, Synced = NewS#state.synced) of
- true -> Synced;
- false -> [Node | Synced]
- end,
- {noreply, NewS#state{known = NKnown, synced = NSynced}};
-
-
-
-
-%% Called when Pid on other node crashed
-handle_cast({async_del_name, Name, Pid}, S) ->
- ?P2({async_del_name, node(), Name, Pid, node(Pid)}),
- case ets:lookup(global_names, Name) of
- [{Name, Pid, _}] ->
- ets:delete(global_names, Name),
- dounlink(Pid);
- _ -> ok
- end,
- ets:delete(global_names_ext, Name),
- {noreply, S};
-
-handle_cast({async_del_lock, _ResourceId, Pid}, S) ->
- del_locks2(ets:tab2list(global_locks), Pid),
-% ets:match_delete(global_locks, {ResourceId, '_', Pid}),
- {noreply, S}.
-
-
-handle_info({'EXIT', Deleter, _Reason}=Exit, #state{the_deleter=Deleter}=S) ->
- {stop, {deleter_died,Exit}, S#state{the_deleter=undefined}};
-handle_info({'EXIT', Pid, _Reason}, #state{the_deleter=Deleter}=S)
- when pid(Pid) ->
- ?P2({global, exit, node(), Pid, node(Pid)}),
- check_exit(Deleter, Pid),
- Syncers = lists:delete(Pid, S#state.syncers),
- Lockers = lists:keydelete(Pid, 2, S#state.lockers),
- ?PRINT({exit, Pid, lockers, node(), S#state.lockers}),
- {noreply, S#state{syncers = Syncers, lockers = Lockers}};
-
-handle_info({nodedown, Node}, S) when Node == S#state.node_name ->
- %% Somebody stopped the distribution dynamically - change
- %% references to old node name (Node) to new node name ('nonode@nohost')
- {noreply, change_our_node_name(node(), S)};
-
-handle_info({nodedown, Node}, S) ->
- ?FORMAT("~p #### nodedown 1 ####### Node ~p",[node(),Node]),
- %% moved up:
- do_node_down(Node),
- #state{known = Known, synced = Syncs} = S,
- NewS = cancel_locker(Node, S),
-
- erase({wait_lock, Node}),
- erase({save_ops, Node}),
- erase({pre_connect, Node}),
- erase({prot_vsn, Node}),
- erase({sync_tag_my, Node}),
- erase({sync_tag_his, Node}),
- {noreply, NewS#state{known = lists:delete(Node, Known),
- synced = lists:delete(Node, Syncs)}};
-
-
-
-handle_info({nodeup, Node}, S) when Node == node() ->
- ?FORMAT("~p #### nodeup S ####### Node ~p~n",[node(), Node]),
- %% Somebody started the distribution dynamically - change
- %% references to old node name ('nonode@nohost') to Node.
- {noreply, change_our_node_name(Node, S)};
-
-handle_info({nodeup, Node}, S) when S#state.connect_all == true ->
- ?FORMAT("~p #### nodeup 1 ####### Node ~p",[node(),Node]),
- IsKnown = lists:member(Node, S#state.known) or
- %% This one is only for double nodeups (shouldn't occur!)
- lists:keymember(Node, 1, S#state.lockers),
- case IsKnown of
- true ->
- {noreply, S};
- false ->
- %% now() is used as a tag to separate different sycnh sessions
- %% from each others. Global could be confused at bursty nodeups
- %% because it couldn't separate the messages between the different
- %% synch sessions started by a nodeup.
- MyTag = now(),
- resend_pre_connect(Node),
-
- %% multi
- S#state.the_locker ! {nodeup, Node, S#state.known, MyTag, self()},
-
- Pid = start_locker(Node, S#state.known, MyTag, self(), S#state.the_locker),
- Ls = S#state.lockers,
- InitC = {init_connect, {?vsn, MyTag}, node(), {locker, Pid, S#state.known}},
- ?P2({putting, MyTag}),
- put({sync_tag_my, Node}, MyTag),
- gen_server:cast({global_name_server, Node}, InitC),
- {noreply, S#state{lockers = [{Node, Pid} | Ls]}}
- end;
-
-
-%% This message is only to test otp-2766 Global may be confused at bursty
-%% nodeup/nodedowns. It's a copy of the complex part of the handling of
-%% the 'nodeup' message.
-handle_info({test_vsn_tag_nodeup, Node}, S) when S#state.connect_all == true,
- Node == node() ->
- {noreply, S};
-handle_info({test_vsn_tag_nodeup, Node}, S) when S#state.connect_all == true ->
- ?FORMAT("~p #### test_nodeup 1 ####### Node ~p~n",[node(), Node]),
- MyTag = now(),
- resend_pre_connect(Node),
- S#state.the_locker ! {nodeup, Node, S#state.known, MyTag, self()},
- Pid = start_locker(Node, S#state.known, MyTag, self(), S#state.the_locker),
- Ls = S#state.lockers,
- InitC = {init_connect, {?vsn, MyTag}, node(), {locker, Pid, S#state.known}},
- put({sync_tag_my, Node}, MyTag),
- gen_server:cast({global_name_server, Node}, InitC),
- ?PRINT({lockers, node(), Ls}),
- {noreply, S#state{lockers = [{Node, Pid} | Ls]}};
-
-
-handle_info({whereis, Name, From}, S) ->
- do_whereis(Name, From),
- {noreply, S};
-
-handle_info(known, S) ->
- io:format(">>>> ~p~n",[S#state.known]),
- {noreply, S};
-
-handle_info(_, S) ->
- {noreply, S}.
-
-
-
-
-%%=======================================================================================
-%%=======================================================================================
-%%=============================== Internal Functions ====================================
-%%=======================================================================================
-%%=======================================================================================
-
-
-
-%%=======================================================================================
-%% Another node wants to synchronize its registered names with us.
-%% Start a locker process. Both nodes must have a lock before they are
-%% allowed to continue.
-%%=======================================================================================
-init_connect(Vsn, Node, InitMsg, HisTag, Lockers, S) ->
- ?P2({init_connect, node(), Node}),
- ?FORMAT("~p #### init_connect Vsn, Node, InitMsg ~p~n",[node(),{Vsn, Node, InitMsg}]),
- %% It is always the responsibility of newer versions to understand
- %% older versions of the protocol.
- put({prot_vsn, Node}, Vsn),
- put({sync_tag_his, Node}, HisTag),
- if
- Vsn =< 3 ->
- case lists:keysearch(Node, 1, Lockers) of
- {value, {_Node, MyLocker}} ->
- %% We both have lockers; let them set the lock
- case InitMsg of
- {locker, HisLocker, HisKnown} -> %% current version
- ?PRINT({init_connect1, node(), self(), Node,
- MyLocker, HisLocker}),
- MyLocker ! {his_locker, HisLocker, HisKnown};
-
- {locker, _HisLocker, HisKnown, HisTheLocker} -> %% multi
- ?PRINT({init_connect1, node(), self(), Node,
- MyLocker, _HisLocker}),
- S#state.the_locker ! {his_the_locker, HisTheLocker,
- HisKnown, S#state.known}
- end;
- false ->
- ?PRINT({init_connect11, node(), self(), Node}),
- put({pre_connect, Node}, {Vsn, InitMsg, HisTag})
- end;
- true -> % Vsn > 3
- ?P2(vsn4),
- case lists:keysearch(Node, 1, Lockers) of
- {value, {_Node, _MyLocker}} ->
- %% We both have lockers; let them set the lock
- case InitMsg of
- {locker, HisLocker, HisKnown} -> %% current version
- ?PRINT({init_connect1, node(), self(), Node,
- _MyLocker, HisLocker}),
- HisLocker ! {his_locker_new, S#state.the_locker,
- {HisKnown, S#state.known}};
-
- {locker, _HisLocker, HisKnown, HisTheLocker} -> %% multi
- ?PRINT({init_connect1, node(), self(), Node,
- _MyLocker, _HisLocker}),
- S#state.the_locker ! {his_the_locker, HisTheLocker,
- HisKnown, S#state.known}
- end;
- false ->
- ?PRINT({init_connect11, node(), self(), Node}),
- put({pre_connect, Node}, {Vsn, InitMsg, HisTag})
- end
- end.
-
-
-
-%%=======================================================================================
-%% In the simple case, we'll get lock_is_set before we get exchange,
-%% but we may get exchange before we get lock_is_set from our locker.
-%% If that's the case, we'll have to remember the exchange info, and
-%% handle it when we get the lock_is_set. We do this by using the
-%% process dictionary - when the lock_is_set msg is received, we store
-%% this info. When exchange is received, we can check the dictionary
-%% if the lock_is_set has been received. If not, we store info about
-%% the exchange instead. In the lock_is_set we must first check if
-%% exchange info is stored, in that case we take care of it.
-%%=======================================================================================
-lock_is_set(Node, Known) ->
- ?FORMAT("~p #### lock_is_set ~p~n",[node(),{Node, Node, Known}]),
- PVsn = get({prot_vsn, Node}),
- case PVsn of
- _ -> % 3 and higher
- gen_server:cast({global_name_server, Node},
- {exchange, node(), get_names(), get_names_ext(),
- get({sync_tag_his, Node})})
- end,
- %% If both have the lock, continue with exchange
- case get({wait_lock, Node}) of
- {exchange, NameList, NameExtList} ->
- %% vsn 2, 3
- put({wait_lock, Node}, lock_is_set),
- exchange(PVsn, Node, {NameList, NameExtList}, Known);
- undefined ->
- put({wait_lock, Node}, lock_is_set)
- end.
-
-
-
-%%=======================================================================================
-%% exchange
-%%=======================================================================================
-%% Vsn 3 and higher of the protocol
-exchange(_Vsn, Node, {NameList, NameExtList}, Known) ->
- ?FORMAT("~p #### 3 lock_is_set exchange ~p~n",[node(),{Node, NameList, NameExtList}]),
- case erase({wait_lock, Node}) of
- lock_is_set ->
- {Ops, Resolved} = exchange_names(NameList, Node, [], []),
- put({save_ops, Node}, Ops),
- gen_server:cast({global_name_server, Node},
- {resolved, node(), Resolved, Known,
- Known, get_names_ext(), get({sync_tag_his, Node})});
- undefined ->
- put({wait_lock, Node}, {exchange, NameList, NameExtList})
- end.
-
-
-
-
-
-resolved(Node, Resolved, {HisKnown, _HisKnown_v2}, Names_ext, S) ->
- ?P2({resolved, node(), Node, S#state.known}),
- ?FORMAT("~p #### 2 resolved ~p~n",[node(),{Node, Resolved, HisKnown, Names_ext}]),
- erase({prot_vsn, Node}),
- Ops = erase({save_ops, Node}) ++ Resolved,
- Known = S#state.known,
- Synced = S#state.synced,
- NewNodes = [Node | HisKnown],
- do_ops(Ops),
- do_ops_ext(Ops,Names_ext),
- gen_server:abcast(Known, global_name_server,
- {new_nodes, node(), Ops, Names_ext, NewNodes, NewNodes}),
- %% I am synced with Node, but not with HisKnown yet
- lists:foreach(fun(Pid) -> Pid ! {synced, [Node]} end, S#state.syncers),
- gen_server:abcast(HisKnown, global_name_server, {in_sync, node(), true}),
- NewS = lists:foldl(fun(Node1, S1) -> cancel_locker(Node1, S1) end,
- S,
- NewNodes),
- %% See (*) below... we're node b in that description
- NewKnown = Known ++ (NewNodes -- Known),
- NewS#state{known = NewKnown, synced = [Node | Synced]}.
-
-
-
-
-new_nodes(Ops, Names_ext, Nodes, S) ->
- ?FORMAT("~p #### 2 new_nodes ~p~n",[node(),{Ops, Names_ext, Nodes}]),
- do_ops(Ops),
- do_ops_ext(Ops,Names_ext),
- Known = S#state.known,
- %% (*) This one requires some thought...
- %% We're node a, other nodes b and c:
- %% The problem is that {in_sync, a} may arrive before {resolved, [a]} to
- %% b from c, leading to b sending {new_nodes, [a]} to us (node a).
- %% Therefore, we make sure we never get duplicates in Known.
- NewNodes = lists:delete(node(), Nodes -- Known),
- gen_server:abcast(NewNodes, global_name_server, {in_sync, node(), true}),
- S#state{known = Known ++ NewNodes}.
-
-
-
-
-
-do_whereis(Name, From) ->
- case is_lock_set(global) of
- false ->
- gen_server:reply(From, where(Name));
- true ->
- send_again({whereis, Name, From})
- end.
-
-terminate(_Reason, _S) ->
- ets:delete(global_names),
- ets:delete(global_names_ext),
- ets:delete(global_locks).
-
-code_change(_OldVsn, S, _Extra) ->
- {ok, S}.
-
-%% Resend init_connect to ourselves.
-resend_pre_connect(Node) ->
- case erase({pre_connect, Node}) of
-% {Vsn, InitMsg, undef} ->
-% %% Vsn 1 & 2
-% ?PRINT({resend_pre_connect2, node(), self(), Node}),
-% gen_server:cast(self(), {init_connect, Vsn, Node, InitMsg});
- {Vsn, InitMsg, HisTag} ->
- %% Vsn 3
- ?PRINT({resend_pre_connect3, node(), self(), Node}),
- gen_server:cast(self(), {init_connect, {Vsn, HisTag}, Node, InitMsg});
- _ ->
- ?PRINT({resend_pre_connect0, node(), self(), Node}),
- ok
- end.
-
-ins_name(Name, Pid, Method) ->
- case ets:lookup(global_names, Name) of
- [{Name, Pid2, _}] ->
- dounlink(Pid2);
- [] ->
- ok
- end,
- dolink(Pid),
- ets:insert(global_names, {Name, Pid, Method}).
-
-ins_name_ext(Name, Pid, RegNode) ->
- case ets:lookup(global_names_ext, Name) of
- [{Name, Pid2, _}] ->
- dounlink(Pid2);
- [] ->
- ok
- end,
- dolink_ext(Pid, RegNode),
- ets:insert(global_names_ext, {Name, Pid, RegNode}).
-
-where(Name) ->
- case ets:lookup(global_names, Name) of
- [{_, Pid, _}] -> Pid;
- [] -> undefined
- end.
-
-handle_set_lock({ResourceId, LockRequesterId}, Pid) ->
- case ets:lookup(global_locks, ResourceId) of
- [{ResourceId, LockRequesterId, Pids}] ->
- case lists:member(Pid, Pids) of
- true ->
- true;
- false ->
- dolink(Pid),
- ets:insert(global_locks, {ResourceId, LockRequesterId, [Pid | Pids]}),
- true
- end;
- [{ResourceId, _LockRequesterId2, _Pid2}] ->
- case ResourceId of
- global ->
- ?P({before,
- LockRequesterId,
- _LockRequesterId2,
- S#state.lockers}),
- false;
- _ ->
- false
- end;
- [] ->
- dolink(Pid),
- ets:insert(global_locks, {ResourceId, LockRequesterId, [Pid]}),
- true
- end.
-
-is_lock_set(ResourceId) ->
- case ets:lookup(global_locks, ResourceId) of
- [_Lock] -> true;
- [] -> false
- end.
-
-handle_del_lock({ResourceId, LockRequesterId}, Pid) ->
- case ets:lookup(global_locks, ResourceId) of
- [{ResourceId, LockRequesterId, Pids}] when [Pid] == Pids ->
- ets:delete(global_locks, ResourceId),
- dounlink(Pid);
- [{ResourceId, LockRequesterId, Pids}] ->
- NewPids = lists:delete(Pid, Pids),
- ets:insert(global_locks, {ResourceId, LockRequesterId, NewPids}),
- dounlink(Pid);
- _ -> ok
- end.
-
-do_ops(Ops) ->
- lists:foreach(fun({insert, Item}) -> ets:insert(global_names, Item);
- ({delete, Name}) ->
- case ets:lookup(global_names, Name) of
- [{Name, Pid, _}] ->
- ?P2({do_ops_delete, node(), Name, Pid, node(Pid)}),
- ets:delete(global_names, Name),
- dounlink(Pid);
- [] ->
- ok
- end
- end, Ops).
-
-%% If a new name, then it must be checked if it is an external name
-%% If delete a name it is always deleted from global_names_ext
-do_ops_ext(Ops, Names_ext) ->
- lists:foreach(fun({insert, {Name, Pid, _Method}}) ->
- case lists:keysearch(Name, 1, Names_ext) of
- {value, {Name, Pid, RegNode}} ->
- ets:insert(global_names_ext, {Name, Pid, RegNode});
- _ ->
- ok
- end;
- ({delete, Name}) ->
- ets:delete(global_names_ext, Name)
- end, Ops).
-
-%%-----------------------------------------------------------------
-%% A locker is a process spawned by global_name_server when a
-%% nodeup is received from a new node. Its purpose is to try to
-%% set a lock in our partition, i.e. on all nodes known to us.
-%% When the lock is set, it tells global about it, and keeps
-%% the lock set. global sends a cancel message to the locker when
-%% the partitions are connected.
-
-%% Versions: at version 2, the messages exchanged between the lockers
-%% include the known nodes (see OTP-3576). There is no way of knowing
-%% the version number of the other side's locker when sending a message
-%% to it, so we send both version 1 and 2, and flush the version 1 if
-%% we receive version 2.
-%%
-%% Due to a mistake, an intermediate version of the new locking protocol
-%% (using 3-tuples) went out in R7, which only understands itself. This patch
-%% to R7 handles all kinds, which means sending all, and flush the ones we
-%% don't want. (It will remain difficult to make a future version of the
-%% protocol communicate with this one.)
-%%
-%%-----------------------------------------------------------------
-%% (Version 2 in patched R7. No named version in R6 and older - let's call that
-%% version 1.)
--define(locker_vsn, 2).
-
-%%% multi
-
--record(multi, {known, others = []}).
-
-start_the_locker(Global) ->
- spawn_link(?MODULE, init_the_locker, [Global]).
-
-%init_the_locker(Global) ->
-% ok;
-init_the_locker(Global) ->
- process_flag(trap_exit, true), %needed?
- loop_the_locker(Global, #multi{}),
- erlang:error(locker_exited).
-
-remove_node(_Node, []) ->
- [];
-remove_node(Node, [{Node, _HisTheLocker, _HisKnown, _MyTag} | Rest]) ->
- Rest;
-remove_node(Node, [E | Rest]) ->
- [E | remove_node(Node, Rest)].
-
-find_node_tag(_Node, []) ->
- false;
-find_node_tag(Node, [{Node, _HisTheLocker, _HisKnown, MyTag} | _Rest]) ->
- {true, MyTag};
-find_node_tag(Node, [_E | Rest]) ->
- find_node_tag(Node, Rest).
-
-loop_the_locker(Global, S) ->
- ?P2({others, node(), S#multi.others}),
-% Known = S#multi.known,
- Timeout = case S#multi.others of
- [] ->
- infinity;
- _ ->
- 0
- end,
- receive
-% {nodeup, Node, Known, Tag, P} ->
-% ?P2({the_locker, nodeup, time(), node(), nodeup, Node, Tag}),
-% loop_the_locker(Global, S);
- {his_the_locker, HisTheLocker, HisKnown, MyKnown} ->
- ?P2({his_the_locker, time(), node(), HisTheLocker,
- node(HisTheLocker)}),
- receive
- {nodeup, Node, _Known, MyTag, _P} when node(HisTheLocker) == Node ->
- ?P2({the_locker, nodeup, node(), Node,
- node(HisTheLocker), MyTag,
- process_info(self(), messages)}),
- Others = S#multi.others,
- loop_the_locker(Global,
- S#multi{known=MyKnown,
- others=[{node(HisTheLocker), HisTheLocker, HisKnown, MyTag} | Others]});
- {cancel, Node, _Tag} when node(HisTheLocker) == Node ->
- loop_the_locker(Global, S)
- after 60000 ->
- ?P2({nodeupnevercame, node(), node(HisTheLocker)}),
- error_logger:error_msg("global: nodeup never came ~w ~w~n",
- [node(), node(HisTheLocker)]),
- loop_the_locker(Global, S)
- end;
- {cancel, Node, undefined} ->
- ?P2({the_locker, cancel1, undefined, node(), Node}),
-%% If we actually cancel something when a cancel message with the tag
-%% 'undefined' arrives, we may be acting on an old nodedown, to cancel
-%% a new nodeup, so we can't do that.
-% receive
-% {nodeup, Node, _Known, _MyTag, _P} ->
-% ?P2({the_locker, cancelnodeup1, node(), Node}),
-% ok
-% after 0 ->
-% ok
-% end,
-% Others = remove_node(Node, S#multi.others),
-% loop_the_locker(Global, S#multi{others = Others});
- loop_the_locker(Global, S);
- {cancel, Node, Tag} ->
- ?P2({the_locker, cancel1, Tag, node(), Node}),
- receive
- {nodeup, Node, _Known, Tag, _P} ->
- ?P2({the_locker, cancelnodeup2, node(), Node}),
- ok
- after 0 ->
- ok
- end,
- Others = remove_node(Node, S#multi.others),
- loop_the_locker(Global, S#multi{others = Others});
- {lock_set, _Pid, false, _} ->
- ?P2({the_locker, spurious, node(), node(_Pid)}),
- loop_the_locker(Global, S);
- {lock_set, Pid, true, HisKnown} ->
- Node = node(Pid),
- ?P2({the_locker, spontaneous, node(), Node}),
-
- NewKnown = gen_server:call(global_name_server, get_known),
-
- Others =
- case find_node_tag(Node, S#multi.others) of
- {true, MyTag} ->
-
- BothsKnown = HisKnown -- (HisKnown -- NewKnown),
- Known1 = if
- node() < Node ->
- [node() | NewKnown];
- true ->
- [node() | NewKnown] -- BothsKnown
- end,
-
- ?P2({lock1, node()}),
- LockId = {global, self()},
- IsLockSet = set_lock(LockId, Known1, 1),
- Pid ! {lock_set, self(), IsLockSet, NewKnown},
- ?P2({the_locker, spontaneous, node(), Node, IsLockSet}),
- case IsLockSet of
- true ->
- gen_server:cast(global_name_server,
- {lock_is_set, Node, MyTag}),
- ?P1({lock_sync_done, time(), node(),
- {Pid, node(Pid)}, self()}),
- %% Wait for global to tell us to remove lock.
- receive
- {cancel, Node, _Tag} ->
- %% All conflicts are resolved,
- %% remove lock.
- ?PRINT({node(), self(), locked1}),
- del_lock(LockId, Known1);
- {'EXIT', Pid, _} ->
- ?PRINT({node(), self(), locked2}),
- %% Other node died;
- %% remove lock and ignore him.
- del_lock(LockId, Known1),
- link(Global)
- end,
- remove_node(Node, S#multi.others);
- false ->
- S#multi.others
- end;
- false ->
- ?P2({the_locker, spontaneous, node(), Node, not_there}),
- Pid ! {lock_set, self(), false, NewKnown},
- S#multi.others
- end,
- loop_the_locker(Global, S#multi{others = Others});
- Other when element(1, Other) /= nodeup ->
- ?P2({the_locker, other_msg, Other}),
- loop_the_locker(Global, S)
- after Timeout ->
- NewKnown = gen_server:call(global_name_server, get_known),
- [{Node, HisTheLocker, HisKnown, MyTag} | Rest] = S#multi.others,
- BothsKnown = HisKnown -- (HisKnown -- NewKnown),
- Known1 = if
- node() < Node ->
- [node() | NewKnown];
- true ->
- [node() | NewKnown] -- BothsKnown
- end,
- ?P2({picking, node(), Node}),
- case lists:member(Node, NewKnown) of
- false ->
- LockId = {global, self()},
- ?P2({lock2, node()}),
- IsLockSet = set_lock(LockId, Known1, 1),
- Others =
- case IsLockSet of
- true ->
- HisTheLocker ! {lock_set, self(),
- IsLockSet, NewKnown},
- %% OTP-4902
- lock_set_loop(Global, S,
- Node, MyTag, Rest,
- Known1,
- LockId);
- false ->
- ?P2({the_locker, not_locked, node(),
- Node}),
- S#multi.others
- end,
- loop_the_locker(Global, S#multi{known=NewKnown,
- others = Others});
- true ->
- ?P2({is_known, node(), Node}),
- loop_the_locker(Global, S#multi{known=NewKnown,
- others = Rest})
- end
- end.
-
-lock_set_loop(Global, S, Node, MyTag, Rest, Known1, LockId) ->
- receive
- {lock_set, P, true, _} when node(P) == Node ->
- ?P2({the_locker, both_set, node(), Node}),
-
- %% do sync
- gen_server:cast(global_name_server, {lock_is_set, Node, MyTag}),
- ?P1({lock_sync_done, time(), node(), {Pid, node(Pid)}, self()}),
-
- %% Wait for global to tell us to remove lock.
- receive
- {cancel, Node, _} ->
- %% All conflicts are resolved, remove lock.
- ?PRINT({node(), self(), locked1}),
- del_lock(LockId, Known1);
- {'EXIT', _Pid, _} ->
- ?PRINT({node(), self(), locked2}),
- %% Other node died; remove lock and ignore him.
- del_lock(LockId, Known1),
- link(Global)
- end,
- Rest;
- {lock_set, P, false, _} when node(P) == Node ->
- ?P2({the_locker, not_both_set, node(), Node}),
- del_lock(LockId, Known1),
- S#multi.others;
- {cancel, Node, _} ->
- ?P2({the_locker, cancel2, node(), Node}),
- del_lock(LockId, Known1),
- remove_node(Node, S#multi.others);
- {'EXIT', _, _} ->
- ?P2({the_locker, exit, node(), Node}),
- del_lock(LockId, Known1),
- S#multi.others
-
- after
- %% OTP-4902
- %% A cyclic deadlock could occur in rare cases where three or
- %% more nodes waited for a reply from each other.
- %% Therefore, reject lock_set attempts in this state from
- %% nodes < this node (its enough if at least one node in
- %% the cycle rejects and thus breaks the deadlock)
- 5000 ->
- reject_lock_set(),
- lock_set_loop(Global, S, Node, MyTag, Rest, Known1, LockId)
- end.
-
-reject_lock_set() ->
- receive
- {lock_set, P, true, _} when node(P) < node() ->
- P ! {lock_set, self(), false, []},
- reject_lock_set()
- after
- 0 ->
- true
- end.
-
-start_locker(Node, Known, MyTag, Global, TheLocker) ->
- %% No link here! The del_lock call would delete the link anyway.
- %% global_name_server has control of these processes anyway...
- %% When the locker process exits due to being sent the 'cancel' message
- %% by the server, the server then removes it from its tables.
- %% When the locker terminates due to other reasons, the server must
- %% be told, so we make a link to it just before exiting.
- spawn(?MODULE, init_locker, [Node, Known, MyTag, Global, TheLocker]).
-
-init_locker(Node, Known, MyTag, Global, TheLocker) ->
- process_flag(trap_exit, true),
- ?PRINT({init_locker, node(), self(), Node}),
- ?P1({init_locker, time(), node(), self(), Node}),
- receive
- {his_locker, Pid, HisKnown} ->
- ?PRINT({init_locker, node(), self(), his_locker, Node}),
- link(Pid),
- %% If two nodes in a group of nodes first disconnect
- %% and then reconnect, this causes global to deadlock.
- %% This because both of the reconnecting nodes
- %% tries to set lock on the other nodes in the group.
- %% This is solved by letting only one of the reconneting nodes set the lock.
- BothsKnown = HisKnown -- (HisKnown -- Known),
- ?P({loop_locker1, node(), {Pid, node(Pid)}}),
- Res = loop_locker(Node, Pid, Known, 1, MyTag, BothsKnown, Global),
- ?P({loop_locker2, node(), {Pid, node(Pid)}}),
- Res;
- {his_locker_new, HisTheLocker, {Known1, Known2}} ->
- %% slide into the vsn 4 stuff
- ?P2({his_locker_new, node()}),
- HisTheLocker ! {his_the_locker, TheLocker, Known1, Known2},
- exit(normal);
- cancel ->
- ?PRINT({init_locker, node(), self(), cancel, Node}),
- exit(normal)
- end.
-
-loop_locker(Node, Pid, Known0, Try, MyTag, BothsKnown, Global) ->
- Known = if
- node() < Node ->
- [node() | Known0];
- true ->
- [node() | Known0] -- BothsKnown
- end,
-
- ?PRINT({locking, node(), self(), Known}),
- LockId = {global, self()},
- ?P2({lock3, node()}),
- IsLockSet = set_lock(LockId, Known, 1),
- ?P({loop_locker, IsLockSet,
- node(), {Pid, node(Pid)}, self(), Try}),
- ?P1({loop_locker, time(), IsLockSet,
- node(), {Pid, node(Pid)}, self(), Try}),
- ?PRINT({locking1, node(), self(), Known, IsLockSet}),
- %% Tell other node that we managed to get the lock.
- Pid ! {lock, ?locker_vsn, IsLockSet, Known},
- Pid ! {lock, IsLockSet, Known},
- Pid ! {lock, IsLockSet},
- %% Wait for other node's result.
- receive
- %% R7 patched and later
- {lock, _LockerVsn, true, _} when IsLockSet == true ->
- receive
- {lock, _} ->
- ok
- end,
- receive
- {lock, _, _} ->
- ok
- end,
- ?PRINT({node(), self(), locked}),
- %% Now we got the lock in both partitions. Tell
- %% global, and let him resolve name conflict.
- ?P1({lock_sync, time(), node(), {Pid, node(Pid)}, self()}),
- gen_server:cast(global_name_server, {lock_is_set, Node, MyTag}),
- ?P1({lock_sync_done, time(), node(), {Pid, node(Pid)}, self()}),
- %% Wait for global to tell us to remove lock.
- receive
- cancel ->
- %% All conflicts are resolved, remove lock.
- ?PRINT({node(), self(), locked1}),
- del_lock(LockId, Known);
- {'EXIT', Pid, _} ->
- ?PRINT({node(), self(), locked2}),
- %% Other node died; remove lock and ignore him.
- del_lock(LockId, Known),
- link(Global)
- end;
- {lock, _LockerVsn, _, HisKnown} ->
- receive
- {lock, _} ->
- ok
- end,
- receive
- {lock, _, _} ->
- ok
- end,
- %% Some of us failed to get the lock; try again
- ?PRINT({node(), self(), locked0}),
- d_lock(IsLockSet, LockId, Known),
- try_again_locker(Node, Pid, Try, MyTag, HisKnown, Global);
- %% R7 unpatched
- {lock, true, _} when IsLockSet == true ->
- ?PRINT({node(), self(), locked}),
- %% Now we got the lock in both partitions. Tell
- %% global, and let him resolve name conflict.
- gen_server:cast(global_name_server, {lock_is_set, Node, MyTag}),
- %% Wait for global to tell us to remove lock.
- receive
- cancel ->
- %% All conflicts are resolved, remove lock.
- ?PRINT({node(), self(), locked1}),
- del_lock(LockId, Known);
- {'EXIT', Pid, _} ->
- ?PRINT({node(), self(), locked2}),
- %% Other node died; remove lock and ignore him.
- del_lock(LockId, Known),
- link(Global)
- end;
- {lock, _, HisKnown} ->
- %% Some of us failed to get the lock; try again
- ?PRINT({node(), self(), locked0}),
- d_lock(IsLockSet, LockId, Known),
- try_again_locker(Node, Pid, Try, MyTag, HisKnown, Global);
- %% R6 and earlier
- {lock, true} when IsLockSet == true ->
- ?PRINT({node(), self(), locked}),
- %% Now we got the lock in both partitions. Tell
- %% global, and let him resolve name conflict.
- gen_server:cast(global_name_server, {lock_is_set, Node, MyTag}),
- %% Wait for global to tell us to remove lock.
- receive
- cancel ->
- %% All conflicts are resolved, remove lock.
- ?PRINT({node(), self(), locked1}),
- del_lock(LockId, Known);
- {'EXIT', Pid, _} ->
- ?PRINT({node(), self(), locked2}),
- %% Other node died; remove lock and ignore him.
- del_lock(LockId, Known),
- link(Global)
- end;
- {lock, _} ->
- %% Some of us failed to get the lock; try again
- ?PRINT({node(), self(), locked0}),
- d_lock(IsLockSet, LockId, Known),
- try_again_locker(Node, Pid, Try, MyTag, BothsKnown, Global);
- {'EXIT', Pid, _} ->
- %% Other node died; remove lock and ignore him.
- ?PRINT({node(), self(), locked7}),
- d_lock(IsLockSet, LockId, Known),
- link(Global);
- cancel ->
- ?PRINT({node(), self(), locked8}),
- d_lock(IsLockSet, LockId, Known)
- end.
-
-d_lock(true, LockId, Known) -> del_lock(LockId, Known);
-d_lock(false, _, _) -> ok.
-
-try_again_locker(Node, Pid, Try, MyTag, HisKnown, Global) ->
- ?PRINT({try_again, node(), self(), Node, Pid, Known, Try, MyTag}),
- ?P1({try_again, time(), node(), self(), Node, Pid, Known, Try, MyTag}),
- random_sleep(Try),
- ?P1({try_again2, time(), node(), self(), Node, Pid, Known, Try, MyTag}),
- NewKnown = gen_server:call(global_name_server, get_known),
- case lists:member(Node, NewKnown) of
- false ->
- BothsKnown1 = HisKnown -- (HisKnown -- NewKnown),
- ?PRINT({node(), self(), Node, again, notknown}),
- ?PRINT({bothknown, BothsKnown, BothsKnown1}),
- loop_locker(Node, Pid, NewKnown, Try+1, MyTag,
- BothsKnown1, Global);
- true ->
- ?PRINT({node(), self(), Node, again, known}),
- link(Global),
- %% Node is already handled, we are ready.
- ok
- end.
-
-cancel_locker(Node, S) ->
- %% multi
- ?P2({cancel, node(), Node, get({sync_tag_my, Node})}),
- S#state.the_locker ! {cancel, Node, get({sync_tag_my, Node})},
-
- Lockers = S#state.lockers,
- case lists:keysearch(Node, 1, Lockers) of
- {value, {_, Pid}} ->
- Pid ! cancel,
- ?PRINT({cancel, Node, lockers, node(), Lockers}),
- S#state{lockers = lists:keydelete(Node, 1, Lockers)};
- _ ->
- S
- end.
-
-%% A node sent us his names. When a name clash is found, the resolve
-%% function is called from the smaller node => all resolve funcs are called
-%% from the same partition.
-exchange_names([{Name, Pid, Method} |Tail], Node, Ops, Res) ->
- case ets:lookup(global_names, Name) of
- [{Name, Pid, _}] ->
- exchange_names(Tail, Node, Ops, Res);
- [{Name, Pid2, Method2}] when node() < Node ->
- %% Name clash! Add the result of resolving to Res(olved).
- %% We know that node(Pid) /= node(), so we don't
- %% need to link/unlink to Pid.
- Node2 = node(Pid2), %%&&&&&& check external node???
- case rpc:call(Node2, ?MODULE, resolve_it,
- [Method2, Name, Pid, Pid2]) of
- Pid ->
- dounlink(Pid2),
- ets:insert(global_names, {Name, Pid, Method}),
- Op = {insert, {Name, Pid, Method}},
- exchange_names(Tail, Node, [Op | Ops], [Op | Res]);
- Pid2 ->
- Op = {insert, {Name, Pid2, Method2}},
- exchange_names(Tail, Node, Ops, [Op | Res]);
- none ->
- dounlink(Pid2),
- ?P2({unregister, node(), Name, Pid2, node(Pid2)}),
- ets:delete(global_names, Name),
- Op = {delete, Name},
- exchange_names(Tail, Node, [Op | Ops], [Op | Res]);
- {badrpc, Badrpc} ->
- error_logger:info_msg("global: badrpc ~w received when "
- "conflicting name ~w was found",
- [Badrpc, Name]),
- dounlink(Pid2),
- ets:insert(global_names, {Name, Pid, Method}),
- Op = {insert, {Name, Pid, Method}},
- exchange_names(Tail, Node, [Op | Ops], [Op | Res]);
- Else ->
- error_logger:info_msg("global: Resolve method ~w for "
- "conflicting name ~w returned ~w~n",
- [Method, Name, Else]),
- dounlink(Pid2),
- ets:delete(global_names, Name),
- Op = {delete, Name},
- exchange_names(Tail, Node, [Op | Ops], [Op | Res])
- end;
- [{Name, _Pid2, _}] ->
- %% The other node will solve the conflict.
- exchange_names(Tail, Node, Ops, Res);
- _ ->
- %% Entirely new name.
- ets:insert(global_names, {Name, Pid, Method}),
- exchange_names(Tail, Node,
- [{insert, {Name, Pid, Method}} | Ops], Res)
- end;
-exchange_names([], _, Ops, Res) ->
- {Ops, Res}.
-
-resolve_it(Method, Name, Pid1, Pid2) ->
- catch Method(Name, Pid1, Pid2).
-
-minmax(P1,P2) ->
- if node(P1) < node(P2) -> {P1, P2}; true -> {P2, P1} end.
-
-random_exit_name(Name, Pid, Pid2) ->
- {Min, Max} = minmax(Pid, Pid2),
- error_logger:info_msg("global: Name conflict terminating ~w~n",
- [{Name, Max}]),
- exit(Max, kill),
- Min.
-
-random_notify_name(Name, Pid, Pid2) ->
- {Min, Max} = minmax(Pid, Pid2),
- Max ! {global_name_conflict, Name},
- Min.
-
-notify_all_name(Name, Pid, Pid2) ->
- Pid ! {global_name_conflict, Name, Pid2},
- Pid2 ! {global_name_conflict, Name, Pid},
- none.
-
-cnode(Name, Pid, Pid2) ->
- {Min, Max} = minmax(Pid, Pid2),
- error_logger:info_msg("global: Name conflict terminating ~w~n",
- [{Name, Max}]),
- Max ! {global_name_conflict, Name},
- Min.
-
-%% Only link to pids on our own node
-dolink(Pid) when node(Pid) == node() ->
- link(Pid);
-dolink(_) -> ok.
-
-%% Only link to pids on our own node
-dolink_ext(Pid, RegNode) when RegNode == node() -> link(Pid);
-dolink_ext(_, _) -> ok.
-
-dounlink(Pid) when node(Pid) == node() ->
- case ets:match(global_names, {'_', Pid, '_'}) of
- [] ->
- case is_pid_used(Pid) of
- false ->
- unlink(Pid);
- true -> ok
- end;
- _ -> ok
- end;
-dounlink(_Pid) ->
- ok.
-
-is_pid_used(Pid) ->
- is_pid_used(ets:tab2list(global_locks), Pid).
-
-is_pid_used([], _Pid) ->
- false;
-is_pid_used([{_ResourceId, _LockReqId, Pids} | Tail], Pid) ->
- case lists:member(Pid, Pids) of
- true ->
- true;
- false ->
- is_pid_used(Tail, Pid)
- end.
-
-
-
-%% check_exit/3 removes the Pid from affected tables.
-%% This function needs to abcast the thingie since only the local
-%% server is linked to the registered process (or the owner of the
-%% lock). All the other servers rely on the nodedown mechanism.
-check_exit(Deleter, Pid) ->
- del_names(Deleter, Pid, ets:tab2list(global_names)),
- del_locks(ets:tab2list(global_locks), Pid).
-
-del_names(Deleter, Pid, [{Name, Pid, _Method} | Tail]) ->
- %% First, delete the Pid from the local ets; then send to other nodes
- ets:delete(global_names, Name),
- ets:delete(global_names_ext, Name),
- dounlink(Pid),
- Deleter ! {delete_name,self(),Name,Pid},
- del_names(Deleter, Pid, Tail);
-del_names(Deleter, Pid, [_|T]) ->
- del_names(Deleter, Pid, T);
-del_names(_Deleter, _Pid, []) -> done.
-
-del_locks([{ResourceId, LockReqId, Pids} | Tail], Pid) ->
- case {lists:member(Pid, Pids), Pids} of
- {true, [Pid]} ->
- ets:delete(global_locks, ResourceId),
- gen_server:abcast(nodes(), global_name_server,
- {async_del_lock, ResourceId, Pid});
- {true, _} ->
- NewPids = lists:delete(Pid, Pids),
- ets:insert(global_locks, {ResourceId, LockReqId, NewPids}),
- gen_server:abcast(nodes(), global_name_server,
- {async_del_lock, ResourceId, Pid});
- _ ->
- continue
- end,
- del_locks(Tail, Pid);
-del_locks([], _Pid) -> done.
-
-del_locks2([{ResourceId, LockReqId, Pids} | Tail], Pid) ->
- case {lists:member(Pid, Pids), Pids} of
- {true, [Pid]} ->
- ets:delete(global_locks, ResourceId);
- {true, _} ->
- NewPids = lists:delete(Pid, Pids),
- ets:insert(global_locks, {ResourceId, LockReqId, NewPids});
- _ ->
- continue
- end,
- del_locks2(Tail, Pid);
-del_locks2([], _Pid) ->
- done.
-
-
-
-%% Unregister all Name/Pid pairs such that node(Pid) == Node
-%% and delete all locks where node(Pid) == Node
-do_node_down(Node) ->
- do_node_down_names(Node, ets:tab2list(global_names)),
- do_node_down_names_ext(Node, ets:tab2list(global_names_ext)),
- do_node_down_locks(Node, ets:tab2list(global_locks)).
-
-do_node_down_names(Node, [{Name, Pid, _Method} | T]) when node(Pid) == Node ->
- ets:delete(global_names, Name),
- do_node_down_names(Node, T);
-do_node_down_names(Node, [_|T]) ->
- do_node_down_names(Node, T);
-do_node_down_names(_, []) -> ok.
-
-%%remove all external names registered on the crashed node
-do_node_down_names_ext(Node, [{Name, _Pid, Node} | T]) ->
- ets:delete(global_names, Name),
- ets:delete(global_names_ext, Name),
- do_node_down_names_ext(Node, T);
-do_node_down_names_ext(Node, [_|T]) ->
- do_node_down_names_ext(Node, T);
-do_node_down_names_ext(_, []) -> ok.
-
-do_node_down_locks(Node, [{ResourceId, LockReqId, Pids} | T]) ->
- case do_node_down_locks2(Pids, Node) of
- [] ->
- continue;
- RemovePids ->
- case Pids -- RemovePids of
- [] ->
- ets:delete(global_locks, ResourceId);
- NewPids ->
- ets:insert(global_locks, {ResourceId, LockReqId, NewPids})
- end
- end,
- do_node_down_locks(Node, T);
-do_node_down_locks(Node, [_|T]) ->
- do_node_down_locks(Node, T);
-do_node_down_locks(_, []) -> done.
-
-
-do_node_down_locks2(Pids, Node) ->
- do_node_down_locks2(Pids, Node, []).
-
-do_node_down_locks2([], _Node, Res) ->
- Res;
-do_node_down_locks2([Pid | Pids], Node, Res) when node(Pid) == Node ->
- do_node_down_locks2(Pids, Node, [Pid | Res]);
-do_node_down_locks2([_ | Pids], Node, Res) ->
- do_node_down_locks2(Pids, Node, Res).
-
-
-get_names() ->
- ets:tab2list(global_names).
-
-get_names_ext() ->
- ets:tab2list(global_names_ext).
-
-random_sleep(Times) ->
- case (Times rem 10) of
- 0 -> erase(random_seed);
- _ -> ok
- end,
- case get(random_seed) of
- undefined ->
- {A1, A2, A3} = now(),
- random:seed(A1, A2, A3 + erlang:phash(node(), 100000));
- _ -> ok
- end,
- %% First time 1/4 seconds, then doubling each time up to 8 seconds max.
- Tmax = if Times > 5 -> 8000;
- true -> ((1 bsl Times) * 1000) div 8
- end,
- T = random:uniform(Tmax),
- ?P({random_sleep, node(), self(), Times, T}),
- receive after T -> ok end.
-
-dec(infinity) -> infinity;
-dec(N) -> N-1.
-
-send_again(Msg) ->
- spawn_link(?MODULE, timer, [self(), Msg]).
-
-timer(Pid, Msg) ->
- random_sleep(5),
- Pid ! Msg.
-
-change_our_node_name(NewNode, S) ->
- S#state{node_name = NewNode}.
-
-
-%%-----------------------------------------------------------------
-%% Each sync process corresponds to one call to sync. Each such
-%% process asks the global_name_server on all Nodes if it is in sync
-%% with Nodes. If not, that (other) node spawns a syncer process that
-%% waits for global to get in sync with all Nodes. When it is in
-%% sync, the syncer process tells the original sync process about it.
-%%-----------------------------------------------------------------
-start_sync(Nodes, From) ->
- spawn_link(?MODULE, sync_init, [Nodes, From]).
-
-sync_init(Nodes, From) ->
- lists:foreach(fun(Node) -> monitor_node(Node, true) end, Nodes),
- sync_loop(Nodes, From).
-
-sync_loop([], From) ->
- gen_server:reply(From, ok);
-sync_loop(Nodes, From) ->
- receive
- {nodedown, Node} ->
- monitor_node(Node, false),
- sync_loop(lists:delete(Node, Nodes), From);
- {synced, SNodes} ->
- lists:foreach(fun(N) -> monitor_node(N, false) end, SNodes),
- sync_loop(Nodes -- SNodes, From)
- end.
-
-
-%%%====================================================================================
-%%% Get the current global_groups definition
-%%%====================================================================================
-check_sync_nodes() ->
- case get_own_nodes() of
- {ok, all} ->
- nodes();
- {ok, NodesNG} ->
- %% global_groups parameter is defined, we are not allowed to sync
- %% with nodes not in our own global group.
- (nodes() -- (nodes() -- NodesNG));
- {error, Error} ->
- {error, Error}
- end.
-
-check_sync_nodes(SyncNodes) ->
- case get_own_nodes() of
- {ok, all} ->
- SyncNodes;
- {ok, NodesNG} ->
- %% global_groups parameter is defined, we are not allowed to sync
- %% with nodes not in our own global group.
- OwnNodeGroup = (nodes() -- (nodes() -- NodesNG)),
- IllegalSyncNodes = (SyncNodes -- [node() | OwnNodeGroup]),
- case IllegalSyncNodes of
- [] -> SyncNodes;
- _ -> {error, {"Trying to sync nodes not defined in the own global group",
- IllegalSyncNodes}}
- end;
- {error, Error} ->
- {error, Error}
- end.
-
-get_own_nodes() ->
- case global_group:get_own_nodes_with_errors() of
- {error, Error} ->
- {error, {"global_groups definition error", Error}};
- OkTup ->
- OkTup
- end.
-
-
-%%-----------------------------------------------------------------
-%% The deleter process is a satellite process to global_name_server
-%% that does background batch deleting of names when a process
-%% that had globally registered names dies. It is started by and
-%% linked to global_name_server.
-%%-----------------------------------------------------------------
-
-start_the_deleter(Global) ->
- spawn_link(
- fun () ->
- loop_the_deleter(Global)
- end).
-
-loop_the_deleter(Global) ->
- Deletions = collect_deletions(Global, []),
- trans({global, self()},
- fun() ->
- lists:map(
- fun ({Name,Pid}) ->
- ?P2({delete_name2, Name, Pid, nodes()}),
- gen_server:abcast(nodes(), global_name_server,
- {async_del_name, Name, Pid})
- end, Deletions)
- end,
- nodes()),
- loop_the_deleter(Global).
-
-collect_deletions(Global, Deletions) ->
- receive
- {delete_name,Global,Name,Pid} ->
- ?P2({delete_name, node(), self(), Name, Pid, nodes()}),
- collect_deletions(Global, [{Name,Pid}|Deletions]);
- Other ->
- error_logger:error_msg("The global_name_server deleter process "
- "received an unexpected message:\n~p\n",
- [Other]),
- collect_deletions(Global, Deletions)
- after case Deletions of
- [] -> infinity;
- _ -> 0
- end ->
- lists:reverse(Deletions)
- end.
diff --git a/lib/dialyzer/test/plt_SUITE.erl b/lib/dialyzer/test/plt_SUITE.erl
new file mode 100644
index 0000000000..aee9f449a6
--- /dev/null
+++ b/lib/dialyzer/test/plt_SUITE.erl
@@ -0,0 +1,21 @@
+%% This suite is the only hand made and simply
+%% checks if we can build a plt.
+
+-module(plt_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include("dialyzer_test_constants.hrl").
+
+-export([suite/0, all/0, build_plt/1]).
+
+suite() ->
+ [{timetrap, ?plt_timeout}].
+
+all() -> [build_plt].
+
+build_plt(Config) ->
+ OutDir = ?config(priv_dir, Config),
+ case dialyzer_common:check_plt(OutDir) of
+ ok -> ok;
+ fail -> ct:fail(plt_build_fail)
+ end.
diff --git a/lib/dialyzer/test/plt_tests_SUITE.erl b/lib/dialyzer/test/plt_tests_SUITE.erl
deleted file mode 100644
index d5881dc3aa..0000000000
--- a/lib/dialyzer/test/plt_tests_SUITE.erl
+++ /dev/null
@@ -1,21 +0,0 @@
-%% This suite is the only hand made and simply
-%% checks if we can build a plt.
-
--module(plt_tests_SUITE).
-
--include_lib("common_test/include/ct.hrl").
--include("dialyzer_test_constants.hrl").
-
--export([suite/0, all/0, build_plt/1]).
-
-suite() ->
- [{timetrap, ?plt_timeout}].
-
-all() -> [build_plt].
-
-build_plt(Config) ->
- OutDir = ?config(priv_dir, Config),
- case dialyzer_common:check_plt(OutDir) of
- ok -> ok;
- fail -> ct:fail(plt_build_fail)
- end.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/dialyzer_options b/lib/dialyzer/test/r9c_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..e00e23bb66
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/dialyzer_options
@@ -0,0 +1,2 @@
+{dialyzer_options, [{defines, [{vsn, 42}]}]}.
+{time_limit, 20}.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/results/asn1 b/lib/dialyzer/test/r9c_SUITE_data/results/asn1
new file mode 100644
index 0000000000..ac83366bc8
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/results/asn1
@@ -0,0 +1,106 @@
+
+asn1ct.erl:1500: The variable Err can never match since previous clauses completely covered the type #type{}
+asn1ct.erl:1596: The variable _ can never match since previous clauses completely covered the type 'ber_bin_v2'
+asn1ct.erl:1673: The pattern 'all' can never match the type 'asn1_module' | 'exclusive_decode' | 'partial_decode'
+asn1ct.erl:672: The pattern <{'false', Result}, _, _> can never match the type <{'true','true'},atom() | binary() | [atom() | binary() | [atom() | binary() | [any()] | char()] | char()],[any()]>
+asn1ct.erl:909: Guard test is_atom(Ext::[49 | 97 | 98 | 100 | 110 | 115]) can never succeed
+asn1ct_check.erl:1698: The pattern {'error', _} can never match the type [any()]
+asn1ct_check.erl:2733: The pattern {'type', Tag, _, _, _, _} can never match the type 'ASN1_OPEN_TYPE' | {_,_} | {'fixedtypevaluefield',_,_}
+asn1ct_check.erl:2738: The pattern <_S, _> can never match since previous clauses completely covered the type <#state{},#ObjectClassFieldType{class::#objectclass{fields::maybe_improper_list() | {_,_,_,_}},fieldname::{_,maybe_improper_list()},type::'ASN1_OPEN_TYPE' | {_,_} | {'fixedtypevaluefield',_,_}}>
+asn1ct_check.erl:2887: The variable Other can never match since previous clauses completely covered the type any()
+asn1ct_check.erl:3188: The pattern <_S, [], B> can never match the type <#state{},{'SingleValue',_},{'ValueRange',_}>
+asn1ct_check.erl:3190: The pattern <_S, A, []> can never match the type <#state{},{'SingleValue',_},{'ValueRange',_}>
+asn1ct_check.erl:3212: The pattern {[], C3} can never match the type {[any(),...],{'ValueRange',{'MIN','MAX'}}}
+asn1ct_check.erl:3225: The pattern {L1, UbNew} can never match the type 'false'
+asn1ct_check.erl:3228: The pattern {L1, LbNew} can never match the type 'false'
+asn1ct_check.erl:3235: The call asn1ct_check:remove_val_from_list(number(),L::[any(),...]) will never return since it differs in the 1st argument from the success typing arguments: ([any()],any())
+asn1ct_check.erl:3240: The call asn1ct_check:remove_val_from_list(number(),L::[any(),...]) will never return since it differs in the 1st argument from the success typing arguments: ([any()],any())
+asn1ct_check.erl:3242: Function remove_val_from_list/2 has no local return
+asn1ct_check.erl:3243: The call lists:member(Val::[any(),...],List::number()) will never return since it differs in the 2nd argument from the success typing arguments: (any(),[any()])
+asn1ct_check.erl:3283: The pattern [] can never match the type [any(),...]
+asn1ct_check.erl:3362: The pattern <_, [], _VR> can never match the type <#state{},[any(),...],[any(),...]>
+asn1ct_check.erl:3364: The pattern <_, _SV, []> can never match the type <#state{},[any(),...],[any(),...]>
+asn1ct_check.erl:4150: The pattern <_, [_]> can never match the type <_,[]>
+asn1ct_check.erl:4314: The pattern can never match the type <#state{},_,[any()]>
+asn1ct_check.erl:4360: The pattern can never match the type <#state{},_,[any()]>
+asn1ct_check.erl:4719: The call asn1ct_check:error({'type',{'asn1',[1..255,...],[any(),...]}}) will never return since it differs in the 1st argument from the success typing arguments: ({'ObjectSet' | 'class' | 'export' | 'ptype' | 'type' | 'value',_,#state{}})
+asn1ct_check.erl:5120: Guard test is_record(Type::{_,_} | {'fixedtypevaluefield',_,_},'type',6) can never succeed
+asn1ct_check.erl:5128: Guard test is_record(Type::{_,_} | {'fixedtypevaluefield',_,_},'type',6) can never succeed
+asn1ct_check.erl:540: The pattern <_S, {'poc', _ObjSet, _Params}> can never match since previous clauses completely covered the type <#state{},_>
+asn1ct_check.erl:5517: The pattern <_, []> can never match the type <_,[{'ABSTRACT-SYNTAX',{_,_,_}} | {'TYPE-IDENTIFIER',{_,_,_}},...]>
+asn1ct_constructed_ber.erl:1075: The pattern {{{'ObjectClassFieldType', _, _, _, {'objectfield', PrimFieldName1, PFNList}}, _}, {'componentrelation', _, _}} can never match the type {#type{},_}
+asn1ct_constructed_ber.erl:695: The pattern {'EXTENSIONMARK', _, _} can never match the type #ComponentType{}
+asn1ct_constructed_ber.erl:748: The pattern can never match the type <_,maybe_improper_list(),[#ComponentType{typespec::{_,_,_,_,_,_}}]>
+asn1ct_constructed_ber_bin_v2.erl:914: The pattern {{{'ObjectClassFieldType', _, _, _, {'objectfield', PrimFieldName1, PFNList}}, _}, {'componentrelation', _, _}} can never match the type {#type{},_}
+asn1ct_gen.erl:740: The pattern [] can never match the type [any(),...]
+asn1ct_gen_ber.erl:974: The pattern can never match the type <_,[#typedef{name::atom(),typespec::{_,_,_,_,_,_}}]>
+asn1ct_gen_ber_bin_v2.erl:975: The pattern can never match the type <_,[#typedef{name::atom(),typespec::{_,_,_,_,_,_}}]>
+asn1ct_gen_per.erl:646: The pattern can never match the type <_,[#typedef{name::atom()}]>
+asn1ct_gen_per_rt2ct.erl:1189: The pattern can never match the type <_,[#typedef{name::atom()}]>
+asn1ct_gen_per_rt2ct.erl:563: The pattern can never match the type <[{'ValueRange',{_,_}},...],[char() | {'asn1_enum',integer()},...],non_neg_integer()>
+asn1ct_gen_per_rt2ct.erl:580: The pattern <_C, 'EXT_MARK', _Count> can never match the type <[{'ValueRange',{_,_}},...],char(),non_neg_integer()>
+asn1ct_gen_per_rt2ct.erl:583: The pattern <_C, {1, EnumName}, Count> can never match the type <[{'ValueRange',{_,_}},...],char(),non_neg_integer()>
+asn1ct_gen_per_rt2ct.erl:587: The pattern can never match the type <[{'ValueRange',{_,_}},...],char(),non_neg_integer()>
+asn1ct_gen_per_rt2ct.erl:656: The pattern can never match since previous clauses completely covered the type <'bitstring' | 'integer',_>
+asn1ct_parser2.erl:2017: Call to missing or unexported function ordsets:list_to_set/1
+asn1ct_parser2.erl:2497: The variable _ can never match since previous clauses completely covered the type 'ok'
+asn1ct_parser2.erl:2628: The pattern {Rlist, ExtList} can never match the type [{_,_,_},...]
+asn1ct_parser2.erl:2660: Call to missing or unexported function ordsets:list_to_set/1
+asn1ct_parser2.erl:2685: Call to missing or unexported function ordsets:list_to_set/1
+asn1ct_parser2.erl:281: The variable Other can never match since previous clauses completely covered the type [any()]
+asn1ct_parser2.erl:529: The variable _ can never match since previous clauses completely covered the type #constraint{}
+asn1ct_parser2.erl:555: The variable _ can never match since previous clauses completely covered the type #constraint{}
+asn1ct_parser2.erl:796: The variable _ can never match since previous clauses completely covered the type {_,_}
+asn1ct_parser2.erl:814: The variable _ can never match since previous clauses completely covered the type {_,_}
+asn1ct_parser2.erl:831: The variable _ can never match since previous clauses completely covered the type {_,_}
+asn1ct_value.erl:247: The pattern <'undefined', Default> can never match the type
+asn1rt_ber_bin.erl:1125: Cons will produce an improper list since its 2nd argument is binary() | tuple()
+asn1rt_ber_bin.erl:1276: The pattern <{{_Min1, Max1}, {Min2, Max2}}, BitListVal, _DoTag> can never match since previous clauses completely covered the type <{_,_},maybe_improper_list(),_>
+asn1rt_ber_bin.erl:2057: The call asn1rt_ber_bin:check_if_valid_tag2('false',[],[],OptOrMand::any()) will never return since it differs in the 2nd argument from the success typing arguments: ('false' | {'APPLICATION',_} | {'CONTEXT',_} | {'PRIVATE',_} | {'UNIVERSAL',_},nonempty_maybe_improper_list(),[] | {_,_,_},any())
+asn1rt_ber_bin.erl:969: The pattern {Val01, Buffer01, Rb01} can never match the type {'MINUS-INFINITY' | 'PLUS-INFINITY' | 0,binary()}
+asn1rt_ber_bin.erl:998: The pattern {FirstLen, {Exp, Buffer3}, RemBytes2} can never match the type {1..1114111,{integer(),binary(),number()},number()}
+asn1rt_ber_bin_v2.erl:1230: The pattern <{{_Min1, Max1}, {Min2, Max2}}, BitListVal, TagIn> can never match since previous clauses completely covered the type <{_,_},maybe_improper_list(),_>
+asn1rt_ber_bin_v2.erl:328: The variable _ can never match since previous clauses completely covered the type {{0 | 1,non_neg_integer(),'indefinite' | non_neg_integer(),binary()},binary() | []}
+asn1rt_ber_bin_v2.erl:337: The variable _ can never match since previous clauses completely covered the type {{0 | 1,non_neg_integer(),'indefinite' | non_neg_integer(),binary()},binary() | []}
+asn1rt_ber_bin_v2.erl:392: The variable _ can never match since previous clauses completely covered the type {{0 | 1,non_neg_integer(),'indefinite' | non_neg_integer(),binary()},binary() | []}
+asn1rt_ber_bin_v2.erl:963: Function decode_real/3 has no local return
+asn1rt_check.erl:100: The variable _ can never match since previous clauses completely covered the type [any()]
+asn1rt_check.erl:85: The variable _ can never match since previous clauses completely covered the type [any()]
+asn1rt_driver_handler.erl:32: The pattern 'already_done' can never match the type {'error',_}
+asn1rt_per.erl:1065: The pattern {'BMPString', {'octets', Ol}} can never match the type {_,[{'bits',1 | 2 | 4 | 8 | 16 | 32,_}]}
+asn1rt_per.erl:1066: Function will never be called
+asn1rt_per.erl:1231: The call erlang:'not'('implemented') will never return since it differs in the 1st argument from the success typing arguments: (boolean())
+asn1rt_per.erl:1233: The call erlang:'not'('implemented') will never return since it differs in the 1st argument from the success typing arguments: (boolean())
+asn1rt_per.erl:1235: The call erlang:'not'('implemented') will never return since it differs in the 1st argument from the success typing arguments: (boolean())
+asn1rt_per.erl:1237: The call erlang:'not'('implemented') will never return since it differs in the 1st argument from the success typing arguments: (boolean())
+asn1rt_per.erl:989: The pattern <_C, 'true', _Val> can never match the type <_,'false',_>
+asn1rt_per_bin.erl:1361: The pattern <_, 'true', _> can never match the type <_,'false',_>
+asn1rt_per_bin.erl:1436: The pattern {'BMPString', {'octets', Ol}} can never match the type {'BMPString' | 'IA5String' | 'NumericString' | 'PrintableString' | 'UniversalString' | 'VisibleString',[{'bits',1 | 2 | 4 | 8 | 16 | 32,_}]}
+asn1rt_per_bin.erl:1437: Function will never be called
+asn1rt_per_bin.erl:161: The call asn1rt_per_bin:getbit({0,maybe_improper_list()}) will never return since it differs in the 1st argument from the success typing arguments: (<<_:8,_:_*8>> | {non_neg_integer(),<<_:1,_:_*1>>})
+asn1rt_per_bin.erl:1812: The pattern {Name, Val} can never match since previous clauses completely covered the type any()
+asn1rt_per_bin.erl:2106: Cons will produce an improper list since its 2nd argument is binary()
+asn1rt_per_bin.erl:2111: Cons will produce an improper list since its 2nd argument is binary()
+asn1rt_per_bin.erl:2111: Cons will produce an improper list since its 2nd argument is integer()
+asn1rt_per_bin.erl:2117: Cons will produce an improper list since its 2nd argument is integer()
+asn1rt_per_bin.erl:2121: Cons will produce an improper list since its 2nd argument is 0
+asn1rt_per_bin.erl:2123: Cons will produce an improper list since its 2nd argument is 0
+asn1rt_per_bin.erl:2127: Cons will produce an improper list since its 2nd argument is 0
+asn1rt_per_bin.erl:2129: Cons will produce an improper list since its 2nd argument is integer()
+asn1rt_per_bin.erl:446: The variable _ can never match since previous clauses completely covered the type integer()
+asn1rt_per_bin.erl:467: The variable _ can never match since previous clauses completely covered the type integer()
+asn1rt_per_bin.erl:474: The pattern <{_N, <<_:8/integer-unit:1,Bs/binary-unit:8>>}, C> can never match since previous clauses completely covered the type <{0,_},integer()>
+asn1rt_per_bin.erl:487: The variable _ can never match since previous clauses completely covered the type integer()
+asn1rt_per_bin.erl:498: The variable _ can never match since previous clauses completely covered the type integer()
+asn1rt_per_bin_rt2ct.erl:152: The call asn1rt_per_bin_rt2ct:getbit({0,maybe_improper_list()}) will never return since it differs in the 1st argument from the success typing arguments: (<<_:8,_:_*8>> | {non_neg_integer(),<<_:1,_:_*1>>})
+asn1rt_per_bin_rt2ct.erl:1533: The pattern {'BMPString', {'octets', Ol}} can never match the type {_,[[any(),...]]}
+asn1rt_per_bin_rt2ct.erl:1534: Function will never be called
+asn1rt_per_bin_rt2ct.erl:1875: The pattern {Name, Val} can never match since previous clauses completely covered the type any()
+asn1rt_per_bin_rt2ct.erl:443: The variable _ can never match since previous clauses completely covered the type integer()
+asn1rt_per_bin_rt2ct.erl:464: The variable _ can never match since previous clauses completely covered the type integer()
+asn1rt_per_bin_rt2ct.erl:471: The pattern <{_N, <<_B:8/integer-unit:1,Bs/binary-unit:8>>}, C> can never match since previous clauses completely covered the type <{0,_},integer()>
+asn1rt_per_bin_rt2ct.erl:484: The variable _ can never match since previous clauses completely covered the type integer()
+asn1rt_per_bin_rt2ct.erl:495: The variable _ can never match since previous clauses completely covered the type integer()
+asn1rt_per_v1.erl:1209: The pattern <_, 'true', _> can never match the type <_,'false',_>
+asn1rt_per_v1.erl:1290: The pattern {'BMPString', {'octets', Ol}} can never match the type {'BMPString' | 'IA5String' | 'NumericString' | 'PrintableString' | 'UniversalString' | 'VisibleString',[{'bits',1 | 2 | 4 | 8 | 16 | 32,_}]}
+asn1rt_per_v1.erl:1291: Function will never be called
diff --git a/lib/dialyzer/test/r9c_SUITE_data/results/inets b/lib/dialyzer/test/r9c_SUITE_data/results/inets
new file mode 100644
index 0000000000..fd5e36a3cd
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/results/inets
@@ -0,0 +1,59 @@
+
+ftp.erl:1243: The pattern {'ok', {N, Bytes}} can never match the type 'eof' | {'error',atom()} | {'ok',binary() | string()}
+ftp.erl:640: The pattern {'closed', _Why} can never match the type 'perm_fname_not_allowed' | 'perm_neg_compl' | 'perm_no_space' | 'pos_compl' | 'pos_interm' | 'pos_interm_acct' | 'trans_neg_compl' | 'trans_no_space' | {'error' | 'perm_fname_not_allowed' | 'perm_neg_compl' | 'perm_no_space' | 'pos_compl' | 'pos_interm' | 'pos_interm_acct' | 'pos_prel' | 'trans_neg_compl' | 'trans_no_space',atom() | [any()] | {'invalid_server_response',[any(),...]}}
+http.erl:117: The pattern {'error', Reason} can never match the type #req_headers{connection::[45 | 97 | 101 | 105 | 107 | 108 | 112 | 118,...],content_length::[48,...],other::[{_,_}]}
+http.erl:138: Function close_session/2 will never be called
+http_lib.erl:286: The call http_lib:close('ip_comm' | {'ssl',_},any()) will never return since it differs in the 1st argument from the success typing arguments: ('http' | 'https',any())
+http_lib.erl:424: The variable _ can never match since previous clauses completely covered the type any()
+http_lib.erl:438: The variable _ can never match since previous clauses completely covered the type any()
+http_lib.erl:99: Function getHeaderValue/2 will never be called
+httpc_handler.erl:322: Function status_continue/2 has no local return
+httpc_handler.erl:37: Function init_connection/2 has no local return
+httpc_handler.erl:65: Function next_response_with_request/2 has no local return
+httpc_handler.erl:660: Function exit_session_ok/2 has no local return
+httpc_manager.erl:145: The pattern {ErrorReply, State2} can never match the type {{'ok',number()},number(),#state{reqid::number()}}
+httpc_manager.erl:160: The pattern {ErrorReply, State2} can never match the type {{'ok',number()},number(),#state{reqid::number()}}
+httpc_manager.erl:478: The pattern {'error', Reason} can never match the type 'ok' | {number(),#session{clientclose::boolean(),pipeline::[],quelength::1}}
+httpc_manager.erl:490: The pattern {'error', Reason} can never match the type 'ok' | {number(),#session{clientclose::boolean(),pipeline::[],quelength::1}}
+httpd.erl:583: The pattern <{'error', Reason}, _Fd, SoFar> can never match the type <[any()],pid(),[[any(),...]]>
+httpd_acceptor.erl:105: The pattern {'error', Reason} can never match the type {'ok',pid()}
+httpd_acceptor.erl:110: Function handle_connection_err/4 will never be called
+httpd_acceptor.erl:168: Function report_error/2 will never be called
+httpd_acceptor.erl:91: The call httpd_acceptor:handle_error({'EXIT',_},ConfigDb::any(),SocketType::any()) will never return since it differs in the 1st argument from the success typing arguments: ('econnaborted' | 'emfile' | 'esslaccept' | 'timeout' | {'enfile',_},any(),any())
+httpd_manager.erl:885: The pattern {'EXIT', Reason} can never match since previous clauses completely covered the type any()
+httpd_manager.erl:919: Function auth_status/1 will never be called
+httpd_manager.erl:926: Function sec_status/1 will never be called
+httpd_manager.erl:933: Function acceptor_status/1 will never be called
+httpd_request_handler.erl:374: The call httpd_response:send_status(Info::#mod{parsed_header::maybe_improper_list()},417,[32 | 66 | 98 | 100 | 103 | 105 | 111 | 116 | 121,...]) will never return since it differs in the 2nd argument from the success typing arguments: (#mod{socket_type::'ip_comm' | {'ssl',_}},100 | 301 | 304 | 400 | 401 | 403 | 404 | 412 | 414 | 416 | 500 | 501 | 503,any())
+httpd_request_handler.erl:378: The call httpd_response:send_status(Info::#mod{parsed_header::maybe_improper_list()},417,[32 | 77 | 97 | 100 | 101 | 104 | 108 | 110 | 111 | 116 | 119,...]) will never return since it differs in the 2nd argument from the success typing arguments: (#mod{socket_type::'ip_comm' | {'ssl',_}},100 | 301 | 304 | 400 | 401 | 403 | 404 | 412 | 414 | 416 | 500 | 501 | 503,any())
+httpd_request_handler.erl:401: The call httpd_response:send_status(Info::#mod{parsed_header::maybe_improper_list()},417,[32 | 77 | 97 | 100 | 101 | 104 | 108 | 110 | 111 | 116 | 119,...]) will never return since it differs in the 2nd argument from the success typing arguments: (#mod{socket_type::'ip_comm' | {'ssl',_}},100 | 301 | 304 | 400 | 401 | 403 | 404 | 412 | 414 | 416 | 500 | 501 | 503,any())
+httpd_request_handler.erl:644: The call lists:reverse(Fields0::{'error',_} | {'ok',[[any()]]}) will never return since it differs in the 1st argument from the success typing arguments: ([any()])
+httpd_request_handler.erl:645: Function will never be called
+httpd_sup.erl:63: The variable Else can never match since previous clauses completely covered the type {'error',_} | {'ok',[any()],_,_}
+httpd_sup.erl:88: The pattern {'error', Reason} can never match the type {'ok',_,_}
+httpd_sup.erl:92: The variable Else can never match since previous clauses completely covered the type {'ok',_,_}
+mod_auth.erl:559: The pattern {'error', Reason} can never match the type {_,integer(),maybe_improper_list(),_}
+mod_auth_dets.erl:120: The call lists:foreach(fun((_) -> 'true' | {'error','no_such_group' | 'no_such_group_member'}),{'ok',[any()]}) will never return since it differs in the 2nd argument from the success typing arguments: (fun((_) -> any()),[any()])
+mod_auth_plain.erl:100: The variable _ can never match since previous clauses completely covered the type {'ok',[any()]}
+mod_auth_plain.erl:159: The variable _ can never match since previous clauses completely covered the type [any()]
+mod_auth_plain.erl:83: The variable O can never match since previous clauses completely covered the type [any()]
+mod_cgi.erl:372: The pattern {'http_response', NewAccResponse} can never match the type 'ok'
+mod_dir.erl:101: The call lists:flatten(nonempty_improper_list(atom() | binary() | [any()] | char(),atom())) will never return since it differs in the 1st argument from the success typing arguments: ([any()])
+mod_dir.erl:72: The pattern {'error', Reason} can never match the type {'ok',[[[any()] | char()],...]}
+mod_get.erl:135: The pattern <{'enfile', _}, _Info, Path> can never match the type
+mod_head.erl:80: The pattern <{'enfile', _}, _Info, Path> can never match the type
+mod_htaccess.erl:460: The pattern {'error', BadData} can never match the type {'ok',_}
+mod_include.erl:193: The pattern {_, Name, {[], []}} can never match the type {[any()],[any()],maybe_improper_list()}
+mod_include.erl:195: The pattern {_, Name, {PathInfo, []}} can never match the type {[any()],[any()],maybe_improper_list()}
+mod_include.erl:197: The pattern {_, Name, {PathInfo, QueryString}} can never match the type {[any()],[any()],maybe_improper_list()}
+mod_include.erl:201: The variable Gurka can never match since previous clauses completely covered the type {[any()],[any()],maybe_improper_list()}
+mod_include.erl:692: The pattern <{'read', Reason}, Info, Path> can never match the type <{'open',atom()},#mod{},atom() | binary() | [atom() | binary() | [atom() | binary() | [any()] | char()] | char()]>
+mod_include.erl:706: The pattern <{'enfile', _}, _Info, Path> can never match the type
+mod_include.erl:716: Function read_error/3 will never be called
+mod_include.erl:719: Function read_error/4 will never be called
+mod_security_server.erl:386: The variable O can never match since previous clauses completely covered the type [any()]
+mod_security_server.erl:433: The variable Other can never match since previous clauses completely covered the type [any()]
+mod_security_server.erl:585: The variable _ can never match since previous clauses completely covered the type [any()]
+mod_security_server.erl:608: The variable _ can never match since previous clauses completely covered the type [any()]
+mod_security_server.erl:641: The variable _ can never match since previous clauses completely covered the type [any()]
+uri.erl:146: The pattern {'error', Error} can never match since previous clauses completely covered the type {_,{[],[]}}
diff --git a/lib/dialyzer/test/r9c_SUITE_data/results/mnesia b/lib/dialyzer/test/r9c_SUITE_data/results/mnesia
new file mode 100644
index 0000000000..e199581a0e
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/results/mnesia
@@ -0,0 +1,34 @@
+
+mnesia.erl:1319: Guard test size(Spec::[{_,_,_},...]) can never succeed
+mnesia.erl:1498: The call mnesia:bad_info_reply(Tab::atom(),Item::'type') will never return since it differs in the 2nd argument from the success typing arguments: (atom(),'memory' | 'size')
+mnesia.erl:331: Function mod2abs/1 has no local return
+mnesia_bup.erl:111: The created fun has no local return
+mnesia_bup.erl:574: Function fallback_receiver/2 has no local return
+mnesia_bup.erl:967: Function uninstall_fallback_master/2 has no local return
+mnesia_checkpoint.erl:1014: The variable Error can never match since previous clauses completely covered the type {'ok',#checkpoint_args{nodes::[any()],retainers::[any(),...]}}
+mnesia_controller.erl:1666: The variable Tab can never match since previous clauses completely covered the type [any()]
+mnesia_controller.erl:1679: The pattern {'stop', Reason, Reply, State2} can never match the type {'noreply',_} | {'reply',_,_} | {'stop','shutdown',#state{}}
+mnesia_controller.erl:1685: The pattern {'noreply', State2, _Timeout} can never match the type {'reply',_,_}
+mnesia_event.erl:77: The pattern 'remove_handler' can never match the type {'ok',_}
+mnesia_event.erl:79: The pattern {'swap_handler', Args1, State1, Mod2, Args2} can never match the type {'ok',_}
+mnesia_frag.erl:294: The call mnesia_frag:remote_collect(Ref::reference(),{'error',_},[],OldSelectFun::fun(() -> [any()])) will never return since it differs in the 2nd argument from the success typing arguments: (reference(),'ok',[any()],fun(() -> [any()]))
+mnesia_frag.erl:304: The call mnesia_frag:remote_collect(Ref::reference(),{'error',{'node_not_running',_}},[],OldSelectFun::fun(() -> [any()])) will never return since it differs in the 2nd argument from the success typing arguments: (reference(),'ok',[any()],fun(() -> [any()]))
+mnesia_frag.erl:312: The call mnesia_frag:remote_collect(Ref::reference(),LocalRes::{'error',_},[],OldSelectFun::fun(() -> [any()])) will never return since it differs in the 2nd argument from the success typing arguments: (reference(),'ok',[any()],fun(() -> [any()]))
+mnesia_index.erl:52: The call mnesia_lib:other_val(Var::{_,'commit_work' | 'index' | 'setorbag' | 'storage_type' | {'index',_}},_ReASoN_::any()) will never return since it differs in the 1st argument from the success typing arguments: ({_,'active_replicas' | 'where_to_read' | 'where_to_write'},any())
+mnesia_lib.erl:957: The pattern {'ok', {0, _}} can never match the type 'eof' | {'error',atom()} | {'ok',binary() | string()}
+mnesia_lib.erl:959: The pattern {'ok', {_, Bin}} can never match the type 'eof' | {'error',atom()} | {'ok',binary() | string()}
+mnesia_loader.erl:36: The call mnesia_lib:other_val(Var::{_,'access_mode' | 'cstruct' | 'db_nodes' | 'setorbag' | 'snmp' | 'storage_type'},Reason::any()) will never return since it differs in the 1st argument from the success typing arguments: ({_,'active_replicas' | 'where_to_read' | 'where_to_write'},any())
+mnesia_locker.erl:1017: Function system_terminate/4 has no local return
+mnesia_log.erl:707: The test {'error',{[1..255,...],[any(),...]}} | {'ok',_} == atom() can never evaluate to 'true'
+mnesia_log.erl:727: The created fun has no local return
+mnesia_monitor.erl:162: The pattern <[], []> can never match the type <[any(),...],[any(),...]>
+mnesia_monitor.erl:354: The pattern {'error', Reason} can never match the type 'ok'
+mnesia_recover.erl:159: The call mnesia_lib:other_val(Var::'latest_transient_decision' | 'max_wait_for_decision' | 'previous_transient_decisions' | 'recover_nodes',Reason::any()) will never return since it differs in the 1st argument from the success typing arguments: ({_,'active_replicas' | 'where_to_read' | 'where_to_write'},any())
+mnesia_recover.erl:884: The pattern {'stop', Reason, Reply, State2} can never match the type {'noreply',_} | {'stop','shutdown',#state{}}
+mnesia_schema.erl:1088: Guard test Storage::'disc_copies' | 'disc_only_copies' | 'ram_copies' == 'unknown' can never succeed
+mnesia_schema.erl:1258: Guard test FromS::'disc_copies' | 'disc_only_copies' | 'ram_copies' == 'unknown' can never succeed
+mnesia_schema.erl:1639: The pattern {'false', 'mandatory'} can never match the type {'false','optional'}
+mnesia_schema.erl:2434: The variable Reason can never match since previous clauses completely covered the type {'error',_} | {'ok',_}
+mnesia_schema.erl:451: Guard test UseDirAnyway::'false' == 'true' can never succeed
+mnesia_tm.erl:1522: Function commit_participant/5 has no local return
+mnesia_tm.erl:2169: Function system_terminate/4 has no local return
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/Makefile b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/Makefile
new file mode 100644
index 0000000000..9dba643327
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/Makefile
@@ -0,0 +1,142 @@
+#
+# Copyright (C) 1997, Ericsson Telecommunications
+# Author: Kenneth Lundin
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(ASN1_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/asn1-$(VSN)
+
+
+
+
+#
+# Common Macros
+#
+# PARSER_SRC = \
+# asn1ct_parser.yrl
+
+# PARSER_MODULE=$(PARSER_SRC:%.yrl=%)
+
+EBIN = ../ebin
+CT_MODULES= \
+ asn1ct \
+ asn1ct_check \
+ asn1_db \
+ asn1ct_pretty_format \
+ asn1ct_gen \
+ asn1ct_gen_per \
+ asn1ct_gen_per_rt2ct \
+ asn1ct_name \
+ asn1ct_constructed_per \
+ asn1ct_constructed_ber \
+ asn1ct_gen_ber \
+ asn1ct_constructed_ber_bin_v2 \
+ asn1ct_gen_ber_bin_v2 \
+ asn1ct_value \
+ asn1ct_tok \
+ asn1ct_parser2
+
+RT_MODULES= \
+ asn1rt \
+ asn1rt_per \
+ asn1rt_per_bin \
+ asn1rt_per_v1 \
+ asn1rt_ber_bin \
+ asn1rt_ber_bin_v2 \
+ asn1rt_per_bin_rt2ct \
+ asn1rt_driver_handler \
+ asn1rt_check
+
+# asn1rt_ber_v1 \
+# asn1rt_ber \
+# the rt module to use is defined in asn1_records.hrl
+# and must be updated when an incompatible change is done in the rt modules
+
+
+MODULES= $(CT_MODULES) $(RT_MODULES)
+
+ERL_FILES = $(MODULES:%=%.erl)
+
+TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+GENERATED_PARSER = $(PARSER_MODULE:%=%.erl)
+
+# internal hrl file
+HRL_FILES = asn1_records.hrl
+
+APP_FILE = asn1.app
+APPUP_FILE = asn1.appup
+
+APP_SRC = $(APP_FILE).src
+APP_TARGET = $(EBIN)/$(APP_FILE)
+
+APPUP_SRC = $(APPUP_FILE).src
+APPUP_TARGET = $(EBIN)/$(APPUP_FILE)
+
+EXAMPLES = \
+ ../examples/P-Record.asn
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_FLAGS +=
+ERL_COMPILE_FLAGS += \
+ -I$(ERL_TOP)/lib/stdlib \
+ +warn_unused_vars
+YRL_FLAGS =
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
+
+
+clean:
+ rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(GENERATED_PARSER)
+ rm -f core *~
+
+docs:
+
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+$(EBIN)/asn1ct.$(EMULATOR):asn1ct.erl
+ $(ERLC) -b$(EMULATOR) -o$(EBIN) $(ERL_COMPILE_FLAGS) -Dvsn=\"$(VSN)\" $<
+
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(PARSER_SRC) $(ERL_FILES) $(HRL_FILES) $(APP_SRC) $(APPUP_SRC) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/examples
+ $(INSTALL_DATA) $(EXAMPLES) $(RELSYSDIR)/examples
+
+# there are no include files to be used by the user
+#$(INSTALL_DIR) $(RELSYSDIR)/include
+#$(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include
+
+release_docs_spec:
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/Restrictions.txt b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/Restrictions.txt
new file mode 100644
index 0000000000..d1d1855dc9
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/Restrictions.txt
@@ -0,0 +1,55 @@
+The following restrictions apply to this implementation of the ASN.1 compiler:
+
+Supported encoding rules are:
+BER
+PER (aligned)
+
+PER (unaligned) IS NOT SUPPORTED
+
+Supported types are:
+
+INTEGER
+BOOLEAN
+ENUMERATION
+SEQUENCE
+SEQUENCE OF
+SET
+SET OF
+CHOICE
+OBJECT IDENTIFIER
+RestrictedCharacterStringTypes
+UnrestrictedCharacterStringTypes
+
+
+NOT SUPPORTED types are:
+ANY IS (IS NOT IN THE STANDARD ANY MORE)
+ANY DEFINED BY (IS NOT IN THE STANDARD ANY MORE)
+EXTERNAL
+EMBEDDED-PDV
+REAL
+
+The support for value definitions in the ASN.1 notation is very limited.
+
+The support for constraints is limited to:
+SizeConstraint SIZE(X)
+SingleValue (1)
+ValueRange (X..Y)
+PermittedAlpabet FROM
+
+The only supported value-notation for SEQUENCE and SET in Erlang is
+the record variant.
+The list notation with named components used by the old ASN.1 compiler
+was supported in the first versions of this compiler both are no longer
+supported.
+
+The decode functions always return a symbolic value if they can.
+
+
+Files with ASN.1 source must have a suffix .asn1 the suffix .py used by the
+old ASN.1 compiler is supported in this version but will not be supported in the future.
+
+Generated files:
+X.asn1db % the intermediate format of a compiled ASN.1 module
+X.hrl % generated Erlang include file for module X
+X.erl % generated Erlang module with encode decode functions for
+ % ASN.1 module X
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1.app.src b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1.app.src
new file mode 100644
index 0000000000..2ec06ff4db
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1.app.src
@@ -0,0 +1,20 @@
+{application, asn1,
+ [{description, "The Erlang ASN1 compiler version %VSN%"},
+ {vsn, "%VSN%"},
+ {modules, [
+ asn1rt,
+ asn1rt_per,
+ asn1rt_per_v1,
+ asn1rt_per_bin,
+ asn1rt_per_bin_rt2ct,
+ asn1rt_ber_bin,
+ asn1rt_ber_bin_v2,
+ asn1rt_check,
+ asn1rt_driver_handler
+ ]},
+ {registered, [
+ asn1_driver_owner
+ ]},
+ {env, []},
+ {applications, [kernel, stdlib]}
+ ]}.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1.appup.src b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1.appup.src
new file mode 100644
index 0000000000..55ef53994a
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1.appup.src
@@ -0,0 +1,162 @@
+{"%VSN%",
+ [
+ {"1.3",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {add_module, asn1rt_per_bin},
+ {add_module, asn1rt_check}
+ {add_module, asn1rt_per_bin_rt2ct},
+ {add_module, asn1rt_ber_bin_v2},
+ {add_module, asn1rt_driver_handler}
+ {remove, {asn1rt_ber_v1, soft_purge, soft_purge}},
+ ]
+ },
+ {"1.3.1",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {add_module, asn1rt_per_bin},
+ {add_module, asn1rt_check}
+ {add_module, asn1rt_per_bin_rt2ct},
+ {add_module, asn1rt_ber_bin_v2},
+ {add_module, asn1rt_driver_handler}
+ {remove, {asn1rt_ber_v1, soft_purge, soft_purge}},
+ ]
+ },
+ {"1.3.1.1",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {add_module, asn1rt_per_bin},
+ {add_module, asn1rt_check}
+ {add_module, asn1rt_per_bin_rt2ct},
+ {add_module, asn1rt_ber_bin_v2},
+ {add_module, asn1rt_driver_handler}
+ {remove, {asn1rt_ber_v1, soft_purge, soft_purge}},
+ ]
+ },
+ {"1.3.2",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt_check, soft_purge, soft_purge, []},
+ {add_module, asn1rt_per_bin_rt2ct},
+ {add_module, asn1rt_ber_bin_v2},
+ {add_module, asn1rt_driver_handler}
+ {remove, {asn1rt_ber_v1, soft_purge, soft_purge}},
+ ]
+ },
+ {"1.3.3",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt_check, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []},
+ {add_module, asn1rt_ber_bin_v2},
+ {add_module, asn1rt_driver_handler}
+ {remove, {asn1rt_ber_v1, soft_purge, soft_purge}},
+ ]
+ },
+ {"1.3.3.1",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt_check, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []},
+ {add_module, asn1rt_ber_bin_v2},
+ {add_module, asn1rt_driver_handler}
+ {remove, {asn1rt_ber_v1, soft_purge, soft_purge}},
+ ]
+ }
+ ],
+ [
+ {"1.3",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {add_module, asn1rt_ber_v1},
+ {remove, {asn1rt_per_bin, soft_purge, soft_purge}},
+ {remove, {asn1rt_check, soft_purge, soft_purge}}
+ {remove, {asn1rt_per_bin_rt2ct, soft_purge, soft_purge}},
+ {remove, {asn1rt_ber_bin_v2, soft_purge, soft_purge}},
+ {remove, {asn1rt_driver_handler, soft_purge, soft_purge}}
+ ]
+ },
+ {"1.3.1",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {add_module, asn1rt_ber_v1},
+ {remove, {asn1rt_per_bin, soft_purge, soft_purge}},
+ {remove, {asn1rt_check, soft_purge, soft_purge}}
+ {remove, {asn1rt_per_bin_rt2ct, soft_purge, soft_purge}},
+ {remove, {asn1rt_ber_bin_v2, soft_purge, soft_purge}},
+ {remove, {asn1rt_driver_handler, soft_purge, soft_purge}}
+ ]
+ },
+ {"1.3.1.1",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {add_module, asn1rt_ber_v1},
+ {remove, {asn1rt_per_bin, soft_purge, soft_purge}},
+ {remove, {asn1rt_check, soft_purge, soft_purge}}
+ {remove, {asn1rt_per_bin_rt2ct, soft_purge, soft_purge}},
+ {remove, {asn1rt_ber_bin_v2, soft_purge, soft_purge}},
+ {remove, {asn1rt_driver_handler, soft_purge, soft_purge}}
+ ]
+ },
+ {"1.3.2",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt_check, soft_purge, soft_purge, []},
+ {add_module, asn1rt_ber_v1},
+ {remove, {asn1rt_per_bin_rt2ct, soft_purge, soft_purge}},
+ {remove, {asn1rt_ber_bin_v2, soft_purge, soft_purge}},
+ {remove, {asn1rt_driver_handler, soft_purge, soft_purge}}
+ ]
+ },
+ {"1.3.3",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt_check, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []},
+ {add_module, asn1rt_ber_v1},
+ {remove, {asn1rt_ber_bin_v2, soft_purge, soft_purge}},
+ {remove, {asn1rt_driver_handler, soft_purge, soft_purge}}
+ ]
+ },
+ {"1.3.3.1",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt_check, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []},
+ {add_module, asn1rt_ber_v1},
+ {remove, {asn1rt_ber_bin_v2, soft_purge, soft_purge}},
+ {remove, {asn1rt_driver_handler, soft_purge, soft_purge}}
+ ]
+ }
+
+ ]}.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1_db.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1_db.erl
new file mode 100644
index 0000000000..d5ddb9582b
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1_db.erl
@@ -0,0 +1,160 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1_db.erl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-module(asn1_db).
+%-compile(export_all).
+-export([dbnew/1,dbsave/2,dbload/1,dbput/3,dbget/2,dbget_all/1]).
+-export([dbget_all_mod/1,dbstop/0,dbclear/0,dberase_module/1,dbstart/1,stop_server/1]).
+%% internal exports
+-export([dbloop0/1,dbloop/2]).
+
+%% Db stuff
+dbstart(Includes) ->
+ start_server(asn1db, asn1_db, dbloop0, [Includes]).
+
+dbloop0(Includes) ->
+ dbloop(Includes, ets:new(asn1, [set,named_table])).
+
+opentab(Tab,Mod,[]) ->
+ opentab(Tab,Mod,["."]);
+opentab(Tab,Mod,Includes) ->
+ Base = lists:concat([Mod,".asn1db"]),
+ opentab2(Tab,Base,Mod,Includes,ok).
+
+opentab2(_Tab,_Base,_Mod,[],Error) ->
+ Error;
+opentab2(Tab,Base,Mod,[Ih|It],_Error) ->
+ File = filename:join(Ih,Base),
+ case ets:file2tab(File) of
+ {ok,Modtab} ->
+ ets:insert(Tab,{Mod, Modtab}),
+ {ok,Modtab};
+ NewErr ->
+ opentab2(Tab,Base,Mod,It,NewErr)
+ end.
+
+
+dbloop(Includes, Tab) ->
+ receive
+ {From,{set, Mod, K2, V}} ->
+ [{_,Modtab}] = ets:lookup(Tab,Mod),
+ ets:insert(Modtab,{K2, V}),
+ From ! {asn1db, ok},
+ dbloop(Includes, Tab);
+ {From, {get, Mod, K2}} ->
+ Result = case ets:lookup(Tab,Mod) of
+ [] ->
+ opentab(Tab,Mod,Includes);
+ [{_,Modtab}] -> {ok,Modtab}
+ end,
+ case Result of
+ {ok,Newtab} ->
+ From ! {asn1db, lookup(Newtab, K2)};
+ _Error ->
+ From ! {asn1db, undefined}
+ end,
+ dbloop(Includes, Tab);
+ {From, {all_mod, Mod}} ->
+ [{_,Modtab}] = ets:lookup(Tab,Mod),
+ From ! {asn1db, ets:tab2list(Modtab)},
+ dbloop(Includes, Tab);
+ {From, {delete_mod, Mod}} ->
+ [{_,Modtab}] = ets:lookup(Tab,Mod),
+ ets:delete(Modtab),
+ ets:delete(Tab,Mod),
+ From ! {asn1db, ok},
+ dbloop(Includes, Tab);
+ {From, {save, OutFile,Mod}} ->
+ [{_,Mtab}] = ets:lookup(Tab,Mod),
+ {From ! {asn1db, ets:tab2file(Mtab,OutFile)}},
+ dbloop(Includes,Tab);
+ {From, {load, Mod}} ->
+ Result = case ets:lookup(Tab,Mod) of
+ [] ->
+ opentab(Tab,Mod,Includes);
+ [{_,Modtab}] -> {ok,Modtab}
+ end,
+ {From, {asn1db,Result}},
+ dbloop(Includes,Tab);
+ {From, {new, Mod}} ->
+ case ets:lookup(Tab,Mod) of
+ [{_,Modtab}] ->
+ ets:delete(Modtab);
+ _ ->
+ true
+ end,
+ Tabname = list_to_atom(lists:concat(["asn1_",Mod])),
+ ets:new(Tabname, [set,named_table]),
+ ets:insert(Tab,{Mod,Tabname}),
+ From ! {asn1db, ok},
+ dbloop(Includes,Tab);
+ {From, stop} ->
+ From ! {asn1db, ok}; %% nothing to store
+ {From, clear} ->
+ ModTabList = [Mt||{_,Mt} <- ets:tab2list(Tab)],
+ lists:foreach(fun(T) -> ets:delete(T) end,ModTabList),
+ ets:delete(Tab),
+ From ! {asn1db, cleared},
+ dbloop(Includes, ets:new(asn1, [set]))
+ end.
+
+
+%%all(Tab, K) ->
+%% pickup(K, ets:match(Tab, {{K, '$1'}, '$2'})).
+%%pickup(K, []) -> [];
+%%pickup(K, [[V1,V2] |T]) ->
+%% [{{K,V1},V2} | pickup(K, T)].
+
+lookup(Tab, K) ->
+ case ets:lookup(Tab, K) of
+ [] -> undefined;
+ [{K,V}] -> V
+ end.
+
+
+dbnew(Module) -> req({new,Module}).
+dbsave(OutFile,Module) -> req({save,OutFile,Module}).
+dbload(Module) -> req({load,Module}).
+
+dbput(Module,K,V) -> req({set, Module, K, V}).
+dbget(Module,K) -> req({get, Module, K}).
+dbget_all(K) -> req({get_all, K}).
+dbget_all_mod(Mod) -> req({all_mod,Mod}).
+dbstop() -> stop_server(asn1db).
+dbclear() -> req(clear).
+dberase_module({module,M})->
+ req({delete_mod, M}).
+
+req(R) ->
+ asn1db ! {self(), R},
+ receive {asn1db, Reply} -> Reply end.
+
+stop_server(Name) ->
+ stop_server(Name, whereis(Name)).
+stop_server(_, undefined) -> stopped;
+stop_server(Name, _Pid) ->
+ Name ! {self(), stop},
+ receive {Name, _} -> stopped end.
+
+
+start_server(Name,Mod,Fun,Args) ->
+ case whereis(Name) of
+ undefined ->
+ register(Name, spawn(Mod,Fun, Args));
+ _Pid ->
+ already_started
+ end.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1_records.hrl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1_records.hrl
new file mode 100644
index 0000000000..6ba4877523
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1_records.hrl
@@ -0,0 +1,96 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1_records.hrl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-define('RT_BER',"asn1rt_ber_v1").
+-define('RT_BER_BIN',"asn1rt_ber_bin").
+-define('RT_PER',"asn1rt_per_v1").
+%% change to this when we have this module -define('RT_PER_BIN',"asn1rt_per_bin").
+-define('RT_PER_BIN',"asn1rt_per_bin").
+
+-record(module,{pos,name,defid,tagdefault='EXPLICIT',exports={exports,[]},imports={imports,[]}, extensiondefault=empty,typeorval}).
+
+-record('SEQUENCE',{pname=false,tablecinf=false,components=[]}).
+-record('SET',{pname=false,sorted=false,tablecinf=false,components=[]}).
+-record('ComponentType',{pos,name,typespec,prop,tags}).
+-record('ObjectClassFieldType',{classname,class,fieldname,type}).
+
+-record(typedef,{checked=false,pos,name,typespec}).
+-record(classdef,{checked=false,pos,name,typespec}).
+-record(valuedef,{checked=false,pos,name,type,value}).
+-record(ptypedef,{checked=false,pos,name,args,typespec}).
+-record(pvaluedef,{checked=false,pos,name,args,type,value}).
+-record(pvaluesetdef,{checked=false,pos,name,args,type,valueset}).
+-record(pobjectdef,{checked=false,pos,name,args,class,def}).
+-record(pobjectsetdef,{checked=false,pos,name,args,class,def}).
+
+-record(typereference,{pos,val}).
+-record(identifier,{pos,val}).
+-record(constraint,{c,e}).
+-record('Constraint',{'SingleValue'=no,'SizeConstraint'=no,'ValueRange'=no,'PermittedAlphabet'=no,
+ 'ContainedSubtype'=no, 'TypeConstraint'=no,'InnerSubtyping'=no,e=no,'Other'=no}).
+-record(simpletableattributes,{objectsetname,c_name,c_index,usedclassfield,
+ uniqueclassfield,valueindex}).
+-record(type,{tag=[],def,constraint=[],tablecinf=[],inlined=no}).
+
+-record(objectclass,{fields=[],syntax}).
+-record('Object',{classname,gen=true,def}).
+-record('ObjectSet',{class,gen=true,uniquefname,set}).
+
+-record(tag,{class,number,type,form=32}). % form = ?CONSTRUCTED
+% This record holds information about allowed constraint types per type
+-record(cmap,{single_value=no,contained_subtype=no,value_range=no,
+ size=no,permitted_alphabet=no,type_constraint=no,
+ inner_subtyping=no}).
+
+
+-record('EXTENSIONMARK',{pos,val}).
+
+% each IMPORT contains a list of 'SymbolsFromModule'
+-record('SymbolsFromModule',{symbols,module,objid}).
+
+% Externaltypereference -> modulename '.' typename
+-record('Externaltypereference',{pos,module,type}).
+% Externalvaluereference -> modulename '.' typename
+-record('Externalvaluereference',{pos,module,value}).
+
+-record(state,{module,mname,type,tname,value,vname,erule,parameters=[],
+ inputmodules,abscomppath=[],recordtopname=[],options}).
+
+%% state record used by backend at partial decode
+%% active is set to 'yes' when a partial decode function is generated.
+%% prefix is set to 'dec-inc-' or 'dec-partial-' is for
+%% incomplete partial decode or partial decode respectively
+%% inc_tag_pattern holds the tags of the significant types/components
+%% for incomplete partial decode.
+%% tag_pattern holds the tags for partial decode.
+%% inc_type_pattern and type_pattern holds the names of the
+%% significant types/components.
+%% func_name holds the name of the function for the toptype.
+%% namelist holds the list of names of types/components that still
+%% haven't been generated.
+%% tobe_refed_funcs is a list of tuples {function names
+%% (Types),namelist of incomplete decode spec}, with function names
+%% that are referenced within other generated partial incomplete
+%% decode functions. They shall be generated as partial incomplete
+%% decode functions.
+
+%% gen_refed_funcs is as list of function names. Unlike
+%% tobe_refed_funcs these have been generated.
+-record(gen_state,{active=false,prefix,inc_tag_pattern,
+ tag_pattern,inc_type_pattern,
+ type_pattern,func_name,namelist,
+ tobe_refed_funcs=[],gen_refed_funcs=[]}).
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct.erl
new file mode 100644
index 0000000000..fd36f1657e
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct.erl
@@ -0,0 +1,1904 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct.erl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-module(asn1ct).
+
+%% Compile Time functions for ASN.1 (e.g ASN.1 compiler).
+
+%%-compile(export_all).
+%% Public exports
+-export([compile/1, compile/2]).
+-export([start/0, start/1, stop/0]).
+-export([encode/2, encode/3, decode/3]).
+-export([test/1, test/2, test/3, value/2]).
+%% Application internal exports
+-export([compile_asn/3,compile_asn1/3,compile_py/3,compile/3,value/1,vsn/0,
+ create_ets_table/2,get_name_of_def/1,get_pos_of_def/1]).
+-export([read_config_data/1,get_gen_state_field/1,get_gen_state/0,
+ partial_inc_dec_toptype/1,save_gen_state/1,update_gen_state/2,
+ get_tobe_refed_func/1,reset_gen_state/0,is_function_generated/1,
+ generated_refed_func/1,next_refed_func/0,pop_namelist/0,
+ next_namelist_el/0,update_namelist/1,step_in_constructed/0,
+ add_tobe_refed_func/1,add_generated_refed_func/1]).
+
+-include("asn1_records.hrl").
+-include_lib("stdlib/include/erl_compile.hrl").
+
+-import(asn1ct_gen_ber_bin_v2,[encode_tag_val/3,decode_class/1]).
+
+-define(unique_names,0).
+-define(dupl_uniquedefs,1).
+-define(dupl_equaldefs,2).
+-define(dupl_eqdefs_uniquedefs,?dupl_equaldefs bor ?dupl_uniquedefs).
+
+-define(CONSTRUCTED, 2#00100000).
+
+%% macros used for partial decode commands
+-define(CHOOSEN,choosen).
+-define(SKIP,skip).
+-define(SKIP_OPTIONAL,skip_optional).
+
+%% macros used for partial incomplete decode commands
+-define(MANDATORY,mandatory).
+-define(DEFAULT,default).
+-define(OPTIONAL,opt).
+-define(PARTS,parts).
+-define(UNDECODED,undec).
+-define(ALTERNATIVE,alt).
+-define(ALTERNATIVE_UNDECODED,alt_undec).
+-define(ALTERNATIVE_PARTS,alt_parts).
+%-define(BINARY,bin).
+
+%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This is the interface to the compiler
+%%
+%%
+
+
+compile(File) ->
+ compile(File,[]).
+
+compile(File,Options) when list(Options) ->
+ Options1 =
+ case {lists:member(optimize,Options),lists:member(ber_bin,Options)} of
+ {true,true} ->
+ [ber_bin_v2|Options--[ber_bin]];
+ _ -> Options
+ end,
+ case (catch input_file_type(File)) of
+ {single_file,PrefixedFile} ->
+ (catch compile1(PrefixedFile,Options1));
+ {multiple_files_file,SetBase,FileName} ->
+ FileList = get_file_list(FileName),
+ (catch compile_set(SetBase,filename:dirname(FileName),
+ FileList,Options1));
+ Err = {input_file_error,_Reason} ->
+ {error,Err}
+ end.
+
+
+compile1(File,Options) when list(Options) ->
+ io:format("Erlang ASN.1 version ~p compiling ~p ~n",[?vsn,File]),
+ io:format("Compiler Options: ~p~n",[Options]),
+ Ext = filename:extension(File),
+ Base = filename:basename(File,Ext),
+ OutFile = outfile(Base,"",Options),
+ DbFile = outfile(Base,"asn1db",Options),
+ Includes = [I || {i,I} <- Options],
+ EncodingRule = get_rule(Options),
+ create_ets_table(asn1_functab,[named_table]),
+ Continue1 = scan({true,true},File,Options),
+ Continue2 = parse(Continue1,File,Options),
+ Continue3 = check(Continue2,File,OutFile,Includes,EncodingRule,
+ DbFile,Options,[]),
+ Continue4 = generate(Continue3,OutFile,EncodingRule,Options),
+ delete_tables([asn1_functab]),
+ compile_erl(Continue4,OutFile,Options).
+
+%%****************************************************************************%%
+%% functions dealing with compiling of several input files to one output file %%
+%%****************************************************************************%%
+compile_set(SetBase,DirName,Files,Options) when list(hd(Files)),list(Options) ->
+ %% case when there are several input files in a list
+ io:format("Erlang ASN.1 version ~p compiling ~p ~n",[?vsn,Files]),
+ io:format("Compiler Options: ~p~n",[Options]),
+ OutFile = outfile(SetBase,"",Options),
+ DbFile = outfile(SetBase,"asn1db",Options),
+ Includes = [I || {i,I} <- Options],
+ EncodingRule = get_rule(Options),
+ create_ets_table(asn1_functab,[named_table]),
+ ScanRes = scan_set(DirName,Files,Options),
+ ParseRes = parse_set(ScanRes,Options),
+ Result =
+ case [X||X <- ParseRes,element(1,X)==true] of
+ [] -> %% all were false, time to quit
+ lists:map(fun(X)->element(2,X) end,ParseRes);
+ ParseRes -> %% all were true, continue with check
+ InputModules =
+ lists:map(
+ fun(F)->
+ E = filename:extension(F),
+ B = filename:basename(F,E),
+ if
+ list(B) -> list_to_atom(B);
+ true -> B
+ end
+ end,
+ Files),
+ check_set(ParseRes,SetBase,OutFile,Includes,
+ EncodingRule,DbFile,Options,InputModules);
+ Other ->
+ {error,{'unexpected error in scan/parse phase',
+ lists:map(fun(X)->element(3,X) end,Other)}}
+ end,
+ delete_tables([asn1_functab]),
+ Result.
+
+check_set(ParseRes,SetBase,OutFile,Includes,EncRule,DbFile,
+ Options,InputModules) ->
+ lists:foreach(fun({_T,M,File})->
+ cmp(M#module.name,File)
+ end,
+ ParseRes),
+ MergedModule = merge_modules(ParseRes,SetBase),
+ SetM = MergedModule#module{name=SetBase},
+ Continue1 = check({true,SetM},SetBase,OutFile,Includes,EncRule,DbFile,
+ Options,InputModules),
+ Continue2 = generate(Continue1,OutFile,EncRule,Options),
+
+ delete_tables([renamed_defs,original_imports,automatic_tags]),
+
+ compile_erl(Continue2,OutFile,Options).
+
+%% merge_modules/2 -> returns a module record where the typeorval lists are merged,
+%% the exports lists are merged, the imports lists are merged when the
+%% elements come from other modules than the merge set, the tagdefault
+%% field gets the shared value if all modules have same tagging scheme,
+%% otherwise a tagging_error exception is thrown,
+%% the extensiondefault ...(not handled yet).
+merge_modules(ParseRes,CommonName) ->
+ ModuleList = lists:map(fun(X)->element(2,X) end,ParseRes),
+ NewModuleList = remove_name_collisions(ModuleList),
+ case ets:info(renamed_defs,size) of
+ 0 -> ets:delete(renamed_defs);
+ _ -> ok
+ end,
+ save_imports(NewModuleList),
+% io:format("~p~n~p~n~p~n~n",[ets:lookup(original_imports,'M1'),ets:lookup(original_imports,'M2'),ets:tab2list(original_imports)]),
+ TypeOrVal = lists:append(lists:map(fun(X)->X#module.typeorval end,
+ NewModuleList)),
+ InputMNameList = lists:map(fun(X)->X#module.name end,
+ NewModuleList),
+ CExports = common_exports(NewModuleList),
+
+ ImportsModuleNameList = lists:map(fun(X)->
+ {X#module.imports,
+ X#module.name} end,
+ NewModuleList),
+ %% ImportsModuleNameList: [{Imports,ModuleName},...]
+ %% Imports is a tuple {imports,[#'SymbolsFromModule'{},...]}
+ CImports = common_imports(ImportsModuleNameList,InputMNameList),
+ TagDefault = check_tagdefault(NewModuleList),
+ #module{name=CommonName,tagdefault=TagDefault,exports=CExports,
+ imports=CImports,typeorval=TypeOrVal}.
+
+%% causes an exit if duplicate definition names exist in a module
+remove_name_collisions(Modules) ->
+ create_ets_table(renamed_defs,[named_table]),
+ %% Name duplicates in the same module is not allowed.
+ lists:foreach(fun exit_if_nameduplicate/1,Modules),
+ %% Then remove duplicates in different modules and return the
+ %% new list of modules.
+ remove_name_collisions2(Modules,[]).
+
+%% For each definition in the first module in module list, find
+%% all definitons with same name and rename both definitions in
+%% the first module and in rest of modules
+remove_name_collisions2([M|Ms],Acc) ->
+ TypeOrVal = M#module.typeorval,
+ MName = M#module.name,
+ %% Test each name in TypeOrVal on all modules in Ms
+ {NewM,NewMs} = remove_name_collisions2(MName,TypeOrVal,Ms,[]),
+ remove_name_collisions2(NewMs,[M#module{typeorval=NewM}|Acc]);
+remove_name_collisions2([],Acc) ->
+ finished_warn_prints(),
+ Acc.
+
+%% For each definition in list of defs find definitions in (rest of)
+%% modules that have same name. If duplicate was found rename def.
+%% Test each name in [T|Ts] on all modules in Ms
+remove_name_collisions2(ModName,[T|Ts],Ms,Acc) ->
+ Name = get_name_of_def(T),
+ case discover_dupl_in_mods(Name,T,Ms,[],?unique_names) of
+ {_,?unique_names} -> % there was no name collision
+ remove_name_collisions2(ModName,Ts,Ms,[T|Acc]);
+ {NewMs,?dupl_uniquedefs} -> % renamed defs in NewMs
+ %% rename T
+ NewT = set_name_of_def(ModName,Name,T), %rename def
+ warn_renamed_def(ModName,get_name_of_def(NewT),Name),
+ ets:insert(renamed_defs,{get_name_of_def(NewT),Name,ModName}),
+ remove_name_collisions2(ModName,Ts,NewMs,[NewT|Acc]);
+ {NewMs,?dupl_equaldefs} -> % name duplicates, but identical defs
+ %% keep name of T
+ warn_kept_def(ModName,Name),
+ remove_name_collisions2(ModName,Ts,NewMs,[T|Acc]);
+ {NewMs,?dupl_eqdefs_uniquedefs} ->
+ %% keep name of T, renamed defs in NewMs
+ warn_kept_def(ModName,Name),
+ remove_name_collisions2(ModName,Ts,NewMs,[T|Acc])
+ end;
+remove_name_collisions2(_,[],Ms,Acc) ->
+ {Acc,Ms}.
+
+%% Name is the name of a definition. If a definition with the same name
+%% is found in the modules Ms the definition will be renamed and returned.
+discover_dupl_in_mods(Name,Def,[M=#module{name=N,typeorval=TorV}|Ms],
+ Acc,AnyRenamed) ->
+ Fun = fun(T,RenamedOrDupl)->
+ case {get_name_of_def(T),compare_defs(Def,T)} of
+ {Name,not_equal} ->
+ %% rename def
+ NewT=set_name_of_def(N,Name,T),
+ warn_renamed_def(N,get_name_of_def(NewT),Name),
+ ets:insert(renamed_defs,{get_name_of_def(NewT),
+ Name,N}),
+ {NewT,?dupl_uniquedefs bor RenamedOrDupl};
+ {Name,equal} ->
+ %% delete def
+ warn_deleted_def(N,Name),
+ {[],?dupl_equaldefs bor RenamedOrDupl};
+ _ ->
+ {T,RenamedOrDupl}
+ end
+ end,
+ {NewTorV,NewAnyRenamed} = lists:mapfoldl(Fun,AnyRenamed,TorV),
+ %% have to flatten the NewTorV to remove any empty list elements
+ discover_dupl_in_mods(Name,Def,Ms,
+ [M#module{typeorval=lists:flatten(NewTorV)}|Acc],
+ NewAnyRenamed);
+discover_dupl_in_mods(_,_,[],Acc,AnyRenamed) ->
+ {Acc,AnyRenamed}.
+
+warn_renamed_def(ModName,NewName,OldName) ->
+ maybe_first_warn_print(),
+ io:format("NOTICE: The ASN.1 definition in module ~p with name ~p has been renamed in generated module. New name is ~p.~n",[ModName,OldName,NewName]).
+
+warn_deleted_def(ModName,DefName) ->
+ maybe_first_warn_print(),
+ io:format("NOTICE: The ASN.1 definition in module ~p with name ~p has been deleted in generated module.~n",[ModName,DefName]).
+
+warn_kept_def(ModName,DefName) ->
+ maybe_first_warn_print(),
+ io:format("NOTICE: The ASN.1 definition in module ~p with name ~p has kept its name due to equal definition as duplicate.~n",[ModName,DefName]).
+
+maybe_first_warn_print() ->
+ case get(warn_duplicate_defs) of
+ undefined ->
+ put(warn_duplicate_defs,true),
+ io:format("~nDue to multiple occurrences of a definition name in "
+ "multi-file compiled files:~n");
+ _ ->
+ ok
+ end.
+finished_warn_prints() ->
+ put(warn_duplicate_defs,undefined).
+
+
+exit_if_nameduplicate(#module{typeorval=TorV}) ->
+ exit_if_nameduplicate(TorV);
+exit_if_nameduplicate([]) ->
+ ok;
+exit_if_nameduplicate([Def|Rest]) ->
+ Name=get_name_of_def(Def),
+ exit_if_nameduplicate2(Name,Rest),
+ exit_if_nameduplicate(Rest).
+
+exit_if_nameduplicate2(Name,Rest) ->
+ Pred=fun(Def)->
+ case get_name_of_def(Def) of
+ Name -> true;
+ _ -> false
+ end
+ end,
+ case lists:any(Pred,Rest) of
+ true ->
+ throw({error,{"more than one definition with same name",Name}});
+ _ ->
+ ok
+ end.
+
+compare_defs(D1,D2) ->
+ compare_defs2(unset_pos(D1),unset_pos(D2)).
+compare_defs2(D,D) ->
+ equal;
+compare_defs2(_,_) ->
+ not_equal.
+
+unset_pos(Def) when record(Def,typedef) ->
+ Def#typedef{pos=undefined};
+unset_pos(Def) when record(Def,classdef) ->
+ Def#classdef{pos=undefined};
+unset_pos(Def) when record(Def,valuedef) ->
+ Def#valuedef{pos=undefined};
+unset_pos(Def) when record(Def,ptypedef) ->
+ Def#ptypedef{pos=undefined};
+unset_pos(Def) when record(Def,pvaluedef) ->
+ Def#pvaluedef{pos=undefined};
+unset_pos(Def) when record(Def,pvaluesetdef) ->
+ Def#pvaluesetdef{pos=undefined};
+unset_pos(Def) when record(Def,pobjectdef) ->
+ Def#pobjectdef{pos=undefined};
+unset_pos(Def) when record(Def,pobjectsetdef) ->
+ Def#pobjectsetdef{pos=undefined}.
+
+get_pos_of_def(#typedef{pos=Pos}) ->
+ Pos;
+get_pos_of_def(#classdef{pos=Pos}) ->
+ Pos;
+get_pos_of_def(#valuedef{pos=Pos}) ->
+ Pos;
+get_pos_of_def(#ptypedef{pos=Pos}) ->
+ Pos;
+get_pos_of_def(#pvaluedef{pos=Pos}) ->
+ Pos;
+get_pos_of_def(#pvaluesetdef{pos=Pos}) ->
+ Pos;
+get_pos_of_def(#pobjectdef{pos=Pos}) ->
+ Pos;
+get_pos_of_def(#pobjectsetdef{pos=Pos}) ->
+ Pos.
+
+
+get_name_of_def(#typedef{name=Name}) ->
+ Name;
+get_name_of_def(#classdef{name=Name}) ->
+ Name;
+get_name_of_def(#valuedef{name=Name}) ->
+ Name;
+get_name_of_def(#ptypedef{name=Name}) ->
+ Name;
+get_name_of_def(#pvaluedef{name=Name}) ->
+ Name;
+get_name_of_def(#pvaluesetdef{name=Name}) ->
+ Name;
+get_name_of_def(#pobjectdef{name=Name}) ->
+ Name;
+get_name_of_def(#pobjectsetdef{name=Name}) ->
+ Name.
+
+set_name_of_def(ModName,Name,OldDef) ->
+ NewName = list_to_atom(lists:concat([Name,ModName])),
+ case OldDef of
+ #typedef{} -> OldDef#typedef{name=NewName};
+ #classdef{} -> OldDef#classdef{name=NewName};
+ #valuedef{} -> OldDef#valuedef{name=NewName};
+ #ptypedef{} -> OldDef#ptypedef{name=NewName};
+ #pvaluedef{} -> OldDef#pvaluedef{name=NewName};
+ #pvaluesetdef{} -> OldDef#pvaluesetdef{name=NewName};
+ #pobjectdef{} -> OldDef#pobjectdef{name=NewName};
+ #pobjectsetdef{} -> OldDef#pobjectsetdef{name=NewName}
+ end.
+
+save_imports(ModuleList)->
+ Fun = fun(M) ->
+ case M#module.imports of
+ {_,[]} -> [];
+ {_,I} ->
+ {M#module.name,I}
+ end
+ end,
+ ImportsList = lists:map(Fun,ModuleList),
+ case lists:flatten(ImportsList) of
+ [] ->
+ ok;
+ ImportsList2 ->
+ create_ets_table(original_imports,[named_table]),
+ ets:insert(original_imports,ImportsList2)
+ end.
+
+
+common_exports(ModuleList) ->
+ %% if all modules exports 'all' then export 'all',
+ %% otherwise export each typeorval name
+ case lists:filter(fun(X)->
+ element(2,X#module.exports) /= all
+ end,
+ ModuleList) of
+ []->
+ {exports,all};
+ ModsWithExpList ->
+ CExports1 =
+ lists:append(lists:map(fun(X)->element(2,X#module.exports) end,
+ ModsWithExpList)),
+ CExports2 = export_all(lists:subtract(ModuleList,ModsWithExpList)),
+ {exports,CExports1++CExports2}
+ end.
+
+export_all([])->[];
+export_all(ModuleList) ->
+ ExpList =
+ lists:map(
+ fun(M)->
+ TorVL=M#module.typeorval,
+ MName = M#module.name,
+ lists:map(
+ fun(Def)->
+ case Def of
+ T when record(T,typedef)->
+ #'Externaltypereference'{pos=0,
+ module=MName,
+ type=T#typedef.name};
+ V when record(V,valuedef) ->
+ #'Externalvaluereference'{pos=0,
+ module=MName,
+ value=V#valuedef.name};
+ C when record(C,classdef) ->
+ #'Externaltypereference'{pos=0,
+ module=MName,
+ type=C#classdef.name};
+ P when record(P,ptypedef) ->
+ #'Externaltypereference'{pos=0,
+ module=MName,
+ type=P#ptypedef.name};
+ PV when record(PV,pvaluesetdef) ->
+ #'Externaltypereference'{pos=0,
+ module=MName,
+ type=PV#pvaluesetdef.name};
+ PO when record(PO,pobjectdef) ->
+ #'Externalvaluereference'{pos=0,
+ module=MName,
+ value=PO#pobjectdef.name}
+ end
+ end,
+ TorVL)
+ end,
+ ModuleList),
+ lists:append(ExpList).
+
+%% common_imports/2
+%% IList is a list of tuples, {Imports,MName}, where Imports is the imports of
+%% the module with name MName.
+%% InputMNameL holds the names of all merged modules.
+%% Returns an import tuple with a list of imports that are external the merged
+%% set of modules.
+common_imports(IList,InputMNameL) ->
+ SetExternalImportsList = remove_in_set_imports(IList,InputMNameL,[]),
+ {imports,remove_import_doubles(SetExternalImportsList)}.
+
+check_tagdefault(ModList) ->
+ case have_same_tagdefault(ModList) of
+ {true,TagDefault} -> TagDefault;
+ {false,TagDefault} ->
+ create_ets_table(automatic_tags,[named_table]),
+ save_automatic_tagged_types(ModList),
+ TagDefault
+ end.
+
+have_same_tagdefault([#module{tagdefault=T}|Ms]) ->
+ have_same_tagdefault(Ms,{true,T}).
+
+have_same_tagdefault([],TagDefault) ->
+ TagDefault;
+have_same_tagdefault([#module{tagdefault=T}|Ms],TDefault={_,T}) ->
+ have_same_tagdefault(Ms,TDefault);
+have_same_tagdefault([#module{tagdefault=T1}|Ms],{_,T2}) ->
+ have_same_tagdefault(Ms,{false,rank_tagdef([T1,T2])}).
+
+rank_tagdef(L) ->
+ case lists:member('EXPLICIT',L) of
+ true -> 'EXPLICIT';
+ _ -> 'IMPLICIT'
+ end.
+
+save_automatic_tagged_types([])->
+ done;
+save_automatic_tagged_types([#module{tagdefault='AUTOMATIC',
+ typeorval=TorV}|Ms]) ->
+ Fun =
+ fun(T) ->
+ ets:insert(automatic_tags,{get_name_of_def(T)})
+ end,
+ lists:foreach(Fun,TorV),
+ save_automatic_tagged_types(Ms);
+save_automatic_tagged_types([_M|Ms]) ->
+ save_automatic_tagged_types(Ms).
+
+%% remove_in_set_imports/3 :
+%% input: list with tuples of each module's imports and module name
+%% respectively.
+%% output: one list with same format but each occured import from a
+%% module in the input set (IMNameL) is removed.
+remove_in_set_imports([{{imports,ImpL},_ModName}|Rest],InputMNameL,Acc) ->
+ NewImpL = remove_in_set_imports1(ImpL,InputMNameL,[]),
+ remove_in_set_imports(Rest,InputMNameL,NewImpL++Acc);
+remove_in_set_imports([],_,Acc) ->
+ lists:reverse(Acc).
+
+remove_in_set_imports1([I|Is],InputMNameL,Acc) ->
+ case I#'SymbolsFromModule'.module of
+ #'Externaltypereference'{type=MName} ->
+ case lists:member(MName,InputMNameL) of
+ true ->
+ remove_in_set_imports1(Is,InputMNameL,Acc);
+ false ->
+ remove_in_set_imports1(Is,InputMNameL,[I|Acc])
+ end;
+ _ ->
+ remove_in_set_imports1(Is,InputMNameL,[I|Acc])
+ end;
+remove_in_set_imports1([],_,Acc) ->
+ lists:reverse(Acc).
+
+remove_import_doubles([]) ->
+ [];
+%% If several modules in the merge set imports symbols from
+%% the same external module it might be doubled.
+%% ImportList has #'SymbolsFromModule' elements
+remove_import_doubles(ImportList) ->
+ MergedImportList =
+ merge_symbols_from_module(ImportList,[]),
+%% io:format("MergedImportList: ~p~n",[MergedImportList]),
+ delete_double_of_symbol(MergedImportList,[]).
+
+merge_symbols_from_module([Imp|Imps],Acc) ->
+ #'Externaltypereference'{type=ModName} = Imp#'SymbolsFromModule'.module,
+ IfromModName =
+ lists:filter(
+ fun(I)->
+ case I#'SymbolsFromModule'.module of
+ #'Externaltypereference'{type=ModName} ->
+ true;
+ #'Externalvaluereference'{value=ModName} ->
+ true;
+ _ -> false
+ end
+ end,
+ Imps),
+ NewImps = lists:subtract(Imps,IfromModName),
+%% io:format("Imp: ~p~nIfromModName: ~p~n",[Imp,IfromModName]),
+ NewImp =
+ Imp#'SymbolsFromModule'{
+ symbols = lists:append(
+ lists:map(fun(SL)->
+ SL#'SymbolsFromModule'.symbols
+ end,[Imp|IfromModName]))},
+ merge_symbols_from_module(NewImps,[NewImp|Acc]);
+merge_symbols_from_module([],Acc) ->
+ lists:reverse(Acc).
+
+delete_double_of_symbol([I|Is],Acc) ->
+ SymL=I#'SymbolsFromModule'.symbols,
+ NewSymL = delete_double_of_symbol1(SymL,[]),
+ delete_double_of_symbol(Is,[I#'SymbolsFromModule'{symbols=NewSymL}|Acc]);
+delete_double_of_symbol([],Acc) ->
+ Acc.
+
+delete_double_of_symbol1([TRef=#'Externaltypereference'{type=TrefName}|Rest],Acc)->
+ NewRest =
+ lists:filter(fun(S)->
+ case S of
+ #'Externaltypereference'{type=TrefName}->
+ false;
+ _ -> true
+ end
+ end,
+ Rest),
+ delete_double_of_symbol1(NewRest,[TRef|Acc]);
+delete_double_of_symbol1([VRef=#'Externalvaluereference'{value=VName}|Rest],Acc) ->
+ NewRest =
+ lists:filter(fun(S)->
+ case S of
+ #'Externalvaluereference'{value=VName}->
+ false;
+ _ -> true
+ end
+ end,
+ Rest),
+ delete_double_of_symbol1(NewRest,[VRef|Acc]);
+delete_double_of_symbol1([TRef={#'Externaltypereference'{type=MRef},
+ #'Externaltypereference'{type=TRef}}|Rest],
+ Acc)->
+ NewRest =
+ lists:filter(
+ fun(S)->
+ case S of
+ {#'Externaltypereference'{type=MRef},
+ #'Externaltypereference'{type=TRef}}->
+ false;
+ _ -> true
+ end
+ end,
+ Rest),
+ delete_double_of_symbol1(NewRest,[TRef|Acc]);
+delete_double_of_symbol1([],Acc) ->
+ Acc.
+
+
+scan_set(DirName,Files,Options) ->
+ lists:map(
+ fun(F)->
+ case scan({true,true},filename:join([DirName,F]),Options) of
+ {false,{error,Reason}} ->
+ throw({error,{'scan error in file:',F,Reason}});
+ {TrueOrFalse,Res} ->
+ {TrueOrFalse,Res,F}
+ end
+ end,
+ Files).
+
+parse_set(ScanRes,Options) ->
+ lists:map(
+ fun({TorF,Toks,F})->
+ case parse({TorF,Toks},F,Options) of
+ {false,{error,Reason}} ->
+ throw({error,{'parse error in file:',F,Reason}});
+ {TrueOrFalse,Res} ->
+ {TrueOrFalse,Res,F}
+ end
+ end,
+ ScanRes).
+
+
+%%***********************************
+
+
+scan({true,_}, File,Options) ->
+ case asn1ct_tok:file(File) of
+ {error,Reason} ->
+ io:format("~p~n",[Reason]),
+ {false,{error,Reason}};
+ Tokens ->
+ case lists:member(ss,Options) of
+ true -> % we terminate after scan
+ {false,Tokens};
+ false -> % continue with next pass
+ {true,Tokens}
+ end
+ end;
+scan({false,Result},_,_) ->
+ Result.
+
+
+parse({true,Tokens},File,Options) ->
+ %Presult = asn1ct_parser2:parse(Tokens),
+ %%case lists:member(p1,Options) of
+ %% true ->
+ %% asn1ct_parser:parse(Tokens);
+ %% _ ->
+ %% asn1ct_parser2:parse(Tokens)
+ %% end,
+ case catch asn1ct_parser2:parse(Tokens) of
+ {error,{{Line,_Mod,Message},_TokTup}} ->
+ if
+ integer(Line) ->
+ BaseName = filename:basename(File),
+ io:format("syntax error at line ~p in module ~s:~n",
+ [Line,BaseName]);
+ true ->
+ io:format("syntax error in module ~p:~n",[File])
+ end,
+ print_error_message(Message),
+ {false,{error,Message}};
+ {error,{Line,_Mod,[Message,Token]}} ->
+ io:format("syntax error: ~p ~p at line ~p~n",
+ [Message,Token,Line]),
+ {false,{error,{Line,[Message,Token]}}};
+ {ok,M} ->
+ case lists:member(sp,Options) of
+ true -> % terminate after parse
+ {false,M};
+ false -> % continue with next pass
+ {true,M}
+ end;
+ OtherError ->
+ io:format("~p~n",[OtherError])
+ end;
+parse({false,Tokens},_,_) ->
+ {false,Tokens}.
+
+check({true,M},File,OutFile,Includes,EncodingRule,DbFile,Options,InputMods) ->
+ cmp(M#module.name,File),
+ start(["."|Includes]),
+ case asn1ct_check:storeindb(M) of
+ ok ->
+ Module = asn1_db:dbget(M#module.name,'MODULE'),
+ State = #state{mname=Module#module.name,
+ module=Module#module{typeorval=[]},
+ erule=EncodingRule,
+ inputmodules=InputMods,
+ options=Options},
+ Check = asn1ct_check:check(State,Module#module.typeorval),
+ case {Check,lists:member(abs,Options)} of
+ {{error,Reason},_} ->
+ {false,{error,Reason}};
+ {{ok,NewTypeOrVal,_},true} ->
+ NewM = Module#module{typeorval=NewTypeOrVal},
+ asn1_db:dbput(NewM#module.name,'MODULE',NewM),
+ pretty2(M#module.name,lists:concat([OutFile,".abs"])),
+ {false,ok};
+ {{ok,NewTypeOrVal,GenTypeOrVal},_} ->
+ NewM = Module#module{typeorval=NewTypeOrVal},
+ asn1_db:dbput(NewM#module.name,'MODULE',NewM),
+ asn1_db:dbsave(DbFile,M#module.name),
+ io:format("--~p--~n",[{generated,DbFile}]),
+ {true,{M,NewM,GenTypeOrVal}}
+ end
+ end;
+check({false,M},_,_,_,_,_,_,_) ->
+ {false,M}.
+
+generate({true,{M,_Module,GenTOrV}},OutFile,EncodingRule,Options) ->
+ debug_on(Options),
+ case lists:member(compact_bit_string,Options) of
+ true -> put(compact_bit_string,true);
+ _ -> ok
+ end,
+ put(encoding_options,Options),
+ create_ets_table(check_functions,[named_table]),
+
+ %% create decoding function names and taglists for partial decode
+ %% For the time being leave errors unnoticed !!!!!!!!!
+% io:format("Options: ~p~n",[Options]),
+ case catch specialized_decode_prepare(EncodingRule,M,GenTOrV,Options) of
+ {error, enoent} -> ok;
+ {error, Reason} -> io:format("WARNING: Error in configuration"
+ "file: ~n~p~n",[Reason]);
+ {'EXIT',Reason} -> io:format("WARNING: Internal error when "
+ "analyzing configuration"
+ "file: ~n~p~n",[Reason]);
+ _ -> ok
+ end,
+
+ asn1ct_gen:pgen(OutFile,EncodingRule,M#module.name,GenTOrV),
+ debug_off(Options),
+ put(compact_bit_string,false),
+ erase(encoding_options),
+ erase(tlv_format), % used in ber_bin, optimize
+ erase(class_default_type),% used in ber_bin, optimize
+ ets:delete(check_functions),
+ case lists:member(sg,Options) of
+ true -> % terminate here , with .erl file generated
+ {false,true};
+ false ->
+ {true,true}
+ end;
+generate({false,M},_,_,_) ->
+ {false,M}.
+
+compile_erl({true,_},OutFile,Options) ->
+ erl_compile(OutFile,Options);
+compile_erl({false,true},_,_) ->
+ ok;
+compile_erl({false,Result},_,_) ->
+ Result.
+
+input_file_type([]) ->
+ {empty_name,[]};
+input_file_type(File) ->
+ case filename:extension(File) of
+ [] ->
+ case file:read_file_info(lists:concat([File,".asn1"])) of
+ {ok,_FileInfo} ->
+ {single_file, lists:concat([File,".asn1"])};
+ _Error ->
+ case file:read_file_info(lists:concat([File,".asn"])) of
+ {ok,_FileInfo} ->
+ {single_file, lists:concat([File,".asn"])};
+ _Error ->
+ {single_file, lists:concat([File,".py"])}
+ end
+ end;
+ ".asn1config" ->
+ case read_config_file(File,asn1_module) of
+ {ok,Asn1Module} ->
+ put(asn1_config_file,File),
+ input_file_type(Asn1Module);
+ Error ->
+ Error
+ end;
+ Asn1PFix ->
+ Base = filename:basename(File,Asn1PFix),
+ case filename:extension(Base) of
+ [] ->
+ {single_file,File};
+ SetPFix when (SetPFix == ".set") ->
+ {multiple_files_file,
+ filename:basename(Base,SetPFix),
+ File};
+ _Error ->
+ throw({input_file_error,{'Bad input file',File}})
+ end
+ end.
+
+get_file_list(File) ->
+ case file:open(File, [read]) of
+ {error,Reason} ->
+ {error,{File,file:format_error(Reason)}};
+ {ok,Stream} ->
+ get_file_list1(Stream,[])
+ end.
+
+get_file_list1(Stream,Acc) ->
+ Ret = io:get_line(Stream,''),
+ case Ret of
+ eof ->
+ file:close(Stream),
+ lists:reverse(Acc);
+ FileName ->
+ PrefixedNameList =
+ case (catch input_file_type(lists:delete($\n,FileName))) of
+ {empty_name,[]} -> [];
+ {single_file,Name} -> [Name];
+ {multiple_files_file,Name} ->
+ get_file_list(Name);
+ Err = {input_file_error,_Reason} ->
+ throw(Err)
+ end,
+ get_file_list1(Stream,PrefixedNameList++Acc)
+ end.
+
+get_rule(Options) ->
+ case [Rule ||Rule <-[per,ber,ber_bin,ber_bin_v2,per_bin],
+ Opt <- Options,
+ Rule==Opt] of
+ [Rule] ->
+ Rule;
+ [Rule|_] ->
+ Rule;
+ [] ->
+ ber
+ end.
+
+erl_compile(OutFile,Options) ->
+% io:format("Options:~n~p~n",[Options]),
+ case lists:member(noobj,Options) of
+ true ->
+ ok;
+ _ ->
+ ErlOptions = remove_asn_flags(Options),
+ case c:c(OutFile,ErlOptions) of
+ {ok,_Module} ->
+ ok;
+ _ ->
+ {error,'no_compilation'}
+ end
+ end.
+
+remove_asn_flags(Options) ->
+ [X || X <- Options,
+ X /= get_rule(Options),
+ X /= optimize,
+ X /= compact_bit_string,
+ X /= debug,
+ X /= keyed_list].
+
+debug_on(Options) ->
+ case lists:member(debug,Options) of
+ true ->
+ put(asndebug,true);
+ _ ->
+ true
+ end,
+ case lists:member(keyed_list,Options) of
+ true ->
+ put(asn_keyed_list,true);
+ _ ->
+ true
+ end.
+
+
+debug_off(_Options) ->
+ erase(asndebug),
+ erase(asn_keyed_list).
+
+
+outfile(Base, Ext, Opts) when atom(Ext) ->
+ outfile(Base, atom_to_list(Ext), Opts);
+outfile(Base, Ext, Opts) ->
+ Obase = case lists:keysearch(outdir, 1, Opts) of
+ {value, {outdir, Odir}} -> filename:join(Odir, Base);
+ _NotFound -> Base % Not found or bad format
+ end,
+ case Ext of
+ [] ->
+ Obase;
+ _ ->
+ Obase++"."++Ext
+ end.
+
+%% compile(AbsFileName, Options)
+%% Compile entry point for erl_compile.
+
+compile_asn(File,OutFile,Options) ->
+ compile(lists:concat([File,".asn"]),OutFile,Options).
+
+compile_asn1(File,OutFile,Options) ->
+ compile(lists:concat([File,".asn1"]),OutFile,Options).
+
+compile_py(File,OutFile,Options) ->
+ compile(lists:concat([File,".py"]),OutFile,Options).
+
+compile(File, _OutFile, Options) ->
+ case catch compile(File, make_erl_options(Options)) of
+ Exit = {'EXIT',_Reason} ->
+ io:format("~p~n~s~n",[Exit,"error"]),
+ error;
+ {error,_Reason} ->
+ %% case occurs due to error in asn1ct_parser2,asn1ct_check
+%% io:format("~p~n",[_Reason]),
+%% io:format("~p~n~s~n",[_Reason,"error"]),
+ error;
+ ok ->
+ io:format("ok~n"),
+ ok;
+ ParseRes when tuple(ParseRes) ->
+ io:format("~p~n",[ParseRes]),
+ ok;
+ ScanRes when list(ScanRes) ->
+ io:format("~p~n",[ScanRes]),
+ ok;
+ Unknown ->
+ io:format("~p~n~s~n",[Unknown,"error"]),
+ error
+ end.
+
+%% Converts generic compiler options to specific options.
+
+make_erl_options(Opts) ->
+
+ %% This way of extracting will work even if the record passed
+ %% has more fields than known during compilation.
+
+ Includes = Opts#options.includes,
+ Defines = Opts#options.defines,
+ Outdir = Opts#options.outdir,
+%% Warning = Opts#options.warning,
+ Verbose = Opts#options.verbose,
+ Specific = Opts#options.specific,
+ Optimize = Opts#options.optimize,
+ OutputType = Opts#options.output_type,
+ Cwd = Opts#options.cwd,
+
+ Options =
+ case Verbose of
+ true -> [verbose];
+ false -> []
+ end ++
+%%% case Warning of
+%%% 0 -> [];
+%%% _ -> [report_warnings]
+%%% end ++
+ [] ++
+ case Optimize of
+ 1 -> [optimize];
+ 999 -> [];
+ _ -> [{optimize,Optimize}]
+ end ++
+ lists:map(
+ fun ({Name, Value}) ->
+ {d, Name, Value};
+ (Name) ->
+ {d, Name}
+ end,
+ Defines) ++
+ case OutputType of
+ undefined -> [ber]; % temporary default (ber when it's ready)
+ ber -> [ber];
+ ber_bin -> [ber_bin];
+ ber_bin_v2 -> [ber_bin_v2];
+ per -> [per];
+ per_bin -> [per_bin]
+ end,
+
+ Options++[report_errors, {cwd, Cwd}, {outdir, Outdir}|
+ lists:map(fun(Dir) -> {i, Dir} end, Includes)]++Specific.
+
+pretty2(Module,AbsFile) ->
+ start(),
+ {ok,F} = file:open(AbsFile, [write]),
+ M = asn1_db:dbget(Module,'MODULE'),
+ io:format(F,"%%%%%%%%%%%%%%%%%%% ~p %%%%%%%%%%%%%%%%%%%~n",[Module]),
+ io:format(F,"~s\n",[asn1ct_pretty_format:term(M#module.defid)]),
+ io:format(F,"~s\n",[asn1ct_pretty_format:term(M#module.tagdefault)]),
+ io:format(F,"~s\n",[asn1ct_pretty_format:term(M#module.exports)]),
+ io:format(F,"~s\n",[asn1ct_pretty_format:term(M#module.imports)]),
+ io:format(F,"~s\n\n",[asn1ct_pretty_format:term(M#module.extensiondefault)]),
+
+ {Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets} = M#module.typeorval,
+ io:format(F,"%%%%%%%%%%%%%%%%%%% TYPES in ~p %%%%%%%%%%%%%%%%%%%~n",[Module]),
+ lists:foreach(fun(T)-> io:format(F,"~s\n",
+ [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
+ end,Types),
+ io:format(F,"%%%%%%%%%%%%%%%%%%% VALUES in ~p %%%%%%%%%%%%%%%%%%%~n",[Module]),
+ lists:foreach(fun(T)-> io:format(F,"~s\n",
+ [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
+ end,Values),
+ io:format(F,"%%%%%%%%%%%%%%%%%%% Parameterized Types in ~p %%%%%%%%%%%%%%%%%%%~n",[Module]),
+ lists:foreach(fun(T)-> io:format(F,"~s\n",
+ [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
+ end,ParameterizedTypes),
+ io:format(F,"%%%%%%%%%%%%%%%%%%% Classes in ~p %%%%%%%%%%%%%%%%%%%~n",[Module]),
+ lists:foreach(fun(T)-> io:format(F,"~s\n",
+ [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
+ end,Classes),
+ io:format(F,"%%%%%%%%%%%%%%%%%%% Objects in ~p %%%%%%%%%%%%%%%%%%%~n",[Module]),
+ lists:foreach(fun(T)-> io:format(F,"~s\n",
+ [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
+ end,Objects),
+ io:format(F,"%%%%%%%%%%%%%%%%%%% Object Sets in ~p %%%%%%%%%%%%%%%%%%%~n",[Module]),
+ lists:foreach(fun(T)-> io:format(F,"~s\n",
+ [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
+ end,ObjectSets).
+start() ->
+ Includes = ["."],
+ start(Includes).
+
+
+start(Includes) when list(Includes) ->
+ asn1_db:dbstart(Includes).
+
+stop() ->
+ save(),
+ asn1_db:stop_server(ns),
+ asn1_db:stop_server(rand),
+ stopped.
+
+save() ->
+ asn1_db:dbstop().
+
+%%clear() ->
+%% asn1_db:dbclear().
+
+encode(Module,Term) ->
+ asn1rt:encode(Module,Term).
+
+encode(Module,Type,Term) when list(Module) ->
+ asn1rt:encode(list_to_atom(Module),Type,Term);
+encode(Module,Type,Term) ->
+ asn1rt:encode(Module,Type,Term).
+
+decode(Module,Type,Bytes) when list(Module) ->
+ asn1rt:decode(list_to_atom(Module),Type,Bytes);
+decode(Module,Type,Bytes) ->
+ asn1rt:decode(Module,Type,Bytes).
+
+
+test(Module) ->
+ start(),
+ M = asn1_db:dbget(Module,'MODULE'),
+ {Types,_Values,_Ptypes,_Classes,_Objects,_ObjectSets} = M#module.typeorval,
+ test_each(Module,Types).
+
+test_each(Module,[Type | Rest]) ->
+ case test(Module,Type) of
+ {ok,_Result} ->
+ test_each(Module,Rest);
+ Error ->
+ Error
+ end;
+test_each(_,[]) ->
+ ok.
+
+test(Module,Type) ->
+ io:format("~p:~p~n",[Module,Type]),
+ case (catch value(Module,Type)) of
+ {ok,Val} ->
+ %% io:format("asn1ct:test/2: ~w~n",[Val]),
+ test(Module,Type,Val);
+ {'EXIT',Reason} ->
+ {error,{asn1,{value,Reason}}}
+ end.
+
+
+test(Module,Type,Value) ->
+ case catch encode(Module,Type,Value) of
+ {ok,Bytes} ->
+ %% io:format("test 1: ~p~n",[{Bytes}]),
+ M = if
+ list(Module) ->
+ list_to_atom(Module);
+ true ->
+ Module
+ end,
+ NewBytes =
+ case M:encoding_rule() of
+ ber ->
+ lists:flatten(Bytes);
+ ber_bin when binary(Bytes) ->
+ Bytes;
+ ber_bin ->
+ list_to_binary(Bytes);
+ ber_bin_v2 when binary(Bytes) ->
+ Bytes;
+ ber_bin_v2 ->
+ list_to_binary(Bytes);
+ per ->
+ lists:flatten(Bytes);
+ per_bin when binary(Bytes) ->
+ Bytes;
+ per_bin ->
+ list_to_binary(Bytes)
+ end,
+ case decode(Module,Type,NewBytes) of
+ {ok,Value} ->
+ {ok,{Module,Type,Value}};
+ {ok,Res} ->
+ {error,{asn1,{encode_decode_mismatch,
+ {{Module,Type,Value},Res}}}};
+ Error ->
+ {error,{asn1,{{decode,
+ {Module,Type,Value},Error}}}}
+ end;
+ Error ->
+ {error,{asn1,{encode,{{Module,Type,Value},Error}}}}
+ end.
+
+value(Module) ->
+ start(),
+ M = asn1_db:dbget(Module,'MODULE'),
+ {Types,_Values,_Ptypes,_Classes,_Objects,_ObjectSets} = M#module.typeorval,
+ lists:map(fun(A) ->value(Module,A) end,Types).
+
+value(Module,Type) ->
+ start(),
+ case catch asn1ct_value:get_type(Module,Type,no) of
+ {error,Reason} ->
+ {error,Reason};
+ {'EXIT',Reason} ->
+ {error,Reason};
+ Result ->
+ {ok,Result}
+ end.
+
+cmp(Module,InFile) ->
+ Base = filename:basename(InFile),
+ Dir = filename:dirname(InFile),
+ Ext = filename:extension(Base),
+ Finfo = file:read_file_info(InFile),
+ Minfo = file:read_file_info(filename:join(Dir,lists:concat([Module,Ext]))),
+ case Finfo of
+ Minfo ->
+ ok;
+ _ ->
+ io:format("asn1error: Modulename and filename must be equal~n",[]),
+ throw(error)
+ end.
+
+vsn() ->
+ ?vsn.
+
+print_error_message([got,H|T]) when list(H) ->
+ io:format(" got:"),
+ print_listing(H,"and"),
+ print_error_message(T);
+print_error_message([expected,H|T]) when list(H) ->
+ io:format(" expected one of:"),
+ print_listing(H,"or"),
+ print_error_message(T);
+print_error_message([H|T]) ->
+ io:format(" ~p",[H]),
+ print_error_message(T);
+print_error_message([]) ->
+ io:format("~n").
+
+print_listing([H1,H2|[]],AndOr) ->
+ io:format(" ~p ~s ~p",[H1,AndOr,H2]);
+print_listing([H1,H2|T],AndOr) ->
+ io:format(" ~p,",[H1]),
+ print_listing([H2|T],AndOr);
+print_listing([H],_AndOr) ->
+ io:format(" ~p",[H]);
+print_listing([],_) ->
+ ok.
+
+
+%% functions to administer ets tables
+
+%% Always creates a new table
+create_ets_table(Name,Options) when atom(Name) ->
+ case ets:info(Name) of
+ undefined ->
+ ets:new(Name,Options);
+ _ ->
+ ets:delete(Name),
+ ets:new(Name,Options)
+ end.
+
+%% Creates a new ets table only if no table exists
+create_if_no_table(Name,Options) ->
+ case ets:info(Name) of
+ undefined ->
+ %% create a new table
+ create_ets_table(Name,Options);
+ _ -> ok
+ end.
+
+
+delete_tables([Table|Ts]) ->
+ case ets:info(Table) of
+ undefined -> ok;
+ _ -> ets:delete(Table)
+ end,
+ delete_tables(Ts);
+delete_tables([]) ->
+ ok.
+
+
+specialized_decode_prepare(Erule,M,TsAndVs,Options) ->
+% Asn1confMember =
+% fun([{asn1config,File}|_],_) ->
+% {true,File};
+% ([],_) -> false;
+% ([_H|T],Fun) ->
+% Fun(T,Fun)
+% end,
+% case Asn1confMember(Options,Asn1confMember) of
+% {true,File} ->
+ case lists:member(asn1config,Options) of
+ true ->
+ partial_decode_prepare(Erule,M,TsAndVs,Options);
+ _ ->
+ ok
+ end.
+%% Reads the configuration file if it exists and stores information
+%% about partial decode and incomplete decode
+partial_decode_prepare(ber_bin_v2,M,TsAndVs,Options) when tuple(TsAndVs) ->
+ %% read configure file
+% Types = element(1,TsAndVs),
+ CfgList = read_config_file(M#module.name),
+ SelectedDecode = get_config_info(CfgList,partial_decode),
+ ExclusiveDecode = get_config_info(CfgList,exclusive_decode),
+ CommandList =
+ create_partial_decode_gen_info(M#module.name,SelectedDecode),
+% io:format("partial_decode = ~p~n",[CommandList]),
+
+ save_config(partial_decode,CommandList),
+ CommandList2 =
+ create_partial_inc_decode_gen_info(M#module.name,ExclusiveDecode),
+% io:format("partial_incomplete_decode = ~p~n",[CommandList2]),
+ Part_inc_tlv_tags = tag_format(ber_bin_v2,Options,CommandList2),
+% io:format("partial_incomplete_decode: tlv_tags = ~p~n",[Part_inc_tlv_tags]),
+ save_config(partial_incomplete_decode,Part_inc_tlv_tags),
+ save_gen_state(ExclusiveDecode,Part_inc_tlv_tags);
+partial_decode_prepare(_,_,_,_) ->
+ ok.
+
+
+
+%% create_partial_inc_decode_gen_info/2
+%%
+%% Creats a list of tags out of the information in TypeNameList that
+%% tells which value will be incomplete decoded, i.e. each end
+%% component/type in TypeNameList. The significant types/components in
+%% the path from the toptype must be specified in the
+%% TypeNameList. Significant elements are all constructed types that
+%% branches the path to the leaf and the leaf it selfs.
+%%
+%% Returns a list of elements, where an element may be one of
+%% mandatory|[opt,Tag]|[bin,Tag]. mandatory correspond to a mandatory
+%% element that shall be decoded as usual. [opt,Tag] matches an
+%% OPTIONAL or DEFAULT element that shall be decoded as
+%% usual. [bin,Tag] corresponds to an element, mandatory, OPTIONAL or
+%% DEFAULT, that shall be left encoded (incomplete decoded).
+create_partial_inc_decode_gen_info(ModName,{Mod,[{Name,L}|Ls]}) when list(L) ->
+ TopTypeName = partial_inc_dec_toptype(L),
+ [{Name,TopTypeName,
+ create_partial_inc_decode_gen_info1(ModName,TopTypeName,{Mod,L})}|
+ create_partial_inc_decode_gen_info(ModName,{Mod,Ls})];
+create_partial_inc_decode_gen_info(_,{_,[]}) ->
+ [];
+create_partial_inc_decode_gen_info(_,[]) ->
+ [].
+
+create_partial_inc_decode_gen_info1(ModName,TopTypeName,{ModName,
+ [_TopType|Rest]}) ->
+ case asn1_db:dbget(ModName,TopTypeName) of
+ #typedef{typespec=TS} ->
+ TagCommand = get_tag_command(TS,?MANDATORY,mandatory),
+ create_pdec_inc_command(ModName,get_components(TS#type.def),
+ Rest,[TagCommand]);
+ _ ->
+ throw({error,{"wrong type list in asn1 config file",
+ TopTypeName}})
+ end;
+create_partial_inc_decode_gen_info1(M1,_,{M2,_}) when M1 /= M2 ->
+ throw({error,{"wrong module name in asn1 config file",
+ M2}});
+create_partial_inc_decode_gen_info1(_,_,TNL) ->
+ throw({error,{"wrong type list in asn1 config file",
+ TNL}}).
+
+%%
+%% Only when there is a 'ComponentType' the config data C1 may be a
+%% list, where the incomplete decode is branched. So, C1 may be a
+%% list, a "binary tuple", a "parts tuple" or an atom. The second
+%% element of a binary tuple and a parts tuple is an atom.
+create_pdec_inc_command(_ModName,_,[],Acc) ->
+ lists:reverse(Acc);
+create_pdec_inc_command(ModName,{Comps1,Comps2},TNL,Acc)
+ when list(Comps1),list(Comps2) ->
+ create_pdec_inc_command(ModName,Comps1 ++ Comps2,TNL,Acc);
+create_pdec_inc_command(ModN,Clist,[CL|_Rest],Acc) when list(CL) ->
+ create_pdec_inc_command(ModN,Clist,CL,Acc);
+create_pdec_inc_command(ModName,
+ CList=[#'ComponentType'{name=Name,typespec=TS,
+ prop=Prop}|Comps],
+ TNL=[C1|Cs],Acc) ->
+ case C1 of
+% Name ->
+% %% In this case C1 is an atom
+% TagCommand = get_tag_command(TS,?MANDATORY,Prop),
+% create_pdec_inc_command(ModName,get_components(TS#type.def),Cs,[TagCommand|Acc]);
+ {Name,undecoded} ->
+ TagCommand = get_tag_command(TS,?UNDECODED,Prop),
+ create_pdec_inc_command(ModName,Comps,Cs,[TagCommand|Acc]);
+ {Name,parts} ->
+ TagCommand = get_tag_command(TS,?PARTS,Prop),
+ create_pdec_inc_command(ModName,Comps,Cs,[TagCommand|Acc]);
+ L when list(L) ->
+ %% This case is only possible as the first element after
+ %% the top type element, when top type is SEGUENCE or SET.
+ %% Follow each element in L. Must note every tag on the
+ %% way until the last command is reached, but it ought to
+ %% be enough to have a "complete" or "complete optional"
+ %% command for each component that is not specified in the
+ %% config file. Then in the TLV decode the components with
+ %% a "complete" command will be decoded by an ordinary TLV
+ %% decode.
+ create_pdec_inc_command(ModName,CList,L,Acc);
+ {Name,RestPartsList} when list(RestPartsList) ->
+ %% Same as previous, but this may occur at any place in
+ %% the structure. The previous is only possible as the
+ %% second element.
+ case get_tag_command(TS,?MANDATORY,Prop) of
+ ?MANDATORY ->
+ InnerDirectives=
+ create_pdec_inc_command(ModName,TS#type.def,
+ RestPartsList,[]),
+ create_pdec_inc_command(ModName,Comps,Cs,
+ [[?MANDATORY,InnerDirectives]|Acc]);
+% create_pdec_inc_command(ModName,Comps,Cs,
+% [InnerDirectives,?MANDATORY|Acc]);
+ [Opt,EncTag] ->
+ InnerDirectives =
+ create_pdec_inc_command(ModName,TS#type.def,
+ RestPartsList,[]),
+ create_pdec_inc_command(ModName,Comps,Cs,
+ [[Opt,EncTag,InnerDirectives]|Acc])
+ end;
+% create_pdec_inc_command(ModName,CList,RestPartsList,Acc);
+%% create_pdec_inc_command(ModName,TS#type.def,RestPartsList,Acc);
+ _ -> %% this component may not be in the config list
+ TagCommand = get_tag_command(TS,?MANDATORY,Prop),
+ create_pdec_inc_command(ModName,Comps,TNL,[TagCommand|Acc])
+ end;
+create_pdec_inc_command(ModName,
+ {'CHOICE',[#'ComponentType'{name=C1,
+ typespec=TS,
+ prop=Prop}|Comps]},
+ [{C1,Directive}|Rest],Acc) ->
+ case Directive of
+ List when list(List) ->
+ [Command,Tag] = get_tag_command(TS,?ALTERNATIVE,Prop),
+ CompAcc = create_pdec_inc_command(ModName,TS#type.def,List,[]),
+ create_pdec_inc_command(ModName,{'CHOICE',Comps},Rest,
+ [[Command,Tag,CompAcc]|Acc]);
+ undecoded ->
+ TagCommand = get_tag_command(TS,?ALTERNATIVE_UNDECODED,Prop),
+ create_pdec_inc_command(ModName,{'CHOICE',Comps},Rest,
+ [TagCommand|Acc]);
+ parts ->
+ TagCommand = get_tag_command(TS,?ALTERNATIVE_PARTS,Prop),
+ create_pdec_inc_command(ModName,{'CHOICE',Comps},Rest,
+ [TagCommand|Acc])
+ end;
+create_pdec_inc_command(ModName,
+ {'CHOICE',[#'ComponentType'{typespec=TS,
+ prop=Prop}|Comps]},
+ TNL,Acc) ->
+ TagCommand = get_tag_command(TS,?ALTERNATIVE,Prop),
+ create_pdec_inc_command(ModName,{'CHOICE',Comps},TNL,[TagCommand|Acc]);
+create_pdec_inc_command(M,{'CHOICE',{Cs1,Cs2}},TNL,Acc)
+ when list(Cs1),list(Cs2) ->
+ create_pdec_inc_command(M,{'CHOICE',Cs1 ++ Cs2},TNL,Acc);
+create_pdec_inc_command(ModName,#'Externaltypereference'{module=M,type=Name},
+ TNL,Acc) ->
+ #type{def=Def} = get_referenced_type(M,Name),
+ create_pdec_inc_command(ModName,get_components(Def),TNL,Acc);
+create_pdec_inc_command(_,_,TNL,_) ->
+ throw({error,{"unexpected error when creating partial "
+ "decode command",TNL}}).
+
+partial_inc_dec_toptype([T|_]) when atom(T) ->
+ T;
+partial_inc_dec_toptype([{T,_}|_]) when atom(T) ->
+ T;
+partial_inc_dec_toptype([L|_]) when list(L) ->
+ partial_inc_dec_toptype(L);
+partial_inc_dec_toptype(_) ->
+ throw({error,{"no top type found for partial incomplete decode"}}).
+
+
+%% Creats a list of tags out of the information in TypeList and Types
+%% that tells which value will be decoded. Each constructed type that
+%% is in the TypeList will get a "choosen" command. Only the last
+%% type/component in the TypeList may be a primitive type. Components
+%% "on the way" to the final element may get the "skip" or the
+%% "skip_optional" command.
+%% CommandList = [Elements]
+%% Elements = {choosen,Tag}|{skip_optional,Tag}|skip
+%% Tag is a binary with the tag BER encoded.
+create_partial_decode_gen_info(ModName,{{_,ModName},TypeList}) ->
+ case TypeList of
+ [TopType|Rest] ->
+ case asn1_db:dbget(ModName,TopType) of
+ #typedef{typespec=TS} ->
+ TagCommand = get_tag_command(TS,?CHOOSEN),
+ create_pdec_command(ModName,get_components(TS#type.def),
+ Rest,[TagCommand]);
+ _ ->
+ throw({error,{"wrong type list in asn1 config file",
+ TypeList}})
+ end;
+ _ ->
+ []
+ end;
+create_partial_decode_gen_info(_,[]) ->
+ [];
+create_partial_decode_gen_info(_M1,{{_,M2},_}) ->
+ throw({error,{"wrong module name in asn1 config file",
+ M2}}).
+
+%% create_pdec_command/4 for each name (type or component) in the
+%% third argument, TypeNameList, a command is created. The command has
+%% information whether the component/type shall be skipped, looked
+%% into or returned. The list of commands is returned.
+create_pdec_command(_ModName,_,[],Acc) ->
+ lists:reverse(Acc);
+create_pdec_command(ModName,[#'ComponentType'{name=C1,typespec=TS}|_Comps],
+ [C1|Cs],Acc) ->
+ %% this component is a constructed type or the last in the
+ %% TypeNameList otherwise the config spec is wrong
+ TagCommand = get_tag_command(TS,?CHOOSEN),
+ create_pdec_command(ModName,get_components(TS#type.def),
+ Cs,[TagCommand|Acc]);
+create_pdec_command(ModName,[#'ComponentType'{typespec=TS,
+ prop=Prop}|Comps],
+ [C2|Cs],Acc) ->
+ TagCommand =
+ case Prop of
+ mandatory ->
+ get_tag_command(TS,?SKIP);
+ _ ->
+ get_tag_command(TS,?SKIP_OPTIONAL)
+ end,
+ create_pdec_command(ModName,Comps,[C2|Cs],[TagCommand|Acc]);
+create_pdec_command(ModName,{'CHOICE',[Comp=#'ComponentType'{name=C1}|_]},TNL=[C1|_Cs],Acc) ->
+ create_pdec_command(ModName,[Comp],TNL,Acc);
+create_pdec_command(ModName,{'CHOICE',[#'ComponentType'{}|Comps]},TNL,Acc) ->
+ create_pdec_command(ModName,{'CHOICE',Comps},TNL,Acc);
+create_pdec_command(ModName,#'Externaltypereference'{module=M,type=C1},
+ TypeNameList,Acc) ->
+ case get_referenced_type(M,C1) of
+ #type{def=Def} ->
+ create_pdec_command(ModName,get_components(Def),TypeNameList,
+ Acc);
+ Err ->
+ throw({error,{"unexpected result when fetching "
+ "referenced element",Err}})
+ end;
+create_pdec_command(ModName,TS=#type{def=Def},[C1|Cs],Acc) ->
+ %% This case when we got the "components" of a SEQUENCE/SET OF
+ case C1 of
+ [1] ->
+ %% A list with an integer is the only valid option in a 'S
+ %% OF', the other valid option would be an empty
+ %% TypeNameList saying that the entire 'S OF' will be
+ %% decoded.
+ TagCommand = get_tag_command(TS,?CHOOSEN),
+ create_pdec_command(ModName,Def,Cs,[TagCommand|Acc]);
+ [N] when integer(N) ->
+ TagCommand = get_tag_command(TS,?SKIP),
+ create_pdec_command(ModName,Def,[[N-1]|Cs],[TagCommand|Acc]);
+ Err ->
+ throw({error,{"unexpected error when creating partial "
+ "decode command",Err}})
+ end;
+create_pdec_command(_,_,TNL,_) ->
+ throw({error,{"unexpected error when creating partial "
+ "decode command",TNL}}).
+
+% get_components({'CHOICE',Components}) ->
+% Components;
+get_components(#'SEQUENCE'{components=Components}) ->
+ Components;
+get_components(#'SET'{components=Components}) ->
+ Components;
+get_components({'SEQUENCE OF',Components}) ->
+ Components;
+get_components({'SET OF',Components}) ->
+ Components;
+get_components(Def) ->
+ Def.
+
+%% get_tag_command(Type,Command)
+
+%% Type is the type that has information about the tag Command tells
+%% what to do with the encoded value with the tag of Type when
+%% decoding.
+get_tag_command(#type{tag=[]},_) ->
+ [];
+get_tag_command(#type{tag=[_Tag]},?SKIP) ->
+ ?SKIP;
+get_tag_command(#type{tag=[Tag]},Command) ->
+ %% encode the tag according to BER
+ [Command,encode_tag_val(decode_class(Tag#tag.class),Tag#tag.form,
+ Tag#tag.number)];
+get_tag_command(T=#type{tag=[Tag|Tags]},Command) ->
+ [get_tag_command(T#type{tag=Tag},Command)|
+ get_tag_command(T#type{tag=Tags},Command)].
+
+%% get_tag_command/3 used by create_pdec_inc_command
+get_tag_command(#type{tag=[]},_,_) ->
+ [];
+get_tag_command(#type{tag=[Tag]},?MANDATORY,Prop) ->
+ case Prop of
+ mandatory ->
+ ?MANDATORY;
+ {'DEFAULT',_} ->
+ [?DEFAULT,encode_tag_val(decode_class(Tag#tag.class),
+ Tag#tag.form,Tag#tag.number)];
+ _ -> [?OPTIONAL,encode_tag_val(decode_class(Tag#tag.class),
+ Tag#tag.form,Tag#tag.number)]
+ end;
+get_tag_command(#type{tag=[Tag]},Command,_) ->
+ [Command,encode_tag_val(decode_class(Tag#tag.class),Tag#tag.form,
+ Tag#tag.number)].
+
+
+get_referenced_type(M,Name) ->
+ case asn1_db:dbget(M,Name) of
+ #typedef{typespec=TS} ->
+ case TS of
+ #type{def=#'Externaltypereference'{module=M2,type=Name2}} ->
+ %% The tags have already been taken care of in the
+ %% first reference where they were gathered in a
+ %% list of tags.
+ get_referenced_type(M2,Name2);
+ #type{} -> TS;
+ _ ->
+ throw({error,{"unexpected element when"
+ " fetching referenced type",TS}})
+ end;
+ T ->
+ throw({error,{"unexpected element when fetching "
+ "referenced type",T}})
+ end.
+
+tag_format(EncRule,_Options,CommandList) ->
+ case EncRule of
+ ber_bin_v2 ->
+ tlv_tags(CommandList);
+ _ ->
+ CommandList
+ end.
+
+tlv_tags([]) ->
+ [];
+tlv_tags([mandatory|Rest]) ->
+ [mandatory|tlv_tags(Rest)];
+tlv_tags([[Command,Tag]|Rest]) when atom(Command),binary(Tag) ->
+ [[Command,tlv_tag(Tag)]|tlv_tags(Rest)];
+tlv_tags([[Command,Directives]|Rest]) when atom(Command),list(Directives) ->
+ [[Command,tlv_tags(Directives)]|tlv_tags(Rest)];
+%% remove all empty lists
+tlv_tags([[]|Rest]) ->
+ tlv_tags(Rest);
+tlv_tags([{Name,TopType,L1}|Rest]) when list(L1),atom(TopType) ->
+ [{Name,TopType,tlv_tags(L1)}|tlv_tags(Rest)];
+tlv_tags([[Command,Tag,L1]|Rest]) when list(L1),binary(Tag) ->
+ [[Command,tlv_tag(Tag),tlv_tags(L1)]|tlv_tags(Rest)];
+tlv_tags([L=[L1|_]|Rest]) when list(L1) ->
+ [tlv_tags(L)|tlv_tags(Rest)].
+
+tlv_tag(<>) when TagNo < 31 ->
+ (Cl bsl 16) + TagNo;
+tlv_tag(<>) ->
+ (Cl bsl 16) + TagNo;
+tlv_tag(<>) ->
+ TagNo = tlv_tag1(Buffer,0),
+ (Cl bsl 16) + TagNo.
+tlv_tag1(<<0:1,PartialTag:7>>,Acc) ->
+ (Acc bsl 7) bor PartialTag;
+tlv_tag1(<<1:1,PartialTag:7,Buffer/binary>>,Acc) ->
+ tlv_tag1(Buffer,(Acc bsl 7) bor PartialTag).
+
+%% reads the content from the configuration file and returns the
+%% selected part choosen by InfoType. Assumes that the config file
+%% content is an Erlang term.
+read_config_file(ModuleName,InfoType) when atom(InfoType) ->
+ CfgList = read_config_file(ModuleName),
+ get_config_info(CfgList,InfoType).
+
+
+read_config_file(ModuleName) ->
+ case file:consult(lists:concat([ModuleName,'.asn1config'])) of
+% case file:consult(ModuleName) of
+ {ok,CfgList} ->
+ CfgList;
+ {error,enoent} ->
+ Options = get(encoding_options),
+ Includes = [I || {i,I} <- Options],
+ read_config_file1(ModuleName,Includes);
+ {error,Reason} ->
+ file:format_error(Reason),
+ throw({error,{"error reading asn1 config file",Reason}})
+ end.
+read_config_file1(ModuleName,[]) ->
+ case filename:extension(ModuleName) of
+ ".asn1config" ->
+ throw({error,enoent});
+ _ ->
+ read_config_file(lists:concat([ModuleName,".asn1config"]))
+ end;
+read_config_file1(ModuleName,[H|T]) ->
+% File = filename:join([H,lists:concat([ModuleName,'.asn1config'])]),
+ File = filename:join([H,ModuleName]),
+ case file:consult(File) of
+ {ok,CfgList} ->
+ CfgList;
+ {error,enoent} ->
+ read_config_file1(ModuleName,T);
+ {error,Reason} ->
+ file:format_error(Reason),
+ throw({error,{"error reading asn1 config file",Reason}})
+ end.
+
+get_config_info(CfgList,InfoType) ->
+ case InfoType of
+ all ->
+ CfgList;
+ _ ->
+ case lists:keysearch(InfoType,1,CfgList) of
+ {value,{InfoType,Value}} ->
+ Value;
+ false ->
+ []
+ end
+ end.
+
+%% save_config/2 saves the Info with the key Key
+%% Before saving anything check if a table exists
+save_config(Key,Info) ->
+ create_if_no_table(asn1_general,[named_table]),
+ ets:insert(asn1_general,{{asn1_config,Key},Info}).
+
+read_config_data(Key) ->
+ case ets:info(asn1_general) of
+ undefined -> undefined;
+ _ ->
+ case ets:lookup(asn1_general,{asn1_config,Key}) of
+ [{_,Data}] -> Data;
+ Err ->
+ io:format("strange data from config file ~w~n",[Err]),
+ Err
+ end
+ end.
+
+
+%%
+%% Functions to manipulate the gen_state record saved in the
+%% asn1_general ets table.
+%%
+
+%% saves input data in a new gen_state record
+save_gen_state({_,ConfList},PartIncTlvTagList) ->
+ %ConfList=[{FunctionName,PatternList}|Rest]
+ StateRec = #gen_state{inc_tag_pattern=PartIncTlvTagList,
+ inc_type_pattern=ConfList},
+ save_config(gen_state,StateRec);
+save_gen_state(_,_) ->
+%% ok.
+ save_config(gen_state,#gen_state{}).
+
+save_gen_state(GenState) when record(GenState,gen_state) ->
+ save_config(gen_state,GenState).
+
+
+%% get_gen_state_field returns undefined if no gen_state exists or if
+%% Field is undefined or the data at the field.
+get_gen_state_field(Field) ->
+ case read_config_data(gen_state) of
+ undefined ->
+ undefined;
+ GenState ->
+ get_gen_state_field(GenState,Field)
+ end.
+get_gen_state_field(#gen_state{active=Active},active) ->
+ Active;
+get_gen_state_field(_,active) ->
+ false;
+get_gen_state_field(GS,prefix) ->
+ GS#gen_state.prefix;
+get_gen_state_field(GS,inc_tag_pattern) ->
+ GS#gen_state.inc_tag_pattern;
+get_gen_state_field(GS,tag_pattern) ->
+ GS#gen_state.tag_pattern;
+get_gen_state_field(GS,inc_type_pattern) ->
+ GS#gen_state.inc_type_pattern;
+get_gen_state_field(GS,type_pattern) ->
+ GS#gen_state.type_pattern;
+get_gen_state_field(GS,func_name) ->
+ GS#gen_state.func_name;
+get_gen_state_field(GS,namelist) ->
+ GS#gen_state.namelist;
+get_gen_state_field(GS,tobe_refed_funcs) ->
+ GS#gen_state.tobe_refed_funcs;
+get_gen_state_field(GS,gen_refed_funcs) ->
+ GS#gen_state.gen_refed_funcs.
+
+
+get_gen_state() ->
+ read_config_data(gen_state).
+
+
+update_gen_state(Field,Data) ->
+ case get_gen_state() of
+ State when record(State,gen_state) ->
+ update_gen_state(Field,State,Data);
+ _ ->
+ exit({error,{asn1,{internal,
+ "tried to update nonexistent gen_state",Field,Data}}})
+ end.
+update_gen_state(active,State,Data) ->
+ save_gen_state(State#gen_state{active=Data});
+update_gen_state(prefix,State,Data) ->
+ save_gen_state(State#gen_state{prefix=Data});
+update_gen_state(inc_tag_pattern,State,Data) ->
+ save_gen_state(State#gen_state{inc_tag_pattern=Data});
+update_gen_state(tag_pattern,State,Data) ->
+ save_gen_state(State#gen_state{tag_pattern=Data});
+update_gen_state(inc_type_pattern,State,Data) ->
+ save_gen_state(State#gen_state{inc_type_pattern=Data});
+update_gen_state(type_pattern,State,Data) ->
+ save_gen_state(State#gen_state{type_pattern=Data});
+update_gen_state(func_name,State,Data) ->
+ save_gen_state(State#gen_state{func_name=Data});
+update_gen_state(namelist,State,Data) ->
+% SData =
+% case Data of
+% [D] when list(D) -> D;
+% _ -> Data
+% end,
+ save_gen_state(State#gen_state{namelist=Data});
+update_gen_state(tobe_refed_funcs,State,Data) ->
+ save_gen_state(State#gen_state{tobe_refed_funcs=Data});
+update_gen_state(gen_refed_funcs,State,Data) ->
+ save_gen_state(State#gen_state{gen_refed_funcs=Data}).
+
+update_namelist(Name) ->
+ case get_gen_state_field(namelist) of
+ [Name,Rest] -> update_gen_state(namelist,Rest);
+ [Name|Rest] -> update_gen_state(namelist,Rest);
+ [{Name,List}] when list(List) -> update_gen_state(namelist,List);
+ [{Name,Atom}|Rest] when atom(Atom) -> update_gen_state(namelist,Rest);
+ Other -> Other
+ end.
+
+pop_namelist() ->
+ DeepTail = %% removes next element in order
+ fun([[{_,A}]|T],_Fun) when atom(A) -> T;
+ ([{_N,L}|T],_Fun) when list(L) -> [L|T];
+ ([[]|T],Fun) -> Fun(T,Fun);
+ ([L1|L2],Fun) when list(L1) ->
+ case lists:flatten(L1) of
+ [] -> Fun([L2],Fun);
+ _ -> [Fun(L1,Fun)|L2]
+ end;
+ ([_H|T],_Fun) -> T
+ end,
+ {Pop,NewNL} =
+ case get_gen_state_field(namelist) of
+ [] -> {[],[]};
+ L ->
+ {next_namelist_el(L),
+ DeepTail(L,DeepTail)}
+ end,
+ update_gen_state(namelist,NewNL),
+ Pop.
+
+%% next_namelist_el fetches the next type/component name in turn in
+%% the namelist, without changing the namelist.
+next_namelist_el() ->
+ case get_gen_state_field(namelist) of
+ undefined -> undefined;
+ L when list(L) -> next_namelist_el(L)
+ end.
+
+next_namelist_el([]) ->
+ [];
+next_namelist_el([L]) when list(L) ->
+ next_namelist_el(L);
+next_namelist_el([H|_]) when atom(H) ->
+ H;
+next_namelist_el([L|T]) when list(L) ->
+ case next_namelist_el(L) of
+ [] ->
+ next_namelist_el([T]);
+ R ->
+ R
+ end;
+next_namelist_el([H={_,A}|_]) when atom(A) ->
+ H.
+
+%% removes a bracket from the namelist
+step_in_constructed() ->
+ case get_gen_state_field(namelist) of
+ [L] when list(L) ->
+ update_gen_state(namelist,L);
+ _ -> ok
+ end.
+
+is_function_generated(Name) ->
+ case get_gen_state_field(gen_refed_funcs) of
+ L when list(L) ->
+ lists:member(Name,L);
+ _ ->
+ false
+ end.
+
+get_tobe_refed_func(Name) ->
+ case get_gen_state_field(tobe_refed_funcs) of
+ L when list(L) ->
+ case lists:keysearch(Name,1,L) of
+ {_,Element} ->
+ Element;
+ _ ->
+ undefined
+ end;
+ _ ->
+ undefined
+ end.
+
+add_tobe_refed_func(Data) ->
+ L = get_gen_state_field(tobe_refed_funcs),
+ update_gen_state(tobe_refed_funcs,[Data|L]).
+
+%% moves Name from the to be list to the generated list.
+generated_refed_func(Name) ->
+ L = get_gen_state_field(tobe_refed_funcs),
+ NewL = lists:keydelete(Name,1,L),
+ update_gen_state(tobe_refed_funcs,NewL),
+ L2 = get_gen_state_field(gen_refed_funcs),
+ update_gen_state(gen_refed_funcs,[Name|L2]).
+
+add_generated_refed_func(Data) ->
+ L = get_gen_state_field(gen_refed_funcs),
+ update_gen_state(gen_refed_funcs,[Data|L]).
+
+
+next_refed_func() ->
+ case get_gen_state_field(tobe_refed_funcs) of
+ [] ->
+ [];
+ [H|T] ->
+ update_gen_state(tobe_refed_funcs,T),
+ H
+ end.
+
+reset_gen_state() ->
+ save_gen_state(#gen_state{}).
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_check.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_check.erl
new file mode 100644
index 0000000000..2f0ada122e
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_check.erl
@@ -0,0 +1,5566 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_check.erl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-module(asn1ct_check).
+
+%% Main Module for ASN.1 compile time functions
+
+%-compile(export_all).
+-export([check/2,storeindb/1]).
+-include("asn1_records.hrl").
+%%% The tag-number for universal types
+-define(N_BOOLEAN, 1).
+-define(N_INTEGER, 2).
+-define(N_BIT_STRING, 3).
+-define(N_OCTET_STRING, 4).
+-define(N_NULL, 5).
+-define(N_OBJECT_IDENTIFIER, 6).
+-define(N_OBJECT_DESCRIPTOR, 7).
+-define(N_EXTERNAL, 8). % constructed
+-define(N_INSTANCE_OF,8).
+-define(N_REAL, 9).
+-define(N_ENUMERATED, 10).
+-define(N_EMBEDDED_PDV, 11). % constructed
+-define(N_SEQUENCE, 16).
+-define(N_SET, 17).
+-define(N_NumericString, 18).
+-define(N_PrintableString, 19).
+-define(N_TeletexString, 20).
+-define(N_VideotexString, 21).
+-define(N_IA5String, 22).
+-define(N_UTCTime, 23).
+-define(N_GeneralizedTime, 24).
+-define(N_GraphicString, 25).
+-define(N_VisibleString, 26).
+-define(N_GeneralString, 27).
+-define(N_UniversalString, 28).
+-define(N_CHARACTER_STRING, 29). % constructed
+-define(N_BMPString, 30).
+
+-define(TAG_PRIMITIVE(Num),
+ case S#state.erule of
+ ber_bin_v2 ->
+ #tag{class='UNIVERSAL',number=Num,type='IMPLICIT',form=0};
+ _ -> []
+ end).
+-define(TAG_CONSTRUCTED(Num),
+ case S#state.erule of
+ ber_bin_v2 ->
+ #tag{class='UNIVERSAL',number=Num,type='IMPLICIT',form=32};
+ _ -> []
+ end).
+
+-record(newt,{type=unchanged,tag=unchanged,constraint=unchanged,inlined=no}). % used in check_type to update type and tag
+-record(newv,{type=unchanged,value=unchanged}). % used in check_value to update type and value
+
+check(S,{Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets}) ->
+ %%Predicates used to filter errors
+ TupleIs = fun({T,_},T) -> true;
+ (_,_) -> false
+ end,
+ IsClass = fun(X) -> TupleIs(X,asn1_class) end,
+ IsObjSet = fun(X) -> TupleIs(X,objectsetdef) end,
+ IsPObjSet = fun(X) -> TupleIs(X,pobjectsetdef) end,
+ IsObject = fun(X) -> TupleIs(X,objectdef) end,
+ IsValueSet = fun(X) -> TupleIs(X,valueset) end,
+ Element2 = fun(X) -> element(2,X) end,
+
+ _Perror = checkp(S,ParameterizedTypes,[]), % must do this before the templates are used
+ Terror = checkt(S,Types,[]),
+
+ %% get parameterized object sets sent to checkt/3
+ %% and update Terror
+
+ {PObjSetNames1,Terror2} = filter_errors(IsPObjSet,Terror),
+
+ Verror = checkv(S,Values ++ ObjectSets,[]), %value sets may be parsed as object sets
+
+ %% get information object classes wrongly sent to checkt/3
+ %% and update Terror2
+
+ {AddClasses,Terror3} = filter_errors(IsClass,Terror2),
+
+ NewClasses = Classes++AddClasses,
+
+ Cerror = checkc(S,NewClasses,[]),
+
+ %% get object sets incorrectly sent to checkv/3
+ %% and update Verror
+
+ {ObjSetNames,Verror2} = filter_errors(IsObjSet,Verror),
+
+ %% get parameterized object sets incorrectly sent to checkv/3
+ %% and update Verror2
+
+ {PObjSetNames,Verror3} = filter_errors(IsPObjSet,Verror2),
+
+ %% get objects incorrectly sent to checkv/3
+ %% and update Verror3
+
+ {ObjectNames,Verror4} = filter_errors(IsObject,Verror3),
+
+ NewObjects = Objects++ObjectNames,
+ NewObjectSets = ObjSetNames ++ PObjSetNames ++ PObjSetNames1,
+
+ %% get value sets
+ %% and update Verror4
+
+ {ValueSetNames,Verror5} = filter_errors(IsValueSet,Verror4),
+
+ asn1ct:create_ets_table(inlined_objects,[named_table]),
+ {Oerror,ExclO,ExclOS} = checko(S,NewObjects ++
+ NewObjectSets,
+ [],[],[]),
+ InlinedObjTuples = ets:tab2list(inlined_objects),
+ InlinedObjects = lists:map(Element2,InlinedObjTuples),
+ ets:delete(inlined_objects),
+
+ Exporterror = check_exports(S,S#state.module),
+ case {Terror3,Verror5,Cerror,Oerror,Exporterror} of
+ {[],[],[],[],[]} ->
+ ContextSwitchTs = context_switch_in_spec(),
+ InstanceOf = instance_of_in_spec(),
+ NewTypes = lists:subtract(Types,AddClasses) ++ ContextSwitchTs
+ ++ InstanceOf,
+ NewValues = lists:subtract(Values,PObjSetNames++ObjectNames++
+ ValueSetNames),
+ {ok,
+ {NewTypes,NewValues,ParameterizedTypes,
+ NewClasses,NewObjects,NewObjectSets},
+ {NewTypes,NewValues,ParameterizedTypes,NewClasses,
+ lists:subtract(NewObjects,ExclO)++InlinedObjects,
+ lists:subtract(NewObjectSets,ExclOS)}};
+ _ ->{error,{asn1,lists:flatten([Terror3,Verror5,Cerror,
+ Oerror,Exporterror])}}
+ end.
+
+context_switch_in_spec() ->
+ L = [{external,'EXTERNAL'},
+ {embedded_pdv,'EMBEDDED PDV'},
+ {character_string,'CHARACTER STRING'}],
+ F = fun({T,TName},Acc) ->
+ case get(T) of
+ generate -> erase(T),
+ [TName|Acc];
+ _ -> Acc
+ end
+ end,
+ lists:foldl(F,[],L).
+
+instance_of_in_spec() ->
+ case get(instance_of) of
+ generate ->
+ erase(instance_of),
+ ['INSTANCE OF'];
+ _ ->
+ []
+ end.
+
+filter_errors(Pred,ErrorList) ->
+ Element2 = fun(X) -> element(2,X) end,
+ RemovedTupleElements = lists:filter(Pred,ErrorList),
+ RemovedNames = lists:map(Element2,RemovedTupleElements),
+ %% remove value set name tuples from Verror
+ RestErrors = lists:subtract(ErrorList,RemovedTupleElements),
+ {RemovedNames,RestErrors}.
+
+
+check_exports(S,Module = #module{}) ->
+ case Module#module.exports of
+ {exports,[]} ->
+ [];
+ {exports,all} ->
+ [];
+ {exports,ExportList} when list(ExportList) ->
+ IsNotDefined =
+ fun(X) ->
+ case catch get_referenced_type(S,X) of
+ {error,{asn1,_}} ->
+ true;
+ _ -> false
+ end
+ end,
+ case lists:filter(IsNotDefined,ExportList) of
+ [] ->
+ [];
+ NoDefExp ->
+ GetName =
+ fun(T = #'Externaltypereference'{type=N})->
+ %%{exported,undefined,entity,N}
+ NewS=S#state{type=T,tname=N},
+ error({export,"exported undefined entity",NewS})
+ end,
+ lists:map(GetName,NoDefExp)
+ end
+ end.
+
+checkt(S,[Name|T],Acc) ->
+ %%io:format("check_typedef:~p~n",[Name]),
+ Result =
+ case asn1_db:dbget(S#state.mname,Name) of
+ undefined ->
+ error({type,{internal_error,'???'},S});
+ Type when record(Type,typedef) ->
+ NewS = S#state{type=Type,tname=Name},
+ case catch(check_type(NewS,Type,Type#typedef.typespec)) of
+ {error,Reason} ->
+ error({type,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({type,{internal_error,Reason},NewS});
+ {asn1_class,_ClassDef} ->
+ {asn1_class,Name};
+ pobjectsetdef ->
+ {pobjectsetdef,Name};
+ pvalueset ->
+ {pvalueset,Name};
+ Ts ->
+ case Type#typedef.checked of
+ true -> % already checked and updated
+ ok;
+ _ ->
+ NewTypeDef = Type#typedef{checked=true,typespec = Ts},
+ %io:format("checkt:dbput:~p, ~p~n",[S#state.mname,NewTypeDef#typedef.name]),
+ asn1_db:dbput(NewS#state.mname,Name,NewTypeDef), % update the type
+ ok
+ end
+ end
+ end,
+ case Result of
+ ok ->
+ checkt(S,T,Acc);
+ _ ->
+ checkt(S,T,[Result|Acc])
+ end;
+checkt(S,[],Acc) ->
+ case check_contextswitchingtypes(S,[]) of
+ [] ->
+ lists:reverse(Acc);
+ L ->
+ checkt(S,L,Acc)
+ end.
+
+check_contextswitchingtypes(S,Acc) ->
+ CSTList=[{external,'EXTERNAL'},
+ {embedded_pdv,'EMBEDDED PDV'},
+ {character_string,'CHARACTER STRING'}],
+ check_contextswitchingtypes(S,CSTList,Acc).
+
+check_contextswitchingtypes(S,[{T,TName}|Ts],Acc) ->
+ case get(T) of
+ unchecked ->
+ put(T,generate),
+ check_contextswitchingtypes(S,Ts,[TName|Acc]);
+ _ ->
+ check_contextswitchingtypes(S,Ts,Acc)
+ end;
+check_contextswitchingtypes(_,[],Acc) ->
+ Acc.
+
+checkv(S,[Name|T],Acc) ->
+ %%io:format("check_valuedef:~p~n",[Name]),
+ Result = case asn1_db:dbget(S#state.mname,Name) of
+ undefined -> error({value,{internal_error,'???'},S});
+ Value when record(Value,valuedef);
+ record(Value,typedef); %Value set may be parsed as object set.
+ record(Value,pvaluedef);
+ record(Value,pvaluesetdef) ->
+ NewS = S#state{value=Value},
+ case catch(check_value(NewS,Value)) of
+ {error,Reason} ->
+ error({value,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({value,{internal_error,Reason},NewS});
+ {pobjectsetdef} ->
+ {pobjectsetdef,Name};
+ {objectsetdef} ->
+ {objectsetdef,Name};
+ {objectdef} ->
+ %% this is an object, save as typedef
+ #valuedef{checked=C,pos=Pos,name=N,type=Type,
+ value=Def}=Value,
+% Currmod = S#state.mname,
+% #type{def=
+% #'Externaltypereference'{module=Mod,
+% type=CName}} = Type,
+ ClassName =
+ Type#type.def,
+% case Mod of
+% Currmod ->
+% {objectclassname,CName};
+% _ ->
+% {objectclassname,Mod,CName}
+% end,
+ NewSpec = #'Object'{classname=ClassName,
+ def=Def},
+ NewDef = #typedef{checked=C,pos=Pos,name=N,
+ typespec=NewSpec},
+ asn1_db:dbput(NewS#state.mname,Name,NewDef),
+ {objectdef,Name};
+ {valueset,VSet} ->
+ Pos = asn1ct:get_pos_of_def(Value),
+ CheckedVSDef = #typedef{checked=true,pos=Pos,
+ name=Name,typespec=VSet},
+ asn1_db:dbput(NewS#state.mname,Name,CheckedVSDef),
+ {valueset,Name};
+ V ->
+ %% update the valuedef
+ asn1_db:dbput(NewS#state.mname,Name,V),
+ ok
+ end
+ end,
+ case Result of
+ ok ->
+ checkv(S,T,Acc);
+ _ ->
+ checkv(S,T,[Result|Acc])
+ end;
+checkv(_S,[],Acc) ->
+ lists:reverse(Acc).
+
+
+checkp(S,[Name|T],Acc) ->
+ %io:format("check_ptypedef:~p~n",[Name]),
+ Result = case asn1_db:dbget(S#state.mname,Name) of
+ undefined ->
+ error({type,{internal_error,'???'},S});
+ Type when record(Type,ptypedef) ->
+ NewS = S#state{type=Type,tname=Name},
+ case catch(check_ptype(NewS,Type,Type#ptypedef.typespec)) of
+ {error,Reason} ->
+ error({type,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({type,{internal_error,Reason},NewS});
+ {asn1_class,_ClassDef} ->
+ {asn1_class,Name};
+ Ts ->
+ NewType = Type#ptypedef{checked=true,typespec = Ts},
+ asn1_db:dbput(NewS#state.mname,Name,NewType), % update the type
+ ok
+ end
+ end,
+ case Result of
+ ok ->
+ checkp(S,T,Acc);
+ _ ->
+ checkp(S,T,[Result|Acc])
+ end;
+checkp(_S,[],Acc) ->
+ lists:reverse(Acc).
+
+
+
+
+checkc(S,[Name|Cs],Acc) ->
+ Result =
+ case asn1_db:dbget(S#state.mname,Name) of
+ undefined ->
+ error({class,{internal_error,'???'},S});
+ Class ->
+ ClassSpec = if
+ record(Class,classdef) ->
+ Class#classdef.typespec;
+ record(Class,typedef) ->
+ Class#typedef.typespec
+ end,
+ NewS = S#state{type=Class,tname=Name},
+ case catch(check_class(NewS,ClassSpec)) of
+ {error,Reason} ->
+ error({class,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({class,{internal_error,Reason},NewS});
+ C ->
+ %% update the classdef
+ NewClass =
+ if
+ record(Class,classdef) ->
+ Class#classdef{checked=true,typespec=C};
+ record(Class,typedef) ->
+ #classdef{checked=true,name=Name,typespec=C}
+ end,
+ asn1_db:dbput(NewS#state.mname,Name,NewClass),
+ ok
+ end
+ end,
+ case Result of
+ ok ->
+ checkc(S,Cs,Acc);
+ _ ->
+ checkc(S,Cs,[Result|Acc])
+ end;
+checkc(_S,[],Acc) ->
+%% include_default_class(S#state.mname),
+ lists:reverse(Acc).
+
+checko(S,[Name|Os],Acc,ExclO,ExclOS) ->
+ Result =
+ case asn1_db:dbget(S#state.mname,Name) of
+ undefined ->
+ error({type,{internal_error,'???'},S});
+ Object when record(Object,typedef) ->
+ NewS = S#state{type=Object,tname=Name},
+ case catch(check_object(NewS,Object,Object#typedef.typespec)) of
+ {error,Reason} ->
+ error({type,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({type,{internal_error,Reason},NewS});
+ {asn1,Reason} ->
+ error({type,Reason,NewS});
+ O ->
+ NewObj = Object#typedef{checked=true,typespec=O},
+ asn1_db:dbput(NewS#state.mname,Name,NewObj),
+ if
+ record(O,'Object') ->
+ case O#'Object'.gen of
+ true ->
+ {ok,ExclO,ExclOS};
+ false ->
+ {ok,[Name|ExclO],ExclOS}
+ end;
+ record(O,'ObjectSet') ->
+ case O#'ObjectSet'.gen of
+ true ->
+ {ok,ExclO,ExclOS};
+ false ->
+ {ok,ExclO,[Name|ExclOS]}
+ end
+ end
+ end;
+ PObject when record(PObject,pobjectdef) ->
+ NewS = S#state{type=PObject,tname=Name},
+ case (catch check_pobject(NewS,PObject)) of
+ {error,Reason} ->
+ error({type,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({type,{internal_error,Reason},NewS});
+ {asn1,Reason} ->
+ error({type,Reason,NewS});
+ PO ->
+ NewPObj = PObject#pobjectdef{def=PO},
+ asn1_db:dbput(NewS#state.mname,Name,NewPObj),
+ {ok,[Name|ExclO],ExclOS}
+ end;
+ PObjSet when record(PObjSet,pvaluesetdef) ->
+ %% this is a parameterized object set. Might be a parameterized
+ %% value set, couldn't it?
+ NewS = S#state{type=PObjSet,tname=Name},
+ case (catch check_pobjectset(NewS,PObjSet)) of
+ {error,Reason} ->
+ error({type,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({type,{internal_error,Reason},NewS});
+ {asn1,Reason} ->
+ error({type,Reason,NewS});
+ POS ->
+ %%NewPObjSet = PObjSet#pvaluesetdef{valueset=POS},
+ asn1_db:dbput(NewS#state.mname,Name,POS),
+ {ok,ExclO,[Name|ExclOS]}
+ end
+ end,
+ case Result of
+ {ok,NewExclO,NewExclOS} ->
+ checko(S,Os,Acc,NewExclO,NewExclOS);
+ _ ->
+ checko(S,Os,[Result|Acc],ExclO,ExclOS)
+ end;
+checko(_S,[],Acc,ExclO,ExclOS) ->
+ {lists:reverse(Acc),lists:reverse(ExclO),lists:reverse(ExclOS)}.
+
+check_class(S,CDef=#classdef{checked=Ch,name=Name,typespec=TS}) ->
+ case Ch of
+ true -> TS;
+ idle -> TS;
+ _ ->
+ NewCDef = CDef#classdef{checked=idle},
+ asn1_db:dbput(S#state.mname,Name,NewCDef),
+ CheckedTS = check_class(S,TS),
+ asn1_db:dbput(S#state.mname,Name,
+ NewCDef#classdef{checked=true,
+ typespec=CheckedTS}),
+ CheckedTS
+ end;
+check_class(S = #state{mname=M,tname=T},ClassSpec)
+ when record(ClassSpec,type) ->
+ Def = ClassSpec#type.def,
+ case Def of
+ #'Externaltypereference'{module=M,type=T} ->
+ #objectclass{fields=Def}; % in case of recursive definitions
+ Tref when record(Tref,'Externaltypereference') ->
+ {_,RefType} = get_referenced_type(S,Tref),
+% case RefType of
+% RefClass when record(RefClass,classdef) ->
+% check_class(S,RefClass#classdef.typespec)
+% end
+ case is_class(S,RefType) of
+ true ->
+ check_class(S,get_class_def(S,RefType));
+ _ ->
+ error({class,{internal_error,RefType},S})
+ end
+ end;
+% check_class(S,{objectclassname,ModuleName,ClassName}) when atom(ModuleName),atom(ClassName) ->
+% 'fix this';
+check_class(S,C) when record(C,objectclass) ->
+ NewFieldSpec = check_class_fields(S,C#objectclass.fields),
+ C#objectclass{fields=NewFieldSpec};
+%check_class(S,{objectclassname,ClassName}) ->
+check_class(S,ClassName) ->
+ {_,Def} = get_referenced_type(S,ClassName),
+ case Def of
+ ClassDef when record(ClassDef,classdef) ->
+ case ClassDef#classdef.checked of
+ true ->
+ ClassDef#classdef.typespec;
+ idle ->
+ ClassDef#classdef.typespec;
+ false ->
+ check_class(S,ClassDef#classdef.typespec)
+ end;
+ TypeDef when record(TypeDef,typedef) ->
+ %% this case may occur when a definition is a reference
+ %% to a class definition.
+ case TypeDef#typedef.typespec of
+ #type{def=Ext} when record(Ext,'Externaltypereference') ->
+ check_class(S,Ext)
+ end
+ end;
+check_class(_S,{poc,_ObjSet,_Params}) ->
+ 'fix this later'.
+
+check_class_fields(S,Fields) ->
+ check_class_fields(S,Fields,[]).
+
+check_class_fields(S,[F|Fields],Acc) ->
+ NewField =
+ case element(1,F) of
+ fixedtypevaluefield ->
+ {_,Name,Type,Unique,OSpec} = F,
+ RefType = check_type(S,#typedef{typespec=Type},Type),
+ {fixedtypevaluefield,Name,RefType,Unique,OSpec};
+ object_or_fixedtypevalue_field ->
+ {_,Name,Type,Unique,OSpec} = F,
+ Cat =
+ case asn1ct_gen:type(asn1ct_gen:get_inner(Type#type.def)) of
+ Def when record(Def,typereference);
+ record(Def,'Externaltypereference') ->
+ {_,D} = get_referenced_type(S,Def),
+ D;
+ {undefined,user} ->
+ %% neither of {primitive,bif} or {constructed,bif}
+%% {_,D} = get_referenced_type(S,#typereference{val=Type#type.def}),
+ {_,D} = get_referenced_type(S,#'Externaltypereference'{module=S#state.mname,type=Type#type.def}),
+ D;
+ _ ->
+ Type
+ end,
+ case Cat of
+ Class when record(Class,classdef) ->
+ {objectfield,Name,Type,Unique,OSpec};
+ _ ->
+ RefType = check_type(S,#typedef{typespec=Type},Type),
+ {fixedtypevaluefield,Name,RefType,Unique,OSpec}
+ end;
+ objectset_or_fixedtypevalueset_field ->
+ {_,Name,Type,OSpec} = F,
+%% RefType = check_type(S,#typedef{typespec=Type},Type),
+ RefType =
+ case (catch check_type(S,#typedef{typespec=Type},Type)) of
+ {asn1_class,_ClassDef} ->
+ case if_current_checked_type(S,Type) of
+ true ->
+ Type#type.def;
+ _ ->
+ check_class(S,Type)
+ end;
+ CheckedType when record(CheckedType,type) ->
+ CheckedType;
+ _ ->
+ error({class,"internal error, check_class_fields",S})
+ end,
+ if
+ record(RefType,'Externaltypereference') ->
+ {objectsetfield,Name,Type,OSpec};
+ record(RefType,classdef) ->
+ {objectsetfield,Name,Type,OSpec};
+ record(RefType,objectclass) ->
+ {objectsetfield,Name,Type,OSpec};
+ true ->
+ {fixedtypevaluesetfield,Name,RefType,OSpec}
+ end;
+ typefield ->
+ case F of
+ {TF,Name,{'DEFAULT',Type}} ->
+ {TF,Name,{'DEFAULT',check_type(S,#typedef{typespec=Type},Type)}};
+ _ -> F
+ end;
+ _ -> F
+ end,
+ check_class_fields(S,Fields,[NewField|Acc]);
+check_class_fields(_S,[],Acc) ->
+ lists:reverse(Acc).
+
+if_current_checked_type(S,#type{def=Def}) ->
+ CurrentCheckedName = S#state.tname,
+ MergedModules = S#state.inputmodules,
+ % CurrentCheckedModule = S#state.mname,
+ case Def of
+ #'Externaltypereference'{module=CurrentCheckedName,
+ type=CurrentCheckedName} ->
+ true;
+ #'Externaltypereference'{module=ModuleName,
+ type=CurrentCheckedName} ->
+ case MergedModules of
+ undefined ->
+ false;
+ _ ->
+ lists:member(ModuleName,MergedModules)
+ end;
+ _ ->
+ false
+ end.
+
+
+
+check_pobject(_S,PObject) when record(PObject,pobjectdef) ->
+ Def = PObject#pobjectdef.def,
+ Def.
+
+
+check_pobjectset(S,PObjSet) ->
+ #pvaluesetdef{pos=Pos,name=Name,args=Args,type=Type,
+ valueset=ValueSet}=PObjSet,
+ {Mod,Def} = get_referenced_type(S,Type#type.def),
+ case Def of
+ #classdef{} ->
+ ClassName = #'Externaltypereference'{module=Mod,
+ type=Def#classdef.name},
+ {valueset,Set} = ValueSet,
+% ObjectSet = #'ObjectSet'{class={objectclassname,ClassName},
+ ObjectSet = #'ObjectSet'{class=ClassName,
+ set=Set},
+ #pobjectsetdef{pos=Pos,name=Name,args=Args,class=Type#type.def,
+ def=ObjectSet};
+ _ ->
+ PObjSet
+ end.
+
+check_object(_S,ObjDef,ObjSpec) when (ObjDef#typedef.checked == true) ->
+ ObjSpec;
+check_object(S,_ObjDef,#'Object'{classname=ClassRef,def=ObjectDef}) ->
+ {_,_ClassDef} = get_referenced_type(S,ClassRef),
+ NewClassRef = check_externaltypereference(S,ClassRef),
+ ClassDef =
+ case _ClassDef#classdef.checked of
+ false ->
+ #classdef{checked=true,
+ typespec=check_class(S,_ClassDef#classdef.typespec)};
+ _ ->
+ _ClassDef
+ end,
+ NewObj =
+ case ObjectDef of
+ Def when tuple(Def), (element(1,Def)==object) ->
+ NewSettingList = check_objectdefn(S,Def,ClassDef),
+ #'Object'{def=NewSettingList};
+% Def when tuple(Def), (element(1,Def)=='ObjectFromObject') ->
+% fixa;
+ {po,{object,DefObj},ArgsList} ->
+ {_,Object} = get_referenced_type(S,DefObj),%DefObj is a
+ %%#'Externalvaluereference' or a #'Externaltypereference'
+ %% Maybe this call should be catched and in case of an exception
+ %% an nonallocated parameterized object should be returned.
+ instantiate_po(S,ClassDef,Object,ArgsList);
+ #'Externalvaluereference'{} ->
+ {_,Object} = get_referenced_type(S,ObjectDef),
+ check_object(S,Object,Object#typedef.typespec);
+ _ ->
+ exit({error,{no_object,ObjectDef},S})
+ end,
+ Gen = gen_incl(S,NewObj#'Object'.def,
+ (ClassDef#classdef.typespec)#objectclass.fields),
+ NewObj#'Object'{classname=NewClassRef,gen=Gen};
+
+%%check_object(S,ObjSetDef,ObjSet=#type{def={pt,ObjSetRef,Args}}) ->
+ %% A parameterized
+
+check_object(S,
+ _ObjSetDef,
+ ObjSet=#'ObjectSet'{class=ClassRef}) ->
+ {_,ClassDef} = get_referenced_type(S,ClassRef),
+ NewClassRef = check_externaltypereference(S,ClassRef),
+ UniqueFieldName =
+ case (catch get_unique_fieldname(ClassDef)) of
+ {error,'__undefined_'} -> {unique,undefined};
+ {asn1,Msg,_} -> error({class,Msg,S});
+ Other -> Other
+ end,
+ NewObjSet=
+ case ObjSet#'ObjectSet'.set of
+ {'SingleValue',Set} when list(Set) ->
+ CheckedSet = check_object_list(S,NewClassRef,Set),
+ NewSet = get_unique_valuelist(S,CheckedSet,UniqueFieldName),
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=NewSet};
+ {'SingleValue',{definedvalue,ObjName}} ->
+ {_,ObjDef} = get_referenced_type(S,#identifier{val=ObjName}),
+ #'Object'{def=CheckedObj} =
+ check_object(S,ObjDef,ObjDef#typedef.typespec),
+ NewSet = get_unique_valuelist(S,[{ObjDef#typedef.name,
+ CheckedObj}],
+ UniqueFieldName),
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=NewSet};
+ {'SingleValue',#'Externalvaluereference'{value=ObjName}} ->
+ {_,ObjDef} = get_referenced_type(S,#identifier{val=ObjName}),
+ #'Object'{def=CheckedObj} =
+ check_object(S,ObjDef,ObjDef#typedef.typespec),
+ NewSet = get_unique_valuelist(S,[{ObjDef#typedef.name,
+ CheckedObj}],
+ UniqueFieldName),
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=NewSet};
+ ['EXTENSIONMARK'] ->
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=['EXTENSIONMARK']};
+ Set when list(Set) ->
+ CheckedSet = check_object_list(S,NewClassRef,Set),
+ NewSet = get_unique_valuelist(S,CheckedSet,UniqueFieldName),
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=NewSet};
+ {Set,Ext} when list(Set) ->
+ CheckedSet = check_object_list(S,NewClassRef,Set++Ext),
+ NewSet = get_unique_valuelist(S,CheckedSet,UniqueFieldName),
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=NewSet++['EXTENSIONMARK']};
+ {{'SingleValue',Set},Ext} ->
+ CheckedSet = check_object_list(S,NewClassRef,
+ merge_sets(Set,Ext)),
+ NewSet = get_unique_valuelist(S,CheckedSet,UniqueFieldName),
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=NewSet++['EXTENSIONMARK']};
+ {Type,{'EXCEPT',Exclusion}} when record(Type,type) ->
+ {_,TDef} = get_referenced_type(S,Type#type.def),
+ OS = TDef#typedef.typespec,
+ NewSet = reduce_objectset(OS#'ObjectSet'.set,Exclusion),
+ NewOS = OS#'ObjectSet'{set=NewSet},
+ check_object(S,TDef#typedef{typespec=NewOS},
+ NewOS);
+ #type{def={pt,DefinedObjSet,ParamList}} ->
+ {_,PObjSetDef} = get_referenced_type(S,DefinedObjSet),
+ instantiate_pos(S,ClassDef,PObjSetDef,ParamList);
+ {ObjDef={object,definedsyntax,_ObjFields},_Ext} ->
+ CheckedSet = check_object_list(S,NewClassRef,[ObjDef]),
+ NewSet = get_unique_valuelist(S,CheckedSet,UniqueFieldName),
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=NewSet++['EXTENSIONMARK']}
+ end,
+ Gen = gen_incl_set(S,NewObjSet#'ObjectSet'.set,
+ ClassDef),
+ NewObjSet#'ObjectSet'{class=NewClassRef,gen=Gen}.
+
+
+merge_sets(Set,Ext) when list(Set),list(Ext) ->
+ Set ++ Ext;
+merge_sets(Set,Ext) when list(Ext) ->
+ [Set|Ext];
+merge_sets(Set,{'SingleValue',Ext}) when list(Set) ->
+ Set ++ [Ext];
+merge_sets(Set,{'SingleValue',Ext}) ->
+ [Set] ++ [Ext].
+
+reduce_objectset(ObjectSet,Exclusion) ->
+ case Exclusion of
+ {'SingleValue',#'Externalvaluereference'{value=Name}} ->
+ case lists:keysearch(Name,1,ObjectSet) of
+ {value,El} ->
+ lists:subtract(ObjectSet,[El]);
+ _ ->
+ ObjectSet
+ end
+ end.
+
+%% Checks a list of objects or object sets and returns a list of selected
+%% information for the code generation.
+check_object_list(S,ClassRef,ObjectList) ->
+ check_object_list(S,ClassRef,ObjectList,[]).
+
+check_object_list(S,ClassRef,[ObjOrSet|Objs],Acc) ->
+ case ObjOrSet of
+ ObjDef when tuple(ObjDef),(element(1,ObjDef)==object) ->
+ Def =
+ check_object(S,#typedef{typespec=ObjDef},
+% #'Object'{classname={objectclassname,ClassRef},
+ #'Object'{classname=ClassRef,
+ def=ObjDef}),
+ check_object_list(S,ClassRef,Objs,[{no_name,Def#'Object'.def}|Acc]);
+ {'SingleValue',{definedvalue,ObjName}} ->
+ {_,ObjectDef} = get_referenced_type(S,#identifier{val=ObjName}),
+ #'Object'{def=Def} = check_object(S,ObjectDef,ObjectDef#typedef.typespec),
+ check_object_list(S,ClassRef,Objs,[{ObjectDef#typedef.name,Def}|Acc]);
+ {'SingleValue',Ref = #'Externalvaluereference'{}} ->
+ {_,ObjectDef} = get_referenced_type(S,Ref),
+ #'Object'{def=Def} = check_object(S,ObjectDef,ObjectDef#typedef.typespec),
+ check_object_list(S,ClassRef,Objs,[{ObjectDef#typedef.name,Def}|Acc]);
+ ObjRef when record(ObjRef,'Externalvaluereference') ->
+ {_,ObjectDef} = get_referenced_type(S,ObjRef),
+ #'Object'{def=Def} = check_object(S,ObjectDef,ObjectDef#typedef.typespec),
+ check_object_list(S,ClassRef,Objs,
+%% [{ObjRef#'Externalvaluereference'.value,Def}|Acc]);
+ [{ObjectDef#typedef.name,Def}|Acc]);
+ {'ValueFromObject',{_,Object},FieldName} ->
+ {_,Def} = get_referenced_type(S,Object),
+%% TypeOrVal = get_fieldname_element(S,Def,FieldName);%% this must result in an object set
+ TypeDef = get_fieldname_element(S,Def,FieldName),
+ (TypeDef#typedef.typespec)#'ObjectSet'.set;
+ ObjSet when record(ObjSet,type) ->
+ ObjSetDef =
+ case ObjSet#type.def of
+ Ref when record(Ref,typereference);
+ record(Ref,'Externaltypereference') ->
+ {_,D} = get_referenced_type(S,ObjSet#type.def),
+ D;
+ Other ->
+ throw({asn1_error,{'unknown objecset',Other,S}})
+ end,
+ #'ObjectSet'{set=ObjectsInSet} =
+ check_object(S,ObjSetDef,ObjSetDef#typedef.typespec),
+ AccList = transform_set_to_object_list(ObjectsInSet,[]),
+ check_object_list(S,ClassRef,Objs,AccList++Acc);
+ union ->
+ check_object_list(S,ClassRef,Objs,Acc);
+ Other ->
+ exit({error,{'unknown object',Other},S})
+ end;
+%% Finally reverse the accumulated list and if there are any extension
+%% marks in the object set put one indicator of that in the end of the
+%% list.
+check_object_list(_,_,[],Acc) ->
+ lists:reverse(Acc).
+%% case lists:member('EXTENSIONMARK',RevAcc) of
+%% true ->
+%% ExclRevAcc = lists:filter(fun(X)->X /= 'EXTENSIONMARK' end,
+%% RevAcc),
+%% ExclRevAcc ++ ['EXTENSIONMARK'];
+%% false ->
+%% RevAcc
+%% end.
+
+
+%% get_fieldname_element/3
+%% gets the type/value/object/... of the referenced element in FieldName
+%% FieldName is a list and may have more than one element.
+%% Each element in FieldName can be either {typefieldreference,AnyFieldName}
+%% or {valuefieldreference,AnyFieldName}
+%% Def is the def of the first object referenced by FieldName
+get_fieldname_element(S,Def,[{_RefType,FieldName}]) when record(Def,typedef) ->
+ {_,_,ObjComps} = (Def#typedef.typespec)#'Object'.def,
+ case lists:keysearch(FieldName,1,ObjComps) of
+ {value,{_,TDef}} when record(TDef,typedef) ->
+ %% ORec = TDef#typedef.typespec, %% XXX This must be made general
+% case TDef#typedef.typespec of
+% ObjSetRec when record(ObjSetRec,'ObjectSet') ->
+% ObjSet = ObjSetRec#'ObjectSet'.set;
+% ObjRec when record(ObjRec,'Object') ->
+% %% now get the field in ObjRec that RestFName points out
+% %ObjRec
+% TDef
+% end;
+ TDef;
+ {value,{_,VDef}} when record(VDef,valuedef) ->
+ check_value(S,VDef);
+ _ ->
+ throw({assigned_object_error,"not_assigned_object",S})
+ end;
+get_fieldname_element(_S,Def,[{_RefType,_FieldName}|_RestFName])
+ when record(Def,typedef) ->
+ ok.
+
+transform_set_to_object_list([{Name,_UVal,Fields}|Objs],Acc) ->
+ transform_set_to_object_list(Objs,[{Name,{object,generatesyntax,Fields}}|Acc]);
+transform_set_to_object_list(['EXTENSIONMARK'|Objs],Acc) ->
+%% transform_set_to_object_list(Objs,['EXTENSIONMARK'|Acc]);
+ transform_set_to_object_list(Objs,Acc);
+transform_set_to_object_list([],Acc) ->
+ Acc.
+
+get_unique_valuelist(_S,ObjSet,{unique,undefined}) -> % no unique field in object
+ lists:map(fun({N,{_,_,F}})->{N,F};
+ (V={_,_,_}) ->V end, ObjSet);
+get_unique_valuelist(S,ObjSet,UFN) ->
+ get_unique_vlist(S,ObjSet,UFN,[]).
+
+get_unique_vlist(S,[],_,Acc) ->
+ case catch check_uniqueness(Acc) of
+ {asn1_error,_} ->
+% exit({error,Reason,S});
+ error({'ObjectSet',"not unique objects in object set",S});
+ true ->
+ lists:reverse(Acc)
+ end;
+get_unique_vlist(S,[{ObjName,Obj}|Rest],UniqueFieldName,Acc) ->
+ {_,_,Fields} = Obj,
+ VDef = get_unique_value(S,Fields,UniqueFieldName),
+ get_unique_vlist(S,Rest,UniqueFieldName,
+ [{ObjName,VDef#valuedef.value,Fields}|Acc]);
+get_unique_vlist(S,[V={_,_,_}|Rest],UniqueFieldName,Acc) ->
+ get_unique_vlist(S,Rest,UniqueFieldName,[V|Acc]).
+
+get_unique_value(S,Fields,UniqueFieldName) ->
+ Module = S#state.mname,
+ case lists:keysearch(UniqueFieldName,1,Fields) of
+ {value,Field} ->
+ case element(2,Field) of
+ VDef when record(VDef,valuedef) ->
+ VDef;
+ {definedvalue,ValName} ->
+ ValueDef = asn1_db:dbget(Module,ValName),
+ case ValueDef of
+ VDef when record(VDef,valuedef) ->
+ ValueDef;
+ undefined ->
+ #valuedef{value=ValName}
+ end;
+ {'ValueFromObject',Object,Name} ->
+ case Object of
+ {object,Ext} when record(Ext,'Externaltypereference') ->
+ OtherModule = Ext#'Externaltypereference'.module,
+ ExtObjName = Ext#'Externaltypereference'.type,
+ ObjDef = asn1_db:dbget(OtherModule,ExtObjName),
+ ObjSpec = ObjDef#typedef.typespec,
+ get_unique_value(OtherModule,element(3,ObjSpec),Name);
+ {object,{_,_,ObjName}} ->
+ ObjDef = asn1_db:dbget(Module,ObjName),
+ ObjSpec = ObjDef#typedef.typespec,
+ get_unique_value(Module,element(3,ObjSpec),Name);
+ {po,Object,_Params} ->
+ exit({error,{'parameterized object not implemented yet',
+ Object},S})
+ end;
+ Value when atom(Value);number(Value) ->
+ #valuedef{value=Value};
+ {'CHOICE',{_,Value}} when atom(Value);number(Value) ->
+ #valuedef{value=Value}
+ end;
+ false ->
+ exit({error,{'no unique value',Fields,UniqueFieldName},S})
+%% io:format("WARNING: no unique value in object"),
+%% exit(uniqueFieldName)
+ end.
+
+check_uniqueness(NameValueList) ->
+ check_uniqueness1(lists:keysort(2,NameValueList)).
+
+check_uniqueness1([]) ->
+ true;
+check_uniqueness1([_]) ->
+ true;
+check_uniqueness1([{_,N,_},{_,N,_}|_Rest]) ->
+ throw({asn1_error,{'objects in set must have unique values in UNIQUE fields',N}});
+check_uniqueness1([_|Rest]) ->
+ check_uniqueness1(Rest).
+
+%% instantiate_po/4
+%% ClassDef is the class of Object,
+%% Object is the Parameterized object, which is referenced,
+%% ArgsList is the list of actual parameters
+%% returns an #'Object' record.
+instantiate_po(S,_ClassDef,Object,ArgsList) when record(Object,pobjectdef) ->
+ FormalParams = get_pt_args(Object),
+ MatchedArgs = match_args(FormalParams,ArgsList,[]),
+ NewS = S#state{type=Object,parameters=MatchedArgs},
+ check_object(NewS,Object,#'Object'{classname=Object#pobjectdef.class,
+ def=Object#pobjectdef.def}).
+
+%% instantiate_pos/4
+%% ClassDef is the class of ObjectSetDef,
+%% ObjectSetDef is the Parameterized object set, which is referenced
+%% on the right side of the assignment,
+%% ArgsList is the list of actual parameters, i.e. real objects
+instantiate_pos(S,ClassDef,ObjectSetDef,ArgsList) ->
+ ClassName = ClassDef#classdef.name,
+ FormalParams = get_pt_args(ObjectSetDef),
+ Set = case get_pt_spec(ObjectSetDef) of
+ {valueset,_Set} -> _Set;
+ _Set -> _Set
+ end,
+ MatchedArgs = match_args(FormalParams,ArgsList,[]),
+ NewS = S#state{type=ObjectSetDef,parameters=MatchedArgs},
+ check_object(NewS,ObjectSetDef,
+ #'ObjectSet'{class=name2Extref(S#state.mname,ClassName),
+ set=Set}).
+
+
+%% gen_incl -> boolean()
+%% If object with Fields has any of the corresponding class' typefields
+%% then return value is true otherwise it is false.
+%% If an object lacks a typefield but the class has a type field that
+%% is OPTIONAL then we want gen to be true
+gen_incl(S,{_,_,Fields},CFields)->
+ gen_incl1(S,Fields,CFields).
+
+gen_incl1(_,_,[]) ->
+ false;
+gen_incl1(S,Fields,[C|CFields]) ->
+ case element(1,C) of
+ typefield ->
+% case lists:keymember(element(2,C),1,Fields) of
+% true ->
+% true;
+% false ->
+% gen_incl1(S,Fields,CFields)
+% end;
+ true; %% should check that field is OPTIONAL or DEFUALT if
+ %% the object lacks this field
+ objectfield ->
+ case lists:keysearch(element(2,C),1,Fields) of
+ {value,Field} ->
+ Type = element(3,C),
+ {_,ClassDef} = get_referenced_type(S,Type#type.def),
+% {_,ClassFields,_} = ClassDef#classdef.typespec,
+ #objectclass{fields=ClassFields} =
+ ClassDef#classdef.typespec,
+ ObjTDef = element(2,Field),
+ case gen_incl(S,(ObjTDef#typedef.typespec)#'Object'.def,
+ ClassFields) of
+ true ->
+ true;
+ _ ->
+ gen_incl1(S,Fields,CFields)
+ end;
+ _ ->
+ gen_incl1(S,Fields,CFields)
+ end;
+ _ ->
+ gen_incl1(S,Fields,CFields)
+ end.
+
+%% first if no unique field in the class return false.(don't generate code)
+gen_incl_set(S,Fields,ClassDef) ->
+ case catch get_unique_fieldname(ClassDef) of
+ Tuple when tuple(Tuple) ->
+ false;
+ _ ->
+ gen_incl_set1(S,Fields,
+ (ClassDef#classdef.typespec)#objectclass.fields)
+ end.
+
+%% if any of the existing or potentially existing objects has a typefield
+%% then return true.
+gen_incl_set1(_,[],_CFields)->
+ false;
+gen_incl_set1(_,['EXTENSIONMARK'],_) ->
+ true;
+%% Fields are the fields of an object in the object set.
+%% CFields are the fields of the class of the object set.
+gen_incl_set1(S,[Object|Rest],CFields)->
+ Fields = element(size(Object),Object),
+ case gen_incl1(S,Fields,CFields) of
+ true ->
+ true;
+ false ->
+ gen_incl_set1(S,Rest,CFields)
+ end.
+
+check_objectdefn(S,Def,CDef) when record(CDef,classdef) ->
+ WithSyntax = (CDef#classdef.typespec)#objectclass.syntax,
+ ClassFields = (CDef#classdef.typespec)#objectclass.fields,
+ case Def of
+ {object,defaultsyntax,Fields} ->
+ check_defaultfields(S,Fields,ClassFields);
+ {object,definedsyntax,Fields} ->
+ {_,WSSpec} = WithSyntax,
+ NewFields =
+ case catch( convert_definedsyntax(S,Fields,WSSpec,
+ ClassFields,[])) of
+ {asn1,{_ErrorType,ObjToken,ClassToken}} ->
+ throw({asn1,{'match error in object',ObjToken,
+ 'found in object',ClassToken,'found in class'}});
+ Err={asn1,_} -> throw(Err);
+ Err={'EXIT',_} -> throw(Err);
+ DefaultFields when list(DefaultFields) ->
+ DefaultFields
+ end,
+ {object,defaultsyntax,NewFields};
+ {object,_ObjectId} -> % This is a DefinedObject
+ fixa;
+ Other ->
+ exit({error,{objectdefn,Other}})
+ end.
+
+check_defaultfields(S,Fields,ClassFields) ->
+ check_defaultfields(S,Fields,ClassFields,[]).
+
+check_defaultfields(_S,[],_ClassFields,Acc) ->
+ {object,defaultsyntax,lists:reverse(Acc)};
+check_defaultfields(S,[{FName,Spec}|Fields],ClassFields,Acc) ->
+ case lists:keysearch(FName,2,ClassFields) of
+ {value,CField} ->
+ NewField = convert_to_defaultfield(S,FName,Spec,CField),
+ check_defaultfields(S,Fields,ClassFields,[NewField|Acc]);
+ _ ->
+ throw({error,{asn1,{'unvalid field in object',FName}}})
+ end.
+%% {object,defaultsyntax,Fields}.
+
+convert_definedsyntax(_S,[],[],_ClassFields,Acc) ->
+ lists:reverse(Acc);
+convert_definedsyntax(S,Fields,WithSyntax,ClassFields,Acc) ->
+ case match_field(S,Fields,WithSyntax,ClassFields) of
+ {MatchedField,RestFields,RestWS} ->
+ if
+ list(MatchedField) ->
+ convert_definedsyntax(S,RestFields,RestWS,ClassFields,
+ lists:append(MatchedField,Acc));
+ true ->
+ convert_definedsyntax(S,RestFields,RestWS,ClassFields,
+ [MatchedField|Acc])
+ end
+%% throw({error,{asn1,{'unvalid syntax in object',WorS}}})
+ end.
+
+match_field(S,Fields,WithSyntax,ClassFields) ->
+ match_field(S,Fields,WithSyntax,ClassFields,[]).
+
+match_field(S,Fields,[W|Ws],ClassFields,Acc) when list(W) ->
+ case catch(match_optional_field(S,Fields,W,ClassFields,[])) of
+ {'EXIT',_} ->
+ match_field(Fields,Ws,ClassFields,Acc); %% add S
+%% {[Result],RestFields} ->
+%% {Result,RestFields,Ws};
+ {Result,RestFields} when list(Result) ->
+ {Result,RestFields,Ws};
+ _ ->
+ match_field(S,Fields,Ws,ClassFields,Acc)
+ end;
+match_field(S,Fields,WithSyntax,ClassFields,_Acc) ->
+ match_mandatory_field(S,Fields,WithSyntax,ClassFields,[]).
+
+match_optional_field(_S,RestFields,[],_,Ret) ->
+ {Ret,RestFields};
+%% An additional optional field within an optional field
+match_optional_field(S,Fields,[W|Ws],ClassFields,Ret) when list(W) ->
+ case catch match_optional_field(S,Fields,W,ClassFields,[]) of
+ {'EXIT',_} ->
+ {Ret,Fields};
+ {asn1,{optional_matcherror,_,_}} ->
+ {Ret,Fields};
+ {OptionalField,RestFields} ->
+ match_optional_field(S,RestFields,Ws,ClassFields,
+ lists:append(OptionalField,Ret))
+ end;
+%% identify and skip word
+%match_optional_field(S,[#'Externaltypereference'{type=WorS}|Rest],
+match_optional_field(S,[{_,_,WorS}|Rest],
+ [WorS|Ws],ClassFields,Ret) ->
+ match_optional_field(S,Rest,Ws,ClassFields,Ret);
+match_optional_field(S,[],_,ClassFields,Ret) ->
+ match_optional_field(S,[],[],ClassFields,Ret);
+%% identify and skip comma
+match_optional_field(S,[{WorS,_}|Rest],[{WorS,_}|Ws],ClassFields,Ret) ->
+ match_optional_field(S,Rest,Ws,ClassFields,Ret);
+%% identify and save field data
+match_optional_field(S,[Setting|Rest],[{_,W}|Ws],ClassFields,Ret) ->
+ WorS =
+ case Setting of
+ Type when record(Type,type) -> Type;
+%% #'Externalvaluereference'{value=WordOrSetting} -> WordOrSetting;
+ {'ValueFromObject',_,_} -> Setting;
+ {object,_,_} -> Setting;
+ {_,_,WordOrSetting} -> WordOrSetting;
+%% Atom when atom(Atom) -> Atom
+ Other -> Other
+ end,
+ case lists:keysearch(W,2,ClassFields) of
+ false ->
+ throw({asn1,{optional_matcherror,WorS,W}});
+ {value,CField} ->
+ NewField = convert_to_defaultfield(S,W,WorS,CField),
+ match_optional_field(S,Rest,Ws,ClassFields,[NewField|Ret])
+ end;
+match_optional_field(_S,[WorS|_Rest],[W|_Ws],_ClassFields,_Ret) ->
+ throw({asn1,{optional_matcherror,WorS,W}}).
+
+match_mandatory_field(_S,[],[],_,[Acc]) ->
+ {Acc,[],[]};
+match_mandatory_field(_S,[],[],_,Acc) ->
+ {Acc,[],[]};
+match_mandatory_field(S,[],[H|T],CF,Acc) when list(H) ->
+ match_mandatory_field(S,[],T,CF,Acc);
+match_mandatory_field(_S,[],WithSyntax,_,_Acc) ->
+ throw({asn1,{mandatory_matcherror,[],WithSyntax}});
+%match_mandatory_field(_S,Fields,WithSyntax=[W|_Ws],_ClassFields,[Acc]) when list(W) ->
+match_mandatory_field(_S,Fields,WithSyntax=[W|_Ws],_ClassFields,Acc) when list(W), length(Acc) >= 1 ->
+ {Acc,Fields,WithSyntax};
+%% identify and skip word
+match_mandatory_field(S,[{_,_,WorS}|Rest],
+ [WorS|Ws],ClassFields,Acc) ->
+ match_mandatory_field(S,Rest,Ws,ClassFields,Acc);
+%% identify and skip comma
+match_mandatory_field(S,[{WorS,_}|Rest],[{WorS,_}|Ws],ClassFields,Ret) ->
+ match_mandatory_field(S,Rest,Ws,ClassFields,Ret);
+%% identify and save field data
+match_mandatory_field(S,[Setting|Rest],[{_,W}|Ws],ClassFields,Acc) ->
+ WorS =
+ case Setting of
+%% Atom when atom(Atom) -> Atom;
+%% #'Externalvaluereference'{value=WordOrSetting} -> WordOrSetting;
+ {object,_,_} -> Setting;
+ {_,_,WordOrSetting} -> WordOrSetting;
+ Type when record(Type,type) -> Type;
+ Other -> Other
+ end,
+ case lists:keysearch(W,2,ClassFields) of
+ false ->
+ throw({asn1,{mandatory_matcherror,WorS,W}});
+ {value,CField} ->
+ NewField = convert_to_defaultfield(S,W,WorS,CField),
+ match_mandatory_field(S,Rest,Ws,ClassFields,[NewField|Acc])
+ end;
+
+match_mandatory_field(_S,[WorS|_Rest],[W|_Ws],_ClassFields,_Acc) ->
+ throw({asn1,{mandatory_matcherror,WorS,W}}).
+
+%% Converts a field of an object from defined syntax to default syntax
+convert_to_defaultfield(S,ObjFieldName,ObjFieldSetting,CField)->
+ CurrMod = S#state.mname,
+ case element(1,CField) of
+ typefield ->
+ TypeDef=
+ case ObjFieldSetting of
+ TypeRec when record(TypeRec,type) -> TypeRec#type.def;
+ TDef when record(TDef,typedef) ->
+ TDef#typedef{typespec=check_type(S,TDef,
+ TDef#typedef.typespec)};
+ _ -> ObjFieldSetting
+ end,
+ Type =
+ if
+ record(TypeDef,typedef) -> TypeDef;
+ true ->
+ case asn1ct_gen:type(asn1ct_gen:get_inner(TypeDef)) of
+ ERef = #'Externaltypereference'{module=CurrMod} ->
+ {_,T} = get_referenced_type(S,ERef),
+ T#typedef{checked=true,
+ typespec=check_type(S,T,
+ T#typedef.typespec)};
+ ERef = #'Externaltypereference'{module=ExtMod} ->
+ {_,T} = get_referenced_type(S,ERef),
+ #typedef{name=Name} = T,
+ check_type(S,T,T#typedef.typespec),
+ #typedef{checked=true,
+ name={ExtMod,Name},
+ typespec=ERef};
+ Bif when Bif=={primitive,bif};Bif=={constructed,bif} ->
+ T = check_type(S,#typedef{typespec=ObjFieldSetting},
+ ObjFieldSetting),
+ #typedef{checked=true,name=Bif,typespec=T};
+ _ ->
+ {Mod,T} =
+ %% get_referenced_type(S,#typereference{val=ObjFieldSetting}),
+ get_referenced_type(S,#'Externaltypereference'{module=S#state.mname,type=ObjFieldSetting}),
+ case Mod of
+ CurrMod ->
+ T;
+ ExtMod ->
+ #typedef{name=Name} = T,
+ T#typedef{name={ExtMod,Name}}
+ end
+ end
+ end,
+ {ObjFieldName,Type};
+ fixedtypevaluefield ->
+ case ObjFieldName of
+ Val when atom(Val) ->
+ %% ObjFieldSetting can be a value,an objectidentifiervalue,
+ %% an element in an enumeration or namednumberlist etc.
+ ValRef =
+ case ObjFieldSetting of
+ #'Externalvaluereference'{} -> ObjFieldSetting;
+ {'ValueFromObject',{_,ObjRef},FieldName} ->
+ {_,Object} = get_referenced_type(S,ObjRef),
+ ChObject = check_object(S,Object,
+ Object#typedef.typespec),
+ get_fieldname_element(S,Object#typedef{typespec=ChObject},
+ FieldName);
+ #valuedef{} ->
+ ObjFieldSetting;
+ _ ->
+ #identifier{val=ObjFieldSetting}
+ end,
+ case ValRef of
+ #valuedef{} ->
+ {ObjFieldName,check_value(S,ValRef)};
+ _ ->
+ ValDef =
+ case catch get_referenced_type(S,ValRef) of
+ {error,_} ->
+ check_value(S,#valuedef{name=Val,
+ type=element(3,CField),
+ value=ObjFieldSetting});
+ {_,VDef} when record(VDef,valuedef) ->
+ check_value(S,VDef);%% XXX
+ {_,VDef} ->
+ check_value(S,#valuedef{name=Val,
+ type=element(3,CField),
+ value=VDef})
+ end,
+ {ObjFieldName,ValDef}
+ end;
+ Val ->
+ {ObjFieldName,Val}
+ end;
+ fixedtypevaluesetfield ->
+ {ObjFieldName,ObjFieldSetting};
+ objectfield ->
+ ObjectSpec =
+ case ObjFieldSetting of
+ Ref when record(Ref,typereference);record(Ref,identifier);
+ record(Ref,'Externaltypereference');
+ record(Ref,'Externalvaluereference') ->
+ {_,R} = get_referenced_type(S,ObjFieldSetting),
+ R;
+ {'ValueFromObject',{_,ObjRef},FieldName} ->
+ %% This is an ObjectFromObject
+ {_,Object} = get_referenced_type(S,ObjRef),
+ ChObject = check_object(S,Object,
+ Object#typedef.typespec),
+ _ObjFromObj=
+ get_fieldname_element(S,Object#typedef{
+ typespec=ChObject},
+ FieldName);
+ %%ClassName = ObjFromObj#'Object'.classname,
+ %%#typedef{name=,
+ %% typespec=
+ %% ObjFromObj#'Object'{classname=
+ %% {objectclassname,ClassName}}};
+ {object,_,_} ->
+ %% An object defined inlined in another object
+ #type{def=Ref} = element(3,CField),
+% CRef = case Ref of
+% #'Externaltypereference'{module=CurrMod,
+% type=CName} ->
+% CName;
+% #'Externaltypereference'{module=ExtMod,
+% type=CName} ->
+% {ExtMod,CName}
+% end,
+ InlinedObjName=
+ list_to_atom(lists:concat([S#state.tname]++
+ ['_',ObjFieldName])),
+% ObjSpec = #'Object'{classname={objectclassname,CRef},
+ ObjSpec = #'Object'{classname=Ref,
+ def=ObjFieldSetting},
+ CheckedObj=
+ check_object(S,#typedef{typespec=ObjSpec},ObjSpec),
+ InlObj = #typedef{checked=true,name=InlinedObjName,
+ typespec=CheckedObj},
+ asn1ct_gen:insert_once(inlined_objects,{InlinedObjName,
+ InlinedObjName}),
+ asn1_db:dbput(S#state.mname,InlinedObjName,InlObj),
+ InlObj;
+ #type{def=Eref} when record(Eref,'Externaltypereference') ->
+ {_,R} = get_referenced_type(S,Eref),
+ R;
+ _ ->
+%% {_,R} = get_referenced_type(S,#typereference{val=ObjFieldSetting}),
+ {_,R} = get_referenced_type(S,#'Externaltypereference'{module=S#state.mname,type=ObjFieldSetting}),
+ R
+ end,
+ {ObjFieldName,
+ ObjectSpec#typedef{checked=true,
+ typespec=check_object(S,ObjectSpec,
+ ObjectSpec#typedef.typespec)}};
+ variabletypevaluefield ->
+ {ObjFieldName,ObjFieldSetting};
+ variabletypevaluesetfield ->
+ {ObjFieldName,ObjFieldSetting};
+ objectsetfield ->
+ {_,ObjSetSpec} =
+ case ObjFieldSetting of
+ Ref when record(Ref,'Externaltypereference');
+ record(Ref,'Externalvaluereference') ->
+ get_referenced_type(S,ObjFieldSetting);
+ ObjectList when list(ObjectList) ->
+ %% an objctset defined in the object,though maybe
+ %% parsed as a SequenceOfValue
+ %% The ObjectList may be a list of references to
+ %% objects, a ValueFromObject
+ {_,_,Type,_} = CField,
+ ClassDef = Type#type.def,
+ case ClassDef#'Externaltypereference'.module of
+ CurrMod ->
+ ClassDef#'Externaltypereference'.type;
+ ExtMod ->
+ {ExtMod,
+ ClassDef#'Externaltypereference'.type}
+ end,
+ {no_name,
+ #typedef{typespec=
+ #'ObjectSet'{class=
+% {objectclassname,ClassRef},
+ ClassDef,
+ set=ObjectList}}};
+ ObjectSet={'SingleValue',_} ->
+ %% a Union of defined objects
+ {_,_,Type,_} = CField,
+ ClassDef = Type#type.def,
+% ClassRef =
+% case ClassDef#'Externaltypereference'.module of
+% CurrMod ->
+% ClassDef#'Externaltypereference'.type;
+% ExtMod ->
+% {ExtMod,
+% ClassDef#'Externaltypereference'.type}
+% end,
+ {no_name,
+% #typedef{typespec=#'ObjectSet'{class={objectclassname,ClassRef},
+ #typedef{typespec=#'ObjectSet'{class=ClassDef,
+ set=ObjectSet}}};
+ {object,_,[#type{def={'TypeFromObject',
+ {object,RefedObj},
+ FieldName}}]} ->
+ %% This case occurs when an ObjectSetFromObjects
+ %% production is used
+ {M,Def} = get_referenced_type(S,RefedObj),
+ {M,get_fieldname_element(S,Def,FieldName)};
+ #type{def=Eref} when
+ record(Eref,'Externaltypereference') ->
+ get_referenced_type(S,Eref);
+ _ ->
+%% get_referenced_type(S,#typereference{val=ObjFieldSetting})
+ get_referenced_type(S,#'Externaltypereference'{module=S#state.mname,type=ObjFieldSetting})
+ end,
+ {ObjFieldName,
+ ObjSetSpec#typedef{checked=true,
+ typespec=check_object(S,ObjSetSpec,
+ ObjSetSpec#typedef.typespec)}}
+ end.
+
+check_value(OldS,V) when record(V,pvaluesetdef) ->
+ #pvaluesetdef{checked=Checked,type=Type} = V,
+ case Checked of
+ true -> V;
+ {error,_} -> V;
+ false ->
+ case get_referenced_type(OldS,Type#type.def) of
+ {_,Class} when record(Class,classdef) ->
+ throw({pobjectsetdef});
+ _ -> continue
+ end
+ end;
+check_value(_OldS,V) when record(V,pvaluedef) ->
+ %% Fix this case later
+ V;
+check_value(OldS,V) when record(V,typedef) ->
+ %% This case when a value set has been parsed as an object set.
+ %% It may be a value set
+ #typedef{typespec=TS} = V,
+ case TS of
+ #'ObjectSet'{class=ClassRef} ->
+ {_,TSDef} = get_referenced_type(OldS,ClassRef),
+ %%IsObjectSet(TSDef);
+ case TSDef of
+ #classdef{} -> throw({objectsetdef});
+ #typedef{typespec=#type{def=Eref}} when
+ record(Eref,'Externaltypereference') ->
+ %% This case if the class reference is a defined
+ %% reference to class
+ check_value(OldS,V#typedef{typespec=TS#'ObjectSet'{class=Eref}});
+ #typedef{} ->
+ % an ordinary value set with a type in #typedef.typespec
+ ValueSet = TS#'ObjectSet'.set,
+ Type=check_type(OldS,TSDef,TSDef#typedef.typespec),
+ Value = check_value(OldS,#valuedef{type=Type,
+ value=ValueSet}),
+ {valueset,Type#type{constraint=Value#valuedef.value}}
+ end;
+ _ ->
+ throw({objectsetdef})
+ end;
+check_value(S,#valuedef{pos=Pos,name=Name,type=Type,
+ value={valueset,Constr}}) ->
+ NewType = Type#type{constraint=[Constr]},
+ {valueset,
+ check_type(S,#typedef{pos=Pos,name=Name,typespec=NewType},NewType)};
+check_value(OldS=#state{recordtopname=TopName},V) when record(V,valuedef) ->
+ #valuedef{name=Name,checked=Checked,type=Vtype,value=Value} = V,
+ case Checked of
+ true ->
+ V;
+ {error,_} ->
+ V;
+ false ->
+ Def = Vtype#type.def,
+ Constr = Vtype#type.constraint,
+ S = OldS#state{type=Vtype,tname=Def,value=V,vname=Name},
+ NewDef =
+ case Def of
+ Ext when record(Ext,'Externaltypereference') ->
+ RecName = Ext#'Externaltypereference'.type,
+ {_,Type} = get_referenced_type(S,Ext),
+ %% If V isn't a value but an object Type is a #classdef{}
+ case Type of
+ #classdef{} ->
+ throw({objectdef});
+ #typedef{} ->
+ case is_contextswitchtype(Type) of
+ true ->
+ #valuedef{value=CheckedVal}=
+ check_value(S,V#valuedef{type=Type#typedef.typespec}),
+ #newv{value=CheckedVal};
+ _ ->
+ #valuedef{value=CheckedVal}=
+ check_value(S#state{recordtopname=[RecName|TopName]},
+ V#valuedef{type=Type#typedef.typespec}),
+ #newv{value=CheckedVal}
+ end
+ end;
+ 'ANY' ->
+ throw({error,{asn1,{'cant check value of type',Def}}});
+ 'INTEGER' ->
+ validate_integer(S,Value,[],Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ {'INTEGER',NamedNumberList} ->
+ validate_integer(S,Value,NamedNumberList,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ {'BIT STRING',NamedNumberList} ->
+ validate_bitstring(S,Value,NamedNumberList,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'NULL' ->
+ validate_null(S,Value,Constr),
+ #newv{};
+ 'OBJECT IDENTIFIER' ->
+ validate_objectidentifier(S,Value,Constr),
+ #newv{value = normalize_value(S,Vtype,Value,[])};
+ 'ObjectDescriptor' ->
+ validate_objectdescriptor(S,Value,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ {'ENUMERATED',NamedNumberList} ->
+ validate_enumerated(S,Value,NamedNumberList,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'BOOLEAN'->
+ validate_boolean(S,Value,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'OCTET STRING' ->
+ validate_octetstring(S,Value,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'NumericString' ->
+ validate_restrictedstring(S,Value,Def,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'TeletexString' ->
+ validate_restrictedstring(S,Value,Def,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'VideotexString' ->
+ validate_restrictedstring(S,Value,Def,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'UTCTime' ->
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+% exit({'cant check value of type' ,Def});
+ 'GeneralizedTime' ->
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+% exit({'cant check value of type' ,Def});
+ 'GraphicString' ->
+ validate_restrictedstring(S,Value,Def,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'VisibleString' ->
+ validate_restrictedstring(S,Value,Def,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'GeneralString' ->
+ validate_restrictedstring(S,Value,Def,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'PrintableString' ->
+ validate_restrictedstring(S,Value,Def,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'IA5String' ->
+ validate_restrictedstring(S,Value,Def,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'BMPString' ->
+ validate_restrictedstring(S,Value,Def,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+%% 'UniversalString' -> %added 6/12 -00
+%% #newv{value=validate_restrictedstring(S,Value,Def,Constr)};
+ Seq when record(Seq,'SEQUENCE') ->
+ SeqVal = validate_sequence(S,Value,
+ Seq#'SEQUENCE'.components,
+ Constr),
+ #newv{value=normalize_value(S,Vtype,SeqVal,TopName)};
+ {'SEQUENCE OF',Components} ->
+ validate_sequenceof(S,Value,Components,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,TopName)};
+ {'CHOICE',Components} ->
+ validate_choice(S,Value,Components,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,TopName)};
+ Set when record(Set,'SET') ->
+ validate_set(S,Value,Set#'SET'.components,
+ Constr),
+ #newv{value=normalize_value(S,Vtype,Value,TopName)};
+ {'SET OF',Components} ->
+ validate_setof(S,Value,Components,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,TopName)};
+ Other ->
+ exit({'cant check value of type' ,Other})
+ end,
+ case NewDef#newv.value of
+ unchanged ->
+ V#valuedef{checked=true,value=Value};
+ ok ->
+ V#valuedef{checked=true,value=Value};
+ {error,Reason} ->
+ V#valuedef{checked={error,Reason},value=Value};
+ _V ->
+ V#valuedef{checked=true,value=_V}
+ end
+ end.
+
+is_contextswitchtype(#typedef{name='EXTERNAL'})->
+ true;
+is_contextswitchtype(#typedef{name='EMBEDDED PDV'}) ->
+ true;
+is_contextswitchtype(#typedef{name='CHARACTER STRING'}) ->
+ true;
+is_contextswitchtype(_) ->
+ false.
+
+% validate_integer(S,{identifier,Pos,Id},NamedNumberList,Constr) ->
+% case lists:keysearch(Id,1,NamedNumberList) of
+% {value,_} -> ok;
+% false -> error({value,"unknown NamedNumber",S})
+% end;
+%% This case occurs when there is a valuereference
+validate_integer(S=#state{mname=M},
+ #'Externalvaluereference'{module=M,value=Id},
+ NamedNumberList,_Constr) ->
+ case lists:keysearch(Id,1,NamedNumberList) of
+ {value,_} -> ok;
+ false -> error({value,"unknown NamedNumber",S})
+ end;
+validate_integer(S,Id,NamedNumberList,_Constr) when atom(Id) ->
+ case lists:keysearch(Id,1,NamedNumberList) of
+ {value,_} -> ok;
+ false -> error({value,"unknown NamedNumber",S})
+ end;
+validate_integer(_S,Value,_NamedNumberList,Constr) when integer(Value) ->
+ check_integer_range(Value,Constr).
+
+check_integer_range(Int,Constr) when list(Constr) ->
+ NewConstr = [X || #constraint{c=X} <- Constr],
+ check_constr(Int,NewConstr);
+
+check_integer_range(_Int,_Constr) ->
+ %%io:format("~p~n",[Constr]),
+ ok.
+
+check_constr(Int,[{'ValueRange',Lb,Ub}|T]) when Int >= Lb, Int =< Ub ->
+ check_constr(Int,T);
+check_constr(_Int,[]) ->
+ ok.
+
+validate_bitstring(_S,_Value,_NamedNumberList,_Constr) ->
+ ok.
+
+validate_null(_S,'NULL',_Constr) ->
+ ok.
+
+%%------------
+%% This can be removed when the old parser is removed
+%% The function removes 'space' atoms from the list
+
+is_space_list([H],Acc) ->
+ lists:reverse([H|Acc]);
+is_space_list([H,space|T],Acc) ->
+ is_space_list(T,[H|Acc]);
+is_space_list([],Acc) ->
+ lists:reverse(Acc);
+is_space_list([H|T],Acc) ->
+ is_space_list(T,[H|Acc]).
+
+validate_objectidentifier(S,L,_) ->
+ case is_space_list(L,[]) of
+ NewL when list(NewL) ->
+ case validate_objectidentifier1(S,NewL) of
+ NewL2 when list(NewL2) ->
+ list_to_tuple(NewL2);
+ Other -> Other
+ end;
+ {error,_} ->
+ error({value, "illegal OBJECT IDENTIFIER", S})
+ end.
+
+validate_objectidentifier1(S, [Id|T]) when record(Id,'Externalvaluereference') ->
+ case catch get_referenced_type(S,Id) of
+ {_,V} when record(V,valuedef) ->
+ case check_value(S,V) of
+ #valuedef{type=#type{def='OBJECT IDENTIFIER'},
+ checked=true,value=Value} when tuple(Value) ->
+ validate_objectid(S, T, lists:reverse(tuple_to_list(Value)));
+ _ ->
+ error({value, "illegal OBJECT IDENTIFIER", S})
+ end;
+ _ ->
+ validate_objectid(S, [Id|T], [])
+ end;
+validate_objectidentifier1(S,V) ->
+ validate_objectid(S,V,[]).
+
+validate_objectid(_, [], Acc) ->
+ lists:reverse(Acc);
+validate_objectid(S, [Value|Vrest], Acc) when integer(Value) ->
+ validate_objectid(S, Vrest, [Value|Acc]);
+validate_objectid(S, [{'NamedNumber',_Name,Value}|Vrest], Acc)
+ when integer(Value) ->
+ validate_objectid(S, Vrest, [Value|Acc]);
+validate_objectid(S, [Id|Vrest], Acc)
+ when record(Id,'Externalvaluereference') ->
+ case catch get_referenced_type(S, Id) of
+ {_,V} when record(V,valuedef) ->
+ case check_value(S, V) of
+ #valuedef{checked=true,value=Value} when integer(Value) ->
+ validate_objectid(S, Vrest, [Value|Acc]);
+ _ ->
+ error({value, "illegal OBJECT IDENTIFIER", S})
+ end;
+ _ ->
+ case reserved_objectid(Id#'Externalvaluereference'.value, Acc) of
+ Value when integer(Value) ->
+ validate_objectid(S, Vrest, [Value|Acc]);
+ false ->
+ error({value, "illegal OBJECT IDENTIFIER", S})
+ end
+ end;
+validate_objectid(S, [{Atom,Value}],[]) when atom(Atom),integer(Value) ->
+ %% this case when an OBJECT IDENTIFIER value has been parsed as a
+ %% SEQUENCE value
+ Rec = #'Externalvaluereference'{module=S#state.mname,
+ value=Atom},
+ validate_objectidentifier1(S,[Rec,Value]);
+validate_objectid(S, [{Atom,EVRef}],[])
+ when atom(Atom),record(EVRef,'Externalvaluereference') ->
+ %% this case when an OBJECT IDENTIFIER value has been parsed as a
+ %% SEQUENCE value OTP-4354
+ Rec = #'Externalvaluereference'{module=S#state.mname,
+ value=Atom},
+ validate_objectidentifier1(S,[Rec,EVRef]);
+validate_objectid(S, _V, _Acc) ->
+ error({value, "illegal OBJECT IDENTIFIER",S}).
+
+
+%% ITU-T Rec. X.680 Annex B - D
+reserved_objectid('itu-t',[]) -> 0;
+reserved_objectid('ccitt',[]) -> 0;
+%% arcs below "itu-t"
+reserved_objectid('recommendation',[0]) -> 0;
+reserved_objectid('question',[0]) -> 1;
+reserved_objectid('administration',[0]) -> 2;
+reserved_objectid('network-operator',[0]) -> 3;
+reserved_objectid('identified-organization',[0]) -> 4;
+%% arcs below "recommendation"
+reserved_objectid('a',[0,0]) -> 1;
+reserved_objectid('b',[0,0]) -> 2;
+reserved_objectid('c',[0,0]) -> 3;
+reserved_objectid('d',[0,0]) -> 4;
+reserved_objectid('e',[0,0]) -> 5;
+reserved_objectid('f',[0,0]) -> 6;
+reserved_objectid('g',[0,0]) -> 7;
+reserved_objectid('h',[0,0]) -> 8;
+reserved_objectid('i',[0,0]) -> 9;
+reserved_objectid('j',[0,0]) -> 10;
+reserved_objectid('k',[0,0]) -> 11;
+reserved_objectid('l',[0,0]) -> 12;
+reserved_objectid('m',[0,0]) -> 13;
+reserved_objectid('n',[0,0]) -> 14;
+reserved_objectid('o',[0,0]) -> 15;
+reserved_objectid('p',[0,0]) -> 16;
+reserved_objectid('q',[0,0]) -> 17;
+reserved_objectid('r',[0,0]) -> 18;
+reserved_objectid('s',[0,0]) -> 19;
+reserved_objectid('t',[0,0]) -> 20;
+reserved_objectid('u',[0,0]) -> 21;
+reserved_objectid('v',[0,0]) -> 22;
+reserved_objectid('w',[0,0]) -> 23;
+reserved_objectid('x',[0,0]) -> 24;
+reserved_objectid('y',[0,0]) -> 25;
+reserved_objectid('z',[0,0]) -> 26;
+
+
+reserved_objectid(iso,[]) -> 1;
+%% arcs below "iso", note that number 1 is not used
+reserved_objectid('standard',[1]) -> 0;
+reserved_objectid('member-body',[1]) -> 2;
+reserved_objectid('identified-organization',[1]) -> 3;
+
+reserved_objectid('joint-iso-itu-t',[]) -> 2;
+reserved_objectid('joint-iso-ccitt',[]) -> 2;
+
+reserved_objectid(_,_) -> false.
+
+
+
+
+
+validate_objectdescriptor(_S,_Value,_Constr) ->
+ ok.
+
+validate_enumerated(S,Id,NamedNumberList,_Constr) when atom(Id) ->
+ case lists:keysearch(Id,1,NamedNumberList) of
+ {value,_} -> ok;
+ false -> error({value,"unknown ENUMERATED",S})
+ end;
+validate_enumerated(S,{identifier,_Pos,Id},NamedNumberList,_Constr) ->
+ case lists:keysearch(Id,1,NamedNumberList) of
+ {value,_} -> ok;
+ false -> error({value,"unknown ENUMERATED",S})
+ end;
+validate_enumerated(S,#'Externalvaluereference'{value=Id},
+ NamedNumberList,_Constr) ->
+ case lists:keysearch(Id,1,NamedNumberList) of
+ {value,_} -> ok;
+ false -> error({value,"unknown ENUMERATED",S})
+ end.
+
+validate_boolean(_S,_Value,_Constr) ->
+ ok.
+
+validate_octetstring(_S,_Value,_Constr) ->
+ ok.
+
+validate_restrictedstring(_S,_Value,_Def,_Constr) ->
+ ok.
+
+validate_sequence(S=#state{type=Vtype},Value,_Components,_Constr) ->
+ case Vtype of
+ #type{tag=[{tag,'UNIVERSAL',8,'IMPLICIT',32}]} ->
+ %% this is an 'EXTERNAL' (or INSTANCE OF)
+ case Value of
+ [{identification,_}|_RestVal] ->
+ to_EXTERNAL1990(S,Value);
+ _ ->
+ Value
+ end;
+ _ ->
+ Value
+ end.
+
+validate_sequenceof(_S,_Value,_Components,_Constr) ->
+ ok.
+
+validate_choice(_S,_Value,_Components,_Constr) ->
+ ok.
+
+validate_set(_S,_Value,_Components,_Constr) ->
+ ok.
+
+validate_setof(_S,_Value,_Components,_Constr) ->
+ ok.
+
+to_EXTERNAL1990(S,[{identification,{'CHOICE',{syntax,Stx}}}|Rest]) ->
+ to_EXTERNAL1990(S,Rest,[{'direct-reference',Stx}]);
+to_EXTERNAL1990(S,[{identification,{'CHOICE',{'presentation-context-id',I}}}|Rest]) ->
+ to_EXTERNAL1990(S,Rest,[{'indirect-reference',I}]);
+to_EXTERNAL1990(S,[{identification,{'CHOICE',{'context-negotiation',[{_,PCid},{_,TrStx}]}}}|Rest]) ->
+ to_EXTERNAL1990(S,Rest,[{'indirect-reference',PCid},{'direct-reference',TrStx}]);
+to_EXTERNAL1990(S,_) ->
+ error({value,"illegal value in EXTERNAL type",S}).
+
+to_EXTERNAL1990(S,[V={'data-value-descriptor',_}|Rest],Acc) ->
+ to_EXTERNAL1990(S,Rest,[V|Acc]);
+to_EXTERNAL1990(_S,[{'data-value',Val}],Acc) ->
+ Encoding = {encoding,{'CHOICE',{'octet-aligned',Val}}},
+ lists:reverse([Encoding|Acc]);
+to_EXTERNAL1990(S,_,_) ->
+ error({value,"illegal value in EXTERNAL type",S}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Functions to normalize the default values of SEQUENCE
+%% and SET components into Erlang valid format
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+normalize_value(_,_,mandatory,_) ->
+ mandatory;
+normalize_value(_,_,'OPTIONAL',_) ->
+ 'OPTIONAL';
+normalize_value(S,Type,{'DEFAULT',Value},NameList) ->
+ case catch get_canonic_type(S,Type,NameList) of
+ {'BOOLEAN',CType,_} ->
+ normalize_boolean(S,Value,CType);
+ {'INTEGER',CType,_} ->
+ normalize_integer(S,Value,CType);
+ {'BIT STRING',CType,_} ->
+ normalize_bitstring(S,Value,CType);
+ {'OCTET STRING',CType,_} ->
+ normalize_octetstring(S,Value,CType);
+ {'NULL',_CType,_} ->
+ %%normalize_null(Value);
+ 'NULL';
+ {'OBJECT IDENTIFIER',_,_} ->
+ normalize_objectidentifier(S,Value);
+ {'ObjectDescriptor',_,_} ->
+ normalize_objectdescriptor(Value);
+ {'REAL',_,_} ->
+ normalize_real(Value);
+ {'ENUMERATED',CType,_} ->
+ normalize_enumerated(Value,CType);
+ {'CHOICE',CType,NewNameList} ->
+ normalize_choice(S,Value,CType,NewNameList);
+ {'SEQUENCE',CType,NewNameList} ->
+ normalize_sequence(S,Value,CType,NewNameList);
+ {'SEQUENCE OF',CType,NewNameList} ->
+ normalize_seqof(S,Value,CType,NewNameList);
+ {'SET',CType,NewNameList} ->
+ normalize_set(S,Value,CType,NewNameList);
+ {'SET OF',CType,NewNameList} ->
+ normalize_setof(S,Value,CType,NewNameList);
+ {restrictedstring,CType,_} ->
+ normalize_restrictedstring(S,Value,CType);
+ _ ->
+ io:format("WARNING: could not check default value ~p~n",[Value]),
+ Value
+ end;
+normalize_value(S,Type,Val,NameList) ->
+ normalize_value(S,Type,{'DEFAULT',Val},NameList).
+
+normalize_boolean(S,{Name,Bool},CType) when atom(Name) ->
+ normalize_boolean(S,Bool,CType);
+normalize_boolean(_,true,_) ->
+ true;
+normalize_boolean(_,false,_) ->
+ false;
+normalize_boolean(S,Bool=#'Externalvaluereference'{},CType) ->
+ get_normalized_value(S,Bool,CType,fun normalize_boolean/3,[]);
+normalize_boolean(_,Other,_) ->
+ throw({error,{asn1,{'invalid default value',Other}}}).
+
+normalize_integer(_S,Int,_) when integer(Int) ->
+ Int;
+normalize_integer(_S,{Name,Int},_) when atom(Name),integer(Int) ->
+ Int;
+normalize_integer(S,{Name,Int=#'Externalvaluereference'{}},
+ Type) when atom(Name) ->
+ normalize_integer(S,Int,Type);
+normalize_integer(S,Int=#'Externalvaluereference'{value=Name},Type) ->
+ case Type of
+ NNL when list(NNL) ->
+ case lists:keysearch(Name,1,NNL) of
+ {value,{Name,Val}} ->
+ Val;
+ false ->
+ get_normalized_value(S,Int,Type,
+ fun normalize_integer/3,[])
+ end;
+ _ ->
+ get_normalized_value(S,Int,Type,fun normalize_integer/3,[])
+ end;
+normalize_integer(_,Int,_) ->
+ exit({'Unknown INTEGER value',Int}).
+
+normalize_bitstring(S,Value,Type)->
+ %% There are four different Erlang formats of BIT STRING:
+ %% 1 - a list of ones and zeros.
+ %% 2 - a list of atoms.
+ %% 3 - as an integer, for instance in hexadecimal form.
+ %% 4 - as a tuple {Unused, Binary} where Unused is an integer
+ %% and tells how many bits of Binary are unused.
+ %%
+ %% normalize_bitstring/3 transforms Value according to:
+ %% A to 3,
+ %% B to 1,
+ %% C to 1 or 3
+ %% D to 2,
+ %% Value can be on format:
+ %% A - {hstring, String}, where String is a hexadecimal string.
+ %% B - {bstring, String}, where String is a string on bit format
+ %% C - #'Externalvaluereference'{value=V}, where V is a defined value
+ %% D - list of #'Externalvaluereference', where each value component
+ %% is an identifier corresponing to NamedBits in Type.
+ case Value of
+ {hstring,String} when list(String) ->
+ hstring_to_int(String);
+ {bstring,String} when list(String) ->
+ bstring_to_bitlist(String);
+ Rec when record(Rec,'Externalvaluereference') ->
+ get_normalized_value(S,Value,Type,
+ fun normalize_bitstring/3,[]);
+ RecList when list(RecList) ->
+ case Type of
+ NBL when list(NBL) ->
+ F = fun(#'Externalvaluereference'{value=Name}) ->
+ case lists:keysearch(Name,1,NBL) of
+ {value,{Name,_}} ->
+ Name;
+ Other ->
+ throw({error,Other})
+ end;
+ (Other) ->
+ throw({error,Other})
+ end,
+ case catch lists:map(F,RecList) of
+ {error,Reason} ->
+ io:format("WARNING: default value not "
+ "compatible with type definition ~p~n",
+ [Reason]),
+ Value;
+ NewList ->
+ NewList
+ end;
+ _ ->
+ io:format("WARNING: default value not "
+ "compatible with type definition ~p~n",
+ [RecList]),
+ Value
+ end;
+ {Name,String} when atom(Name) ->
+ normalize_bitstring(S,String,Type);
+ Other ->
+ io:format("WARNING: illegal default value ~p~n",[Other]),
+ Value
+ end.
+
+hstring_to_int(L) when list(L) ->
+ hstring_to_int(L,0).
+hstring_to_int([H|T],Acc) when H >= $A, H =< $F ->
+ hstring_to_int(T,(Acc bsl 4) + (H - $A + 10) ) ;
+hstring_to_int([H|T],Acc) when H >= $0, H =< $9 ->
+ hstring_to_int(T,(Acc bsl 4) + (H - $0));
+hstring_to_int([],Acc) ->
+ Acc.
+
+bstring_to_bitlist([H|T]) when H == $0; H == $1 ->
+ [H - $0 | bstring_to_bitlist(T)];
+bstring_to_bitlist([]) ->
+ [].
+
+%% normalize_octetstring/1 changes representation of input Value to a
+%% list of octets.
+%% Format of Value is one of:
+%% {bstring,String} each element in String corresponds to one bit in an octet
+%% {hstring,String} each element in String corresponds to one byte in an octet
+%% #'Externalvaluereference'
+normalize_octetstring(S,Value,CType) ->
+ case Value of
+ {bstring,String} ->
+ bstring_to_octetlist(String);
+ {hstring,String} ->
+ hstring_to_octetlist(String);
+ Rec when record(Rec,'Externalvaluereference') ->
+ get_normalized_value(S,Value,CType,
+ fun normalize_octetstring/3,[]);
+ {Name,String} when atom(Name) ->
+ normalize_octetstring(S,String,CType);
+ List when list(List) ->
+ %% check if list elements are valid octet values
+ lists:map(fun([])-> ok;
+ (H)when H > 255->
+ io:format("WARNING: not legal octet value ~p in OCTET STRING, ~p~n",[H,List]);
+ (_)-> ok
+ end, List),
+ List;
+ Other ->
+ io:format("WARNING: unknown default value ~p~n",[Other]),
+ Value
+ end.
+
+
+bstring_to_octetlist([]) ->
+ [];
+bstring_to_octetlist([H|T]) when H == $0 ; H == $1 ->
+ bstring_to_octetlist(T,6,[(H - $0) bsl 7]).
+bstring_to_octetlist([H|T],0,[Hacc|Tacc]) when H == $0; H == $1 ->
+ bstring_to_octetlist(T, 7, [0,Hacc + (H -$0)| Tacc]);
+bstring_to_octetlist([H|T],BSL,[Hacc|Tacc]) when H == $0; H == $1 ->
+ bstring_to_octetlist(T, BSL-1, [Hacc + ((H - $0) bsl BSL)| Tacc]);
+bstring_to_octetlist([],7,[0|Acc]) ->
+ lists:reverse(Acc);
+bstring_to_octetlist([],_,Acc) ->
+ lists:reverse(Acc).
+
+hstring_to_octetlist([]) ->
+ [];
+hstring_to_octetlist(L) ->
+ hstring_to_octetlist(L,4,[]).
+hstring_to_octetlist([H|T],0,[Hacc|Tacc]) when H >= $A, H =< $F ->
+ hstring_to_octetlist(T,4,[Hacc + (H - $A + 10)|Tacc]);
+hstring_to_octetlist([H|T],BSL,Acc) when H >= $A, H =< $F ->
+ hstring_to_octetlist(T,0,[(H - $A + 10) bsl BSL|Acc]);
+hstring_to_octetlist([H|T],0,[Hacc|Tacc]) when H >= $0; H =< $9 ->
+ hstring_to_octetlist(T,4,[Hacc + (H - $0)|Tacc]);
+hstring_to_octetlist([H|T],BSL,Acc) when H >= $0; H =< $9 ->
+ hstring_to_octetlist(T,0,[(H - $0) bsl BSL|Acc]);
+hstring_to_octetlist([],_,Acc) ->
+ lists:reverse(Acc).
+
+normalize_objectidentifier(S,Value) ->
+ validate_objectidentifier(S,Value,[]).
+
+normalize_objectdescriptor(Value) ->
+ Value.
+
+normalize_real(Value) ->
+ Value.
+
+normalize_enumerated(#'Externalvaluereference'{value=V},CType)
+ when list(CType) ->
+ normalize_enumerated2(V,CType);
+normalize_enumerated(Value,CType) when atom(Value),list(CType) ->
+ normalize_enumerated2(Value,CType);
+normalize_enumerated({Name,EnumV},CType) when atom(Name) ->
+ normalize_enumerated(EnumV,CType);
+normalize_enumerated(Value,{CType1,CType2}) when list(CType1), list(CType2)->
+ normalize_enumerated(Value,CType1++CType2);
+normalize_enumerated(V,CType) ->
+ io:format("WARNING: Enumerated unknown type ~p~n",[CType]),
+ V.
+normalize_enumerated2(V,Enum) ->
+ case lists:keysearch(V,1,Enum) of
+ {value,{Val,_}} -> Val;
+ _ ->
+ io:format("WARNING: Enumerated value is not correct ~p~n",[V]),
+ V
+ end.
+
+normalize_choice(S,{'CHOICE',{C,V}},CType,NameList) when atom(C) ->
+ Value =
+ case V of
+ Rec when record(Rec,'Externalvaluereference') ->
+ get_normalized_value(S,V,CType,
+ fun normalize_choice/4,
+ [NameList]);
+ _ -> V
+ end,
+ case catch lists:keysearch(C,#'ComponentType'.name,CType) of
+ {value,#'ComponentType'{typespec=CT,name=Name}} ->
+ {C,normalize_value(S,CT,{'DEFAULT',Value},
+ [Name|NameList])};
+ Other ->
+ io:format("WARNING: Wrong format of type/value ~p/~p~n",
+ [Other,Value]),
+ {C,Value}
+ end;
+normalize_choice(S,{'DEFAULT',ValueList},CType,NameList) ->
+ lists:map(fun(X)-> normalize_choice(S,X,CType,NameList) end, ValueList);
+normalize_choice(S,Val=#'Externalvaluereference'{},CType,NameList) ->
+ {_,#valuedef{value=V}}=get_referenced_type(S,Val),
+ normalize_choice(S,{'CHOICE',V},CType,NameList);
+% get_normalized_value(S,Val,CType,fun normalize_choice/4,[NameList]);
+normalize_choice(S,{Name,ChoiceVal},CType,NameList)
+ when atom(Name) ->
+ normalize_choice(S,ChoiceVal,CType,NameList).
+
+normalize_sequence(S,{Name,Value},Components,NameList)
+ when atom(Name),list(Value) ->
+ normalize_sequence(S,Value,Components,NameList);
+normalize_sequence(S,Value,Components,NameList) ->
+ normalized_record('SEQUENCE',S,Value,Components,NameList).
+
+normalize_set(S,{Name,Value},Components,NameList)
+ when atom(Name),list(Value) ->
+ normalized_record('SET',S,Value,Components,NameList);
+normalize_set(S,Value,Components,NameList) ->
+ normalized_record('SET',S,Value,Components,NameList).
+
+normalized_record(SorS,S,Value,Components,NameList) ->
+ NewName = list_to_atom(asn1ct_gen:list2name(NameList)),
+ NoComps = length(Components),
+ case normalize_seq_or_set(SorS,S,Value,Components,NameList,[]) of
+ ListOfVals when length(ListOfVals) == NoComps ->
+ list_to_tuple([NewName|ListOfVals]);
+ _ ->
+ error({type,{illegal,default,value,Value},S})
+ end.
+
+normalize_seq_or_set(SorS,S,[{Cname,V}|Vs],
+ [#'ComponentType'{name=Cname,typespec=TS}|Cs],
+ NameList,Acc) ->
+ NewNameList =
+ case TS#type.def of
+ #'Externaltypereference'{type=TName} ->
+ [TName];
+ _ -> [Cname|NameList]
+ end,
+ NVal = normalize_value(S,TS,{'DEFAULT',V},NewNameList),
+ normalize_seq_or_set(SorS,S,Vs,Cs,NameList,[NVal|Acc]);
+normalize_seq_or_set(SorS,S,Values=[{_Cname1,_V}|_Vs],
+ [#'ComponentType'{prop='OPTIONAL'}|Cs],
+ NameList,Acc) ->
+ normalize_seq_or_set(SorS,S,Values,Cs,NameList,[asn1_NOVALUE|Acc]);
+normalize_seq_or_set(SorS,S,Values=[{_Cname1,_V}|_Vs],
+ [#'ComponentType'{name=Cname2,typespec=TS,
+ prop={'DEFAULT',Value}}|Cs],
+ NameList,Acc) ->
+ NewNameList =
+ case TS#type.def of
+ #'Externaltypereference'{type=TName} ->
+ [TName];
+ _ -> [Cname2|NameList]
+ end,
+ NVal = normalize_value(S,TS,{'DEFAULT',Value},NewNameList),
+ normalize_seq_or_set(SorS,S,Values,Cs,NameList,[NVal|Acc]);
+normalize_seq_or_set(_SorS,_S,[],[],_,Acc) ->
+ lists:reverse(Acc);
+%% If default value is {} ComponentTypes in SEQUENCE are marked DEFAULT
+%% or OPTIONAL (or the type is defined SEQUENCE{}, which is handled by
+%% the previous case).
+normalize_seq_or_set(SorS,S,[],
+ [#'ComponentType'{name=Name,typespec=TS,
+ prop={'DEFAULT',Value}}|Cs],
+ NameList,Acc) ->
+ NewNameList =
+ case TS#type.def of
+ #'Externaltypereference'{type=TName} ->
+ [TName];
+ _ -> [Name|NameList]
+ end,
+ NVal = normalize_value(S,TS,{'DEFAULT',Value},NewNameList),
+ normalize_seq_or_set(SorS,S,[],Cs,NameList,[NVal|Acc]);
+normalize_seq_or_set(SorS,S,[],[#'ComponentType'{prop='OPTIONAL'}|Cs],
+ NameList,Acc) ->
+ normalize_seq_or_set(SorS,S,[],Cs,NameList,[asn1_NOVALUE|Acc]);
+normalize_seq_or_set(SorS,S,Value=#'Externalvaluereference'{},
+ Cs,NameList,Acc) ->
+ get_normalized_value(S,Value,Cs,fun normalize_seq_or_set/6,
+ [SorS,NameList,Acc]);
+normalize_seq_or_set(_SorS,S,V,_,_,_) ->
+ error({type,{illegal,default,value,V},S}).
+
+normalize_seqof(S,Value,Type,NameList) ->
+ normalize_s_of('SEQUENCE OF',S,Value,Type,NameList).
+
+normalize_setof(S,Value,Type,NameList) ->
+ normalize_s_of('SET OF',S,Value,Type,NameList).
+
+normalize_s_of(SorS,S,Value,Type,NameList) when list(Value) ->
+ DefValueList = lists:map(fun(X) -> {'DEFAULT',X} end,Value),
+ Suffix = asn1ct_gen:constructed_suffix(SorS,Type),
+ Def = Type#type.def,
+ InnerType = asn1ct_gen:get_inner(Def),
+ WhatKind = asn1ct_gen:type(InnerType),
+ NewNameList =
+ case WhatKind of
+ {constructed,bif} ->
+ [Suffix|NameList];
+ #'Externaltypereference'{type=Name} ->
+ [Name];
+ _ -> []
+ end,
+ NormFun = fun (X) -> normalize_value(S,Type,X,
+ NewNameList) end,
+ case catch lists:map(NormFun, DefValueList) of
+ List when list(List) ->
+ List;
+ _ ->
+ io:format("WARNING: ~p could not handle value ~p~n",
+ [SorS,Value]),
+ Value
+ end;
+normalize_s_of(SorS,S,Value,Type,NameList)
+ when record(Value,'Externalvaluereference') ->
+ get_normalized_value(S,Value,Type,fun normalize_s_of/5,
+ [SorS,NameList]).
+% case catch get_referenced_type(S,Value) of
+% {_,#valuedef{value=V}} ->
+% normalize_s_of(SorS,S,V,Type);
+% {error,Reason} ->
+% io:format("WARNING: ~p could not handle value ~p~n",
+% [SorS,Value]),
+% Value;
+% {_,NewVal} ->
+% normalize_s_of(SorS,S,NewVal,Type);
+% _ ->
+% io:format("WARNING: ~p could not handle value ~p~n",
+% [SorS,Value]),
+% Value
+% end.
+
+
+%% normalize_restrictedstring handles all format of restricted strings.
+%% tuple case
+normalize_restrictedstring(_S,[Int1,Int2],_) when integer(Int1),integer(Int2) ->
+ {Int1,Int2};
+%% quadruple case
+normalize_restrictedstring(_S,[Int1,Int2,Int3,Int4],_) when integer(Int1),
+ integer(Int2),
+ integer(Int3),
+ integer(Int4) ->
+ {Int1,Int2,Int3,Int4};
+%% character string list case
+normalize_restrictedstring(S,[H|T],CType) when list(H);tuple(H) ->
+ [normalize_restrictedstring(S,H,CType)|normalize_restrictedstring(S,T,CType)];
+%% character sting case
+normalize_restrictedstring(_S,CString,_) when list(CString) ->
+ Fun =
+ fun(X) ->
+ if
+ $X =< 255, $X >= 0 ->
+ ok;
+ true ->
+ io:format("WARNING: illegal character in string"
+ " ~p~n",[X])
+ end
+ end,
+ lists:foreach(Fun,CString),
+ CString;
+%% definedvalue case or argument in a parameterized type
+normalize_restrictedstring(S,ERef,CType) when record(ERef,'Externalvaluereference') ->
+ get_normalized_value(S,ERef,CType,
+ fun normalize_restrictedstring/3,[]);
+%%
+normalize_restrictedstring(S,{Name,Val},CType) when atom(Name) ->
+ normalize_restrictedstring(S,Val,CType).
+
+
+get_normalized_value(S,Val,Type,Func,AddArg) ->
+ case catch get_referenced_type(S,Val) of
+ {_,#valuedef{type=_T,value=V}} ->
+ %% should check that Type and T equals
+ call_Func(S,V,Type,Func,AddArg);
+ {error,_} ->
+ io:format("WARNING: default value not "
+ "comparable ~p~n",[Val]),
+ Val;
+ {_,NewVal} ->
+ call_Func(S,NewVal,Type,Func,AddArg);
+ _ ->
+ io:format("WARNING: default value not "
+ "comparable ~p~n",[Val]),
+ Val
+ end.
+
+call_Func(S,Val,Type,Func,ArgList) ->
+ case ArgList of
+ [] ->
+ Func(S,Val,Type);
+ [LastArg] ->
+ Func(S,Val,Type,LastArg);
+ [Arg1,LastArg1] ->
+ Func(Arg1,S,Val,Type,LastArg1);
+ [Arg1,LastArg1,LastArg2] ->
+ Func(Arg1,S,Val,Type,LastArg1,LastArg2)
+ end.
+
+
+get_canonic_type(S,Type,NameList) ->
+ {InnerType,NewType,NewNameList} =
+ case Type#type.def of
+ Name when atom(Name) ->
+ {Name,Type,NameList};
+ Ref when record(Ref,'Externaltypereference') ->
+ {_,#typedef{name=Name,typespec=RefedType}} =
+ get_referenced_type(S,Ref),
+ get_canonic_type(S,RefedType,[Name]);
+ {Name,T} when atom(Name) ->
+ {Name,T,NameList};
+ Seq when record(Seq,'SEQUENCE') ->
+ {'SEQUENCE',Seq#'SEQUENCE'.components,NameList};
+ Set when record(Set,'SET') ->
+ {'SET',Set#'SET'.components,NameList}
+ end,
+ {asn1ct_gen:unify_if_string(InnerType),NewType,NewNameList}.
+
+
+
+check_ptype(_S,Type,Ts) when record(Ts,type) ->
+ %Tag = Ts#type.tag,
+ %Constr = Ts#type.constraint,
+ Def = Ts#type.def,
+ NewDef=
+ case Def of
+ Seq when record(Seq,'SEQUENCE') ->
+ #newt{type=Seq#'SEQUENCE'{pname=Type#ptypedef.name}};
+ Set when record(Set,'SET') ->
+ #newt{type=Set#'SET'{pname=Type#ptypedef.name}};
+ _Other ->
+ #newt{}
+ end,
+ Ts2 = case NewDef of
+ #newt{type=unchanged} ->
+ Ts;
+ #newt{type=TDef}->
+ Ts#type{def=TDef}
+ end,
+ Ts2.
+
+
+% check_type(S,Type,ObjSpec={{objectclassname,_},_}) ->
+% check_class(S,ObjSpec);
+check_type(_S,Type,Ts) when record(Type,typedef),
+ (Type#typedef.checked==true) ->
+ Ts;
+check_type(_S,Type,Ts) when record(Type,typedef),
+ (Type#typedef.checked==idle) -> % the check is going on
+ Ts;
+check_type(S=#state{recordtopname=TopName},Type,Ts) when record(Ts,type) ->
+ {Def,Tag,Constr} =
+ case match_parameters(Ts#type.def,S#state.parameters) of
+ #type{constraint=_Ctmp,def=Dtmp} ->
+ {Dtmp,Ts#type.tag,Ts#type.constraint};
+ Dtmp ->
+ {Dtmp,Ts#type.tag,Ts#type.constraint}
+ end,
+ TempNewDef = #newt{type=Def,tag=Tag,constraint=Constr},
+ TestFun =
+ fun(Tref) ->
+ {_,MaybeChoice} = get_referenced_type(S,Tref),
+ case catch((MaybeChoice#typedef.typespec)#type.def) of
+ {'CHOICE',_} ->
+ maybe_illicit_implicit_tag(choice,Tag);
+ 'ANY' ->
+ maybe_illicit_implicit_tag(open_type,Tag);
+ 'ANY DEFINED BY' ->
+ maybe_illicit_implicit_tag(open_type,Tag);
+ 'ASN1_OPEN_TYPE' ->
+ maybe_illicit_implicit_tag(open_type,Tag);
+ _ ->
+ Tag
+ end
+ end,
+ NewDef=
+ case Def of
+ Ext when record(Ext,'Externaltypereference') ->
+ {_,RefTypeDef} = get_referenced_type(S,Ext),
+% case RefTypeDef of
+% Class when record(Class,classdef) ->
+% throw({asn1_class,Class});
+% _ -> ok
+% end,
+ case is_class(S,RefTypeDef) of
+ true -> throw({asn1_class,RefTypeDef});
+ _ -> ok
+ end,
+ Ct = TestFun(Ext),
+ RefType =
+%case S#state.erule of
+% ber_bin_v2 ->
+ case RefTypeDef#typedef.checked of
+ true ->
+ RefTypeDef#typedef.typespec;
+ _ ->
+ NewRefTypeDef1 = RefTypeDef#typedef{checked=idle},
+ asn1_db:dbput(S#state.mname,
+ NewRefTypeDef1#typedef.name,NewRefTypeDef1),
+ RefType1 =
+ check_type(S,RefTypeDef,RefTypeDef#typedef.typespec),
+ NewRefTypeDef2 =
+ RefTypeDef#typedef{checked=true,typespec = RefType1},
+ asn1_db:dbput(S#state.mname,
+ NewRefTypeDef2#typedef.name,NewRefTypeDef2),
+ %% update the type and mark as checked
+ RefType1
+ end,
+% _ -> RefTypeDef#typedef.typespec
+% end,
+
+ case asn1ct_gen:prim_bif(asn1ct_gen:get_inner(RefType#type.def)) of
+ true ->
+ %% Here we expand to a built in type and inline it
+ TempNewDef#newt{
+ type=
+ RefType#type.def,
+ tag=
+ merge_tags(Ct,RefType#type.tag),
+ constraint=
+ merge_constraints(check_constraints(S,Constr),
+ RefType#type.constraint)};
+ _ ->
+ %% Here we only expand the tags and keep the ext ref
+
+ TempNewDef#newt{
+ type=
+ check_externaltypereference(S,Ext),
+ tag =
+ case S#state.erule of
+ ber_bin_v2 ->
+ merge_tags(Ct,RefType#type.tag);
+ _ ->
+ Ct
+ end
+ }
+ end;
+ 'ANY' ->
+ Ct=maybe_illicit_implicit_tag(open_type,Tag),
+ TempNewDef#newt{type='ASN1_OPEN_TYPE',tag=Ct};
+ {'ANY_DEFINED_BY',_} ->
+ Ct=maybe_illicit_implicit_tag(open_type,Tag),
+ TempNewDef#newt{type='ASN1_OPEN_TYPE',tag=Ct};
+ 'INTEGER' ->
+ check_integer(S,[],Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_INTEGER))};
+
+ {'INTEGER',NamedNumberList} ->
+ TempNewDef#newt{type={'INTEGER',check_integer(S,NamedNumberList,Constr)},
+ tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_INTEGER))};
+ {'BIT STRING',NamedNumberList} ->
+ NewL = check_bitstring(S,NamedNumberList,Constr),
+%% erlang:display({asn1ct_check,NamedNumberList,NewL}),
+ TempNewDef#newt{type={'BIT STRING',NewL},
+ tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_BIT_STRING))};
+ 'NULL' ->
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_NULL))};
+ 'OBJECT IDENTIFIER' ->
+ check_objectidentifier(S,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_OBJECT_IDENTIFIER))};
+ 'ObjectDescriptor' ->
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_OBJECT_DESCRIPTOR))};
+ 'EXTERNAL' ->
+%% AssociatedType = asn1_db:dbget(S#state.mname,'EXTERNAL'),
+%% #newt{type=check_type(S,Type,AssociatedType)};
+ put(external,unchecked),
+ TempNewDef#newt{type=
+ #'Externaltypereference'{module=S#state.mname,
+ type='EXTERNAL'},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_EXTERNAL))};
+ {'INSTANCE OF',DefinedObjectClass,Constraint} ->
+ %% check that DefinedObjectClass is of TYPE-IDENTIFIER class
+ %% If Constraint is empty make it the general INSTANCE OF type
+ %% If Constraint is not empty make an inlined type
+ %% convert INSTANCE OF to the associated type
+ IOFDef=check_instance_of(S,DefinedObjectClass,Constraint),
+ TempNewDef#newt{type=IOFDef,
+ tag=merge_tags(Tag,?TAG_CONSTRUCTED(?N_INSTANCE_OF))};
+ {'ENUMERATED',NamedNumberList} ->
+ TempNewDef#newt{type=
+ {'ENUMERATED',
+ check_enumerated(S,NamedNumberList,Constr)},
+ tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_ENUMERATED))};
+ 'EMBEDDED PDV' ->
+% AssociatedType = asn1_db:dbget(S#state.mname,'EMBEDDED PDV'),
+% CheckedType = check_type(S,Type,
+% AssociatedType#typedef.typespec),
+ put(embedded_pdv,unchecked),
+ TempNewDef#newt{type=
+ #'Externaltypereference'{module=S#state.mname,
+ type='EMBEDDED PDV'},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_EMBEDDED_PDV))};
+ 'BOOLEAN'->
+ check_boolean(S,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_BOOLEAN))};
+ 'OCTET STRING' ->
+ check_octetstring(S,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_OCTET_STRING))};
+ 'NumericString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_NumericString))};
+ 'TeletexString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_TeletexString))};
+ 'VideotexString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_VideotexString))};
+ 'UTCTime' ->
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_UTCTime))};
+ 'GeneralizedTime' ->
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_GeneralizedTime))};
+ 'GraphicString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_GraphicString))};
+ 'VisibleString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_VisibleString))};
+ 'GeneralString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_GeneralString))};
+ 'PrintableString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_PrintableString))};
+ 'IA5String' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_IA5String))};
+ 'BMPString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_BMPString))};
+ 'UniversalString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_UniversalString))};
+ 'CHARACTER STRING' ->
+% AssociatedType = asn1_db:dbget(S#state.mname,
+% 'CHARACTER STRING'),
+% CheckedType = check_type(S,Type,
+% AssociatedType#typedef.typespec),
+ put(character_string,unchecked),
+ TempNewDef#newt{type=
+ #'Externaltypereference'{module=S#state.mname,
+ type='CHARACTER STRING'},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_CHARACTER_STRING))};
+ Seq when record(Seq,'SEQUENCE') ->
+ RecordName =
+ case TopName of
+ [] ->
+ [Type#typedef.name];
+ _ ->
+ TopName
+ end,
+ {TableCInf,Components} =
+ check_sequence(S#state{recordtopname=
+ RecordName},
+ Type,Seq#'SEQUENCE'.components),
+ TempNewDef#newt{type=Seq#'SEQUENCE'{tablecinf=TableCInf,
+ components=Components},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_SEQUENCE))};
+ {'SEQUENCE OF',Components} ->
+ TempNewDef#newt{type={'SEQUENCE OF',check_sequenceof(S,Type,Components)},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_SEQUENCE))};
+ {'CHOICE',Components} ->
+ Ct = maybe_illicit_implicit_tag(choice,Tag),
+ TempNewDef#newt{type={'CHOICE',check_choice(S,Type,Components)},tag=Ct};
+ Set when record(Set,'SET') ->
+ RecordName=
+ case TopName of
+ [] ->
+ [Type#typedef.name];
+ _ ->
+ TopName
+ end,
+ {Sorted,TableCInf,Components} =
+ check_set(S#state{recordtopname=RecordName},
+ Type,Set#'SET'.components),
+ TempNewDef#newt{type=Set#'SET'{sorted=Sorted,
+ tablecinf=TableCInf,
+ components=Components},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_SET))};
+ {'SET OF',Components} ->
+ TempNewDef#newt{type={'SET OF',check_setof(S,Type,Components)},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_SET))};
+ %% This is a temporary hack until the full Information Obj Spec
+ %% in X.681 is supported
+ {{typereference,_,'TYPE-IDENTIFIER'},[{typefieldreference,_,'Type'}]} ->
+ Ct=maybe_illicit_implicit_tag(open_type,Tag),
+ TempNewDef#newt{type='ASN1_OPEN_TYPE',tag=Ct};
+
+ {#'Externaltypereference'{type='TYPE-IDENTIFIER'},
+ [{typefieldreference,_,'Type'}]} ->
+ Ct=maybe_illicit_implicit_tag(open_type,Tag),
+ TempNewDef#newt{type='ASN1_OPEN_TYPE',tag=Ct};
+
+ {pt,Ptype,ParaList} ->
+ %% Ptype might be a parameterized - type, object set or
+ %% value set. If it isn't a parameterized type notify the
+ %% calling function.
+ {_,Ptypedef} = get_referenced_type(S,Ptype),
+ notify_if_not_ptype(S,Ptypedef),
+ NewParaList = [match_parameters(TmpParam,S#state.parameters)||
+ TmpParam <- ParaList],
+ Instance = instantiate_ptype(S,Ptypedef,NewParaList),
+ TempNewDef#newt{type=Instance#type.def,
+ tag=merge_tags(Tag,Instance#type.tag),
+ constraint=Instance#type.constraint,
+ inlined=yes};
+
+% {ClRef,FieldRefList} when record(ClRef,'Externaltypereference') ->
+ OCFT=#'ObjectClassFieldType'{class=ClRef} ->
+ %% this case occures in a SEQUENCE when
+ %% the type of the component is a ObjectClassFieldType
+ ClassSpec = check_class(S,ClRef),
+ NewTypeDef = maybe_open_type(S,ClassSpec,OCFT,Constr),
+ InnerTag = get_innertag(S,NewTypeDef),
+ MergedTag = merge_tags(Tag,InnerTag),
+ Ct =
+ case is_open_type(NewTypeDef) of
+ true ->
+ maybe_illicit_implicit_tag(open_type,MergedTag);
+ _ ->
+ MergedTag
+ end,
+ TempNewDef#newt{type=NewTypeDef,tag=Ct};
+ {valueset,Vtype} ->
+ TempNewDef#newt{type={valueset,check_type(S,Type,Vtype)}};
+ Other ->
+ exit({'cant check' ,Other})
+ end,
+ Ts2 = case NewDef of
+ #newt{type=unchanged} ->
+ Ts#type{def=Def};
+ #newt{type=TDef}->
+ Ts#type{def=TDef}
+ end,
+ NewTag = case NewDef of
+ #newt{tag=unchanged} ->
+ Tag;
+ #newt{tag=TT} ->
+ TT
+ end,
+ T3 = Ts2#type{tag = lists:map(fun(TempTag = #tag{type={default,TTx}}) ->
+ TempTag#tag{type=TTx};
+ (Else) -> Else end, NewTag)},
+ T4 = case NewDef of
+ #newt{constraint=unchanged} ->
+ T3#type{constraint=Constr};
+ #newt{constraint=NewConstr} ->
+ T3#type{constraint=NewConstr}
+ end,
+ T5 = T4#type{inlined=NewDef#newt.inlined},
+ T5#type{constraint=check_constraints(S,T5#type.constraint)}.
+
+
+get_innertag(_S,#'ObjectClassFieldType'{type=Type}) ->
+ case Type of
+ #type{tag=Tag} -> Tag;
+ {fixedtypevaluefield,_,#type{tag=Tag}} -> Tag;
+ {TypeFieldName,_} when atom(TypeFieldName) -> [];
+ _ -> []
+ end;
+get_innertag(_S,_) ->
+ [].
+
+is_class(_S,#classdef{}) ->
+ true;
+is_class(S,#typedef{typespec=#type{def=Eref}})
+ when record(Eref,'Externaltypereference')->
+ {_,NextDef} = get_referenced_type(S,Eref),
+ is_class(S,NextDef);
+is_class(_,_) ->
+ false.
+
+get_class_def(_S,CD=#classdef{}) ->
+ CD;
+get_class_def(S,#typedef{typespec=#type{def=Eref}})
+ when record(Eref,'Externaltypereference') ->
+ {_,NextDef} = get_referenced_type(S,Eref),
+ get_class_def(S,NextDef).
+
+maybe_illicit_implicit_tag(Kind,Tag) ->
+ case Tag of
+ [#tag{type='IMPLICIT'}|_T] ->
+ throw({error,{asn1,{implicit_tag_before,Kind}}});
+ [ChTag = #tag{type={default,_}}|T] ->
+ case Kind of
+ open_type ->
+ [ChTag#tag{type='EXPLICIT',form=32}|T]; %X.680 30.6c, X.690 8.14.2
+ choice ->
+ [ChTag#tag{type='EXPLICIT',form=32}|T] % X.680 28.6 c, 30.6c
+ end;
+ _ ->
+ Tag % unchanged
+ end.
+
+%% maybe_open_type/2 -> {ClassSpec,FieldRefList} | 'ASN1_OPEN_TYPE'
+%% if the FieldRefList points out a typefield and the class don't have
+%% any UNIQUE field, so that a component relation constraint cannot specify
+%% the type of a typefield, return 'ASN1_OPEN_TYPE', otherwise return
+%% {ClassSpec,FieldRefList}.
+maybe_open_type(S,ClassSpec=#objectclass{fields=Fs},
+ OCFT=#'ObjectClassFieldType'{fieldname=FieldRefList},
+ Constr) ->
+ Type = get_ObjectClassFieldType(S,Fs,FieldRefList),
+ FieldNames=get_referenced_fieldname(FieldRefList),
+ case lists:last(FieldRefList) of
+ {valuefieldreference,_} ->
+ OCFT#'ObjectClassFieldType'{class=ClassSpec,
+ fieldname=FieldNames,
+ type=Type};
+ {typefieldreference,_} ->
+ case {catch get_unique_fieldname(#classdef{typespec=ClassSpec}),
+ asn1ct_gen:get_constraint(Constr,componentrelation)}of
+ {Tuple,_} when tuple(Tuple) ->
+ OCFT#'ObjectClassFieldType'{class=ClassSpec,
+ fieldname=FieldNames,
+ type='ASN1_OPEN_TYPE'};
+ {_,no} ->
+ OCFT#'ObjectClassFieldType'{class=ClassSpec,
+ fieldname=FieldNames,
+ type='ASN1_OPEN_TYPE'};
+ _ ->
+ OCFT#'ObjectClassFieldType'{class=ClassSpec,
+ fieldname=FieldNames,
+ type=Type}
+ end
+ end.
+
+is_open_type(#'ObjectClassFieldType'{type='ASN1_OPEN_TYPE'}) ->
+ true;
+is_open_type(#'ObjectClassFieldType'{}) ->
+ false.
+
+
+notify_if_not_ptype(S,#pvaluesetdef{type=Type}) ->
+ case Type#type.def of
+ Ref when record(Ref,'Externaltypereference') ->
+ case get_referenced_type(S,Ref) of
+ {_,#classdef{}} ->
+ throw(pobjectsetdef);
+ {_,#typedef{}} ->
+ throw(pvalueset)
+ end;
+ T when record(T,type) -> % this must be a value set
+ throw(pvalueset)
+ end;
+notify_if_not_ptype(_S,#ptypedef{}) ->
+ ok.
+
+% fix me
+instantiate_ptype(S,Ptypedef,ParaList) ->
+ #ptypedef{args=Args,typespec=Type} = Ptypedef,
+% Args = get_pt_args(Ptypedef),
+% Type = get_pt_spec(Ptypedef),
+ MatchedArgs = match_args(Args, ParaList, []),
+ NewS = S#state{type=Type,parameters=MatchedArgs,abscomppath=[]},
+ %The abscomppath must be empty since a table constraint in a
+ %parameterized type only can refer to components within the type
+ check_type(NewS, Ptypedef, Type).
+
+get_pt_args(#ptypedef{args=Args}) ->
+ Args;
+get_pt_args(#pvaluesetdef{args=Args}) ->
+ Args;
+get_pt_args(#pvaluedef{args=Args}) ->
+ Args;
+get_pt_args(#pobjectdef{args=Args}) ->
+ Args;
+get_pt_args(#pobjectsetdef{args=Args}) ->
+ Args.
+
+get_pt_spec(#ptypedef{typespec=Type}) ->
+ Type;
+get_pt_spec(#pvaluedef{value=Value}) ->
+ Value;
+get_pt_spec(#pvaluesetdef{valueset=VS}) ->
+ VS;
+get_pt_spec(#pobjectdef{def=Def}) ->
+ Def;
+get_pt_spec(#pobjectsetdef{def=Def}) ->
+ Def.
+
+
+
+match_args([FormArg|Ft], [ActArg|At], Acc) ->
+ match_args(Ft, At, [{FormArg,ActArg}|Acc]);
+match_args([], [], Acc) ->
+ lists:reverse(Acc);
+match_args(_, _, _) ->
+ throw({error,{asn1,{wrong_number_of_arguments}}}).
+
+check_constraints(S,C) when list(C) ->
+ check_constraints(S, C, []);
+check_constraints(S,C) when record(C,constraint) ->
+ check_constraints(S, C#constraint.c, []).
+
+
+resolv_tuple_or_list(S,List) when list(List) ->
+ lists:map(fun(X)->resolv_value(S,X) end, List);
+resolv_tuple_or_list(S,{Lb,Ub}) ->
+ {resolv_value(S,Lb),resolv_value(S,Ub)}.
+
+%%%-----------------------------------------
+%% If the constraint value is a defined value the valuename
+%% is replaced by the actual value
+%%
+resolv_value(S,Val) ->
+ case match_parameters(Val, S#state.parameters) of
+ Id -> % unchanged
+ resolv_value1(S,Id);
+ Other ->
+ resolv_value(S,Other)
+ end.
+
+resolv_value1(S = #state{mname=M,inputmodules=InpMods},
+ V=#'Externalvaluereference'{pos=Pos,module=ExtM,value=Name}) ->
+ case ExtM of
+ M -> resolv_value2(S,M,Name,Pos);
+ _ ->
+ case lists:member(ExtM,InpMods) of
+ true ->
+ resolv_value2(S,M,Name,Pos);
+ false ->
+ V
+ end
+ end;
+resolv_value1(S,{gt,V}) ->
+ case V of
+ Int when integer(Int) ->
+ V + 1;
+ #valuedef{value=Int} ->
+ 1 + resolv_value(S,Int);
+ Other ->
+ throw({error,{asn1,{undefined_type_or_value,Other}}})
+ end;
+resolv_value1(S,{lt,V}) ->
+ case V of
+ Int when integer(Int) ->
+ V - 1;
+ #valuedef{value=Int} ->
+ resolv_value(S,Int) - 1;
+ Other ->
+ throw({error,{asn1,{undefined_type_or_value,Other}}})
+ end;
+resolv_value1(S,{'ValueFromObject',{object,Object},[{valuefieldreference,
+ FieldName}]}) ->
+ %% FieldName can hold either a fixed-type value or a variable-type value
+ %% Object is a DefinedObject, i.e. a #'Externaltypereference'
+ {_,ObjTDef} = get_referenced_type(S,Object),
+ TS = check_object(S,ObjTDef,ObjTDef#typedef.typespec),
+ {_,_,Components} = TS#'Object'.def,
+ case lists:keysearch(FieldName,1,Components) of
+ {value,{_,#valuedef{value=Val}}} ->
+ Val;
+ _ ->
+ error({value,"illegal value in constraint",S})
+ end;
+% resolv_value1(S,{'ValueFromObject',{po,Object,Params},FieldName}) ->
+% %% FieldName can hold either a fixed-type value or a variable-type value
+% %% Object is a ParameterizedObject
+resolv_value1(_,V) ->
+ V.
+
+resolv_value2(S,ModuleName,Name,Pos) ->
+ case asn1_db:dbget(ModuleName,Name) of
+ undefined ->
+ case imported(S,Name) of
+ {ok,Imodule} ->
+ {_,V2} = get_referenced(S,Imodule,Name,Pos),
+ V2#valuedef.value;
+ _ ->
+ throw({error,{asn1,{undefined_type_or_value,Name}}})
+ end;
+ Val ->
+ Val#valuedef.value
+ end.
+
+check_constraints(S,[{'ContainedSubtype',Type} | Rest], Acc) ->
+ {_,CTDef} = get_referenced_type(S,Type#type.def),
+ CType = check_type(S,S#state.tname,CTDef#typedef.typespec),
+ check_constraints(S,Rest,CType#type.constraint ++ Acc);
+check_constraints(S,[C | Rest], Acc) ->
+ check_constraints(S,Rest,[check_constraint(S,C) | Acc]);
+check_constraints(S,[],Acc) ->
+% io:format("Acc: ~p~n",[Acc]),
+ C = constraint_merge(S,lists:reverse(Acc)),
+% io:format("C: ~p~n",[C]),
+ lists:flatten(C).
+
+
+range_check(F={FixV,FixV}) ->
+% FixV;
+ F;
+range_check(VR={Lb,Ub}) when Lb < Ub ->
+ VR;
+range_check(Err={_,_}) ->
+ throw({error,{asn1,{illegal_size_constraint,Err}}});
+range_check(Value) ->
+ Value.
+
+check_constraint(S,Ext) when record(Ext,'Externaltypereference') ->
+ check_externaltypereference(S,Ext);
+
+
+check_constraint(S,{'SizeConstraint',{Lb,Ub}})
+ when list(Lb);tuple(Lb),size(Lb)==2 ->
+ case Lb of
+ #'Externalvaluereference'{} ->
+ check_constraint(S,{'SizeConstraint',{resolv_value(S,Lb),Ub}});
+ _ ->
+ NewLb = range_check(resolv_tuple_or_list(S,Lb)),
+ NewUb = range_check(resolv_tuple_or_list(S,Ub)),
+ {'SizeConstraint',{NewLb,NewUb}}
+ end;
+check_constraint(S,{'SizeConstraint',{Lb,Ub}}) ->
+ case {resolv_value(S,Lb),resolv_value(S,Ub)} of
+ {FixV,FixV} ->
+ {'SizeConstraint',FixV};
+ {Low,High} when Low < High ->
+ {'SizeConstraint',{Low,High}};
+ Err ->
+ throw({error,{asn1,{illegal_size_constraint,Err}}})
+ end;
+check_constraint(S,{'SizeConstraint',Lb}) ->
+ {'SizeConstraint',resolv_value(S,Lb)};
+
+check_constraint(S,{'SingleValue', L}) when list(L) ->
+ F = fun(A) -> resolv_value(S,A) end,
+ {'SingleValue',lists:map(F,L)};
+
+check_constraint(S,{'SingleValue', V}) when integer(V) ->
+ Val = resolv_value(S,V),
+%% [{'SingleValue',Val},{'ValueRange',{Val,Val}}]; % Why adding value range?
+ {'SingleValue',Val};
+check_constraint(S,{'SingleValue', V}) ->
+ {'SingleValue',resolv_value(S,V)};
+
+check_constraint(S,{'ValueRange', {Lb, Ub}}) ->
+ {'ValueRange',{resolv_value(S,Lb),resolv_value(S,Ub)}};
+
+%%check_constraint(S,{'ContainedSubtype',Type}) ->
+%% #typedef{typespec=TSpec} =
+%% check_type(S,S#state.tname,get_referenced_type(S,Type#type.def)),
+%% [C] = TSpec#type.constraint,
+%% C;
+
+check_constraint(S,{valueset,Type}) ->
+ {valueset,check_type(S,S#state.tname,Type)};
+
+check_constraint(S,{simpletable,Type}) ->
+ OSName = (Type#type.def)#'Externaltypereference'.type,
+ C = match_parameters(Type#type.def,S#state.parameters),
+ case C of
+ #'Externaltypereference'{} ->
+ Type#type{def=check_externaltypereference(S,C)},
+ {simpletable,OSName};
+ _ ->
+ check_type(S,S#state.tname,Type),
+ {simpletable,OSName}
+ end;
+
+check_constraint(S,{componentrelation,{objectset,Opos,Objset},Id}) ->
+ %% Objset is an 'Externaltypereference' record, since Objset is
+ %% a DefinedObjectSet.
+ RealObjset = match_parameters(Objset,S#state.parameters),
+ Ext = check_externaltypereference(S,RealObjset),
+ {componentrelation,{objectset,Opos,Ext},Id};
+
+check_constraint(S,Type) when record(Type,type) ->
+ #type{def=Def} = check_type(S,S#state.tname,Type),
+ Def;
+
+check_constraint(S,C) when list(C) ->
+ lists:map(fun(X)->check_constraint(S,X) end,C);
+% else keep the constraint unchanged
+check_constraint(_S,Any) ->
+% io:format("Constraint = ~p~n",[Any]),
+ Any.
+
+%% constraint_merge/2
+%% Compute the intersection of the outermost level of the constraint list.
+%% See Dubuisson second paragraph and fotnote on page 285.
+%% If constraints with extension are included in combined constraints. The
+%% resulting combination will have the extension of the last constraint. Thus,
+%% there will be no extension if the last constraint is without extension.
+%% The rootset of all constraints are considered in the "outermoust
+%% intersection". See section 13.1.2 in Dubuisson.
+constraint_merge(_S,C=[H])when tuple(H) ->
+ C;
+constraint_merge(_S,[]) ->
+ [];
+constraint_merge(S,C) ->
+ %% skip all extension but the last
+ C1 = filter_extensions(C),
+ %% perform all internal level intersections, intersections first
+ %% since they have precedence over unions
+ C2 = lists:map(fun(X)when list(X)->constraint_intersection(S,X);
+ (X) -> X end,
+ C1),
+ %% perform all internal level unions
+ C3 = lists:map(fun(X)when list(X)->constraint_union(S,X);
+ (X) -> X end,
+ C2),
+
+ %% now get intersection of the outermost level
+ %% get the least common single value constraint
+ SVs = get_constraints(C3,'SingleValue'),
+ CombSV = intersection_of_sv(S,SVs),
+ %% get the least common value range constraint
+ VRs = get_constraints(C3,'ValueRange'),
+ CombVR = intersection_of_vr(S,VRs),
+ %% get the least common size constraint
+ SZs = get_constraints(C3,'SizeConstraint'),
+ CombSZ = intersection_of_size(S,SZs),
+ CminusSVs=ordsets:subtract(ordsets:from_list(C3),ordsets:from_list(SVs)),
+ % CminusSVsVRs = ordsets:subtract(ordsets:from_list(CminusSVs),
+% ordsets:from_list(VRs)),
+ RestC = ordsets:subtract(ordsets:from_list(CminusSVs),
+ ordsets:from_list(SZs)),
+ %% get the least common combined constraint. That is the union of each
+ %% deep costraint and merge of single value and value range constraints
+ combine_constraints(S,CombSV,CombVR,CombSZ++RestC).
+
+%% constraint_union(S,C) takes a list of constraints as input and
+%% merge them to a union. Unions are performed when two
+%% constraints is found with an atom union between.
+%% The list may be nested. Fix that later !!!
+constraint_union(_S,[]) ->
+ [];
+constraint_union(_S,C=[_E]) ->
+ C;
+constraint_union(S,C) when list(C) ->
+ case lists:member(union,C) of
+ true ->
+ constraint_union1(S,C,[]);
+ _ ->
+ C
+ end;
+% SV = get_constraints(C,'SingleValue'),
+% SV1 = constraint_union_sv(S,SV),
+% VR = get_constraints(C,'ValueRange'),
+% VR1 = constraint_union_vr(VR),
+% RestC = ordsets:filter(fun({'SingleValue',_})->false;
+% ({'ValueRange',_})->false;
+% (_) -> true end,ordsets:from_list(C)),
+% SV1++VR1++RestC;
+constraint_union(_S,C) ->
+ [C].
+
+constraint_union1(S,[A={'ValueRange',_},union,B={'ValueRange',_}|Rest],Acc) ->
+ AunionB = constraint_union_vr([A,B]),
+ constraint_union1(S,Rest,AunionB++Acc);
+constraint_union1(S,[A={'SingleValue',_},union,B={'SingleValue',_}|Rest],Acc) ->
+ AunionB = constraint_union_sv(S,[A,B]),
+ constraint_union1(S,Rest,AunionB++Acc);
+constraint_union1(S,[A={'SingleValue',_},union,B={'ValueRange',_}|Rest],Acc) ->
+ AunionB = union_sv_vr(S,A,B),
+ constraint_union1(S,Rest,AunionB++Acc);
+constraint_union1(S,[A={'ValueRange',_},union,B={'SingleValue',_}|Rest],Acc) ->
+ AunionB = union_sv_vr(S,B,A),
+ constraint_union1(S,Rest,AunionB++Acc);
+constraint_union1(S,[union|Rest],Acc) -> %skip when unsupported constraints
+ constraint_union1(S,Rest,Acc);
+constraint_union1(S,[A|Rest],Acc) ->
+ constraint_union1(S,Rest,[A|Acc]);
+constraint_union1(_S,[],Acc) ->
+ lists:reverse(Acc).
+
+constraint_union_sv(_S,SV) ->
+ Values=lists:map(fun({_,V})->V end,SV),
+ case ordsets:from_list(Values) of
+ [] -> [];
+ [N] -> [{'SingleValue',N}];
+ L -> [{'SingleValue',L}]
+ end.
+
+%% REMOVE????
+%%constraint_union(S,VR,'ValueRange') ->
+%% constraint_union_vr(VR).
+
+%% constraint_union_vr(VR)
+%% VR = [{'ValueRange',{Lb,Ub}},...]
+%% Lb = 'MIN' | integer()
+%% Ub = 'MAX' | integer()
+%% Returns if possible only one ValueRange tuple with a range that
+%% is a union of all ranges in VR.
+constraint_union_vr(VR) ->
+ %% Sort VR by Lb in first hand and by Ub in second hand
+ Fun=fun({_,{'MIN',_B1}},{_,{A2,_B2}}) when integer(A2)->true;
+ ({_,{A1,_B1}},{_,{'MAX',_B2}}) when integer(A1) -> true;
+ ({_,{A1,_B1}},{_,{A2,_B2}}) when integer(A1),integer(A2),A1 true;
+ ({_,{A,B1}},{_,{A,B2}}) when B1=true;
+ (_,_)->false end,
+ constraint_union_vr(lists:usort(Fun,VR),[]).
+
+constraint_union_vr([],Acc) ->
+ lists:reverse(Acc);
+constraint_union_vr([C|Rest],[]) ->
+ constraint_union_vr(Rest,[C]);
+constraint_union_vr([{_,{Lb,Ub2}}|Rest],[{_,{Lb,_Ub1}}|Acc]) -> %Ub2 > Ub1
+ constraint_union_vr(Rest,[{'ValueRange',{Lb,Ub2}}|Acc]);
+constraint_union_vr([{_,{_,Ub}}|Rest],A=[{_,{_,Ub}}|_Acc]) ->
+ constraint_union_vr(Rest,A);
+constraint_union_vr([{_,{Lb2,Ub2}}|Rest],[{_,{Lb1,Ub1}}|Acc]) when Lb2=Ub1->
+ constraint_union_vr(Rest,[{'ValueRange',{Lb1,Ub2}}|Acc]);
+constraint_union_vr([{_,{_,Ub2}}|Rest],A=[{_,{_,Ub1}}|_Acc]) when Ub2=
+ constraint_union_vr(Rest,A);
+constraint_union_vr([VR|Rest],Acc) ->
+ constraint_union_vr(Rest,[VR|Acc]).
+
+union_sv_vr(_S,[],B) ->
+ [B];
+union_sv_vr(_S,A,[]) ->
+ [A];
+union_sv_vr(_S,C1={'SingleValue',SV},C2={'ValueRange',VR={Lb,Ub}})
+ when integer(SV) ->
+ case is_int_in_vr(SV,C2) of
+ true -> [C2];
+ _ ->
+ case VR of
+ {'MIN',Ub} when SV==Ub+1 -> [{'ValueRange',{'MIN',SV}}];
+ {Lb,'MAX'} when SV==Lb-1 -> [{'ValueRange',{SV,'MAX'}}];
+ {Lb,Ub} when SV==Ub+1 -> [{'ValueRange',{Lb,SV}}];
+ {Lb,Ub} when SV==Lb-1 -> [{'ValueRange',{SV,Ub}}];
+ _ ->
+ [C1,C2]
+ end
+ end;
+union_sv_vr(_S,C1={'SingleValue',SV},C2={'ValueRange',{_Lb,_Ub}})
+ when list(SV) ->
+ case lists:filter(fun(X)->is_int_in_vr(X,C2) end,SV) of
+ [] -> [C2];
+ L ->
+ case expand_vr(L,C2) of
+ {[],C3} -> [C3];
+ {L,C2} -> [C1,C2];
+ {[Val],C3} -> [{'SingleValue',Val},C3];
+ {L2,C3} -> [{'SingleValue',L2},C3]
+ end
+ end.
+
+expand_vr(L,VR={_,{Lb,Ub}}) ->
+ case lower_Lb(L,Lb) of
+ false ->
+ case higher_Ub(L,Ub) of
+ false ->
+ {L,VR};
+ {L1,UbNew} ->
+ expand_vr(L1,{'ValueRange',{Lb,UbNew}})
+ end;
+ {L1,LbNew} ->
+ expand_vr(L1,{'ValueRange',{LbNew,Ub}})
+ end.
+
+lower_Lb(_,'MIN') ->
+ false;
+lower_Lb(L,Lb) ->
+ remove_val_from_list(Lb - 1,L).
+
+higher_Ub(_,'MAX') ->
+ false;
+higher_Ub(L,Ub) ->
+ remove_val_from_list(Ub + 1,L).
+
+remove_val_from_list(List,Val) ->
+ case lists:member(Val,List) of
+ true ->
+ {lists:delete(Val,List),Val};
+ false ->
+ false
+ end.
+
+%% get_constraints/2
+%% Arguments are a list of constraints, which has the format {key,value},
+%% and a constraint type
+%% Returns a list of constraints only of the requested type or the atom
+%% 'no' if no such constraints were found
+get_constraints(L=[{CType,_}],CType) ->
+ L;
+get_constraints(C,CType) ->
+ keysearch_allwithkey(CType,1,C).
+
+%% keysearch_allwithkey(Key,Ix,L)
+%% Types:
+%% Key = atom()
+%% Ix = integer()
+%% L = [TwoTuple]
+%% TwoTuple = [{atom(),term()}|...]
+%% Returns a List that contains all
+%% elements from L that has a key Key as element Ix
+keysearch_allwithkey(Key,Ix,L) ->
+ lists:filter(fun(X) when tuple(X) ->
+ case element(Ix,X) of
+ Key -> true;
+ _ -> false
+ end;
+ (_) -> false
+ end, L).
+
+
+%% filter_extensions(C)
+%% takes a list of constraints as input and
+%% returns a list with the intersection of all extension roots
+%% and only the extension of the last constraint kept if any
+%% extension in the last constraint
+filter_extensions([]) ->
+ [];
+filter_extensions(C=[_H]) ->
+ C;
+filter_extensions(C) when list(C) ->
+ filter_extensions(C,[]).
+
+filter_extensions([C],Acc) ->
+ lists:reverse([C|Acc]);
+filter_extensions([{C,_E},H2|T],Acc) when tuple(C) ->
+ filter_extensions([H2|T],[C|Acc]);
+filter_extensions([{'SizeConstraint',{A,_B}},H2|T],Acc)
+ when list(A);tuple(A) ->
+ filter_extensions([H2|T],[{'SizeConstraint',A}|Acc]);
+filter_extensions([H1,H2|T],Acc) ->
+ filter_extensions([H2|T],[H1|Acc]).
+
+%% constraint_intersection(S,C) takes a list of constraints as input and
+%% performs intersections. Intersecions are performed when an
+%% atom intersection is found between two constraints.
+%% The list may be nested. Fix that later !!!
+constraint_intersection(_S,[]) ->
+ [];
+constraint_intersection(_S,C=[_E]) ->
+ C;
+constraint_intersection(S,C) when list(C) ->
+% io:format("constraint_intersection: ~p~n",[C]),
+ case lists:member(intersection,C) of
+ true ->
+ constraint_intersection1(S,C,[]);
+ _ ->
+ C
+ end;
+constraint_intersection(_S,C) ->
+ [C].
+
+constraint_intersection1(S,[A,intersection,B|Rest],Acc) ->
+ AisecB = c_intersect(S,A,B),
+ constraint_intersection1(S,Rest,AisecB++Acc);
+constraint_intersection1(S,[A|Rest],Acc) ->
+ constraint_intersection1(S,Rest,[A|Acc]);
+constraint_intersection1(_,[],Acc) ->
+ lists:reverse(Acc).
+
+c_intersect(S,C1={'SingleValue',_},C2={'SingleValue',_}) ->
+ intersection_of_sv(S,[C1,C2]);
+c_intersect(S,C1={'ValueRange',_},C2={'ValueRange',_}) ->
+ intersection_of_vr(S,[C1,C2]);
+c_intersect(S,C1={'ValueRange',_},C2={'SingleValue',_}) ->
+ intersection_sv_vr(S,[C2],[C1]);
+c_intersect(S,C1={'SingleValue',_},C2={'ValueRange',_}) ->
+ intersection_sv_vr(S,[C1],[C2]);
+c_intersect(_S,C1,C2) ->
+ [C1,C2].
+
+%% combine_constraints(S,SV,VR,CComb)
+%% Types:
+%% S = record(state,S)
+%% SV = [] | [SVC]
+%% VR = [] | [VRC]
+%% CComb = [] | [Lists]
+%% SVC = {'SingleValue',integer()} | {'SingleValue',[integer(),...]}
+%% VRC = {'ValueRange',{Lb,Ub}}
+%% Lists = List of lists containing any constraint combination
+%% Lb = 'MIN' | integer()
+%% Ub = 'MAX' | integer()
+%% Returns a combination of the least common constraint among SV,VR and all
+%% elements in CComb
+combine_constraints(_S,[],VR,CComb) ->
+ VR ++ CComb;
+% combine_combined_cnstr(S,VR,CComb);
+combine_constraints(_S,SV,[],CComb) ->
+ SV ++ CComb;
+% combine_combined_cnstr(S,SV,CComb);
+combine_constraints(S,SV,VR,CComb) ->
+ C=intersection_sv_vr(S,SV,VR),
+ C ++ CComb.
+% combine_combined_cnstr(S,C,CComb).
+
+intersection_sv_vr(_,[],_VR) ->
+ [];
+intersection_sv_vr(_,_SV,[]) ->
+ [];
+intersection_sv_vr(_S,[C1={'SingleValue',SV}],[C2={'ValueRange',{_Lb,_Ub}}])
+ when integer(SV) ->
+ case is_int_in_vr(SV,C2) of
+ true -> [C1];
+ _ -> %%error({type,{"asn1 illegal constraint",C1,C2},S})
+ throw({error,{"asn1 illegal constraint",C1,C2}})
+ end;
+intersection_sv_vr(_S,[C1={'SingleValue',SV}],[C2])
+ when list(SV) ->
+ case lists:filter(fun(X)->is_int_in_vr(X,C2) end,SV) of
+ [] ->
+ %%error({type,{"asn1 illegal constraint",C1,C2},S});
+ throw({error,{"asn1 illegal constraint",C1,C2}});
+ [V] -> [{'SingleValue',V}];
+ L -> [{'SingleValue',L}]
+ end.
+
+
+
+intersection_of_size(_,[]) ->
+ [];
+intersection_of_size(_,C=[_SZ]) ->
+ C;
+intersection_of_size(S,[SZ,SZ|Rest]) ->
+ intersection_of_size(S,[SZ|Rest]);
+intersection_of_size(S,C=[C1={_,Int},{_,Range}|Rest])
+ when integer(Int),tuple(Range) ->
+ case Range of
+ {Lb,Ub} when Int >= Lb,
+ Int =< Ub ->
+ intersection_of_size(S,[C1|Rest]);
+ _ ->
+ throw({error,{asn1,{illegal_size_constraint,C}}})
+ end;
+intersection_of_size(S,[C1={_,Range},C2={_,Int}|Rest])
+ when integer(Int),tuple(Range) ->
+ intersection_of_size(S,[C2,C1|Rest]);
+intersection_of_size(S,[{_,{Lb1,Ub1}},{_,{Lb2,Ub2}}|Rest]) ->
+ Lb=greatest_LB(ordsets:from_list([Lb1,Lb2])),
+ Ub=smallest_UB(ordsets:from_list([Ub1,Ub2])),
+ intersection_of_size(S,[{'SizeConstraint',{Lb,Ub}}|Rest]);
+intersection_of_size(_,SZ) ->
+ throw({error,{asn1,{illegal_size_constraint,SZ}}}).
+
+intersection_of_vr(_,[]) ->
+ [];
+intersection_of_vr(_,VR=[_C]) ->
+ VR;
+intersection_of_vr(S,[{_,{Lb1,Ub1}},{_,{Lb2,Ub2}}|Rest]) ->
+ Lb=greatest_LB(ordsets:from_list([Lb1,Lb2])),
+ Ub=smallest_UB(ordsets:from_list([Ub1,Ub2])),
+ intersection_of_vr(S,[{'ValueRange',{Lb,Ub}}|Rest]);
+intersection_of_vr(_S,VR) ->
+ %%error({type,{asn1,{illegal_value_range_constraint,VR}},S});
+ throw({error,{asn1,{illegal_value_range_constraint,VR}}}).
+
+intersection_of_sv(_,[]) ->
+ [];
+intersection_of_sv(_,SV=[_C]) ->
+ SV;
+intersection_of_sv(S,[SV,SV|Rest]) ->
+ intersection_of_sv(S,[SV|Rest]);
+intersection_of_sv(S,[{_,Int},{_,SV}|Rest]) when integer(Int),
+ list(SV) ->
+ SV2=intersection_of_sv1(S,Int,SV),
+ intersection_of_sv(S,[SV2|Rest]);
+intersection_of_sv(S,[{_,SV},{_,Int}|Rest]) when integer(Int),
+ list(SV) ->
+ SV2=intersection_of_sv1(S,Int,SV),
+ intersection_of_sv(S,[SV2|Rest]);
+intersection_of_sv(S,[{_,SV1},{_,SV2}|Rest]) when list(SV1),
+ list(SV2) ->
+ SV3=common_set(SV1,SV2),
+ intersection_of_sv(S,[SV3|Rest]);
+intersection_of_sv(_S,SV) ->
+ %%error({type,{asn1,{illegal_single_value_constraint,SV}},S}).
+ throw({error,{asn1,{illegal_single_value_constraint,SV}}}).
+
+intersection_of_sv1(_S,Int,SV) when integer(Int),list(SV) ->
+ case lists:member(Int,SV) of
+ true -> {'SingleValue',Int};
+ _ ->
+ %%error({type,{asn1,{illegal_single_value_constraint,Int,SV}},S})
+ throw({error,{asn1,{illegal_single_value_constraint,Int,SV}}})
+ end;
+intersection_of_sv1(_S,SV1,SV2) ->
+ %%error({type,{asn1,{illegal_single_value_constraint,SV1,SV2}},S}).
+ throw({error,{asn1,{illegal_single_value_constraint,SV1,SV2}}}).
+
+greatest_LB([H]) ->
+ H;
+greatest_LB(L) ->
+ greatest_LB1(lists:reverse(L)).
+greatest_LB1(['MIN',H2|_T])->
+ H2;
+greatest_LB1([H|_T]) ->
+ H.
+smallest_UB(L) ->
+ hd(L).
+
+common_set(SV1,SV2) ->
+ lists:filter(fun(X)->lists:member(X,SV1) end,SV2).
+
+is_int_in_vr(Int,{_,{'MIN','MAX'}}) when integer(Int) ->
+ true;
+is_int_in_vr(Int,{_,{'MIN',Ub}}) when integer(Int),Int =< Ub ->
+ true;
+is_int_in_vr(Int,{_,{Lb,'MAX'}}) when integer(Int),Int >= Lb ->
+ true;
+is_int_in_vr(Int,{_,{Lb,Ub}}) when integer(Int),Int >= Lb,Int =< Ub ->
+ true;
+is_int_in_vr(_,_) ->
+ false.
+
+
+
+check_imported(_S,Imodule,Name) ->
+ case asn1_db:dbget(Imodule,'MODULE') of
+ undefined ->
+ io:format("~s.asn1db not found~n",[Imodule]),
+ io:format("Type ~s imported from non existing module ~s~n",[Name,Imodule]);
+ Im when record(Im,module) ->
+ case is_exported(Im,Name) of
+ false ->
+ io:format("Imported type ~s not exported from module ~s~n",[Name,Imodule]);
+ _ ->
+ ok
+ end
+ end,
+ ok.
+
+is_exported(Module,Name) when record(Module,module) ->
+ {exports,Exports} = Module#module.exports,
+ case Exports of
+ all ->
+ true;
+ [] ->
+ false;
+ L when list(L) ->
+ case lists:keysearch(Name,#'Externaltypereference'.type,Exports) of
+ false -> false;
+ _ -> true
+ end
+ end.
+
+
+
+check_externaltypereference(S,Etref=#'Externaltypereference'{module=Emod})->
+ Currmod = S#state.mname,
+ MergedMods = S#state.inputmodules,
+ case Emod of
+ Currmod ->
+ %% reference to current module or to imported reference
+ check_reference(S,Etref);
+ _ ->
+ %% io:format("Type ~s IMPORTED FROM ~s~n",[Etype,Emod]),
+ case lists:member(Emod,MergedMods) of
+ true ->
+ check_reference(S,Etref);
+ false ->
+ Etref
+ end
+ end.
+
+check_reference(S,#'Externaltypereference'{pos=Pos,module=Emod,type=Name}) ->
+ ModName = S#state.mname,
+ case asn1_db:dbget(ModName,Name) of
+ undefined ->
+ case imported(S,Name) of
+ {ok,Imodule} ->
+ check_imported(S,Imodule,Name),
+ #'Externaltypereference'{module=Imodule,type=Name};
+ _ ->
+ %may be a renamed type in multi file compiling!
+ {_,T}=renamed_reference(S,Name,Emod),
+ NewName = asn1ct:get_name_of_def(T),
+ NewPos = asn1ct:get_pos_of_def(T),
+ #'Externaltypereference'{pos=NewPos,
+ module=ModName,
+ type=NewName}
+ end;
+ _ ->
+ %% cannot do check_type here due to recursive definitions, like
+ %% S ::= SEQUENCE {a INTEGER, b S}. This implies that references
+ %% that appear before the definition will be an
+ %% Externaltypereference in the abstract syntax tree
+ #'Externaltypereference'{pos=Pos,module=ModName,type=Name}
+ end.
+
+
+name2Extref(_Mod,Name) when record(Name,'Externaltypereference') ->
+ Name;
+name2Extref(Mod,Name) ->
+ #'Externaltypereference'{module=Mod,type=Name}.
+
+get_referenced_type(S,Ext) when record(Ext,'Externaltypereference') ->
+ case match_parameters(Ext, S#state.parameters) of
+ Ext ->
+ #'Externaltypereference'{pos=Pos,module=Emod,type=Etype} = Ext,
+ case S#state.mname of
+ Emod -> % a local reference in this module
+ get_referenced1(S,Emod,Etype,Pos);
+ _ ->% always when multi file compiling
+ case lists:member(Emod,S#state.inputmodules) of
+ true ->
+ get_referenced1(S,Emod,Etype,Pos);
+ false ->
+ get_referenced(S,Emod,Etype,Pos)
+ end
+ end;
+ Other ->
+ {undefined,Other}
+ end;
+get_referenced_type(S=#state{mname=Emod},
+ ERef=#'Externalvaluereference'{pos=P,module=Emod,
+ value=Eval}) ->
+ case match_parameters(ERef,S#state.parameters) of
+ ERef ->
+ get_referenced1(S,Emod,Eval,P);
+ OtherERef when record(OtherERef,'Externalvaluereference') ->
+ get_referenced_type(S,OtherERef);
+ Value ->
+ {Emod,Value}
+ end;
+get_referenced_type(S,ERef=#'Externalvaluereference'{pos=Pos,module=Emod,
+ value=Eval}) ->
+ case match_parameters(ERef,S#state.parameters) of
+ ERef ->
+ case lists:member(Emod,S#state.inputmodules) of
+ true ->
+ get_referenced1(S,Emod,Eval,Pos);
+ false ->
+ get_referenced(S,Emod,Eval,Pos)
+ end;
+ OtherERef ->
+ get_referenced_type(S,OtherERef)
+ end;
+get_referenced_type(S,#identifier{val=Name,pos=Pos}) ->
+ get_referenced1(S,undefined,Name,Pos);
+get_referenced_type(_S,Type) ->
+ {undefined,Type}.
+
+%% get_referenced/3
+%% The referenced entity Ename may in case of an imported parameterized
+%% type reference imported entities in the other module, which implies that
+%% asn1_db:dbget will fail even though the referenced entity exists. Thus
+%% Emod may be the module that imports the entity Ename and not holds the
+%% data about Ename.
+get_referenced(S,Emod,Ename,Pos) ->
+ case asn1_db:dbget(Emod,Ename) of
+ undefined ->
+ %% May be an imported entity in module Emod
+% throw({error,{asn1,{undefined_type_or_value,{Emod,Ename}}}});
+ NewS = S#state{module=asn1_db:dbget(Emod,'MODULE')},
+ get_imported(NewS,Ename,Emod,Pos);
+ T when record(T,typedef) ->
+ Spec = T#typedef.typespec,
+ case Spec#type.def of
+ Tref when record(Tref,typereference) ->
+ Def = #'Externaltypereference'{module=Emod,
+ type=Tref#typereference.val,
+ pos=Tref#typereference.pos},
+
+
+ {Emod,T#typedef{typespec=Spec#type{def=Def}}};
+ _ ->
+ {Emod,T} % should add check that T is exported here
+ end;
+ V -> {Emod,V}
+ end.
+
+get_referenced1(S,ModuleName,Name,Pos) ->
+ case asn1_db:dbget(S#state.mname,Name) of
+ undefined ->
+ %% ModuleName may be other than S#state.mname when
+ %% multi file compiling is used.
+ get_imported(S,Name,ModuleName,Pos);
+ T ->
+ {S#state.mname,T}
+ end.
+
+get_imported(S,Name,Module,Pos) ->
+ case imported(S,Name) of
+ {ok,Imodule} ->
+ case asn1_db:dbget(Imodule,'MODULE') of
+ undefined ->
+ throw({error,{asn1,{module_not_found,Imodule}}});
+ Im when record(Im,module) ->
+ case is_exported(Im,Name) of
+ false ->
+ throw({error,
+ {asn1,{not_exported,{Im,Name}}}});
+ _ ->
+ get_referenced_type(S,
+ #'Externaltypereference'
+ {module=Imodule,
+ type=Name,pos=Pos})
+ end
+ end;
+ _ ->
+ renamed_reference(S,Name,Module)
+ end.
+
+renamed_reference(S,Name,Module) ->
+ %% first check if there is a renamed type in this module
+ %% second check if any type was imported with this name
+ case ets:info(renamed_defs) of
+ undefined -> throw({error,{asn1,{undefined_type,Name}}});
+ _ ->
+ case ets:match(renamed_defs,{'$1',Name,Module}) of
+ [] ->
+ case ets:info(original_imports) of
+ undefined ->
+ throw({error,{asn1,{undefined_type,Name}}});
+ _ ->
+ case ets:match(original_imports,{Module,'$1'}) of
+ [] ->
+ throw({error,{asn1,{undefined_type,Name}}});
+ [[ImportsList]] ->
+ case get_importmoduleoftype(ImportsList,Name) of
+ undefined ->
+ throw({error,{asn1,{undefined_type,Name}}});
+ NextMod ->
+ renamed_reference(S,Name,NextMod)
+ end
+ end
+ end;
+ [[NewTypeName]] ->
+ get_referenced1(S,Module,NewTypeName,undefined)
+ end
+ end.
+
+get_importmoduleoftype([I|Is],Name) ->
+ Index = #'Externaltypereference'.type,
+ case lists:keysearch(Name,Index,I#'SymbolsFromModule'.symbols) of
+ {value,_Ref} ->
+ (I#'SymbolsFromModule'.module)#'Externaltypereference'.type;
+ _ ->
+ get_importmoduleoftype(Is,Name)
+ end;
+get_importmoduleoftype([],_) ->
+ undefined.
+
+
+match_parameters(Name,[]) ->
+ Name;
+
+match_parameters(#'Externaltypereference'{type=Name},[{#'Externaltypereference'{type=Name},NewName}|_T]) ->
+ NewName;
+match_parameters(#'Externaltypereference'{type=Name},[{{_,#'Externaltypereference'{type=Name}},NewName}|_T]) ->
+ NewName;
+% match_parameters(#'Externaltypereference'{type=Name},[{#typereference{val=Name},NewName}|T]) ->
+% NewName;
+% match_parameters(#'Externaltypereference'{type=Name},[{{_,#typereference{val=Name}},NewName}|T]) ->
+% NewName;
+%match_parameters(#typereference{val=Name},[{#typereference{val=Name},NewName}|T]) ->
+% NewName;
+match_parameters(#'Externalvaluereference'{value=Name},[{#'Externalvaluereference'{value=Name},NewName}|_T]) ->
+ NewName;
+match_parameters(#'Externalvaluereference'{value=Name},[{{_,#'Externalvaluereference'{value=Name}},NewName}|_T]) ->
+ NewName;
+% match_parameters(#identifier{val=Name},[{#identifier{val=Name},NewName}|T]) ->
+% NewName;
+% match_parameters(#identifier{val=Name},[{{_,#identifier{val=Name}},NewName}|T]) ->
+% NewName;
+match_parameters({valueset,#type{def=#'Externaltypereference'{type=Name}}},
+ [{{_,#'Externaltypereference'{type=Name}},{valueset,#type{def=NewName}}}|_T]) ->
+ NewName;
+match_parameters({valueset,#type{def=#'Externaltypereference'{type=Name}}},
+ [{{_,#'Externaltypereference'{type=Name}},NewName}|_T]) ->
+ NewName;
+% match_parameters({valueset,#type{def=#'Externaltypereference'{type=Name}}},
+% [{{_,#typereference{val=Name}},{valueset,#type{def=NewName}}}|T]) ->
+% NewName;
+% match_parameters({valueset,#type{def=#'Externaltypereference'{type=Name}}},
+% [{{_,#typereference{val=Name}},NewName}|T]) ->
+% NewName;
+
+match_parameters(Name, [_H|T]) ->
+ %%io:format("match_parameters(~p,~p)~n",[Name,[H|T]]),
+ match_parameters(Name,T).
+
+imported(S,Name) ->
+ {imports,Ilist} = (S#state.module)#module.imports,
+ imported1(Name,Ilist).
+
+imported1(Name,
+ [#'SymbolsFromModule'{symbols=Symlist,
+ module=#'Externaltypereference'{type=ModuleName}}|T]) ->
+ case lists:keysearch(Name,#'Externaltypereference'.type,Symlist) of
+ {value,_V} ->
+ {ok,ModuleName};
+ _ ->
+ imported1(Name,T)
+ end;
+imported1(_Name,[]) ->
+ false.
+
+
+check_integer(_S,[],_C) ->
+ ok;
+check_integer(S,NamedNumberList,_C) ->
+ case check_unique(NamedNumberList,2) of
+ [] ->
+ check_int(S,NamedNumberList,[]);
+ L when list(L) ->
+ error({type,{duplicates,L},S}),
+ unchanged
+
+ end.
+
+check_int(S,[{'NamedNumber',Id,Num}|T],Acc) when integer(Num) ->
+ check_int(S,T,[{Id,Num}|Acc]);
+check_int(S,[{'NamedNumber',Id,{identifier,_,Name}}|T],Acc) ->
+ Val = dbget_ex(S,S#state.mname,Name),
+ check_int(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc);
+check_int(_S,[],Acc) ->
+ lists:keysort(2,Acc).
+
+
+
+check_bitstring(_S,[],_Constr) ->
+ [];
+check_bitstring(S,NamedNumberList,_Constr) ->
+ case check_unique(NamedNumberList,2) of
+ [] ->
+ check_bitstr(S,NamedNumberList,[]);
+ L when list(L) ->
+ error({type,{duplicates,L},S}),
+ unchanged
+ end.
+
+check_bitstr(S,[{'NamedNumber',Id,Num}|T],Acc)when integer(Num) ->
+ check_bitstr(S,T,[{Id,Num}|Acc]);
+check_bitstr(S,[{'NamedNumber',Id,Name}|T],Acc) when atom(Name) ->
+%%check_bitstr(S,[{'NamedNumber',Id,{identifier,_,Name}}|T],Acc) ->
+%% io:format("asn1ct_check:check_bitstr/3 hej hop ~w~n",[Name]),
+ Val = dbget_ex(S,S#state.mname,Name),
+%% io:format("asn1ct_check:check_bitstr/3: ~w~n",[Val]),
+ check_bitstr(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc);
+check_bitstr(S,[],Acc) ->
+ case check_unique(Acc,2) of
+ [] ->
+ lists:keysort(2,Acc);
+ L when list(L) ->
+ error({type,{duplicate_values,L},S}),
+ unchanged
+ end.
+
+%%check_bitstring(S,NamedNumberList,Constr) ->
+%% NamedNumberList.
+
+%% Check INSTANCE OF
+%% check that DefinedObjectClass is of TYPE-IDENTIFIER class
+%% If Constraint is empty make it the general INSTANCE OF type
+%% If Constraint is not empty make an inlined type
+%% convert INSTANCE OF to the associated type
+check_instance_of(S,DefinedObjectClass,Constraint) ->
+ check_type_identifier(S,DefinedObjectClass),
+ iof_associated_type(S,Constraint).
+
+
+check_type_identifier(_S,'TYPE-IDENTIFIER') ->
+ ok;
+check_type_identifier(S,Eref=#'Externaltypereference'{}) ->
+ case get_referenced_type(S,Eref) of
+ {_,#classdef{name='TYPE-IDENTIFIER'}} -> ok;
+ {_,TD=#typedef{typespec=#type{def=#'Externaltypereference'{}}}} ->
+ check_type_identifier(S,(TD#typedef.typespec)#type.def);
+ _ ->
+ error({type,{"object set in type INSTANCE OF "
+ "not of class TYPE-IDENTIFIER",Eref},S})
+ end.
+
+iof_associated_type(S,[]) ->
+ %% in this case encode/decode functions for INSTANCE OF must be
+ %% generated
+ case get(instance_of) of
+ undefined ->
+ AssociateSeq = iof_associated_type1(S,[]),
+ Tag =
+ case S#state.erule of
+ ber_bin_v2 ->
+ [?TAG_CONSTRUCTED(?N_INSTANCE_OF)];
+ _ -> []
+ end,
+ TypeDef=#typedef{checked=true,
+ name='INSTANCE OF',
+ typespec=#type{tag=Tag,
+ def=AssociateSeq}},
+ asn1_db:dbput(S#state.mname,'INSTANCE OF',TypeDef),
+ put(instance_of,generate);
+ _ ->
+ ok
+ end,
+ #'Externaltypereference'{module=S#state.mname,type='INSTANCE OF'};
+iof_associated_type(S,C) ->
+ iof_associated_type1(S,C).
+
+iof_associated_type1(S,C) ->
+ {TableCInf,Comp1Cnstr,Comp2Cnstr,Comp2tablecinf}=
+ instance_of_constraints(S,C),
+
+ ModuleName = S#state.mname,
+ Typefield_type=
+ case C of
+ [] -> 'ASN1_OPEN_TYPE';
+ _ -> {typefield,'Type'}
+ end,
+ {ObjIdTag,C1TypeTag}=
+ case S#state.erule of
+ ber_bin_v2 ->
+ {[{'UNIVERSAL',8}],
+ [#tag{class='UNIVERSAL',
+ number=6,
+ type='IMPLICIT',
+ form=0}]};
+ _ -> {[{'UNIVERSAL','INTEGER'}],[]}
+ end,
+ TypeIdentifierRef=#'Externaltypereference'{module=ModuleName,
+ type='TYPE-IDENTIFIER'},
+ ObjectIdentifier =
+ #'ObjectClassFieldType'{classname=TypeIdentifierRef,
+ class=[],
+ fieldname={id,[]},
+ type={fixedtypevaluefield,id,
+ #type{def='OBJECT IDENTIFIER'}}},
+ Typefield =
+ #'ObjectClassFieldType'{classname=TypeIdentifierRef,
+ class=[],
+ fieldname={'Type',[]},
+ type=Typefield_type},
+ IOFComponents =
+ [#'ComponentType'{name='type-id',
+ typespec=#type{tag=C1TypeTag,
+ def=ObjectIdentifier,
+ constraint=Comp1Cnstr},
+ prop=mandatory,
+ tags=ObjIdTag},
+ #'ComponentType'{name=value,
+ typespec=#type{tag=[#tag{class='CONTEXT',
+ number=0,
+ type='EXPLICIT',
+ form=32}],
+ def=Typefield,
+ constraint=Comp2Cnstr,
+ tablecinf=Comp2tablecinf},
+ prop=mandatory,
+ tags=[{'CONTEXT',0}]}],
+ #'SEQUENCE'{tablecinf=TableCInf,
+ components=IOFComponents}.
+
+
+%% returns the leading attribute, the constraint of the components and
+%% the tablecinf value for the second component.
+instance_of_constraints(_,[]) ->
+ {false,[],[],[]};
+instance_of_constraints(S,#constraint{c={simpletable,Type}}) ->
+ #type{def=#'Externaltypereference'{type=Name}} = Type,
+ ModuleName = S#state.mname,
+ ObjectSetRef=#'Externaltypereference'{module=ModuleName,
+ type=Name},
+ CRel=[{componentrelation,{objectset,
+ undefined, %% pos
+ ObjectSetRef},
+ [{innermost,
+ [#'Externalvaluereference'{module=ModuleName,
+ value=type}]}]}],
+ TableCInf=#simpletableattributes{objectsetname=Name,
+ c_name='type-id',
+ c_index=1,
+ usedclassfield=id,
+ uniqueclassfield=id,
+ valueindex=[]},
+ {TableCInf,[{simpletable,Name}],CRel,[{objfun,ObjectSetRef}]}.
+
+%% Check ENUMERATED
+%% ****************************************
+%% Check that all values are unique
+%% assign values to un-numbered identifiers
+%% check that the constraints are allowed and correct
+%% put the updated info back into database
+check_enumerated(_S,[{Name,Number}|Rest],_Constr) when atom(Name), integer(Number)->
+ %% already checked , just return the same list
+ [{Name,Number}|Rest];
+check_enumerated(S,NamedNumberList,_Constr) ->
+ check_enum(S,NamedNumberList,[],[]).
+
+%% identifiers are put in Acc2
+%% returns either [{Name,Number}] or {[{Name,Number}],[{ExtName,ExtNumber}]}
+%% the latter is returned if the ENUMERATION contains EXTENSIONMARK
+check_enum(S,[{'NamedNumber',Id,Num}|T],Acc1,Acc2) when integer(Num) ->
+ check_enum(S,T,[{Id,Num}|Acc1],Acc2);
+check_enum(S,[{'NamedNumber',Id,{identifier,_,Name}}|T],Acc1,Acc2) ->
+ Val = dbget_ex(S,S#state.mname,Name),
+ check_enum(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc1,Acc2);
+check_enum(S,['EXTENSIONMARK'|T],Acc1,Acc2) ->
+ NewAcc2 = lists:keysort(2,Acc1),
+ NewList = enum_number(lists:reverse(Acc2),NewAcc2,0,[]),
+ { NewList, check_enum(S,T,[],[])};
+check_enum(S,[Id|T],Acc1,Acc2) when atom(Id) ->
+ check_enum(S,T,Acc1,[Id|Acc2]);
+check_enum(_S,[],Acc1,Acc2) ->
+ NewAcc2 = lists:keysort(2,Acc1),
+ enum_number(lists:reverse(Acc2),NewAcc2,0,[]).
+
+
+% assign numbers to identifiers , numbers from 0 ... but must not
+% be the same as already assigned to NamedNumbers
+enum_number([H|T],[{Id,Num}|T2],Cnt,Acc) when Num > Cnt ->
+ enum_number(T,[{Id,Num}|T2],Cnt+1,[{H,Cnt}|Acc]);
+enum_number([H|T],[{Id,Num}|T2],Cnt,Acc) when Num < Cnt -> % negative Num
+ enum_number(T,T2,Cnt+1,[{H,Cnt},{Id,Num}|Acc]);
+enum_number([],L2,_Cnt,Acc) ->
+ lists:concat([lists:reverse(Acc),L2]);
+enum_number(L,[{Id,Num}|T2],Cnt,Acc) -> % Num == Cnt
+ enum_number(L,T2,Cnt+1,[{Id,Num}|Acc]);
+enum_number([H|T],[],Cnt,Acc) ->
+ enum_number(T,[],Cnt+1,[{H,Cnt}|Acc]).
+
+
+check_boolean(_S,_Constr) ->
+ ok.
+
+check_octetstring(_S,_Constr) ->
+ ok.
+
+% check all aspects of a SEQUENCE
+% - that all component names are unique
+% - that all TAGS are ok (when TAG default is applied)
+% - that each component is of a valid type
+% - that the extension marks are valid
+
+check_sequence(S,Type,Comps) ->
+ Components = expand_components(S,Comps),
+ case check_unique([C||C <- Components ,record(C,'ComponentType')]
+ ,#'ComponentType'.name) of
+ [] ->
+ %% sort_canonical(Components),
+ Components2 = maybe_automatic_tags(S,Components),
+ %% check the table constraints from here. The outermost type
+ %% is Type, the innermost is Comps (the list of components)
+ NewComps =
+ case check_each_component(S,Type,Components2) of
+ NewComponents when list(NewComponents) ->
+ check_unique_sequence_tags(S,NewComponents),
+ NewComponents;
+ Ret = {NewComponents,NewEcomps} ->
+ TagComps = NewComponents ++
+ [Comp#'ComponentType'{prop='OPTIONAL'}|| Comp <- NewEcomps],
+ %% extension components are like optionals when it comes to tagging
+ check_unique_sequence_tags(S,TagComps),
+ Ret
+ end,
+ %% CRelInf is the "leading attribute" information
+ %% necessary for code generating of the look up in the
+ %% object set table,
+ %% i.e. getenc_ObjectSet/getdec_ObjectSet.
+ %% {objfun,ERef} tuple added in NewComps2 in tablecinf
+ %% field in type record of component relation constrained
+ %% type
+% io:format("NewComps: ~p~n",[NewComps]),
+ {CRelInf,NewComps2} = componentrelation_leadingattr(S,NewComps),
+% io:format("CRelInf: ~p~n",[CRelInf]),
+% io:format("NewComps2: ~p~n",[NewComps2]),
+ %% CompListWithTblInf has got a lot unecessary info about
+ %% the involved class removed, as the class of the object
+ %% set.
+ CompListWithTblInf = get_tableconstraint_info(S,Type,NewComps2),
+% io:format("CompListWithTblInf: ~p~n",[CompListWithTblInf]),
+ {CRelInf,CompListWithTblInf};
+ Dupl ->
+ throw({error,{asn1,{duplicate_components,Dupl}}})
+ end.
+
+expand_components(S, [{'COMPONENTS OF',Type}|T]) ->
+ CompList =
+ case get_referenced_type(S,Type#type.def) of
+ {_,#typedef{typespec=#type{def=Seq}}} when record(Seq,'SEQUENCE') ->
+ case Seq#'SEQUENCE'.components of
+ {Root,_Ext} -> Root;
+ Root -> Root
+ end;
+ Err -> throw({error,{asn1,{illegal_COMPONENTS_OF,Err}}})
+ end,
+ expand_components(S,CompList) ++ expand_components(S,T);
+expand_components(S,[H|T]) ->
+ [H|expand_components(S,T)];
+expand_components(_,[]) ->
+ [].
+
+check_unique_sequence_tags(S,[#'ComponentType'{prop=mandatory}|Rest]) ->
+ check_unique_sequence_tags(S,Rest);
+check_unique_sequence_tags(S,[C|Rest]) when record(C,'ComponentType') ->
+ check_unique_sequence_tags1(S,Rest,[C]);% optional or default
+check_unique_sequence_tags(S,[_ExtensionMarker|Rest]) ->
+ check_unique_sequence_tags(S,Rest);
+check_unique_sequence_tags(_S,[]) ->
+ true.
+
+check_unique_sequence_tags1(S,[C|Rest],Acc) when record(C,'ComponentType') ->
+ case C#'ComponentType'.prop of
+ mandatory ->
+ check_unique_tags(S,lists:reverse([C|Acc])),
+ check_unique_sequence_tags(S,Rest);
+ _ ->
+ check_unique_sequence_tags1(S,Rest,[C|Acc]) % default or optional
+ end;
+check_unique_sequence_tags1(S,[H|Rest],Acc) ->
+ check_unique_sequence_tags1(S,Rest,[H|Acc]);
+check_unique_sequence_tags1(S,[],Acc) ->
+ check_unique_tags(S,lists:reverse(Acc)).
+
+check_sequenceof(S,Type,Component) when record(Component,type) ->
+ check_type(S,Type,Component).
+
+check_set(S,Type,Components) ->
+ {TableCInf,NewComponents} = check_sequence(S,Type,Components),
+ case lists:member(der,S#state.options) of
+ true when S#state.erule == ber;
+ S#state.erule == ber_bin ->
+ {Sorted,SortedComponents} =
+ sort_components(S#state.tname,
+ (S#state.module)#module.tagdefault,
+ NewComponents),
+ {Sorted,TableCInf,SortedComponents};
+ _ ->
+ {false,TableCInf,NewComponents}
+ end.
+
+sort_components(_TypeName,'AUTOMATIC',Components) ->
+ {true,Components};
+sort_components(TypeName,_TagDefault,Components) ->
+ case untagged_choice(Components) of
+ false ->
+ {true,sort_components1(TypeName,Components,[],[],[],[])};
+ true ->
+ {dynamic,Components} % sort in run-time
+ end.
+
+sort_components1(TypeName,[C=#'ComponentType'{tags=[{'UNIVERSAL',_}|_R]}|Cs],
+ UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
+ sort_components1(TypeName,Cs,[C|UnivAcc],ApplAcc,ContAcc,PrivAcc);
+sort_components1(TypeName,[C=#'ComponentType'{tags=[{'APPLICATION',_}|_R]}|Cs],
+ UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
+ sort_components1(TypeName,Cs,UnivAcc,[C|ApplAcc],ContAcc,PrivAcc);
+sort_components1(TypeName,[C=#'ComponentType'{tags=[{'CONTEXT',_}|_R]}|Cs],
+ UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
+ sort_components1(TypeName,Cs,UnivAcc,ApplAcc,[C|ContAcc],PrivAcc);
+sort_components1(TypeName,[C=#'ComponentType'{tags=[{'PRIVATE',_}|_R]}|Cs],
+ UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
+ sort_components1(TypeName,Cs,UnivAcc,ApplAcc,ContAcc,[C|PrivAcc]);
+sort_components1(TypeName,[],UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
+ I = #'ComponentType'.tags,
+ ascending_order_check(TypeName,sort_universal_type(UnivAcc)) ++
+ ascending_order_check(TypeName,lists:keysort(I,ApplAcc)) ++
+ ascending_order_check(TypeName,lists:keysort(I,ContAcc)) ++
+ ascending_order_check(TypeName,lists:keysort(I,PrivAcc)).
+
+ascending_order_check(TypeName,Components) ->
+ ascending_order_check1(TypeName,Components),
+ Components.
+
+ascending_order_check1(TypeName,
+ [C1 = #'ComponentType'{tags=[{_,T}|_]},
+ C2 = #'ComponentType'{tags=[{_,T}|_]}|Rest]) ->
+ io:format("WARNING: Indistinct tag ~p in SET ~p, components ~p and ~p~n",
+ [T,TypeName,C1#'ComponentType'.name,C2#'ComponentType'.name]),
+ ascending_order_check1(TypeName,[C2|Rest]);
+ascending_order_check1(TypeName,
+ [C1 = #'ComponentType'{tags=[{'UNIVERSAL',T1}|_]},
+ C2 = #'ComponentType'{tags=[{'UNIVERSAL',T2}|_]}|Rest]) ->
+ case (asn1ct_gen_ber:decode_type(T1) == asn1ct_gen_ber:decode_type(T2)) of
+ true ->
+ io:format("WARNING: Indistinct tags ~p and ~p in"
+ " SET ~p, components ~p and ~p~n",
+ [T1,T2,TypeName,C1#'ComponentType'.name,
+ C2#'ComponentType'.name]),
+ ascending_order_check1(TypeName,[C2|Rest]);
+ _ ->
+ ascending_order_check1(TypeName,[C2|Rest])
+ end;
+ascending_order_check1(N,[_|Rest]) ->
+ ascending_order_check1(N,Rest);
+ascending_order_check1(_,[_]) ->
+ ok;
+ascending_order_check1(_,[]) ->
+ ok.
+
+sort_universal_type(Components) ->
+ List = lists:map(fun(C) ->
+ #'ComponentType'{tags=[{_,T}|_]} = C,
+ {asn1ct_gen_ber:decode_type(T),C}
+ end,
+ Components),
+ SortedList = lists:keysort(1,List),
+ lists:map(fun(X)->element(2,X) end,SortedList).
+
+untagged_choice([#'ComponentType'{typespec=#type{tag=[],def={'CHOICE',_}}}|_Rest]) ->
+ true;
+untagged_choice([_|Rest]) ->
+ untagged_choice(Rest);
+untagged_choice([]) ->
+ false.
+
+check_setof(S,Type,Component) when record(Component,type) ->
+ check_type(S,Type,Component).
+
+check_restrictedstring(_S,_Def,_Constr) ->
+ ok.
+
+check_objectidentifier(_S,_Constr) ->
+ ok.
+
+% check all aspects of a CHOICE
+% - that all alternative names are unique
+% - that all TAGS are ok (when TAG default is applied)
+% - that each alternative is of a valid type
+% - that the extension marks are valid
+check_choice(S,Type,Components) when list(Components) ->
+ case check_unique([C||C <- Components,
+ record(C,'ComponentType')],#'ComponentType'.name) of
+ [] ->
+ %% sort_canonical(Components),
+ Components2 = maybe_automatic_tags(S,Components),
+ %NewComps =
+ case check_each_alternative(S,Type,Components2) of
+ {NewComponents,NewEcomps} ->
+ check_unique_tags(S,NewComponents ++ NewEcomps),
+ {NewComponents,NewEcomps};
+ NewComponents ->
+ check_unique_tags(S,NewComponents),
+ NewComponents
+ end;
+%% CompListWithTblInf = get_tableconstraint_info(S,Type,NewComps);
+ Dupl ->
+ throw({error,{asn1,{duplicate_choice_alternatives,Dupl}}})
+ end;
+check_choice(_S,_,[]) ->
+ [].
+
+%% probably dead code that should be removed
+%%maybe_automatic_tags(S,{Rc,Ec}) ->
+%% {maybe_automatic_tags1(S,Rc,0),maybe_automatic_tags1(S,Ec,length(Rc))};
+maybe_automatic_tags(#state{erule=per},C) ->
+ C;
+maybe_automatic_tags(#state{erule=per_bin},C) ->
+ C;
+maybe_automatic_tags(S,C) ->
+ maybe_automatic_tags1(S,C,0).
+
+maybe_automatic_tags1(S,C,TagNo) ->
+ case (S#state.module)#module.tagdefault of
+ 'AUTOMATIC' ->
+ generate_automatic_tags(S,C,TagNo);
+ _ ->
+ %% maybe is the module a multi file module were only some of
+ %% the modules have defaulttag AUTOMATIC TAGS then the names
+ %% of those types are saved in the table automatic_tags
+ Name= S#state.tname,
+ case is_automatic_tagged_in_multi_file(Name) of
+ true ->
+ generate_automatic_tags(S,C,TagNo);
+ false ->
+ C
+ end
+ end.
+
+is_automatic_tagged_in_multi_file(Name) ->
+ case ets:info(automatic_tags) of
+ undefined ->
+ %% this case when not multifile compilation
+ false;
+ _ ->
+ case ets:member(automatic_tags,Name) of
+ true ->
+ true;
+ _ ->
+ false
+ end
+ end.
+
+generate_automatic_tags(_S,C,TagNo) ->
+ case any_manual_tag(C) of
+ true ->
+ C;
+ false ->
+ generate_automatic_tags1(C,TagNo)
+ end.
+
+generate_automatic_tags1([H|T],TagNo) when record(H,'ComponentType') ->
+ #'ComponentType'{typespec=Ts} = H,
+ NewTs = Ts#type{tag=[#tag{class='CONTEXT',
+ number=TagNo,
+ type={default,'IMPLICIT'},
+ form= 0 }]}, % PRIMITIVE
+ [H#'ComponentType'{typespec=NewTs}|generate_automatic_tags1(T,TagNo+1)];
+generate_automatic_tags1([ExtMark|T],TagNo) -> % EXTENSIONMARK
+ [ExtMark | generate_automatic_tags1(T,TagNo)];
+generate_automatic_tags1([],_) ->
+ [].
+
+any_manual_tag([#'ComponentType'{typespec=#type{tag=[]}}|Rest]) ->
+ any_manual_tag(Rest);
+any_manual_tag([{'EXTENSIONMARK',_,_}|Rest]) ->
+ any_manual_tag(Rest);
+any_manual_tag([_|_Rest]) ->
+ true;
+any_manual_tag([]) ->
+ false.
+
+
+check_unique_tags(S,C) ->
+ case (S#state.module)#module.tagdefault of
+ 'AUTOMATIC' ->
+ case any_manual_tag(C) of
+ false -> true;
+ _ -> collect_and_sort_tags(C,[])
+ end;
+ _ ->
+ collect_and_sort_tags(C,[])
+ end.
+
+collect_and_sort_tags([C|Rest],Acc) when record(C,'ComponentType') ->
+ collect_and_sort_tags(Rest,C#'ComponentType'.tags ++ Acc);
+collect_and_sort_tags([_|Rest],Acc) ->
+ collect_and_sort_tags(Rest,Acc);
+collect_and_sort_tags([],Acc) ->
+ {Dupl,_}= lists:mapfoldl(fun(El,El)->{{dup,El},El};(El,_Prev)-> {El,El} end,notag,lists:sort(Acc)),
+ Dupl2 = [Dup|| {dup,Dup} <- Dupl],
+ if
+ length(Dupl2) > 0 ->
+ throw({error,{asn1,{duplicates_of_the_tags,Dupl2}}});
+ true ->
+ true
+ end.
+
+check_unique(L,Pos) ->
+ Slist = lists:keysort(Pos,L),
+ check_unique2(Slist,Pos,[]).
+
+check_unique2([A,B|T],Pos,Acc) when element(Pos,A) == element(Pos,B) ->
+ check_unique2([B|T],Pos,[element(Pos,B)|Acc]);
+check_unique2([_|T],Pos,Acc) ->
+ check_unique2(T,Pos,Acc);
+check_unique2([],_,Acc) ->
+ lists:reverse(Acc).
+
+check_each_component(S,Type,{Rlist,ExtList}) ->
+ {check_each_component(S,Type,Rlist),
+ check_each_component(S,Type,ExtList)};
+check_each_component(S,Type,Components) ->
+ check_each_component(S,Type,Components,[],[],noext).
+
+check_each_component(S = #state{abscomppath=Path,recordtopname=TopName},Type,
+ [C|Ct],Acc,Extacc,Ext) when record(C,'ComponentType') ->
+ #'ComponentType'{name=Cname,typespec=Ts,prop=Prop} = C,
+ NewAbsCPath =
+ case Ts#type.def of
+ #'Externaltypereference'{} -> [];
+ _ -> [Cname|Path]
+ end,
+ CheckedTs = check_type(S#state{abscomppath=NewAbsCPath,
+ recordtopname=[Cname|TopName]},Type,Ts),
+ NewTags = get_taglist(S,CheckedTs),
+
+ NewProp =
+% case lists:member(der,S#state.options) of
+% true ->
+% True ->
+ case normalize_value(S,CheckedTs,Prop,[Cname|TopName]) of
+ mandatory -> mandatory;
+ 'OPTIONAL' -> 'OPTIONAL';
+ DefaultValue -> {'DEFAULT',DefaultValue}
+ end,
+% _ ->
+% Prop
+% end,
+ NewC = C#'ComponentType'{typespec=CheckedTs,prop=NewProp,tags=NewTags},
+ case Ext of
+ noext ->
+ check_each_component(S,Type,Ct,[NewC|Acc],Extacc,Ext);
+ ext ->
+ check_each_component(S,Type,Ct,Acc,[NewC|Extacc],Ext)
+ end;
+check_each_component(S,Type,[_|Ct],Acc,Extacc,noext) -> % skip 'EXTENSIONMARK'
+ check_each_component(S,Type,Ct,Acc,Extacc,ext);
+check_each_component(_S,_,[_C|_Ct],_,_,ext) -> % skip 'EXTENSIONMARK'
+ throw({error,{asn1,{too_many_extension_marks}}});
+check_each_component(_S,_,[],Acc,Extacc,ext) ->
+ {lists:reverse(Acc),lists:reverse(Extacc)};
+check_each_component(_S,_,[],Acc,_,noext) ->
+ lists:reverse(Acc).
+
+check_each_alternative(S,Type,{Rlist,ExtList}) ->
+ {check_each_alternative(S,Type,Rlist),
+ check_each_alternative(S,Type,ExtList)};
+check_each_alternative(S,Type,[C|Ct]) ->
+ check_each_alternative(S,Type,[C|Ct],[],[],noext).
+
+check_each_alternative(S=#state{abscomppath=Path,recordtopname=TopName},Type,[C|Ct],
+ Acc,Extacc,Ext) when record(C,'ComponentType') ->
+ #'ComponentType'{name=Cname,typespec=Ts,prop=_Prop} = C,
+ NewAbsCPath =
+ case Ts#type.def of
+ #'Externaltypereference'{} -> [];
+ _ -> [Cname|Path]
+ end,
+ NewState =
+ S#state{abscomppath=NewAbsCPath,recordtopname=[Cname|TopName]},
+ CheckedTs = check_type(NewState,Type,Ts),
+ NewTags = get_taglist(S,CheckedTs),
+ NewC = C#'ComponentType'{typespec=CheckedTs,tags=NewTags},
+ case Ext of
+ noext ->
+ check_each_alternative(S,Type,Ct,[NewC|Acc],Extacc,Ext);
+ ext ->
+ check_each_alternative(S,Type,Ct,Acc,[NewC|Extacc],Ext)
+ end;
+
+check_each_alternative(S,Type,[_|Ct],Acc,Extacc,noext) -> % skip 'EXTENSIONMARK'
+ check_each_alternative(S,Type,Ct,Acc,Extacc,ext);
+check_each_alternative(_S,_,[_C|_Ct],_,_,ext) -> % skip 'EXTENSIONMARK'
+ throw({error,{asn1,{too_many_extension_marks}}});
+check_each_alternative(_S,_,[],Acc,Extacc,ext) ->
+ {lists:reverse(Acc),lists:reverse(Extacc)};
+check_each_alternative(_S,_,[],Acc,_,noext) ->
+ lists:reverse(Acc).
+
+%% componentrelation_leadingattr/2 searches the structure for table
+%% constraints, if any is found componentrelation_leadingattr/5 is
+%% called.
+componentrelation_leadingattr(S,CompList) ->
+% {Cs1,Cs2} =
+ Cs =
+ case CompList of
+ {Components,EComponents} when list(Components) ->
+% {Components,Components};
+ Components ++ EComponents;
+ CompList when list(CompList) ->
+% {CompList,CompList}
+ CompList
+ end,
+% case any_simple_table(S,Cs1,[]) of
+
+ %% get_simple_table_if_used/2 should find out whether there are any
+ %% component relation constraints in the entire tree of Cs1 that
+ %% relates to this level. It returns information about the simple
+ %% table constraint necessary for the the call to
+ %% componentrelation_leadingattr/6. The step when the leading
+ %% attribute and the syntax tree is modified to support the code
+ %% generating.
+ case get_simple_table_if_used(S,Cs) of
+ [] -> {false,CompList};
+ STList ->
+% componentrelation_leadingattr(S,Cs1,Cs2,STList,[],[])
+ componentrelation_leadingattr(S,Cs,Cs,STList,[],[])
+ end.
+
+%% componentrelation_leadingattr/6 when all components are searched
+%% the new modified components are returned together with the "leading
+%% attribute" information, which later is stored in the tablecinf
+%% field in the SEQUENCE/SET record. The "leading attribute"
+%% information is used to generate the lookup in the object set
+%% table. The other information gathered in the #type.tablecinf field
+%% is used in code generating phase too, to recognice the proper
+%% components for "open type" encoding and to propagate the result of
+%% the object set lookup when needed.
+componentrelation_leadingattr(_,[],_CompList,_,[],NewCompList) ->
+ {false,lists:reverse(NewCompList)};
+componentrelation_leadingattr(_,[],_CompList,_,LeadingAttr,NewCompList) ->
+ {lists:last(LeadingAttr),lists:reverse(NewCompList)}; %send all info in Ts later
+componentrelation_leadingattr(S,[C|Cs],CompList,STList,Acc,CompAcc) ->
+ {LAAcc,NewC} =
+ case catch componentrelation1(S,C#'ComponentType'.typespec,
+ [C#'ComponentType'.name]) of
+ {'EXIT',_} ->
+ {[],C};
+ {CRI=[{_A1,_B1,_C1,_D1}|_Rest],NewTSpec} ->
+ %% {ObjectSet,AtPath,ClassDef,Path}
+ %% _A1 is a reference to the object set of the
+ %% component relation constraint.
+ %% _B1 is the path of names in the at-list of the
+ %% component relation constraint.
+ %% _C1 is the class definition of the
+ %% ObjectClassFieldType.
+ %% _D1 is the path of components that was traversed to
+ %% find this constraint.
+ case leading_attr_index(S,CompList,CRI,
+ lists:reverse(S#state.abscomppath),[]) of
+ [] ->
+ {[],C};
+ [{ObjSet,Attr,N,ClassDef,_Path,ValueIndex}|_NewRest] ->
+ OS = object_set_mod_name(S,ObjSet),
+ UniqueFieldName =
+ case (catch get_unique_fieldname(#classdef{typespec=ClassDef})) of
+ {error,'__undefined_'} ->
+ no_unique;
+ {asn1,Msg,_} ->
+ error({type,Msg,S});
+ Other -> Other
+ end,
+% UsedFieldName = get_used_fieldname(S,Attr,STList),
+ %% Res should be done differently: even though
+ %% a unique field name exists it is not
+ %% certain that the ObjectClassFieldType of
+ %% the simple table constraint picks that
+ %% class field.
+ Res = #simpletableattributes{objectsetname=OS,
+%% c_name=asn1ct_gen:un_hyphen_var(Attr),
+ c_name=Attr,
+ c_index=N,
+ usedclassfield=UniqueFieldName,
+ uniqueclassfield=UniqueFieldName,
+ valueindex=ValueIndex},
+ {[Res],C#'ComponentType'{typespec=NewTSpec}}
+ end;
+ _ ->
+ %% no constraint was found
+ {[],C}
+ end,
+ componentrelation_leadingattr(S,Cs,CompList,STList,LAAcc++Acc,
+ [NewC|CompAcc]).
+
+object_set_mod_name(_S,ObjSet) when atom(ObjSet) ->
+ ObjSet;
+object_set_mod_name(#state{mname=M},
+ #'Externaltypereference'{module=M,type=T}) ->
+ T;
+object_set_mod_name(S,#'Externaltypereference'{module=M,type=T}) ->
+ case lists:member(M,S#state.inputmodules) of
+ true ->
+ T;
+ false ->
+ {M,T}
+ end.
+
+%% get_used_fieldname gets the used field of the class referenced by
+%% the ObjectClassFieldType construct in the simple table constraint
+%% corresponding to the component relation constraint that depends on
+%% it.
+% get_used_fieldname(_S,CName,[{[CName|_Rest],_,ClFieldName}|_RestSimpleT]) ->
+% ClFieldName;
+% get_used_fieldname(S,CName,[_SimpleTC|Rest]) ->
+% get_used_fieldname(S,CName,Rest);
+% get_used_fieldname(S,_,[]) ->
+% error({type,"Error in Simple table constraint",S}).
+
+%% any_simple_table/3 checks if any of the components on this level is
+%% constrained by a simple table constraint. It returns a list of
+%% tuples with three elements. It is a name path to the place in the
+%% type structure where the constraint is, and the name of the object
+%% set and the referenced field in the class.
+% any_simple_table(S = #state{mname=M,abscomppath=Path},
+% [#'ComponentType'{name=Name,typespec=Type}|Cs],Acc) ->
+% Constraint = Type#type.constraint,
+% case lists:keysearch(simpletable,1,Constraint) of
+% {value,{_,#type{def=Ref}}} ->
+% %% This ObjectClassFieldType, which has a simple table
+% %% constraint, must pick a fixed type value, mustn't it ?
+% {ClassDef,[{_,ClassFieldName}]} = Type#type.def,
+% ST =
+% case Ref of
+% #'Externaltypereference'{module=M,type=ObjSetName} ->
+% {[Name|Path],ObjSetName,ClassFieldName};
+% _ ->
+% {[Name|Path],Ref,ClassFieldName}
+% end,
+% any_simple_table(S,Cs,[ST|Acc]);
+% false ->
+% any_simple_table(S,Cs,Acc)
+% end;
+% any_simple_table(_,[],Acc) ->
+% lists:reverse(Acc);
+% any_simple_table(S,[_|Cs],Acc) ->
+% any_simple_table(S,Cs,Acc).
+
+%% get_simple_table_if_used/2 searches the structure of Cs for any
+%% component relation constraints due to the present level of the
+%% structure. If there are any, the necessary information for code
+%% generation of the look up functionality in the object set table are
+%% returned.
+get_simple_table_if_used(S,Cs) ->
+ CNames = lists:map(fun(#'ComponentType'{name=Name}) -> Name;
+ (_) -> [] %% in case of extension marks
+ end,
+ Cs),
+ RefedSimpleTable=any_component_relation(S,Cs,CNames,[],[]),
+ get_simple_table_info(S,Cs,remove_doubles(RefedSimpleTable)).
+
+remove_doubles(L) ->
+ remove_doubles(L,[]).
+remove_doubles([H|T],Acc) ->
+ NewT = remove_doubles1(H,T),
+ remove_doubles(NewT,[H|Acc]);
+remove_doubles([],Acc) ->
+ Acc.
+
+remove_doubles1(El,L) ->
+ case lists:delete(El,L) of
+ L -> L;
+ NewL -> remove_doubles1(El,NewL)
+ end.
+
+%% get_simple_table_info searches the commponents Cs by the path from
+%% an at-list (third argument), and follows into a component of it if
+%% necessary, to get information needed for code generating.
+%%
+%% Returns a list of tuples with three elements. It holds a list of
+%% atoms that is the path, the name of the field of the class that are
+%% referred to in the ObjectClassFieldType, and the name of the unique
+%% field of the class of the ObjectClassFieldType.
+%%
+% %% The level information outermost/innermost must be kept. There are
+% %% at least two possibilities to cover here for an outermost case: 1)
+% %% Both the simple table and the component relation have a common path
+% %% at least one step below the outermost level, i.e. the leading
+% %% information shall be on a sub level. 2) They don't have any common
+% %% path.
+get_simple_table_info(S,Cs,[AtList|Rest]) ->
+%% [get_simple_table_info1(S,Cs,AtList,S#state.abscomppath)|get_simple_table_info(S,Cs,Rest)];
+ [get_simple_table_info1(S,Cs,AtList,[])|get_simple_table_info(S,Cs,Rest)];
+get_simple_table_info(_,_,[]) ->
+ [].
+get_simple_table_info1(S,Cs,[Cname|Cnames],Path) when list(Cs) ->
+ case lists:keysearch(Cname,#'ComponentType'.name,Cs) of
+ {value,C} ->
+ get_simple_table_info1(S,C,Cnames,[Cname|Path]);
+ _ ->
+ error({type,"Missing expected simple table constraint",S})
+ end;
+get_simple_table_info1(S,#'ComponentType'{typespec=TS},[],Path) ->
+ %% In this component there must be a simple table constraint
+ %% o.w. the asn1 code is wrong.
+ #type{def=OCFT,constraint=Cnstr} = TS,
+ case Cnstr of
+ [{simpletable,_OSRef}] ->
+ #'ObjectClassFieldType'{classname=ClRef,
+ class=ObjectClass,
+ fieldname=FieldName} = OCFT,
+% #'ObjectClassFieldType'{ObjectClass,FieldType} = ObjectClassFieldType,
+ ObjectClassFieldName =
+ case FieldName of
+ {LastFieldName,[]} -> LastFieldName;
+ {_FirstFieldName,FieldNames} ->
+ lists:last(FieldNames)
+ end,
+ %%ObjectClassFieldName is the last element in the dotted
+ %%list of the ObjectClassFieldType. The last element may
+ %%be of another class, that is referenced from the class
+ %%of the ObjectClassFieldType
+ ClassDef =
+ case ObjectClass of
+ [] ->
+ {_,CDef}=get_referenced_type(S,ClRef),
+ CDef;
+ _ -> #classdef{typespec=ObjectClass}
+ end,
+ UniqueName =
+ case (catch get_unique_fieldname(ClassDef)) of
+ {error,'__undefined_'} -> no_unique;
+ {asn1,Msg,_} ->
+ error({type,Msg,S});
+ Other -> Other
+ end,
+ {lists:reverse(Path),ObjectClassFieldName,UniqueName};
+ _ ->
+ error({type,{asn1,"missing expected simple table constraint",
+ Cnstr},S})
+ end;
+get_simple_table_info1(S,#'ComponentType'{typespec=TS},Cnames,Path) ->
+ Components = get_atlist_components(TS#type.def),
+ get_simple_table_info1(S,Components,Cnames,Path).
+
+%% any_component_relation searches for all component relation
+%% constraints that refers to the actual level and returns a list of
+%% the "name path" in the at-list to the component relation constraint
+%% that must refer to a simple table constraint. The list is empty if
+%% no component relation constraints were found.
+%%
+%% NamePath has the names of all components that are followed from the
+%% beginning of the search. CNames holds the names of all components
+%% of the start level, this info is used if an outermost at-notation
+%% is found to check the validity of the at-list.
+any_component_relation(S,[C|Cs],CNames,NamePath,Acc) ->
+ CName = C#'ComponentType'.name,
+ Type = C#'ComponentType'.typespec,
+ CRelPath =
+ case Type#type.constraint of
+ [{componentrelation,_,AtNotation}] ->
+ %% Found component relation constraint, now check
+ %% whether this constraint is relevant for the level
+ %% where the search started
+ AtNot = extract_at_notation(AtNotation),
+ %% evaluate_atpath returns the relative path to the
+ %% simple table constraint from where the component
+ %% relation is found.
+ evaluate_atpath(S#state.abscomppath,NamePath,CNames,AtNot);
+ _ ->
+ []
+ end,
+ InnerAcc =
+ case {Type#type.inlined,
+ asn1ct_gen:type(asn1ct_gen:get_inner(Type#type.def))} of
+ {no,{constructed,bif}} ->
+ InnerCs =
+ case get_components(Type#type.def) of
+ {IC1,_IC2} -> IC1 ++ IC1;
+ IC -> IC
+ end,
+ %% here we are interested in components of an
+ %% SEQUENCE/SET OF as well as SEQUENCE, SET and CHOICE
+ any_component_relation(S,InnerCs,CNames,[CName|NamePath],[]);
+ _ ->
+ []
+ end,
+ any_component_relation(S,Cs,CNames,NamePath,InnerAcc++CRelPath++Acc);
+any_component_relation(_,[],_,_,Acc) ->
+ Acc.
+
+%% evaluate_atpath/4 finds out whether the at notation refers to the
+%% search level. The list of referenced names in the AtNot list shall
+%% begin with a name that exists on the level it refers to. If the
+%% found AtPath is refering to the same sub-branch as the simple table
+%% has, then there shall not be any leading attribute info on this
+%% level.
+evaluate_atpath(_,[],Cnames,{innermost,AtPath=[Ref|_Refs]}) ->
+ %% any innermost constraint found deeper in the structure is
+ %% ignored.
+ case lists:member(Ref,Cnames) of
+ true -> [AtPath];
+ false -> []
+ end;
+%% In this case must check that the AtPath doesn't step any step of
+%% the NamePath, in that case the constraint will be handled in an
+%% inner level.
+evaluate_atpath(TopPath,NamePath,Cnames,{outermost,AtPath=[_Ref|_Refs]}) ->
+ AtPathBelowTop =
+ case TopPath of
+ [] -> AtPath;
+ _ ->
+ case lists:prefix(TopPath,AtPath) of
+ true ->
+ lists:subtract(AtPath,TopPath);
+ _ -> []
+ end
+ end,
+ case {NamePath,AtPathBelowTop} of
+ {[H|_T1],[H|_T2]} -> []; % this must be handled in lower level
+ {_,[]} -> [];% this must be handled in an above level
+ {_,[H|_T]} ->
+ case lists:member(H,Cnames) of
+ true -> [AtPathBelowTop];
+ _ -> error({type,{asn1,"failed to analyze at-path",AtPath}})
+ end
+ end;
+evaluate_atpath(_,_,_,_) ->
+ [].
+
+%% Type may be any of SEQUENCE, SET, CHOICE, SEQUENCE OF, SET OF but
+%% only the three first have valid components.
+get_atlist_components(Def) ->
+ get_components(atlist,Def).
+
+get_components(Def) ->
+ get_components(any,Def).
+
+get_components(_,#'SEQUENCE'{components=Cs}) ->
+ Cs;
+get_components(_,#'SET'{components=Cs}) ->
+ Cs;
+get_components(_,{'CHOICE',Cs}) ->
+ Cs;
+get_components(any,{'SEQUENCE OF',#type{def=Def}}) ->
+ get_components(any,Def);
+get_components(any,{'SET OF',#type{def=Def}}) ->
+ get_components(any,Def);
+get_components(_,_) ->
+ [].
+
+
+extract_at_notation([{Level,[#'Externalvaluereference'{value=Name}|Rest]}]) ->
+ {Level,[Name|extract_at_notation1(Rest)]};
+extract_at_notation(At) ->
+ exit({error,{asn1,{at_notation,At}}}).
+extract_at_notation1([#'Externalvaluereference'{value=Name}|Rest]) ->
+ [Name|extract_at_notation1(Rest)];
+extract_at_notation1([]) ->
+ [].
+
+%% componentrelation1/1 identifies all componentrelation constraints
+%% that exist in C or in the substructure of C. Info about the found
+%% constraints are returned in a list. It is ObjectSet, the reference
+%% to the object set, AttrPath, the name atoms extracted from the
+%% at-list in the component relation constraint, ClassDef, the
+%% objectclass record of the class of the ObjectClassFieldType, Path,
+%% that is the component name "path" from the searched level to this
+%% constraint.
+%%
+%% The function is called with one component of the type in turn and
+%% with the component name in Path at the first call. When called from
+%% within, the name of the inner component is added to Path.
+componentrelation1(S,C = #type{def=Def,constraint=Constraint,tablecinf=TCI},
+ Path) ->
+ Ret =
+ case Constraint of
+ [{componentrelation,{_,_,ObjectSet},AtList}|_Rest] ->
+ [{_,AL=[#'Externalvaluereference'{}|_R1]}|_R2] = AtList,
+ %% Note: if Path is longer than one,i.e. it is within
+ %% an inner type of the actual level, then the only
+ %% relevant at-list is of "outermost" type.
+%% #'ObjectClassFieldType'{class=ClassDef} = Def,
+ ClassDef = get_ObjectClassFieldType_classdef(S,Def),
+ AtPath =
+ lists:map(fun(#'Externalvaluereference'{value=V})->V end,
+ AL),
+ {[{ObjectSet,AtPath,ClassDef,Path}],Def};
+ _Other ->
+ %% check the inner type of component
+ innertype_comprel(S,Def,Path)
+ end,
+ case Ret of
+ nofunobj ->
+ nofunobj; %% ignored by caller
+ {CRelI=[{ObjSet,_,_,_}],NewDef} -> %%
+ TCItmp = lists:subtract(TCI,[{objfun,ObjSet}]),
+ {CRelI,C#type{tablecinf=[{objfun,ObjSet}|TCItmp],def=NewDef}};
+ {CompRelInf,NewDef} -> %% more than one tuple in CompRelInf
+ TCItmp = lists:subtract(TCI,[{objfun,anyset}]),
+ {CompRelInf,C#type{tablecinf=[{objfun,anyset}|TCItmp],def=NewDef}}
+ end.
+
+innertype_comprel(S,{'SEQUENCE OF',Type},Path) ->
+ case innertype_comprel1(S,Type,Path) of
+ nofunobj ->
+ nofunobj;
+ {CompRelInf,NewType} ->
+ {CompRelInf,{'SEQUENCE OF',NewType}}
+ end;
+innertype_comprel(S,{'SET OF',Type},Path) ->
+ case innertype_comprel1(S,Type,Path) of
+ nofunobj ->
+ nofunobj;
+ {CompRelInf,NewType} ->
+ {CompRelInf,{'SET OF',NewType}}
+ end;
+innertype_comprel(S,{'CHOICE',CTypeList},Path) ->
+ case componentlist_comprel(S,CTypeList,[],Path,[]) of
+ nofunobj ->
+ nofunobj;
+ {CompRelInf,NewCs} ->
+ {CompRelInf,{'CHOICE',NewCs}}
+ end;
+innertype_comprel(S,Seq = #'SEQUENCE'{components=Cs},Path) ->
+ case componentlist_comprel(S,Cs,[],Path,[]) of
+ nofunobj ->
+ nofunobj;
+ {CompRelInf,NewCs} ->
+ {CompRelInf,Seq#'SEQUENCE'{components=NewCs}}
+ end;
+innertype_comprel(S,Set = #'SET'{components=Cs},Path) ->
+ case componentlist_comprel(S,Cs,[],Path,[]) of
+ nofunobj ->
+ nofunobj;
+ {CompRelInf,NewCs} ->
+ {CompRelInf,Set#'SET'{components=NewCs}}
+ end;
+innertype_comprel(_,_,_) ->
+ nofunobj.
+
+componentlist_comprel(S,[C = #'ComponentType'{name=Name,typespec=Type}|Cs],
+ Acc,Path,NewCL) ->
+ case catch componentrelation1(S,Type,Path++[Name]) of
+ {'EXIT',_} ->
+ componentlist_comprel(S,Cs,Acc,Path,[C|NewCL]);
+ nofunobj ->
+ componentlist_comprel(S,Cs,Acc,Path,[C|NewCL]);
+ {CRelInf,NewType} ->
+ componentlist_comprel(S,Cs,CRelInf++Acc,Path,
+ [C#'ComponentType'{typespec=NewType}|NewCL])
+ end;
+componentlist_comprel(_,[],Acc,_,NewCL) ->
+ case Acc of
+ [] ->
+ nofunobj;
+ _ ->
+ {Acc,lists:reverse(NewCL)}
+ end.
+
+innertype_comprel1(S,T = #type{def=Def,constraint=Cons,tablecinf=TCI},Path) ->
+ Ret =
+ case Cons of
+ [{componentrelation,{_,_,ObjectSet},AtList}|_Rest] ->
+ %% This AtList must have an "outermost" at sign to be
+ %% relevent here.
+ [{_,AL=[#'Externalvaluereference'{value=_Attr}|_R1]}|_R2]
+ = AtList,
+%% #'ObjectClassFieldType'{class=ClassDef} = Def,
+ ClassDef = get_ObjectClassFieldType_classdef(S,Def),
+ AtPath =
+ lists:map(fun(#'Externalvaluereference'{value=V})->V end,
+ AL),
+ [{ObjectSet,AtPath,ClassDef,Path}];
+ _ ->
+ innertype_comprel(S,Def,Path)
+ end,
+ case Ret of
+ nofunobj -> nofunobj;
+ L = [{ObjSet,_,_,_}] ->
+ TCItmp = lists:subtract(TCI,[{objfun,ObjSet}]),
+ {L,T#type{tablecinf=[{objfun,ObjSet}|TCItmp]}};
+ {CRelInf,NewDef} ->
+ TCItmp = lists:subtract(TCI,[{objfun,anyset}]),
+ {CRelInf,T#type{def=NewDef,tablecinf=[{objfun,anyset}|TCItmp]}}
+ end.
+
+
+%% leading_attr_index counts the index and picks the name of the
+%% component that is at the actual level in the at-list of the
+%% component relation constraint (AttrP). AbsP is the path of
+%% component names from the top type level to the actual level. AttrP
+%% is a list with the atoms from the at-list.
+leading_attr_index(S,Cs,[H={_,AttrP,_,_}|T],AbsP,Acc) ->
+ AttrInfo =
+ case lists:prefix(AbsP,AttrP) of
+ %% why this ?? It is necessary when in same situation as
+ %% TConstrChoice, there is an inner structure with an
+ %% outermost at-list and the "leading attribute" code gen
+ %% may be at a level some steps below the outermost level.
+ true ->
+ RelativAttrP = lists:subtract(AttrP,AbsP),
+ %% The header is used to calculate the index of the
+ %% component and to give the fun, received from the
+ %% object set look up, an unique name. The tail is
+ %% used to match the proper value input to the fun.
+ {hd(RelativAttrP),tl(RelativAttrP)};
+ false ->
+ {hd(AttrP),tl(AttrP)}
+ end,
+ case leading_attr_index1(S,Cs,H,AttrInfo,1) of
+ 0 ->
+ leading_attr_index(S,Cs,T,AbsP,Acc);
+ Res ->
+ leading_attr_index(S,Cs,T,AbsP,[Res|Acc])
+ end;
+leading_attr_index(_,_Cs,[],_,Acc) ->
+ lists:reverse(Acc).
+
+leading_attr_index1(_,[],_,_,_) ->
+ 0;
+leading_attr_index1(S,[C|Cs],Arg={ObjectSet,_,CDef,P},
+ AttrInfo={Attr,SubAttr},N) ->
+ case C#'ComponentType'.name of
+ Attr ->
+ ValueMatch = value_match(S,C,Attr,SubAttr),
+ {ObjectSet,Attr,N,CDef,P,ValueMatch};
+ _ ->
+ leading_attr_index1(S,Cs,Arg,AttrInfo,N+1)
+ end.
+
+%% value_math gathers information for a proper value match in the
+%% generated encode function. For a SEQUENCE or a SET the index of the
+%% component is counted. For a CHOICE the index is 2.
+value_match(S,C,Name,SubAttr) ->
+ value_match(S,C,Name,SubAttr,[]). % C has name Name
+value_match(_S,#'ComponentType'{},_Name,[],Acc) ->
+ Acc;% do not reverse, indexes in reverse order
+value_match(S,#'ComponentType'{typespec=Type},Name,[At|Ats],Acc) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ Components =
+ case get_atlist_components(Type#type.def) of
+ [] -> error({type,{asn1,"element in at list must be a "
+ "SEQUENCE, SET or CHOICE.",Name},S});
+ Comps -> Comps
+ end,
+ {Index,ValueIndex} = component_value_index(S,InnerType,At,Components),
+ value_match(S,lists:nth(Index,Components),At,Ats,[ValueIndex|Acc]).
+
+component_value_index(S,'CHOICE',At,Components) ->
+ {component_index(S,At,Components),2};
+component_value_index(S,_,At,Components) ->
+ %% SEQUENCE or SET
+ Index = component_index(S,At,Components),
+ {Index,{Index+1,At}}.
+
+component_index(S,Name,Components) ->
+ component_index1(S,Name,Components,1).
+component_index1(_S,Name,[#'ComponentType'{name=Name}|_Cs],N) ->
+ N;
+component_index1(S,Name,[_C|Cs],N) ->
+ component_index1(S,Name,Cs,N+1);
+component_index1(S,Name,[],_) ->
+ error({type,{asn1,"component of at-list was not"
+ " found in substructure",Name},S}).
+
+get_unique_fieldname(ClassDef) ->
+%% {_,Fields,_} = ClassDef#classdef.typespec,
+ Fields = (ClassDef#classdef.typespec)#objectclass.fields,
+ get_unique_fieldname(Fields,[]).
+
+get_unique_fieldname([],[]) ->
+ throw({error,'__undefined_'});
+get_unique_fieldname([],[Name]) ->
+ Name;
+get_unique_fieldname([],Acc) ->
+ throw({asn1,'only one UNIQUE field is allowed in CLASS',Acc});
+get_unique_fieldname([{fixedtypevaluefield,Name,_,'UNIQUE',_}|Rest],Acc) ->
+ get_unique_fieldname(Rest,[Name|Acc]);
+get_unique_fieldname([_H|T],Acc) ->
+ get_unique_fieldname(T,Acc).
+
+get_tableconstraint_info(S,Type,{CheckedTs,EComps}) ->
+ {get_tableconstraint_info(S,Type,CheckedTs,[]),
+ get_tableconstraint_info(S,Type,EComps,[])};
+get_tableconstraint_info(S,Type,CheckedTs) ->
+ get_tableconstraint_info(S,Type,CheckedTs,[]).
+
+get_tableconstraint_info(_S,_Type,[],Acc) ->
+ lists:reverse(Acc);
+get_tableconstraint_info(S,Type,[C|Cs],Acc) ->
+ CheckedTs = C#'ComponentType'.typespec,
+ AccComp =
+ case CheckedTs#type.def of
+ %% ObjectClassFieldType
+ OCFT=#'ObjectClassFieldType'{class=#objectclass{},
+ type=_AType} ->
+% AType = get_ObjectClassFieldType(S,Fields,FieldRef),
+% RefedFieldName =
+% get_referencedclassfield(CheckedTs#type.def),%is probably obsolete
+ NewOCFT =
+ OCFT#'ObjectClassFieldType'{class=[]},
+ C#'ComponentType'{typespec=
+ CheckedTs#type{
+% def=AType,
+ def=NewOCFT
+ }};
+% constraint=[{tableconstraint_info,
+% FieldRef}]}};
+ {'SEQUENCE OF',SOType} when record(SOType,type),
+ (element(1,SOType#type.def)=='CHOICE') ->
+ CTypeList = element(2,SOType#type.def),
+ NewInnerCList =
+ get_tableconstraint_info(S,Type,CTypeList,[]),
+ C#'ComponentType'{typespec=
+ CheckedTs#type{
+ def={'SEQUENCE OF',
+ SOType#type{def={'CHOICE',
+ NewInnerCList}}}}};
+ {'SET OF',SOType} when record(SOType,type),
+ (element(1,SOType#type.def)=='CHOICE') ->
+ CTypeList = element(2,SOType#type.def),
+ NewInnerCList =
+ get_tableconstraint_info(S,Type,CTypeList,[]),
+ C#'ComponentType'{typespec=
+ CheckedTs#type{
+ def={'SET OF',
+ SOType#type{def={'CHOICE',
+ NewInnerCList}}}}};
+ _ ->
+ C
+ end,
+ get_tableconstraint_info(S,Type,Cs,[AccComp|Acc]).
+
+get_referenced_fieldname([{_,FirstFieldname}]) ->
+ {FirstFieldname,[]};
+get_referenced_fieldname([{_,FirstFieldname}|Rest]) ->
+ {FirstFieldname,lists:map(fun(X)->element(2,X) end,Rest)};
+get_referenced_fieldname(Def) ->
+ {no_type,Def}.
+
+%% get_ObjectClassFieldType extracts the type from the chain of
+%% objects that leads to a final type.
+get_ObjectClassFieldType(S,ERef,PrimFieldNameList) when
+ record(ERef,'Externaltypereference') ->
+ {_,Type} = get_referenced_type(S,ERef),
+ ClassSpec = check_class(S,Type),
+ Fields = ClassSpec#objectclass.fields,
+ get_ObjectClassFieldType(S,Fields,PrimFieldNameList);
+get_ObjectClassFieldType(S,Fields,L=[_PrimFieldName1|_Rest]) ->
+ check_PrimitiveFieldNames(S,Fields,L),
+ get_OCFType(S,Fields,L).
+
+check_PrimitiveFieldNames(_S,_Fields,_) ->
+ ok.
+
+%% get_ObjectClassFieldType_classdef gets the def of the class of the
+%% ObjectClassFieldType, i.e. the objectclass record. If the type has
+%% been checked (it may be a field type of an internal SEQUENCE) the
+%% class field = [], then the classdef has to be fetched by help of
+%% the class reference in the classname field.
+get_ObjectClassFieldType_classdef(S,#'ObjectClassFieldType'{classname=Name,
+ class=[]}) ->
+ {_,#classdef{typespec=TS}} = get_referenced_type(S,Name),
+ TS;
+get_ObjectClassFieldType_classdef(_,#'ObjectClassFieldType'{class=Cl}) ->
+ Cl.
+
+get_OCFType(S,Fields,[{_FieldType,PrimFieldName}|Rest]) ->
+ case lists:keysearch(PrimFieldName,2,Fields) of
+ {value,{fixedtypevaluefield,_,Type,_Unique,_OptSpec}} ->
+ {fixedtypevaluefield,PrimFieldName,Type};
+ {value,{objectfield,_,Type,_Unique,_OptSpec}} ->
+ {_,ClassDef} = get_referenced_type(S,Type#type.def),
+ CheckedCDef = check_class(S#state{type=ClassDef,
+ tname=ClassDef#classdef.name},
+ ClassDef#classdef.typespec),
+ get_OCFType(S,CheckedCDef#objectclass.fields,Rest);
+ {value,{objectsetfield,_,Type,_OptSpec}} ->
+ {_,ClassDef} = get_referenced_type(S,Type#type.def),
+ CheckedCDef = check_class(S#state{type=ClassDef,
+ tname=ClassDef#classdef.name},
+ ClassDef#classdef.typespec),
+ get_OCFType(S,CheckedCDef#objectclass.fields,Rest);
+
+ {value,Other} ->
+ {element(1,Other),PrimFieldName};
+ _ ->
+ error({type,"undefined FieldName in ObjectClassFieldType",S})
+ end.
+
+get_taglist(#state{erule=per},_) ->
+ [];
+get_taglist(#state{erule=per_bin},_) ->
+ [];
+get_taglist(S,Ext) when record(Ext,'Externaltypereference') ->
+ {_,T} = get_referenced_type(S,Ext),
+ get_taglist(S,T#typedef.typespec);
+get_taglist(S,Tref) when record(Tref,typereference) ->
+ {_,T} = get_referenced_type(S,Tref),
+ get_taglist(S,T#typedef.typespec);
+get_taglist(S,Type) when record(Type,type) ->
+ case Type#type.tag of
+ [] ->
+ get_taglist(S,Type#type.def);
+ [Tag|_] ->
+% case lists:member(S#state.erule,[ber,ber_bin]) of
+% true ->
+% lists:map(fun(Tx) -> asn1ct_gen:def_to_tag(Tx) end,Type#type.tag);
+% _ ->
+ [asn1ct_gen:def_to_tag(Tag)]
+% end
+ end;
+get_taglist(S,{'CHOICE',{Rc,Ec}}) ->
+ get_taglist(S,{'CHOICE',Rc ++ Ec});
+get_taglist(S,{'CHOICE',Components}) ->
+ get_taglist1(S,Components);
+%% ObjectClassFieldType OTP-4390
+get_taglist(_S,#'ObjectClassFieldType'{type={typefield,_}}) ->
+ [];
+get_taglist(S,#'ObjectClassFieldType'{type={fixedtypevaluefield,_,Type}}) ->
+ get_taglist(S,Type);
+get_taglist(S,{ERef=#'Externaltypereference'{},FieldNameList})
+ when list(FieldNameList) ->
+ case get_ObjectClassFieldType(S,ERef,FieldNameList) of
+ Type when record(Type,type) ->
+ get_taglist(S,Type);
+ {fixedtypevaluefield,_,Type} -> get_taglist(S,Type);
+ {TypeFieldName,_} when atom(TypeFieldName) -> []%should check if allowed
+ end;
+get_taglist(S,{ObjCl,FieldNameList}) when record(ObjCl,objectclass),
+ list(FieldNameList) ->
+ case get_ObjectClassFieldType(S,ObjCl#objectclass.fields,FieldNameList) of
+ Type when record(Type,type) ->
+ get_taglist(S,Type);
+ {fixedtypevaluefield,_,Type} -> get_taglist(S,Type);
+ {TypeFieldName,_} when atom(TypeFieldName) -> []%should check if allowed
+ end;
+get_taglist(S,Def) ->
+ case lists:member(S#state.erule,[ber_bin_v2]) of
+ false ->
+ case Def of
+ 'ASN1_OPEN_TYPE' -> % open_type has no UNIVERSAL tag as such
+ [];
+ _ ->
+ [asn1ct_gen:def_to_tag(Def)]
+ end;
+ _ ->
+ []
+ end.
+
+get_taglist1(S,[#'ComponentType'{name=_Cname,tags=TagL}|Rest]) when list(TagL) ->
+ %% tag_list has been here , just return TagL and continue with next alternative
+ TagL ++ get_taglist1(S,Rest);
+get_taglist1(S,[#'ComponentType'{typespec=Ts,tags=undefined}|Rest]) ->
+ get_taglist(S,Ts) ++ get_taglist1(S,Rest);
+get_taglist1(S,[_H|Rest]) -> % skip EXTENSIONMARK
+ get_taglist1(S,Rest);
+get_taglist1(_S,[]) ->
+ [].
+
+dbget_ex(_S,Module,Key) ->
+ case asn1_db:dbget(Module,Key) of
+ undefined ->
+
+ throw({error,{asn1,{undefined,{Module,Key}}}}); % this is catched on toplevel type or value
+ T -> T
+ end.
+
+merge_tags(T1, T2) when list(T2) ->
+ merge_tags2(T1 ++ T2, []);
+merge_tags(T1, T2) ->
+ merge_tags2(T1 ++ [T2], []).
+
+merge_tags2([T1= #tag{type='IMPLICIT'}, T2 |Rest], Acc) ->
+ merge_tags2([T1#tag{type=T2#tag.type, form=T2#tag.form}|Rest],Acc);
+merge_tags2([T1= #tag{type={default,'IMPLICIT'}}, T2 |Rest], Acc) ->
+ merge_tags2([T1#tag{type=T2#tag.type, form=T2#tag.form}|Rest],Acc);
+merge_tags2([H|T],Acc) ->
+ merge_tags2(T, [H|Acc]);
+merge_tags2([], Acc) ->
+ lists:reverse(Acc).
+
+merge_constraints(C1, []) ->
+ C1;
+merge_constraints([], C2) ->
+ C2;
+merge_constraints(C1, C2) ->
+ {SList,VList,PAList,Rest} = splitlist(C1++C2,[],[],[],[]),
+ SizeC = merge_constraints(SList),
+ ValueC = merge_constraints(VList),
+ PermAlphaC = merge_constraints(PAList),
+ case Rest of
+ [] ->
+ SizeC ++ ValueC ++ PermAlphaC;
+ _ ->
+ throw({error,{asn1,{not_implemented,{merge_constraints,Rest}}}})
+ end.
+
+merge_constraints([]) -> [];
+merge_constraints([C1 = {_,{Low1,High1}},{_,{Low2,High2}}|Rest]) when Low1 >= Low2,
+ High1 =< High2 ->
+ merge_constraints([C1|Rest]);
+merge_constraints([C1={'PermittedAlphabet',_},C2|Rest]) ->
+ [C1|merge_constraints([C2|Rest])];
+merge_constraints([C1 = {_,{_Low1,_High1}},C2 = {_,{_Low2,_High2}}|_Rest]) ->
+ throw({error,asn1,{conflicting_constraints,{C1,C2}}});
+merge_constraints([C]) ->
+ [C].
+
+splitlist([C={'SizeConstraint',_}|Rest],Sacc,Vacc,PAacc,Restacc) ->
+ splitlist(Rest,[C|Sacc],Vacc,PAacc,Restacc);
+splitlist([C={'ValueRange',_}|Rest],Sacc,Vacc,PAacc,Restacc) ->
+ splitlist(Rest,Sacc,[C|Vacc],PAacc,Restacc);
+splitlist([C={'PermittedAlphabet',_}|Rest],Sacc,Vacc,PAacc,Restacc) ->
+ splitlist(Rest,Sacc,Vacc,[C|PAacc],Restacc);
+splitlist([C|Rest],Sacc,Vacc,PAacc,Restacc) ->
+ splitlist(Rest,Sacc,Vacc,PAacc,[C|Restacc]);
+splitlist([],Sacc,Vacc,PAacc,Restacc) ->
+ {lists:reverse(Sacc),
+ lists:reverse(Vacc),
+ lists:reverse(PAacc),
+ lists:reverse(Restacc)}.
+
+
+
+storeindb(M) when record(M,module) ->
+ TVlist = M#module.typeorval,
+ NewM = M#module{typeorval=findtypes_and_values(TVlist)},
+ asn1_db:dbnew(NewM#module.name),
+ asn1_db:dbput(NewM#module.name,'MODULE', NewM),
+ Res = storeindb(NewM#module.name,TVlist,[]),
+ include_default_class(NewM#module.name),
+ include_default_type(NewM#module.name),
+ Res.
+
+storeindb(Module,[H|T],ErrAcc) when record(H,typedef) ->
+ storeindb(Module,H#typedef.name,H,T,ErrAcc);
+storeindb(Module,[H|T],ErrAcc) when record(H,valuedef) ->
+ storeindb(Module,H#valuedef.name,H,T,ErrAcc);
+storeindb(Module,[H|T],ErrAcc) when record(H,ptypedef) ->
+ storeindb(Module,H#ptypedef.name,H,T,ErrAcc);
+storeindb(Module,[H|T],ErrAcc) when record(H,classdef) ->
+ storeindb(Module,H#classdef.name,H,T,ErrAcc);
+storeindb(Module,[H|T],ErrAcc) when record(H,pvaluesetdef) ->
+ storeindb(Module,H#pvaluesetdef.name,H,T,ErrAcc);
+storeindb(Module,[H|T],ErrAcc) when record(H,pobjectdef) ->
+ storeindb(Module,H#pobjectdef.name,H,T,ErrAcc);
+storeindb(Module,[H|T],ErrAcc) when record(H,pvaluedef) ->
+ storeindb(Module,H#pvaluedef.name,H,T,ErrAcc);
+storeindb(_,[],[]) -> ok;
+storeindb(_,[],ErrAcc) ->
+ {error,ErrAcc}.
+
+storeindb(Module,Name,H,T,ErrAcc) ->
+ case asn1_db:dbget(Module,Name) of
+ undefined ->
+ asn1_db:dbput(Module,Name,H),
+ storeindb(Module,T,ErrAcc);
+ _ ->
+ case H of
+ _Type when record(H,typedef) ->
+ error({type,"already defined",
+ #state{mname=Module,type=H,tname=Name}});
+ _Type when record(H,valuedef) ->
+ error({value,"already defined",
+ #state{mname=Module,value=H,vname=Name}});
+ _Type when record(H,ptypedef) ->
+ error({ptype,"already defined",
+ #state{mname=Module,type=H,tname=Name}});
+ _Type when record(H,pobjectdef) ->
+ error({ptype,"already defined",
+ #state{mname=Module,type=H,tname=Name}});
+ _Type when record(H,pvaluesetdef) ->
+ error({ptype,"already defined",
+ #state{mname=Module,type=H,tname=Name}});
+ _Type when record(H,pvaluedef) ->
+ error({ptype,"already defined",
+ #state{mname=Module,type=H,tname=Name}});
+ _Type when record(H,classdef) ->
+ error({class,"already defined",
+ #state{mname=Module,value=H,vname=Name}})
+ end,
+ storeindb(Module,T,[H|ErrAcc])
+ end.
+
+findtypes_and_values(TVList) ->
+ findtypes_and_values(TVList,[],[],[],[],[],[]).%% Types,Values,
+%% Parameterizedtypes,Classes,Objects and ObjectSets
+
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,typedef),record(H#typedef.typespec,'Object') ->
+ findtypes_and_values(T,Tacc,Vacc,Pacc,Cacc,[H#typedef.name|Oacc],OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,typedef),record(H#typedef.typespec,'ObjectSet') ->
+ findtypes_and_values(T,Tacc,Vacc,Pacc,Cacc,Oacc,[H#typedef.name|OSacc]);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,typedef) ->
+ findtypes_and_values(T,[H#typedef.name|Tacc],Vacc,Pacc,Cacc,Oacc,OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,valuedef) ->
+ findtypes_and_values(T,Tacc,[H#valuedef.name|Vacc],Pacc,Cacc,Oacc,OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,ptypedef) ->
+ findtypes_and_values(T,Tacc,Vacc,[H#ptypedef.name|Pacc],Cacc,Oacc,OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,classdef) ->
+ findtypes_and_values(T,Tacc,Vacc,Pacc,[H#classdef.name|Cacc],Oacc,OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,pvaluedef) ->
+ findtypes_and_values(T,Tacc,[H#pvaluedef.name|Vacc],Pacc,Cacc,Oacc,OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,pvaluesetdef) ->
+ findtypes_and_values(T,Tacc,[H#pvaluesetdef.name|Vacc],Pacc,Cacc,Oacc,OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,pobjectdef) ->
+ findtypes_and_values(T,Tacc,Vacc,Pacc,Cacc,[H#pobjectdef.name|Oacc],OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,pobjectsetdef) ->
+ findtypes_and_values(T,Tacc,Vacc,Pacc,Cacc,Oacc,[H#pobjectsetdef.name|OSacc]);
+findtypes_and_values([],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc) ->
+ {lists:reverse(Tacc),lists:reverse(Vacc),lists:reverse(Pacc),
+ lists:reverse(Cacc),lists:reverse(Oacc),lists:reverse(OSacc)}.
+
+
+
+error({export,Msg,#state{mname=Mname,type=Ref,tname=Typename}}) ->
+ Pos = Ref#'Externaltypereference'.pos,
+ io:format("asn1error:~p:~p:~p ~p~n",[Pos,Mname,Typename,Msg]),
+ {error,{export,Pos,Mname,Typename,Msg}};
+error({type,Msg,#state{mname=Mname,type=Type,tname=Typename}})
+ when record(Type,typedef) ->
+ io:format("asn1error:~p:~p:~p ~p~n",
+ [Type#typedef.pos,Mname,Typename,Msg]),
+ {error,{type,Type#typedef.pos,Mname,Typename,Msg}};
+error({type,Msg,#state{mname=Mname,type=Type,tname=Typename}})
+ when record(Type,ptypedef) ->
+ io:format("asn1error:~p:~p:~p ~p~n",
+ [Type#ptypedef.pos,Mname,Typename,Msg]),
+ {error,{type,Type#ptypedef.pos,Mname,Typename,Msg}};
+error({type,Msg,#state{mname=Mname,value=Value,vname=Valuename}})
+ when record(Value,valuedef) ->
+ io:format("asn1error:~p:~p:~p ~p~n",[Value#valuedef.pos,Mname,Valuename,Msg]),
+ {error,{type,Value#valuedef.pos,Mname,Valuename,Msg}};
+error({type,Msg,#state{mname=Mname,type=Type,tname=Typename}})
+ when record(Type,pobjectdef) ->
+ io:format("asn1error:~p:~p:~p ~p~n",
+ [Type#pobjectdef.pos,Mname,Typename,Msg]),
+ {error,{type,Type#pobjectdef.pos,Mname,Typename,Msg}};
+error({value,Msg,#state{mname=Mname,value=Value,vname=Valuename}}) ->
+ io:format("asn1error:~p:~p:~p ~p~n",[Value#valuedef.pos,Mname,Valuename,Msg]),
+ {error,{value,Value#valuedef.pos,Mname,Valuename,Msg}};
+error({Other,Msg,#state{mname=Mname,value=#valuedef{pos=Pos},vname=Valuename}}) ->
+ io:format("asn1error:~p:~p:~p ~p~n",[Pos,Mname,Valuename,Msg]),
+ {error,{Other,Pos,Mname,Valuename,Msg}};
+error({Other,Msg,#state{mname=Mname,type=#typedef{pos=Pos},tname=Typename}}) ->
+ io:format("asn1error:~p:~p:~p ~p~n",[Pos,Mname,Typename,Msg]),
+ {error,{Other,Pos,Mname,Typename,Msg}};
+error({Other,Msg,#state{mname=Mname,type=#classdef{pos=Pos},tname=Typename}}) ->
+ io:format("asn1error:~p:~p:~p ~p~n",[Pos,Mname,Typename,Msg]),
+ {error,{Other,Pos,Mname,Typename,Msg}}.
+
+include_default_type(Module) ->
+ NameAbsList = default_type_list(),
+ include_default_type1(Module,NameAbsList).
+
+include_default_type1(_,[]) ->
+ ok;
+include_default_type1(Module,[{Name,TS}|Rest]) ->
+ case asn1_db:dbget(Module,Name) of
+ undefined ->
+ T = #typedef{name=Name,
+ typespec=TS},
+ asn1_db:dbput(Module,Name,T);
+ _ -> ok
+ end,
+ include_default_type1(Module,Rest).
+
+default_type_list() ->
+ %% The EXTERNAL type is represented, according to ASN.1 1997,
+ %% as a SEQUENCE with components: identification, data-value-descriptor
+ %% and data-value.
+ Syntax =
+ #'ComponentType'{name=syntax,
+ typespec=#type{def='OBJECT IDENTIFIER'},
+ prop=mandatory},
+ Presentation_Cid =
+ #'ComponentType'{name='presentation-context-id',
+ typespec=#type{def='INTEGER'},
+ prop=mandatory},
+ Transfer_syntax =
+ #'ComponentType'{name='transfer-syntax',
+ typespec=#type{def='OBJECT IDENTIFIER'},
+ prop=mandatory},
+ Negotiation_items =
+ #type{def=
+ #'SEQUENCE'{components=
+ [Presentation_Cid,
+ Transfer_syntax#'ComponentType'{prop=mandatory}]}},
+ Context_negot =
+ #'ComponentType'{name='context-negotiation',
+ typespec=Negotiation_items,
+ prop=mandatory},
+
+ Data_value_descriptor =
+ #'ComponentType'{name='data-value-descriptor',
+ typespec=#type{def='ObjectDescriptor'},
+ prop='OPTIONAL'},
+ Data_value =
+ #'ComponentType'{name='data-value',
+ typespec=#type{def='OCTET STRING'},
+ prop=mandatory},
+
+ %% The EXTERNAL type is represented, according to ASN.1 1990,
+ %% as a SEQUENCE with components: direct-reference, indirect-reference,
+ %% data-value-descriptor and encoding.
+
+ Direct_reference =
+ #'ComponentType'{name='direct-reference',
+ typespec=#type{def='OBJECT IDENTIFIER'},
+ prop='OPTIONAL'},
+
+ Indirect_reference =
+ #'ComponentType'{name='indirect-reference',
+ typespec=#type{def='INTEGER'},
+ prop='OPTIONAL'},
+
+ Single_ASN1_type =
+ #'ComponentType'{name='single-ASN1-type',
+ typespec=#type{tag=[{tag,'CONTEXT',0,
+ 'EXPLICIT',32}],
+ def='ANY'},
+ prop=mandatory},
+
+ Octet_aligned =
+ #'ComponentType'{name='octet-aligned',
+ typespec=#type{tag=[{tag,'CONTEXT',1,
+ 'IMPLICIT',32}],
+ def='OCTET STRING'},
+ prop=mandatory},
+
+ Arbitrary =
+ #'ComponentType'{name=arbitrary,
+ typespec=#type{tag=[{tag,'CONTEXT',2,
+ 'IMPLICIT',32}],
+ def={'BIT STRING',[]}},
+ prop=mandatory},
+
+ Encoding =
+ #'ComponentType'{name=encoding,
+ typespec=#type{def={'CHOICE',
+ [Single_ASN1_type,Octet_aligned,
+ Arbitrary]}},
+ prop=mandatory},
+
+ EXTERNAL_components1990 =
+ [Direct_reference,Indirect_reference,Data_value_descriptor,Encoding],
+
+ %% The EMBEDDED PDV type is represented by a SEQUENCE type
+ %% with components: identification and data-value
+ Abstract =
+ #'ComponentType'{name=abstract,
+ typespec=#type{def='OBJECT IDENTIFIER'},
+ prop=mandatory},
+ Transfer =
+ #'ComponentType'{name=transfer,
+ typespec=#type{def='OBJECT IDENTIFIER'},
+ prop=mandatory},
+ AbstractTrSeq =
+ #'SEQUENCE'{components=[Abstract,Transfer]},
+ Syntaxes =
+ #'ComponentType'{name=syntaxes,
+ typespec=#type{def=AbstractTrSeq},
+ prop=mandatory},
+ Fixed = #'ComponentType'{name=fixed,
+ typespec=#type{def='NULL'},
+ prop=mandatory},
+ Negotiations =
+ [Syntaxes,Syntax,Presentation_Cid,Context_negot,
+ Transfer_syntax,Fixed],
+ Identification2 =
+ #'ComponentType'{name=identification,
+ typespec=#type{def={'CHOICE',Negotiations}},
+ prop=mandatory},
+ EmbeddedPdv_components =
+ [Identification2,Data_value],
+
+ %% The CHARACTER STRING type is represented by a SEQUENCE type
+ %% with components: identification and string-value
+ String_value =
+ #'ComponentType'{name='string-value',
+ typespec=#type{def='OCTET STRING'},
+ prop=mandatory},
+ CharacterString_components =
+ [Identification2,String_value],
+
+ [{'EXTERNAL',
+ #type{tag=[#tag{class='UNIVERSAL',
+ number=8,
+ type='IMPLICIT',
+ form=32}],
+ def=#'SEQUENCE'{components=
+ EXTERNAL_components1990}}},
+ {'EMBEDDED PDV',
+ #type{tag=[#tag{class='UNIVERSAL',
+ number=11,
+ type='IMPLICIT',
+ form=32}],
+ def=#'SEQUENCE'{components=EmbeddedPdv_components}}},
+ {'CHARACTER STRING',
+ #type{tag=[#tag{class='UNIVERSAL',
+ number=29,
+ type='IMPLICIT',
+ form=32}],
+ def=#'SEQUENCE'{components=CharacterString_components}}}
+ ].
+
+
+include_default_class(Module) ->
+ NameAbsList = default_class_list(),
+ include_default_class1(Module,NameAbsList).
+
+include_default_class1(_,[]) ->
+ ok;
+include_default_class1(Module,[{Name,TS}|_Rest]) ->
+ case asn1_db:dbget(Module,Name) of
+ undefined ->
+ C = #classdef{checked=true,name=Name,
+ typespec=TS},
+ asn1_db:dbput(Module,Name,C);
+ _ -> ok
+ end.
+
+default_class_list() ->
+ [{'TYPE-IDENTIFIER',
+ {objectclass,
+ [{fixedtypevaluefield,
+ id,
+ {type,[],'OBJECT IDENTIFIER',[]},
+ 'UNIQUE',
+ 'MANDATORY'},
+ {typefield,'Type','MANDATORY'}],
+ {'WITH SYNTAX',
+ [{typefieldreference,'Type'},
+ 'IDENTIFIED',
+ 'BY',
+ {valuefieldreference,id}]}}},
+ {'ABSTRACT-SYNTAX',
+ {objectclass,
+ [{fixedtypevaluefield,
+ id,
+ {type,[],'OBJECT IDENTIFIER',[]},
+ 'UNIQUE',
+ 'MANDATORY'},
+ {typefield,'Type','MANDATORY'},
+ {fixedtypevaluefield,
+ property,
+ {type,
+ [],
+ {'BIT STRING',[]},
+ []},
+ undefined,
+ {'DEFAULT',
+ [0,1,0]}}],
+ {'WITH SYNTAX',
+ [{typefieldreference,'Type'},
+ 'IDENTIFIED',
+ 'BY',
+ {valuefieldreference,id},
+ ['HAS',
+ 'PROPERTY',
+ {valuefieldreference,property}]]}}}].
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_constructed_ber.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_constructed_ber.erl
new file mode 100644
index 0000000000..695f648924
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_constructed_ber.erl
@@ -0,0 +1,1468 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_constructed_ber.erl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-module(asn1ct_constructed_ber).
+
+-export([gen_encode_sequence/3]).
+-export([gen_decode_sequence/3]).
+-export([gen_encode_set/3]).
+-export([gen_decode_set/3]).
+-export([gen_encode_sof/4]).
+-export([gen_decode_sof/4]).
+-export([gen_encode_choice/3]).
+-export([gen_decode_choice/3]).
+
+%%%% Application internal exports
+-export([match_tag/2]).
+
+-include("asn1_records.hrl").
+
+-import(asn1ct_gen, [emit/1,demit/1]).
+
+% the encoding of class of tag bits 8 and 7
+-define(UNIVERSAL, 0).
+-define(APPLICATION, 16#40).
+-define(CONTEXT, 16#80).
+-define(PRIVATE, 16#C0).
+
+% primitive or constructed encoding % bit 6
+-define(PRIMITIVE, 0).
+-define(CONSTRUCTED, 2#00100000).
+
+
+
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Encode/decode SEQUENCE
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+gen_encode_sequence(Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ asn1ct_name:new(term),
+ asn1ct_name:new(bytes),
+
+ %% if EXTERNAL type the input value must be transformed to
+ %% ASN1 1990 format
+ case Typename of
+ ['EXTERNAL'] ->
+ emit([" NewVal = asn1rt_check:transform_to_EXTERNAL1990(Val),",
+ nl]);
+ _ ->
+ ok
+ end,
+
+ {SeqOrSet,TableConsInfo,CompList} =
+ case D#type.def of
+ #'SEQUENCE'{tablecinf=TCI,components=CL} ->
+ {'SEQUENCE',TCI,CL};
+ #'SET'{tablecinf=TCI,components=CL} ->
+ {'SET',TCI,CL}
+ end,
+ Ext = extensible(CompList),
+ CompList1 = case CompList of
+ {Rl,El} -> Rl ++ El;
+ _ -> CompList
+ end,
+ EncObj =
+ case TableConsInfo of
+ #simpletableattributes{usedclassfield=Used,
+ uniqueclassfield=Unique} when Used /= Unique ->
+ false;
+ %% ObjectSet, name of the object set in constraints
+ %%
+ %%{ObjectSet,AttrN,N,UniqueFieldName}
+ #simpletableattributes{objectsetname=ObjectSet,
+ c_name=AttrN,
+ c_index=N,
+ usedclassfield=UniqueFieldName,
+ uniqueclassfield=UniqueFieldName,
+ valueindex=ValueIndex
+ } ->
+ OSDef =
+ case ObjectSet of
+ {Module,OSName} ->
+ asn1_db:dbget(Module,OSName);
+ OSName ->
+ asn1_db:dbget(get(currmod),OSName)
+ end,
+% io:format("currmod: ~p~nOSName: ~p~nAttrN: ~p~nN: ~p~nUniqueFieldName: ~p~n",
+% [get(currmod),OSName,AttrN,N,UniqueFieldName]),
+ case (OSDef#typedef.typespec)#'ObjectSet'.gen of
+ true ->
+% Val = lists:concat(["?RT_BER:cindex(",
+% N+1,",Val,"]),
+ ObjectEncode =
+ asn1ct_gen:un_hyphen_var(lists:concat(['Obj',
+ AttrN])),
+ emit({ObjectEncode," = ",nl}),
+ emit({" 'getenc_",ObjectSet,"'(",{asis,UniqueFieldName},
+ ", ",nl}),
+% emit({indent(35),"?RT_BER:cindex(",N+1,", Val,",
+% {asis,AttrN},")),",nl}),
+ emit([indent(10+length(atom_to_list(ObjectSet))),
+ "value_match(",{asis,ValueIndex},",",
+ "?RT_BER:cindex(",N+1,",Val,",
+ {asis,AttrN},"))),",nl]),
+ notice_value_match(),
+ {AttrN,ObjectEncode};
+ _ ->
+ false
+ end;
+ _ ->
+ case D#type.tablecinf of
+ [{objfun,_}|_] ->
+ %% when the simpletableattributes was at an
+ %% outer level and the objfun has been passed
+ %% through the function call
+ {"got objfun through args","ObjFun"};
+ _ ->
+ false
+ end
+ end,
+
+ gen_enc_sequence_call(Erules,Typename,CompList1,1,Ext,EncObj),
+
+ MyTag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}|| X <- D#type.tag]
+ ++
+ [#tag{class = asn1ct_gen_ber:decode_class('UNIVERSAL'),
+ number = asn1ct_gen_ber:decode_type(SeqOrSet),
+ form = ?CONSTRUCTED,
+ type = 'IMPLICIT'}],
+ emit([nl," BytesSoFar = "]),
+ case SeqOrSet of
+ 'SET' when (D#type.def)#'SET'.sorted == dynamic ->
+ emit("?RT_BER:dynamicsort_SET_components(["),
+ mkvlist(asn1ct_name:all(encBytes)),
+ emit(["]),",nl]);
+ _ ->
+ emit("["),
+ mkvlist(asn1ct_name:all(encBytes)),
+ emit(["],",nl])
+ end,
+ emit(" LenSoFar = "),
+ case asn1ct_name:all(encLen) of
+ [] -> emit("0");
+ AllLengths ->
+ mkvplus(AllLengths)
+ end,
+ emit([",",nl]),
+% emit(["{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ ",
+ emit([" ?RT_BER:encode_tags(TagIn ++ ",
+ {asis,MyTag},", BytesSoFar, LenSoFar).",nl]).
+
+
+gen_decode_sequence(Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+% asn1ct_name:new(term),
+ asn1ct_name:new(tag),
+ #'SEQUENCE'{tablecinf=TableConsInfo,components=CList} = D#type.def,
+ Ext = extensible(CList),
+ CompList = case CList of
+ {Rl,El} -> Rl ++ El;
+ _ -> CList
+ end,
+
+ emit({" %%-------------------------------------------------",nl}),
+ emit({" %% decode tag and length ",nl}),
+ emit({" %%-------------------------------------------------",nl}),
+
+ asn1ct_name:new(rb),
+ MyTag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}|| X <- D#type.tag]
+ ++
+ [#tag{class = asn1ct_gen_ber:decode_class('UNIVERSAL'),
+ number = asn1ct_gen_ber:decode_type('SEQUENCE'),
+ form = ?CONSTRUCTED,
+ type = 'IMPLICIT'}],
+ emit([" {{_,",asn1ct_gen_ber:unused_var("Len",D#type.def),"},",{next,bytes},",",{curr,rb},
+ "} = ?RT_BER:check_tags(TagIn ++ ",{asis,MyTag},", ",
+ {curr,bytes},", OptOrMand), ",nl]),
+ asn1ct_name:new(bytes),
+ asn1ct_name:new(len),
+
+ case CompList of
+ [] -> true;
+ _ ->
+ emit({"{",{next,bytes},
+ ",RemBytes} = ?RT_BER:split_list(",
+ {curr,bytes},
+ ",", {prev,len},"),",nl}),
+ asn1ct_name:new(bytes)
+ end,
+
+ {DecObjInf,UniqueFName,ValueIndex} =
+ case TableConsInfo of
+ #simpletableattributes{objectsetname=ObjectSet,
+ c_name=AttrN,
+ usedclassfield=UniqueFieldName,
+ uniqueclassfield=UniqueFieldName,
+ valueindex=ValIndex
+ } ->
+ F = fun(#'ComponentType'{typespec=CT})->
+ case {CT#type.constraint,CT#type.tablecinf} of
+ {[],[{objfun,_}|_R]} -> true;
+ _ -> false
+ end
+ end,
+ case lists:any(F,CompList) of
+ %%AttributeName = asn1ct_gen:un_hyphen_var(AttrN),
+ true -> % when component relation constraint establish
+ %% relation from a component to another components
+ %% subtype component
+ {{AttrN,{deep,ObjectSet,UniqueFieldName,
+ ValIndex}},
+ UniqueFieldName,ValIndex};
+ false ->
+ {{AttrN,ObjectSet},UniqueFieldName,ValIndex}
+ end;
+ _ ->
+ {false,false,false}
+ end,
+ case gen_dec_sequence_call(Erules,Typename,CompList,Ext,DecObjInf) of
+ no_terms -> % an empty sequence
+ emit([nl,nl]),
+ demit({"Result = "}), %dbg
+ %% return value as record
+ asn1ct_name:new(rb),
+ emit([" {{'",asn1ct_gen:list2rname(Typename),"'}, ",{curr,bytes},",",nl," "]),
+ asn1ct_gen_ber:add_removed_bytes(),
+ emit(["}.",nl]);
+ {LeadingAttrTerm,PostponedDecArgs} ->
+ emit([com,nl,nl]),
+ case {LeadingAttrTerm,PostponedDecArgs} of
+ {[],[]} ->
+ ok;
+ {_,[]} ->
+ ok;
+ {[{ObjSet,LeadingAttr,Term}],PostponedDecArgs} ->
+ DecObj = asn1ct_gen:un_hyphen_var(lists:concat(['DecObj',LeadingAttr,Term])),
+ ValueMatch = value_match(ValueIndex,Term),
+ emit([DecObj," =",nl," 'getdec_",ObjSet,"'(",
+% {asis,UniqueFName},", ",Term,"),",nl}),
+ {asis,UniqueFName},", ",ValueMatch,"),",nl]),
+ gen_dec_postponed_decs(DecObj,PostponedDecArgs)
+ end,
+ demit({"Result = "}), %dbg
+ %% return value as record
+ asn1ct_name:new(rb),
+ asn1ct_name:new(bytes),
+ ExtStatus = case Ext of
+ {ext,_,_} -> ext;
+ noext -> noext
+ end,
+ emit([" {",{next,bytes},",",{curr,rb},"} = ?RT_BER:restbytes2(RemBytes, ",
+ {curr,bytes},",",ExtStatus,"),",nl]),
+ asn1ct_name:new(rb),
+ case Typename of
+ ['EXTERNAL'] ->
+ emit([" OldFormat={'",asn1ct_gen:list2rname(Typename),
+ "', "]),
+ mkvlist(asn1ct_name:all(term)),
+ emit(["},",nl]),
+ emit([" ASN11994Format =",nl,
+ " asn1rt_check:transform_to_EXTERNAL1994",
+ "(OldFormat),",nl]),
+ emit([" {ASN11994Format,",{next,bytes},", "]);
+ _ ->
+ emit([" {{'",asn1ct_gen:list2rname(Typename),"', "]),
+ mkvlist(asn1ct_name:all(term)),
+ emit(["}, ",{next,bytes},", "])
+ end,
+ asn1ct_gen_ber:add_removed_bytes(),
+ emit(["}.",nl])
+ end.
+
+gen_dec_postponed_decs(_,[]) ->
+ emit(nl);
+gen_dec_postponed_decs(DecObj,[{_Cname,{FirstPFN,PFNList},Term,TmpTerm,_Tag,OptOrMand}|Rest]) ->
+% asn1ct_name:new(term),
+ asn1ct_name:new(tmpterm),
+ asn1ct_name:new(reason),
+
+ emit({"{",Term,", _, _} = ",nl}),
+ N = case OptOrMand of
+ mandatory -> 0;
+ 'OPTIONAL' ->
+ emit_opt_or_mand_check(asn1_NOVALUE,TmpTerm),
+ 6;
+ {'DEFAULT',Val} ->
+ emit_opt_or_mand_check(Val,TmpTerm),
+ 6
+ end,
+ emit({indent(N+3),"case (catch ",DecObj,"(",{asis,FirstPFN},
+% ", ",TmpTerm,", ", {asis,Tag},", ",{asis,PFNList},")) of",nl}),
+ ", ",TmpTerm,", [], ",{asis,PFNList},")) of",nl}),
+ emit({indent(N+6),"{'EXIT', ",{curr,reason},"} ->",nl}),
+ emit({indent(N+9),"exit({'Type not compatible with table constraint',",
+ {curr,reason},"});",nl}),
+ emit({indent(N+6),{curr,tmpterm}," ->",nl}),
+ emit({indent(N+9),{curr,tmpterm},nl}),
+
+ case OptOrMand of
+ mandatory -> emit([indent(N+3),"end,",nl]);
+ _ ->
+ emit([indent(N+3),"end",nl,
+ indent(3),"end,",nl])
+ end,
+% emit({indent(3),"end,",nl}),
+ gen_dec_postponed_decs(DecObj,Rest).
+
+
+emit_opt_or_mand_check(Value,TmpTerm) ->
+ emit([indent(3),"case ",TmpTerm," of",nl,
+ indent(6),{asis,Value}," -> {",{asis,Value},",[],[]};",nl,
+ indent(6),"_ ->",nl]).
+
+%%============================================================================
+%% Encode/decode SET
+%%
+%%============================================================================
+
+gen_encode_set(Erules,Typename,D) when record(D,type) ->
+ gen_encode_sequence(Erules,Typename,D).
+
+gen_decode_set(Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ asn1ct_name:new(term),
+ asn1ct_name:new(tag),
+ #'SET'{components=TCompList} = D#type.def,
+ Ext = extensible(TCompList),
+ CompList = case TCompList of
+ {Rl,El} -> Rl ++ El;
+ _ -> TCompList
+ end,
+
+ emit([" %%-------------------------------------------------",nl]),
+ emit([" %% decode tag and length ",nl]),
+ emit([" %%-------------------------------------------------",nl]),
+
+ asn1ct_name:new(rb),
+ MyTag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}|| X <- D#type.tag]
+ ++
+ [#tag{class = asn1ct_gen_ber:decode_class('UNIVERSAL'),
+ number = asn1ct_gen_ber:decode_type('SET'),
+ form = ?CONSTRUCTED,
+ type = 'IMPLICIT'}],
+ emit([" {{_,Len},",{next,bytes},",",{curr,rb},
+ "} = ?RT_BER:check_tags(TagIn ++ ",{asis,MyTag},", ",
+ {curr,bytes},", OptOrMand), ",nl]),
+ asn1ct_name:new(bytes),
+ asn1ct_name:new(len),
+ asn1ct_name:new(rb),
+
+ emit([" {SetTerm, SetBytes, ",{curr,rb},"} = ?RT_BER:decode_set(0, Len, ",
+ {curr,bytes},", OptOrMand, ",
+ "fun 'dec_",asn1ct_gen:list2name(Typename),"_fun'/2, []),",nl]),
+
+ asn1ct_name:new(rb),
+ emit([" 'dec_",asn1ct_gen:list2name(Typename),"_result'(lists:sort(SetTerm), SetBytes, "]),
+ asn1ct_gen_ber:add_removed_bytes(),
+ emit([").",nl,nl,nl]),
+
+ emit({"%%-------------------------------------------------",nl}),
+ emit({"%% Set loop fun for ",asn1ct_gen:list2name(Typename),nl}),
+ emit({"%%-------------------------------------------------",nl}),
+
+ asn1ct_name:clear(),
+ asn1ct_name:new(term),
+ emit(["'dec_",asn1ct_gen:list2name(Typename),"_fun'(",{curr,bytes},
+ ", OptOrMand) ->",nl]),
+
+ asn1ct_name:new(bytes),
+ gen_dec_set(Erules,Typename,CompList,1,Ext),
+
+ emit([" %% tag not found, if extensionmark we should skip bytes here",nl]),
+ emit([indent(6),"_ -> {[], Bytes,0}",nl]),
+ emit([indent(3),"end.",nl,nl,nl]),
+
+
+ emit({"%%-------------------------------------------------",nl}),
+ emit({"%% Result ",asn1ct_gen:list2name(Typename),nl}),
+ emit({"%%-------------------------------------------------",nl}),
+
+ asn1ct_name:clear(),
+ emit({"'dec_",asn1ct_gen:list2name(Typename),"_result'(",
+ asn1ct_gen_ber:unused_var("TermList",D#type.def),", Bytes, Rb) ->",nl}),
+
+ case gen_dec_set_result(Erules,Typename,CompList) of
+ no_terms ->
+ %% return value as record
+ asn1ct_name:new(rb),
+ emit({" {{'",asn1ct_gen:list2rname(Typename),"'}, Bytes, Rb}.",nl});
+ _ ->
+ emit({nl," case ",{curr,termList}," of",nl}),
+ emit({" [] -> {{'",asn1ct_gen:list2rname(Typename),"', "}),
+ mkvlist(asn1ct_name:all(term)),
+ emit({"}, Bytes, Rb};",nl}),
+ emit({" ExtraAtt -> exit({error,{asn1,{too_many_attributes, ExtraAtt}}})",nl}),
+ emit({" end.",nl}),
+ emit({nl,nl,nl})
+ end.
+
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Encode/decode SEQUENCE OF and SET OF
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+gen_encode_sof(Erules,Typename,_InnerTypename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ {SeqOrSetOf, Cont} = D#type.def,
+
+ Objfun = case D#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _ ->
+ ""
+ end,
+
+ emit({" {EncBytes,EncLen} = 'enc_",asn1ct_gen:list2name(Typename),
+ "_components'(Val",Objfun,",[],0),",nl}),
+
+ MyTag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}|| X <- D#type.tag]
+ ++
+ [#tag{class = asn1ct_gen_ber:decode_class('UNIVERSAL'),
+ number = asn1ct_gen_ber:decode_type(SeqOrSetOf),
+ form = ?CONSTRUCTED,
+ type = 'IMPLICIT'}],
+% gen_encode_tags(Erules,MyTag,"EncLen","EncBytes"),
+ emit([" ?RT_BER:encode_tags(TagIn ++ ",
+ {asis,MyTag},", EncBytes, EncLen).",nl,nl]),
+
+ gen_encode_sof_components(Erules,Typename,SeqOrSetOf,Cont).
+% gen_enc_line(Erules,Typename,TypeNameSuffix,Cont,"H",0,
+% mandatory,"{EncBytes,EncLen} = "),
+
+
+gen_decode_sof(Erules,Typename,_InnerTypename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ {SeqOrSetOf, TypeTag, Cont} =
+ case D#type.def of
+ {'SET OF',_Cont} -> {'SET OF','SET',_Cont};
+ {'SEQUENCE OF',_Cont} -> {'SEQUENCE OF','SEQUENCE',_Cont}
+ end,
+ TypeNameSuffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,Cont#type.def),
+
+ emit({" %%-------------------------------------------------",nl}),
+ emit({" %% decode tag and length ",nl}),
+ emit({" %%-------------------------------------------------",nl}),
+
+ asn1ct_name:new(rb),
+ MyTag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}|| X <- D#type.tag]
+ ++
+ [#tag{class = asn1ct_gen_ber:decode_class('UNIVERSAL'),
+ number = asn1ct_gen_ber:decode_type(TypeTag),
+ form = ?CONSTRUCTED,
+ type = 'IMPLICIT'}],
+ emit([" {{_,Len},",{next,bytes},",",{curr,rb},
+ "} = ?RT_BER:check_tags(TagIn ++ ",{asis,MyTag},", ",
+ {curr,bytes},", OptOrMand), ",nl]),
+
+ emit([" ?RT_BER:decode_components(",{curr,rb}]),
+ InnerType = asn1ct_gen:get_inner(Cont#type.def),
+ ContName = case asn1ct_gen:type(InnerType) of
+ Atom when atom(Atom) -> Atom;
+ _ -> TypeNameSuffix
+ end,
+ emit([", Len, ",{next,bytes},", "]),
+% NewCont =
+% case Cont#type.def of
+% {'ENUMERATED',_,Components}->
+% Cont#type{def={'ENUMERATED',Components}};
+% _ -> Cont
+% end,
+ ObjFun =
+ case D#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _ ->
+ []
+ end,
+ gen_dec_line_sof(Erules,Typename,ContName,Cont,ObjFun),
+ emit([", []).",nl,nl,nl]).
+
+
+gen_encode_sof_components(Erules,Typename,SeqOrSetOf,Cont)
+ when record(Cont,type)->
+
+ {Objfun,ObjFun_novar,EncObj} =
+ case Cont#type.tablecinf of
+ [{objfun,_}|_R] ->
+ {", ObjFun",", _",{no_attr,"ObjFun"}};
+ _ ->
+ {"","",false}
+ end,
+ emit(["'enc_",asn1ct_gen:list2name(Typename),
+ "_components'([]",ObjFun_novar,", AccBytes, AccLen) -> ",nl]),
+
+ case catch lists:member(der,get(encoding_options)) of
+ true ->
+ emit([indent(3),
+ "{?RT_BER:dynamicsort_SETOF(AccBytes),AccLen};",nl,nl]);
+ _ ->
+ emit([indent(3),"{lists:reverse(AccBytes),AccLen};",nl,nl])
+ end,
+ emit(["'enc_",asn1ct_gen:list2name(Typename),
+ "_components'([H|T]",Objfun,",AccBytes, AccLen) ->",nl]),
+ TypeNameSuffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,Cont#type.def),
+ gen_enc_line(Erules,Typename,TypeNameSuffix,Cont,"H",3,
+ mandatory,"{EncBytes,EncLen} = ",EncObj),
+ emit([",",nl]),
+ emit([indent(3),"'enc_",asn1ct_gen:list2name(Typename),
+ "_components'(T",Objfun,","]),
+ emit(["[EncBytes|AccBytes], AccLen + EncLen).",nl,nl]).
+
+%%============================================================================
+%% Encode/decode CHOICE
+%%
+%%============================================================================
+
+gen_encode_choice(Erules,Typename,D) when record(D,type) ->
+ ChoiceTag = D#type.tag,
+ {'CHOICE',CompList} = D#type.def,
+ Ext = extensible(CompList),
+ CompList1 = case CompList of
+ {Rl,El} -> Rl ++ El;
+ _ -> CompList
+ end,
+ gen_enc_choice(Erules,Typename,ChoiceTag,CompList1,Ext),
+ emit({nl,nl}).
+
+gen_decode_choice(Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ asn1ct_name:new(bytes),
+ ChoiceTag = D#type.tag,
+ {'CHOICE',CompList} = D#type.def,
+ Ext = extensible(CompList),
+ CompList1 = case CompList of
+ {Rl,El} -> Rl ++ El;
+ _ -> CompList
+ end,
+ gen_dec_choice(Erules,Typename,ChoiceTag,CompList1,Ext),
+ emit({".",nl}).
+
+
+%%============================================================================
+%% Encode SEQUENCE
+%%
+%%============================================================================
+
+gen_enc_sequence_call(Erules,TopType,[#'ComponentType'{name=Cname,typespec=Type,prop=Prop}|Rest],Pos,Ext,EncObj) ->
+ asn1ct_name:new(encBytes),
+ asn1ct_name:new(encLen),
+ Element =
+ case TopType of
+ ['EXTERNAL'] ->
+ io_lib:format("?RT_BER:cindex(~w,NewVal,~w)",[Pos+1,Cname]);
+ _ ->
+ io_lib:format("?RT_BER:cindex(~w,Val,~w)",[Pos+1,Cname])
+ end,
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ print_attribute_comment(InnerType,Pos,Prop),
+ gen_enc_line(Erules,TopType,Cname,Type,Element,3,Prop,EncObj),
+ case Rest of
+ [] ->
+ emit({com,nl});
+ _ ->
+ emit({com,nl}),
+ gen_enc_sequence_call(Erules,TopType,Rest,Pos+1,Ext,EncObj)
+ end;
+
+gen_enc_sequence_call(_Erules,_TopType,[],_Num,_,_) ->
+ true.
+
+%%============================================================================
+%% Decode SEQUENCE
+%%
+%%============================================================================
+
+gen_dec_sequence_call(Erules,TopType,CompList,Ext,DecObjInf) ->
+ gen_dec_sequence_call1(Erules,TopType, CompList, 1, Ext,DecObjInf,[],[]).
+
+
+gen_dec_sequence_call1(Erules,TopType,[#'ComponentType'{name=Cname,typespec=Type,prop=Prop,tags=Tags}|Rest],Num,Ext,DecObjInf,LeadingAttrAcc,ArgsAcc) ->
+ {LA,PostponedDec} =
+ gen_dec_component(Erules,TopType,Cname,Tags,Type,Num,Prop,
+ Ext,DecObjInf),
+ case Rest of
+ [] ->
+ {LA ++ LeadingAttrAcc,PostponedDec ++ ArgsAcc};
+ _ ->
+ emit({com,nl}),
+% asn1ct_name:new(term),
+ asn1ct_name:new(bytes),
+ gen_dec_sequence_call1(Erules,TopType,Rest,Num+1,Ext,DecObjInf,
+ LA++LeadingAttrAcc,PostponedDec++ArgsAcc)
+ end;
+
+gen_dec_sequence_call1(_Erules,_TopType,[],1,_,_,_,_) ->
+ no_terms.
+%%gen_dec_sequence_call1(Erules,_TopType,[],Num,_) ->
+%% true.
+
+
+
+%%----------------------------
+%%SEQUENCE mandatory
+%%----------------------------
+
+gen_dec_component(Erules,TopType,Cname,CTags,Type,Pos,Prop,Ext,DecObjInf) ->
+ InnerType =
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=OCFTType} -> OCFTType;
+ _ -> asn1ct_gen:get_inner(Type#type.def)
+ end,
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info) of
+% no ->
+% asn1ct_gen:get_inner(Type#type.def);
+% _ ->
+% Type#type.def
+% end,
+ Prop1 = case {Prop,Ext} of
+ {mandatory,{ext,Epos,_}} when Pos >= Epos ->
+ 'OPTIONAL';
+ _ ->
+ Prop
+ end,
+ print_attribute_comment(InnerType,Pos,Prop1),
+ emit(" "),
+
+ case {InnerType,DecObjInf} of
+ {{typefield,_},NotFalse} when NotFalse /= false ->
+ asn1ct_name:new(term),
+ asn1ct_name:new(tmpterm),
+ emit({"{",{curr,tmpterm},", ",{next,bytes},",",{next,rb},"} = "});
+ {{objectfield,_,_},_} ->
+ asn1ct_name:new(term),
+ asn1ct_name:new(tmpterm),
+ emit({"{",{curr,tmpterm},", ",{next,bytes},",",{next,rb},"} = "});
+ _ ->
+ asn1ct_name:new(term),
+ emit({"{",{curr,term},",",{next,bytes},",",{next,rb},"} = "})
+ end,
+ asn1ct_name:new(rb),
+ PostponedDec =
+ gen_dec_line(Erules,TopType,Cname,CTags,Type,Prop1,DecObjInf),
+ asn1ct_name:new(form),
+ PostponedDec.
+
+
+%%-------------------------------------
+%% Decode SET
+%%-------------------------------------
+
+gen_dec_set(Erules,TopType,CompList,Pos,_Ext) ->
+ TagList = get_all_choice_tags(CompList),
+ emit({indent(3),
+ {curr,tagList}," = ",{asis,TagList},",",nl}),
+ emit({indent(3),
+ "case ?RT_BER:check_if_valid_tag(Bytes, ",
+ {curr,tagList},", OptOrMand) of",nl}),
+ asn1ct_name:new(tagList),
+ asn1ct_name:new(rbCho),
+ asn1ct_name:new(choTags),
+ gen_dec_set_cases(Erules,TopType,CompList,TagList,Pos),
+ asn1ct_name:new(tag),
+ asn1ct_name:new(bytes).
+
+
+
+gen_dec_set_cases(_,_,[],_,_) ->
+ ok;
+gen_dec_set_cases(Erules,TopType,[H|T],List,Pos) ->
+ case H of
+ {'EXTENSIONMARK', _, _} ->
+ gen_dec_set_cases(Erules,TopType,T,List,Pos);
+ _ ->
+ Name = H#'ComponentType'.name,
+ Type = H#'ComponentType'.typespec,
+
+ emit({indent(6),"'",Name,"' ->",nl}),
+ case Type#type.def of
+ {'CHOICE',_NewCompList} ->
+ gen_dec_set_cases_choice(Erules,TopType,H,Pos);
+ _ ->
+ gen_dec_set_cases_type(Erules,TopType,H,Pos)
+ end,
+ gen_dec_set_cases(Erules,TopType,T,List,Pos+1)
+ end.
+
+
+
+
+gen_dec_set_cases_choice(_Erules,TopType,H,Pos) ->
+ Cname = H#'ComponentType'.name,
+ Tag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}
+ || X <- (H#'ComponentType'.typespec)#type.tag],
+ asn1ct_name:new(rbCho),
+ emit({indent(9),"{Dec, Rest, ",{curr,rbCho},"} = "}),
+ emit({"'dec_",asn1ct_gen:list2name([Cname|TopType]),
+ "'(Bytes,OptOrMand,",{asis,Tag},"),",nl}),
+ emit([" {{",Pos,",Dec}, Rest, ",{curr,rbCho},"}"]),
+ emit([";",nl,nl]).
+
+
+gen_dec_set_cases_type(Erules,TopType,H,Pos) ->
+ Cname = H#'ComponentType'.name,
+ Type = H#'ComponentType'.typespec,
+ %% always use Prop = mandatory here Prop = H#'ComponentType'.prop,
+
+ asn1ct_name:new(rbCho),
+ emit({indent(9),"{Dec, Rest, ",{curr,rbCho},"} = "}),
+ asn1ct_name:delete(bytes),
+ %% we have already seen the tag so now we must find the value
+ %% that why we always use 'mandatory' here
+ gen_dec_line(Erules,TopType,Cname,[],Type,mandatory,decObjInf),
+ asn1ct_name:new(bytes),
+
+ emit([",",nl]),
+ emit(["{{",Pos,",Dec}, Rest, ",{curr,rbCho},"}"]),
+ emit([";",nl,nl]).
+
+
+%%---------------------------------
+%% Decode SET result
+%%---------------------------------
+
+gen_dec_set_result(Erules,TopType,{CompList,_ExtList}) ->
+ gen_dec_set_result1(Erules,TopType, CompList, 1);
+gen_dec_set_result(Erules,TopType,CompList) ->
+ gen_dec_set_result1(Erules,TopType, CompList, 1).
+
+gen_dec_set_result1(Erules,TopType,
+ [#'ComponentType'{name=Cname,
+ typespec=Type,
+ prop=Prop}|Rest],Num) ->
+ gen_dec_set_component(Erules,TopType,Cname,Type,Num,Prop),
+ case Rest of
+ [] ->
+ true;
+ _ ->
+ gen_dec_set_result1(Erules,TopType,Rest,Num+1)
+ end;
+
+gen_dec_set_result1(_Erules,_TopType,[],1) ->
+ no_terms;
+gen_dec_set_result1(_Erules,_TopType,[],_Num) ->
+ true.
+
+
+gen_dec_set_component(_Erules,_TopType,_Cname,Type,Pos,Prop) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ print_attribute_comment(InnerType,Pos,Prop),
+ emit({" {",{next,term},com,{next,termList},"} =",nl}),
+ emit({" case ",{curr,termList}," of",nl}),
+ emit({" [{",Pos,com,{curr,termTmp},"}|",
+ {curr,rest},"] -> "}),
+ emit({"{",{curr,termTmp},com,
+ {curr,rest},"};",nl}),
+ case Prop of
+ 'OPTIONAL' ->
+ emit([indent(10),"_ -> {asn1_NOVALUE, ",{curr,termList},"}",nl]);
+ {'DEFAULT', DefVal} ->
+ emit([indent(10),
+ "_ -> {",{asis,DefVal},", ",{curr,termList},"}",nl]);
+ mandatory ->
+ emit([indent(10),
+ "_ -> exit({error,{asn1,{mandatory_attribute_no, ",
+ Pos,", missing}}})",nl])
+ end,
+ emit([indent(6),"end,",nl]),
+ asn1ct_name:new(rest),
+ asn1ct_name:new(term),
+ asn1ct_name:new(termList),
+ asn1ct_name:new(termTmp).
+
+
+%%---------------------------------------------
+%% Encode CHOICE
+%%---------------------------------------------
+%% for BER we currently do care (a little) if the choice has an EXTENSIONMARKER
+
+
+gen_enc_choice(Erules,TopType,Tag,CompList,_Ext) ->
+ gen_enc_choice1(Erules,TopType,Tag,CompList,_Ext).
+
+gen_enc_choice1(Erules,TopType,Tag,CompList,_Ext) ->
+ asn1ct_name:clear(),
+ emit({" {EncBytes,EncLen} = case element(1,Val) of",nl}),
+ gen_enc_choice2(Erules,TopType,CompList),
+ emit([nl," end,",nl,nl]),
+ NewTag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}|| X <- Tag],
+% gen_encode_tags(Erules,NewTag,"EncLen","EncBytes").
+ emit(["?RT_BER:encode_tags(TagIn ++",{asis,NewTag},", EncBytes, EncLen).",nl]).
+
+
+
+gen_enc_choice2(Erules,TopType,[H1|T]) when record(H1,'ComponentType') ->
+ Cname = H1#'ComponentType'.name,
+ Type = H1#'ComponentType'.typespec,
+ emit({" ",{asis,Cname}," ->",nl}),
+ {Encobj,Assign} =
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info) of
+ case {Type#type.def,asn1ct_gen:get_constraint(Type#type.constraint,
+ componentrelation)} of
+ {#'ObjectClassFieldType'{},{componentrelation,_,_}} ->
+ asn1ct_name:new(tmpBytes),
+ asn1ct_name:new(encBytes),
+ asn1ct_name:new(encLen),
+ Emit = ["{",{curr,tmpBytes},", _} = "],
+ {{no_attr,"ObjFun"},Emit};
+ _ ->
+ {false,[]}
+ end,
+ gen_enc_line(Erules,TopType,Cname,Type,"element(2,Val)",9,
+ mandatory,Assign,Encobj),
+ case Encobj of
+ false -> ok;
+ _ ->
+ emit({",",nl,indent(9),"{",{curr,encBytes},", ",
+ {curr,encLen},"}"})
+ end,
+ emit({";",nl}),
+ case T of
+ [] ->
+ emit([indent(6), "Else -> ",nl,
+ indent(9),"exit({error,{asn1,{invalid_choice_type,Else}}})"]);
+ _ ->
+ true
+ end,
+ gen_enc_choice2(Erules,TopType,T);
+
+gen_enc_choice2(_,_,[]) ->
+ true.
+
+
+
+
+%%--------------------------------------------
+%% Decode CHOICE
+%%--------------------------------------------
+
+gen_dec_choice(Erules,TopType, ChTag, CompList, Ext) ->
+ asn1ct_name:delete(bytes),
+ Tags = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}|| X <- ChTag],
+
+ emit([" {{_,Len},",{next,bytes},
+ ", RbExp} = ?RT_BER:check_tags(TagIn++",
+ {asis,Tags},", ",
+ {curr,bytes},", OptOrMand),",nl]),
+ asn1ct_name:new(bytes),
+ asn1ct_name:new(len),
+ gen_dec_choice_indef_funs(Erules),
+ case Erules of
+ ber_bin ->
+ emit([indent(3),"case ",{curr,bytes}," of",nl]);
+ ber ->
+ emit([indent(3),
+ "case (catch ?RT_BER:peek_tag(",{curr,bytes},")) of",nl])
+ end,
+ asn1ct_name:new(tagList),
+ asn1ct_name:new(choTags),
+ gen_dec_choice_cases(Erules,TopType,CompList),
+ case Ext of
+ noext ->
+ emit([indent(6), {curr,else}," -> ",nl]),
+ emit([indent(9),"case OptOrMand of",nl,
+ indent(12),"mandatory ->","exit({error,{asn1,",
+ "{invalid_choice_tag,",{curr,else},"}}});",nl,
+ indent(12),"_ ->","exit({error,{asn1,{no_optional_tag,",
+ {curr,else},"}}})",nl,
+ indent(9),"end",nl]);
+ _ ->
+ emit([indent(6),"_ -> ",nl]),
+ emit([indent(9),"{{asn1_ExtAlt,",{curr,bytes},"},",
+ empty_lb(Erules),", RbExp}",nl])
+ end,
+ emit([indent(3),"end"]),
+ asn1ct_name:new(tag),
+ asn1ct_name:new(else).
+
+gen_dec_choice_indef_funs(Erules) ->
+ emit({indent(3),"IndefEndBytes = fun(indefinite,",indefend_match(Erules,used_var),
+ ")-> R; (_,B)-> B end,",nl}),
+ emit({indent(3),"IndefEndRb = fun(indefinite,",indefend_match(Erules,unused_var),
+ ")-> 2; (_,_)-> 0 end,",nl}).
+
+
+gen_dec_choice_cases(_,_, []) ->
+ ok;
+gen_dec_choice_cases(Erules,TopType, [H|T]) ->
+ asn1ct_name:push(rbCho),
+ Name = H#'ComponentType'.name,
+ emit([nl,"%% '",Name,"'",nl]),
+ Fcases = fun([T1,T2|Tail],Fun) ->
+ emit([indent(6),match_tag(Erules,T1)," ->",nl]),
+ gen_dec_choice_cases_type(Erules,TopType, H),
+ Fun([T2|Tail],Fun);
+ ([T1],_) ->
+ emit([indent(6),match_tag(Erules,T1)," ->",nl]),
+ gen_dec_choice_cases_type(Erules,TopType, H)
+ end,
+ Fcases(H#'ComponentType'.tags,Fcases),
+ asn1ct_name:pop(rbCho),
+ gen_dec_choice_cases(Erules,TopType, T).
+
+
+
+gen_dec_choice_cases_type(Erules,TopType,H) ->
+ Cname = H#'ComponentType'.name,
+ Type = H#'ComponentType'.typespec,
+ Prop = H#'ComponentType'.prop,
+ emit({indent(9),"{Dec, Rest, ",{curr,rbCho},"} = "}),
+ gen_dec_line(Erules,TopType,Cname,[],Type,Prop,false),
+ emit([",",nl,indent(9),"{{",{asis,Cname},
+ ", Dec}, IndefEndBytes(Len,Rest), RbExp + ",
+ {curr,rbCho}," + IndefEndRb(Len,Rest)};",nl,nl]).
+
+encode_tag_val(Erules,{Class,TagNo}) when integer(TagNo) ->
+ Rtmod = rtmod(Erules),
+ Rtmod:encode_tag_val({asn1ct_gen_ber:decode_class(Class),
+ 0,TagNo});
+encode_tag_val(Erules,{Class,TypeName}) ->
+ Rtmod = rtmod(Erules),
+ Rtmod:encode_tag_val({asn1ct_gen_ber:decode_class(Class),
+ 0,asn1ct_gen_ber:decode_type(TypeName)}).
+
+
+match_tag(ber_bin,Arg) ->
+ match_tag_with_bitsyntax(Arg);
+match_tag(Erules,Arg) ->
+ io_lib:format("~p",[encode_tag_val(Erules,Arg)]).
+
+match_tag_with_bitsyntax({Class,TagNo}) when integer(TagNo) ->
+ match_tag_with_bitsyntax1({asn1ct_gen_ber:decode_class(Class),
+ 0,TagNo});
+match_tag_with_bitsyntax({Class,TypeName}) ->
+ match_tag_with_bitsyntax1({asn1ct_gen_ber:decode_class(Class),
+ 0,asn1ct_gen_ber:decode_type(TypeName)}).
+
+match_tag_with_bitsyntax1({Class, _Form, TagNo}) when (TagNo =< 30) ->
+ io_lib:format("<<~p:2,_:1,~p:5,_/binary>>",[Class bsr 6,TagNo]);
+
+match_tag_with_bitsyntax1({Class, _Form, TagNo}) ->
+ {Octets,Len} = mk_object_val(TagNo),
+ OctForm = case Len of
+ 1 -> "~p";
+ 2 -> "~p,~p";
+ 3 -> "~p,~p,~p";
+ 4 -> "~p,~p,~p,~p"
+ end,
+ io_lib:format("<<~p:2,_:1,31:5," ++ OctForm ++ ",_/binary>>",
+ [Class bsr 6] ++ Octets).
+
+%%%%%%%%%%%
+%% mk_object_val(Value) -> {OctetList, Len}
+%% returns a Val as a list of octets, the 8 bit is allways set to one except
+%% for the last octet, where its 0
+%%
+
+
+mk_object_val(Val) when Val =< 127 ->
+ {[255 band Val], 1};
+mk_object_val(Val) ->
+ mk_object_val(Val bsr 7, [Val band 127], 1).
+mk_object_val(0, Ack, Len) ->
+ {Ack, Len};
+mk_object_val(Val, Ack, Len) ->
+ mk_object_val(Val bsr 7, [((Val band 127) bor 128) | Ack], Len + 1).
+
+
+get_all_choice_tags(ComponentTypeList) ->
+ get_all_choice_tags(ComponentTypeList,[]).
+
+get_all_choice_tags([],TagList) ->
+ TagList;
+get_all_choice_tags([H|T],TagList) ->
+ Tags = H#'ComponentType'.tags,
+ get_all_choice_tags(T, TagList ++ [{H#'ComponentType'.name, Tags}]).
+
+
+
+%%---------------------------------------
+%% Generate the encode/decode code
+%%---------------------------------------
+
+gen_enc_line(Erules,TopType,Cname,
+ Type=#type{constraint=[{componentrelation,_,_}],
+ def=#'ObjectClassFieldType'{type={typefield,_}}},
+ Element,Indent,OptOrMand=mandatory,EncObj)
+ when list(Element) ->
+ asn1ct_name:new(tmpBytes),
+ gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,
+ ["{",{curr,tmpBytes},",_} = "],EncObj);
+gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,EncObj)
+ when list(Element) ->
+ gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,
+ ["{",{curr,encBytes},",",{curr,encLen},"} = "],EncObj).
+
+gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,Assign,EncObj)
+ when list(Element) ->
+ IndDeep = indent(Indent),
+
+ Tag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}
+ || X <- Type#type.tag],
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ WhatKind = asn1ct_gen:type(InnerType),
+ emit(IndDeep),
+ emit(Assign),
+ gen_optormand_case(OptOrMand,Erules,TopType,Cname,Type,InnerType,WhatKind,
+ Element),
+ case {Type,asn1ct_gen:get_constraint(Type#type.constraint,
+ componentrelation)} of
+% #type{constraint=[{tableconstraint_info,RefedFieldName}],
+% def={typefield,_}} ->
+ {#type{def=#'ObjectClassFieldType'{type={typefield,_},
+ fieldname=RefedFieldName}},
+ {componentrelation,_,_}} ->
+ {_LeadingAttrName,Fun} = EncObj,
+ case RefedFieldName of
+ {notype,T} ->
+ throw({error,{notype,type_from_object,T}});
+ {Name,RestFieldNames} when atom(Name) ->
+ case OptOrMand of
+ mandatory -> ok;
+ _ ->
+% emit(["{",{curr,tmpBytes},",",{curr,tmpLen},
+ emit(["{",{curr,tmpBytes},", _} = "])
+%% asn1ct_name:new(tmpBytes),
+%% asn1ct_name:new(tmpLen)
+ end,
+ emit({Fun,"(",{asis,Name},", ",Element,", [], ",
+ {asis,RestFieldNames},"),",nl}),
+ emit(IndDeep),
+ case OptOrMand of
+ mandatory ->
+ emit({"{",{curr,encBytes},", ",{curr,encLen},"} = "}),
+ emit({"?RT_BER:encode_open_type(",{curr,tmpBytes},
+ ",",{asis,Tag},")"});
+ _ ->
+% emit({"{",{next,tmpBytes},", _} = "}),
+ emit({"{",{next,tmpBytes},", ",{curr,tmpLen},
+ "} = "}),
+ emit({"?RT_BER:encode_open_type(",{curr,tmpBytes},
+ ",",{asis,Tag},"),",nl}),
+ emit(IndDeep),
+ emit({"{",{next,tmpBytes},", ",{curr,tmpLen},"}"})
+ end;
+ _ ->
+ throw({asn1,{'internal error'}})
+ end;
+% #type{constraint=[{tableconstraint_info,_}],
+% def={objectfield,PrimFieldName1,PFNList}} ->
+ {{#'ObjectClassFieldType'{type={objectfield,PrimFieldName1,
+ PFNList}},_},
+ {componentrelation,_,_}} ->
+ %% this is when the dotted list in the FieldName has more
+ %% than one element
+ {_LeadingAttrName,Fun} = EncObj,
+ emit({"?RT_BER:encode_open_type(",Fun,"(",{asis,PrimFieldName1},
+ ", ",Element,", ",{asis,PFNList},"),",{asis,Tag},")"});
+ _ ->
+ case WhatKind of
+ {primitive,bif} ->
+ EncType =
+ case Type#type.def of
+ #'ObjectClassFieldType'{
+ type={fixedtypevaluefield,
+ _,Btype}} ->
+ Btype;
+ _ ->
+ Type
+ end,
+ asn1ct_gen_ber:gen_encode_prim(ber,EncType,{asis,Tag},
+ Element);
+ {notype,_} ->
+ emit({"'enc_",InnerType,"'(",Element,", ",{asis,Tag},")"});
+ 'ASN1_OPEN_TYPE' ->
+ asn1ct_gen_ber:gen_encode_prim(ber,Type#type{def='ASN1_OPEN_TYPE'},{asis,Tag},Element);
+ _ ->
+ {EncFunName, _, _} =
+ mkfuncname(TopType,Cname,WhatKind,enc),
+ case {WhatKind,Type#type.tablecinf,EncObj} of
+ {{constructed,bif},[{objfun,_}|_R],{_,Fun}} ->
+ emit([EncFunName,"(",Element,", ",{asis,Tag},
+ ", ",Fun,")"]);
+ _ ->
+ emit([EncFunName,"(",Element,", ",{asis,Tag},")"])
+ end
+ end
+ end,
+ case OptOrMand of
+ mandatory -> true;
+ _ ->
+ emit({nl,indent(7),"end"})
+ end.
+
+
+
+gen_optormand_case(mandatory,_,_,_,_,_,_, _) ->
+ ok;
+gen_optormand_case('OPTIONAL',Erules,_,_,_,_,_,Element) ->
+ emit({" case ",Element," of",nl}),
+ emit({indent(9),"asn1_NOVALUE -> {",
+ empty_lb(Erules),",0};",nl}),
+ emit({indent(9),"_ ->",nl,indent(12)});
+gen_optormand_case({'DEFAULT',DefaultValue},Erules,TopType,Cname,Type,
+ InnerType,WhatKind,Element) ->
+ CurrMod = get(currmod),
+ case catch lists:member(der,get(encoding_options)) of
+ true ->
+ emit(" case catch "),
+ asn1ct_gen:gen_check_call(TopType,Cname,Type,InnerType,
+ WhatKind,{asis,DefaultValue},
+ Element),
+ emit({" of",nl}),
+ emit({indent(12),"true -> {[],0};",nl});
+ _ ->
+ emit({" case ",Element," of",nl}),
+ emit({indent(9),"asn1_DEFAULT -> {",
+ empty_lb(Erules),
+ ",0};",nl}),
+ case DefaultValue of
+ #'Externalvaluereference'{module=CurrMod,
+ value=V} ->
+ emit({indent(9),"?",{asis,V}," -> {",
+ empty_lb(Erules),",0};",nl});
+ _ ->
+ emit({indent(9),{asis,
+ DefaultValue}," -> {",
+ empty_lb(Erules),",0};",nl})
+ end
+ end,
+ emit({indent(9),"_ ->",nl,indent(12)}).
+
+
+
+
+gen_dec_line_sof(_Erules,TopType,Cname,Type,ObjFun) ->
+
+ Tag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}
+ || X <- Type#type.tag],
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ WhatKind = asn1ct_gen:type(InnerType),
+ case WhatKind of
+ {primitive,bif} ->
+ asn1ct_name:delete(len),
+
+ asn1ct_name:new(len),
+ emit(["fun(FBytes,_,_)->",nl]),
+ EncType = case Type#type.def of
+ #'ObjectClassFieldType'{
+ type={fixedtypevaluefield,
+ _,Btype}} ->
+ Btype;
+ _ ->
+ Type
+ end,
+ asn1ct_gen_ber:gen_dec_prim(ber,EncType,"FBytes",Tag,
+ [],no_length,?PRIMITIVE,
+ mandatory),
+ emit([nl,"end, []"]);
+ _ ->
+ case ObjFun of
+ [] ->
+ {DecFunName, _, _} =
+ mkfunname(TopType,Cname,WhatKind,dec,3),
+ emit([DecFunName,", ",{asis,Tag}]);
+ _ ->
+ {DecFunName, _, _} =
+ mkfunname(TopType,Cname,WhatKind,dec,4),
+ emit([DecFunName,", ",{asis,Tag},", ObjFun"])
+ end
+ end.
+
+
+gen_dec_line(Erules,TopType,Cname,CTags,Type,OptOrMand,DecObjInf) ->
+ BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
+ Tag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}
+ || X <- Type#type.tag],
+ InnerType =
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=OCFTType} ->
+ OCFTType;
+ _ ->
+ asn1ct_gen:get_inner(Type#type.def)
+ end,
+ PostpDec =
+ case OptOrMand of
+ mandatory ->
+ gen_dec_call(InnerType,Erules,TopType,Cname,Type,
+ BytesVar,Tag,mandatory,", mandatory, ",
+ DecObjInf,OptOrMand);
+ _ -> %optional or default
+ case {CTags,Erules} of
+ {[CTag],ber_bin} ->
+ emit(["case ",{curr,bytes}," of",nl]),
+ emit([match_tag(Erules,CTag)," ->",nl]),
+ PostponedDec =
+ gen_dec_call(InnerType,Erules,TopType,Cname,Type,
+ BytesVar,Tag,mandatory,
+ ", opt_or_default, ",DecObjInf,
+ OptOrMand),
+ emit([";",nl]),
+ emit(["_ ->",nl]),
+ case OptOrMand of
+ {'DEFAULT', Def} ->
+ emit(["{",{asis,Def},",",
+ BytesVar,", 0 }",nl]);
+ 'OPTIONAL' ->
+ emit(["{ asn1_NOVALUE, ",
+ BytesVar,", 0 }",nl])
+ end,
+ emit("end"),
+ PostponedDec;
+ _ ->
+ emit("case (catch "),
+ PostponedDec =
+ gen_dec_call(InnerType,Erules,TopType,Cname,Type,
+ BytesVar,Tag,OptOrMand,
+ ", opt_or_default, ",DecObjInf,
+ OptOrMand),
+ emit([") of",nl]),
+ case OptOrMand of
+ {'DEFAULT', Def} ->
+ emit(["{'EXIT',{error,{asn1,{no_optional_tag,_}}}}",
+ " -> {",{asis,Def},",",
+ BytesVar,", 0 };",nl]);
+ 'OPTIONAL' ->
+ emit(["{'EXIT',{error,{asn1,{no_optional_tag,_}}}}",
+ " -> { asn1_NOVALUE, ",
+ BytesVar,", 0 };",nl])
+ end,
+ asn1ct_name:new(casetmp),
+ emit([{curr,casetmp},"-> ",{curr,casetmp},nl,"end"]),
+ PostponedDec
+ end
+ end,
+ case DecObjInf of
+ {Cname,ObjSet} -> % this must be the component were an object is
+ %% choosen from the object set according to the table
+ %% constraint.
+ {[{ObjSet,Cname,asn1ct_gen:mk_var(asn1ct_name:curr(term))}],
+ PostpDec};
+ _ -> {[],PostpDec}
+ end.
+
+
+gen_dec_call({typefield,_},Erules,_,_,Type,_,Tag,_,_,false,_) ->
+ %% this in case of a choice with typefield components
+ asn1ct_name:new(reason),
+ {FirstPFName,RestPFName} =
+% asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info),
+ (Type#type.def)#'ObjectClassFieldType'.fieldname,
+ emit([nl,indent(6),"begin",nl]),
+ emit([indent(9),"{OpenDec,TmpRest,TmpRbCho} =",nl,indent(12),
+ "?RT_BER:decode_open_type(",Erules,",",{curr,bytes},",",
+ {asis,Tag},"),",nl]),
+ emit([indent(9),"case (catch ObjFun(",{asis,FirstPFName},
+ ", OpenDec, [], ",{asis,RestPFName},
+ ")) of", nl]),%% ??? What about Tag
+ emit([indent(12),"{'EXIT',",{curr,reason},"} ->",nl]),
+%% emit({indent(15),"throw({runtime_error,{'Type not ",
+%% "compatible with tableconstraint', OpenDec}});",nl}),
+ emit([indent(15),"exit({'Type not ",
+ "compatible with table constraint', ",{curr,reason},"});",nl]),
+ emit([indent(12),"{TmpDec,_ ,_} ->",nl]),
+ emit([indent(15),"{TmpDec, TmpRest, TmpRbCho}",nl]),
+ emit([indent(9),"end",nl,indent(6),"end",nl]),
+ [];
+gen_dec_call({typefield,_},_Erules,_,Cname,Type,_BytesVar,Tag,_,_,
+ _DecObjInf,OptOrMandComp) ->
+ emit(["?RT_BER:decode_open_type(",{curr,bytes},",",{asis,Tag},")"]),
+ RefedFieldName =
+ (Type#type.def)#'ObjectClassFieldType'.fieldname,
+% asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info),
+ [{Cname,RefedFieldName,
+ asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+% asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),[],OptOrMandComp}];
+ asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),Tag,OptOrMandComp}];
+gen_dec_call({objectfield,PrimFieldName,PFNList},_Erules,_,Cname,_,_,Tag,_,_,_,
+ OptOrMandComp) ->
+ emit(["?RT_BER:decode_open_type(",{curr,bytes},",",{asis,Tag},")"]),
+ [{Cname,{PrimFieldName,PFNList},
+ asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+% asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),[],OptOrMandComp}];
+ asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),Tag,OptOrMandComp}];
+gen_dec_call(InnerType,Erules,TopType,Cname,Type,BytesVar,Tag,PrimOptOrMand,
+ OptOrMand,DecObjInf,_) ->
+ WhatKind = asn1ct_gen:type(InnerType),
+ gen_dec_call1(WhatKind,InnerType,Erules,TopType,Cname,Type,BytesVar,Tag,
+ PrimOptOrMand,OptOrMand),
+ case DecObjInf of
+ {Cname,{_,OSet,UniqueFName,ValIndex}} ->
+ Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+ ValueMatch = value_match(ValIndex,Term),
+ emit({",",nl,"ObjFun = 'getdec_",OSet,"'(",
+% {asis,UniqueFName},", ",{curr,term},")"});
+ {asis,UniqueFName},", ",ValueMatch,")"});
+ _ ->
+ ok
+ end,
+ [].
+gen_dec_call1({primitive,bif},InnerType,Erules,_,_,Type,BytesVar,
+ Tag,OptOrMand,_) ->
+ case InnerType of
+ {fixedtypevaluefield,_,Btype} ->
+ asn1ct_gen_ber:gen_dec_prim(Erules,Btype,BytesVar,Tag,[],no_length,
+ ?PRIMITIVE,OptOrMand);
+ _ ->
+ asn1ct_gen_ber:gen_dec_prim(Erules,Type,BytesVar,Tag,[],no_length,
+ ?PRIMITIVE,OptOrMand)
+ end;
+gen_dec_call1('ASN1_OPEN_TYPE',_InnerType,Erules,_,_,Type,BytesVar,
+ Tag,OptOrMand,_) ->
+ asn1ct_gen_ber:gen_dec_prim(Erules,Type#type{def='ASN1_OPEN_TYPE'},
+ BytesVar,Tag,[],no_length,
+ ?PRIMITIVE,OptOrMand);
+gen_dec_call1(WhatKind,_,_Erules,TopType,Cname,Type,_,Tag,_,OptOrMand) ->
+ {DecFunName,_,_} =
+ mkfuncname(TopType,Cname,WhatKind,dec),
+ case {WhatKind,Type#type.tablecinf} of
+ {{constructed,bif},[{objfun,_}|_R]} ->
+ emit({DecFunName,"(",{curr,bytes},OptOrMand,{asis,Tag},", ObjFun)"});
+ _ ->
+ emit({DecFunName,"(",{curr,bytes},OptOrMand,{asis,Tag},")"})
+ end.
+
+
+%%------------------------------------------------------
+%% General and special help functions (not exported)
+%%------------------------------------------------------
+
+
+indent(N) ->
+ lists:duplicate(N,32). % 32 = space
+
+
+mkvlist([H,T1|T], Sep) -> % Sep is a string e.g ", " or "+ "
+ emit([{var,H},Sep]),
+ mkvlist([T1|T], Sep);
+mkvlist([H|T], Sep) ->
+ emit([{var,H}]),
+ mkvlist(T, Sep);
+mkvlist([], _) ->
+ true.
+
+mkvlist(L) ->
+ mkvlist(L,", ").
+
+mkvplus(L) ->
+ mkvlist(L," + ").
+
+extensible(CompList) when list(CompList) ->
+ noext;
+extensible({RootList,ExtList}) ->
+ {ext,length(RootList)+1,length(ExtList)}.
+
+
+print_attribute_comment(InnerType,Pos,Prop) ->
+ CommentLine = "%%-------------------------------------------------",
+ emit([nl,CommentLine]),
+ case InnerType of
+ {typereference,_,Name} ->
+ emit([nl,"%% attribute number ",Pos," with type ",Name]);
+ {'Externaltypereference',_,XModule,Name} ->
+ emit([nl,"%% attribute number ",Pos," External ",XModule,":",Name]);
+ _ ->
+ emit([nl,"%% attribute number ",Pos," with type ",InnerType])
+ end,
+ case Prop of
+ mandatory ->
+ continue;
+ {'DEFAULT', Def} ->
+ emit([" DEFAULT = ",{asis,Def}]);
+ 'OPTIONAL' ->
+ emit([" OPTIONAL"])
+ end,
+ emit([nl,CommentLine,nl]).
+
+
+mkfuncname(TopType,Cname,WhatKind,DecOrEnc) ->
+ CurrMod = get(currmod),
+ case WhatKind of
+ #'Externaltypereference'{module=CurrMod,type=EType} ->
+ F = lists:concat(["'",DecOrEnc,"_",EType,"'"]),
+ {F, "?MODULE", F};
+ #'Externaltypereference'{module=Mod,type=EType} ->
+ {lists:concat(["'",Mod,"':'",DecOrEnc,"_",EType,"'"]),Mod,
+ lists:concat(["'",DecOrEnc,"_",EType,"'"])};
+ {constructed,bif} ->
+ F = lists:concat(["'",DecOrEnc,"_",asn1ct_gen:list2name([Cname|TopType]),"'"]),
+ {F, "?MODULE", F}
+ end.
+
+mkfunname(TopType,Cname,WhatKind,DecOrEnc,Arity) ->
+ CurrMod = get(currmod),
+ case WhatKind of
+ #'Externaltypereference'{module=CurrMod,type=EType} ->
+ F = lists:concat(["fun '",DecOrEnc,"_",EType,"'/",Arity]),
+ {F, "?MODULE", F};
+ #'Externaltypereference'{module=Mod,type=EType} ->
+ {lists:concat(["{'",Mod,"','",DecOrEnc,"_",EType,"'}"]),Mod,
+ lists:concat(["'",DecOrEnc,"_",EType,"'"])};
+ {constructed,bif} ->
+ F =
+ lists:concat(["fun '",DecOrEnc,"_",
+ asn1ct_gen:list2name([Cname|TopType]),"'/",
+ Arity]),
+ {F, "?MODULE", F}
+ end.
+
+empty_lb(ber) ->
+ "[]";
+empty_lb(ber_bin) ->
+ "<<>>".
+
+rtmod(ber) ->
+ list_to_atom(?RT_BER);
+rtmod(ber_bin) ->
+ list_to_atom(?RT_BER_BIN).
+
+indefend_match(ber,used_var) ->
+ "[0,0|R]";
+indefend_match(ber,unused_var) ->
+ "[0,0|_R]";
+indefend_match(ber_bin,used_var) ->
+ "<<0,0,R/binary>>";
+indefend_match(ber_bin,unused_var) ->
+ "<<0,0,_R/binary>>".
+
+notice_value_match() ->
+ Module = get(currmod),
+ put(value_match,{true,Module}).
+
+value_match(Index,Value) when atom(Value) ->
+ value_match(Index,atom_to_list(Value));
+value_match([],Value) ->
+ Value;
+value_match([{VI,_Cname}|VIs],Value) ->
+ value_match1(Value,VIs,lists:concat(["element(",VI,","]),1).
+value_match1(Value,[],Acc,Depth) ->
+ Acc ++ Value ++ lists:concat(lists:duplicate(Depth,")"));
+value_match1(Value,[{VI,_Cname}|VIs],Acc,Depth) ->
+ value_match1(Value,VIs,Acc++lists:concat(["element(",VI,","]),Depth+1).
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_constructed_ber_bin_v2.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_constructed_ber_bin_v2.erl
new file mode 100644
index 0000000000..991240731e
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_constructed_ber_bin_v2.erl
@@ -0,0 +1,1357 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_constructed_ber_bin_v2.erl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-module(asn1ct_constructed_ber_bin_v2).
+
+-export([gen_encode_sequence/3]).
+-export([gen_decode_sequence/3]).
+-export([gen_encode_set/3]).
+-export([gen_decode_set/3]).
+-export([gen_encode_sof/4]).
+-export([gen_decode_sof/4]).
+-export([gen_encode_choice/3]).
+-export([gen_decode_choice/3]).
+
+
+-include("asn1_records.hrl").
+
+-import(asn1ct_gen, [emit/1,demit/1]).
+-import(asn1ct_constructed_ber,[match_tag/2]).
+
+-define(ASN1CT_GEN_BER,asn1ct_gen_ber_bin_v2).
+
+% the encoding of class of tag bits 8 and 7
+-define(UNIVERSAL, 0).
+-define(APPLICATION, 16#40).
+-define(CONTEXT, 16#80).
+-define(PRIVATE, 16#C0).
+
+% primitive or constructed encoding % bit 6
+-define(PRIMITIVE, 0).
+-define(CONSTRUCTED, 2#00100000).
+
+
+
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Encode/decode SEQUENCE (and SET)
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+gen_encode_sequence(Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ asn1ct_name:new(term),
+ asn1ct_name:new(bytes),
+
+ %% if EXTERNAL type the input value must be transformed to
+ %% ASN1 1990 format
+ ValName =
+ case Typename of
+ ['EXTERNAL'] ->
+ emit([indent(4),
+ "NewVal = asn1rt_check:transform_to_EXTERNAL1990(Val),",
+ nl]),
+ "NewVal";
+ _ ->
+ "Val"
+ end,
+
+ {SeqOrSet,TableConsInfo,CompList} =
+ case D#type.def of
+ #'SEQUENCE'{tablecinf=TCI,components=CL} ->
+ {'SEQUENCE',TCI,CL};
+ #'SET'{tablecinf=TCI,components=CL} ->
+ {'SET',TCI,CL}
+ end,
+ Ext = extensible(CompList),
+ CompList1 = case CompList of
+ {Rl,El} -> Rl ++ El;
+ _ -> CompList
+ end,
+
+%% don't match recordname for now, because of compatibility reasons
+%% emit(["{'",asn1ct_gen:list2rname(Typename),"'"]),
+ emit(["{_"]),
+ case length(CompList1) of
+ 0 ->
+ true;
+ CompListLen ->
+ emit([","]),
+ mkcindexlist([Tc || Tc <- lists:seq(1,CompListLen)])
+ end,
+ emit(["} = ",ValName,",",nl]),
+ EncObj =
+ case TableConsInfo of
+ #simpletableattributes{usedclassfield=Used,
+ uniqueclassfield=Unique} when Used /= Unique ->
+ false;
+ %% ObjectSet, name of the object set in constraints
+ %%
+ #simpletableattributes{objectsetname=ObjectSet,
+ c_name=AttrN,
+ c_index=N,
+ usedclassfield=UniqueFieldName,
+ uniqueclassfield=UniqueFieldName,
+ valueindex=ValueIndex} -> %% N is index of attribute that determines constraint
+ OSDef =
+ case ObjectSet of
+ {Module,OSName} ->
+ asn1_db:dbget(Module,OSName);
+ OSName ->
+ asn1_db:dbget(get(currmod),OSName)
+ end,
+% io:format("currmod: ~p~nOSName: ~p~nAttrN: ~p~nN: ~p~nUniqueFieldName: ~p~n",
+% [get(currmod),OSName,AttrN,N,UniqueFieldName]),
+ case (OSDef#typedef.typespec)#'ObjectSet'.gen of
+ true ->
+ ObjectEncode =
+ asn1ct_gen:un_hyphen_var(lists:concat(['Obj',
+ AttrN])),
+ emit([ObjectEncode," = ",nl]),
+ emit([" 'getenc_",ObjectSet,"'(",{asis,UniqueFieldName},
+ ", ",nl]),
+ ValueMatch = value_match(ValueIndex,
+ lists:concat(["Cindex",N])),
+ emit([indent(35),ValueMatch,"),",nl]),
+ {AttrN,ObjectEncode};
+ _ ->
+ false
+ end;
+ _ ->
+ case D#type.tablecinf of
+ [{objfun,_}|_] ->
+ %% when the simpletableattributes was at an outer
+ %% level and the objfun has been passed through the
+ %% function call
+ {"got objfun through args","ObjFun"};
+ _ ->
+ false
+ end
+ end,
+
+ gen_enc_sequence_call(Erules,Typename,CompList1,1,Ext,EncObj),
+
+ emit([nl," BytesSoFar = "]),
+ case SeqOrSet of
+ 'SET' when (D#type.def)#'SET'.sorted == dynamic ->
+ emit("?RT_BER:dynamicsort_SET_components(["),
+ mkvlist(asn1ct_name:all(encBytes)),
+ emit(["]),",nl]);
+ _ ->
+ emit("["),
+ mkvlist(asn1ct_name:all(encBytes)),
+ emit(["],",nl])
+ end,
+ emit("LenSoFar = "),
+ case asn1ct_name:all(encLen) of
+ [] -> emit("0");
+ AllLengths ->
+ mkvplus(AllLengths)
+ end,
+ emit([",",nl]),
+ emit(["?RT_BER:encode_tags(TagIn, BytesSoFar, LenSoFar)."
+ ,nl]).
+
+gen_decode_sequence(Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ asn1ct_name:new(tag),
+ #'SEQUENCE'{tablecinf=TableConsInfo,components=CList} = D#type.def,
+ Ext = extensible(CList),
+ CompList = case CList of
+ {Rl,El} -> Rl ++ El;
+ _ -> CList
+ end,
+
+ emit([" %%-------------------------------------------------",nl]),
+ emit([" %% decode tag and length ",nl]),
+ emit([" %%-------------------------------------------------",nl]),
+
+ asn1ct_name:new(tlv),
+ case CompList of
+ EmptyCL when EmptyCL == [];EmptyCL == {[],[]}-> % empty sequence
+ true;
+ _ ->
+ emit([{curr,tlv}," = "])
+ end,
+ emit(["?RT_BER:match_tags(",{prev,tlv},",TagIn), ",nl]),
+ asn1ct_name:new(tlv),
+ asn1ct_name:new(v),
+
+ {DecObjInf,UniqueFName,ValueIndex} =
+ case TableConsInfo of
+ #simpletableattributes{objectsetname=ObjectSet,
+ c_name=AttrN,
+ usedclassfield=UniqueFieldName,
+ uniqueclassfield=UniqueFieldName,
+ valueindex=ValIndex} ->
+% {ObjectSet,AttrN,_N,UniqueFieldName} ->%% N is index of attribute that determines constraint
+ F = fun(#'ComponentType'{typespec=CT})->
+ case {CT#type.constraint,CT#type.tablecinf} of
+ {[],[{objfun,_}|_]} -> true;
+ _ -> false
+ end
+ end,
+ case lists:any(F,CompList) of
+ true -> % when component relation constraint establish
+ %% relation from a component to another components
+ %% subtype component
+ {{AttrN,{deep,ObjectSet,UniqueFieldName,ValIndex}},
+ UniqueFieldName,ValIndex};
+ false ->
+ {{AttrN,ObjectSet},UniqueFieldName,ValIndex}
+ end;
+ _ ->
+% case D#type.tablecinf of
+% [{objfun,_}|_] ->
+% {{"got objfun through args","ObjFun"},false,false};
+% _ ->
+ {false,false,false}
+% end
+ end,
+ case gen_dec_sequence_call(Erules,Typename,CompList,Ext,DecObjInf) of
+ no_terms -> % an empty sequence
+ emit([nl,nl]),
+ demit(["Result = "]), %dbg
+ %% return value as record
+ asn1ct_name:new(rb),
+ emit([" {'",asn1ct_gen:list2rname(Typename),"'}.",nl,nl]);
+ {LeadingAttrTerm,PostponedDecArgs} ->
+ emit([com,nl,nl]),
+ case {LeadingAttrTerm,PostponedDecArgs} of
+ {[],[]} ->
+ ok;
+ {_,[]} ->
+ ok;
+ {[{ObjSet,LeadingAttr,Term}],PostponedDecArgs} ->
+ DecObj = asn1ct_gen:un_hyphen_var(lists:concat(['DecObj',LeadingAttr,Term])),
+ ValueMatch = value_match(ValueIndex,Term),
+ emit([DecObj," =",nl," 'getdec_",ObjSet,"'(",
+ {asis,UniqueFName},", ",ValueMatch,"),",nl]),
+ gen_dec_postponed_decs(DecObj,PostponedDecArgs)
+ end,
+ demit(["Result = "]), %dbg
+ %% return value as record
+ case Ext of
+ {ext,_,_} ->
+ emit(["case ",{prev,tlv}," of [] -> true; _ -> true end, % ... extra fields skipped",nl]);
+ noext ->
+ emit(["case ",{prev,tlv}," of",nl,
+ "[] -> true;",
+ "_ -> exit({error,{asn1, {unexpected,",{prev,tlv},
+ "}}}) % extra fields not allowed",nl,
+ "end,",nl])
+ end,
+ asn1ct_name:new(rb),
+ case Typename of
+ ['EXTERNAL'] ->
+ emit([" OldFormat={'",asn1ct_gen:list2rname(Typename),
+ "', "]),
+ mkvlist(asn1ct_name:all(term)),
+ emit(["},",nl]),
+ emit([" asn1rt_check:transform_to_EXTERNAL1994",
+ "(OldFormat).",nl]);
+ _ ->
+ emit([" {'",asn1ct_gen:list2rname(Typename),"', "]),
+ mkvlist(asn1ct_name:all(term)),
+ emit(["}.",nl,nl])
+ end
+ end.
+
+gen_dec_postponed_decs(_,[]) ->
+ emit(nl);
+gen_dec_postponed_decs(DecObj,[{_Cname,{FirstPFN,PFNList},Term,
+ TmpTerm,_Tag,OptOrMand}|Rest]) ->
+
+ asn1ct_name:new(tmpterm),
+ asn1ct_name:new(reason),
+ asn1ct_name:new(tmptlv),
+
+ emit([Term," = ",nl]),
+ N = case OptOrMand of
+ mandatory -> 0;
+ 'OPTIONAL' ->
+ emit_opt_or_mand_check(asn1_NOVALUE,TmpTerm),
+ 6;
+ {'DEFAULT',Val} ->
+ emit_opt_or_mand_check(Val,TmpTerm),
+ 6
+ end,
+ emit([indent(N+3),"case (catch ",DecObj,"(",{asis,FirstPFN},
+ ", ",TmpTerm,", ",{asis,PFNList},")) of",nl]),
+ emit([indent(N+6),"{'EXIT', ",{curr,reason},"} ->",nl]),
+ emit([indent(N+9),"exit({'Type not compatible with table constraint',",
+ {curr,reason},"});",nl]),
+ emit([indent(N+6),{curr,tmpterm}," ->",nl]),
+ emit([indent(N+9),{curr,tmpterm},nl]),
+
+ case OptOrMand of
+ mandatory -> emit([indent(N+3),"end,",nl]);
+ _ ->
+ emit([indent(N+3),"end",nl,
+ indent(3),"end,",nl])
+ end,
+ gen_dec_postponed_decs(DecObj,Rest).
+
+emit_opt_or_mand_check(Value,TmpTerm) ->
+ emit([indent(3),"case ",TmpTerm," of",nl,
+ indent(6),{asis,Value}," ->",{asis,Value},";",nl,
+ indent(6),"_ ->",nl]).
+
+%%============================================================================
+%% Encode/decode SET
+%%
+%%============================================================================
+
+gen_encode_set(Erules,Typename,D) when record(D,type) ->
+ gen_encode_sequence(Erules,Typename,D).
+
+gen_decode_set(Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ asn1ct_name:new(term),
+ asn1ct_name:new(tag),
+ #'SET'{tablecinf=TableConsInfo,components=TCompList} = D#type.def,
+ Ext = extensible(TCompList),
+ CompList = case TCompList of
+ {Rl,El} -> Rl ++ El;
+ _ -> TCompList
+ end,
+
+ asn1ct_name:clear(),
+ asn1ct_name:new(tlv),
+ case CompList of
+ EmptyCL when EmptyCL == [];EmptyCL == {[],[]}-> % empty sequence
+ true;
+ _ ->
+ emit([{curr,tlv}," = "])
+ end,
+ emit(["?RT_BER:match_tags(",{prev,tlv},",TagIn), ",nl]),
+ asn1ct_name:new(v),
+
+
+ {DecObjInf,UniqueFName} =
+ case TableConsInfo of
+ {ObjectSet,AttrN,_N,UniqueFieldName} ->%% N is index of attribute that determines constraint
+ F = fun(#'ComponentType'{typespec=CT})->
+ case {CT#type.constraint,CT#type.tablecinf} of
+ {[],[{objfun,_}|_]} -> true;
+ _ -> false
+ end
+ end,
+ case lists:any(F,CompList) of
+ true -> % when component relation constraint establish
+ %% relation from a component to another components
+ %% subtype component
+ {{AttrN,{deep,ObjectSet,UniqueFieldName}},
+ UniqueFieldName};
+ false ->
+ {{AttrN,ObjectSet},UniqueFieldName}
+ end;
+ _ ->
+ {false,false}
+ end,
+
+ case CompList of
+ [] -> % empty set
+ true;
+ _ ->
+ emit(["SetFun = fun(FunTlv) ->", nl]),
+ emit(["case FunTlv of ",nl]),
+ NextNum = gen_dec_set_cases(Erules,Typename,CompList,1),
+ emit([indent(6), {curr,else}," -> ",nl,
+ indent(9),"{",NextNum,", ",{curr,else},"}",nl]),
+ emit([indent(3),"end",nl]),
+ emit([indent(3),"end,",nl]),
+
+ emit(["PositionList = [SetFun(TempTlv)|| TempTlv <- ",{curr,tlv},"],",nl]),
+ asn1ct_name:new(tlv),
+ emit([{curr,tlv}," = [Stlv || {_,Stlv} <- lists:sort(PositionList)],",nl]),
+ asn1ct_name:new(tlv)
+
+ end,
+ case gen_dec_sequence_call(Erules,Typename,CompList,Ext,DecObjInf) of
+ no_terms -> % an empty sequence
+ emit([nl,nl]),
+ demit(["Result = "]), %dbg
+ %% return value as record
+ emit([" {'",asn1ct_gen:list2rname(Typename),"'}.",nl]);
+ {LeadingAttrTerm,PostponedDecArgs} ->
+ emit([com,nl,nl]),
+ case {LeadingAttrTerm,PostponedDecArgs} of
+ {[],[]} ->
+ ok;
+ {_,[]} ->
+ ok;
+ {[{ObjSet,LeadingAttr,Term}],PostponedDecArgs} ->
+ DecObj = lists:concat(['DecObj',LeadingAttr,Term]),
+ emit([DecObj," =",nl," 'getdec_",ObjSet,"'(",
+ {asis,UniqueFName},", ",Term,"),",nl]),
+ gen_dec_postponed_decs(DecObj,PostponedDecArgs)
+ end,
+ demit(["Result = "]), %dbg
+ %% return value as record
+ case Ext of
+ {ext,_,_} ->
+ emit(["case ",{prev,tlv}," of [] -> true; _ -> true end, % ... extra fields skipped",nl]);
+ noext ->
+ emit(["case ",{prev,tlv}," of",nl,
+ "[] -> true;",
+ "_ -> exit({error,{asn1, {unexpected,",{prev,tlv},
+ "}}}) % extra fields not allowed",nl,
+ "end,",nl])
+ end,
+ emit([" {'",asn1ct_gen:list2rname(Typename),"', "]),
+ mkvlist(asn1ct_name:all(term)),
+ emit(["}.",nl])
+ end.
+
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Encode/decode SEQUENCE OF and SET OF
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+gen_encode_sof(Erules,Typename,_InnerTypename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ {SeqOrSetOf, Cont} = D#type.def,
+
+ Objfun = case D#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _ ->
+ ""
+ end,
+
+ emit([" {EncBytes,EncLen} = 'enc_",asn1ct_gen:list2name(Typename),
+ "_components'(Val",Objfun,",[],0),",nl]),
+
+ emit([" ?RT_BER:encode_tags(TagIn, EncBytes, EncLen).",nl,nl]),
+
+ gen_encode_sof_components(Erules,Typename,SeqOrSetOf,Cont).
+
+
+gen_decode_sof(Erules,TypeName,_InnerTypeName,D) when record(D,type) ->
+ asn1ct_name:start(),
+ {SeqOrSetOf, _TypeTag, Cont} =
+ case D#type.def of
+ {'SET OF',_Cont} -> {'SET OF','SET',_Cont};
+ {'SEQUENCE OF',_Cont} -> {'SEQUENCE OF','SEQUENCE',_Cont}
+ end,
+ TypeNameSuffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,Cont#type.def),
+
+ emit([" %%-------------------------------------------------",nl]),
+ emit([" %% decode tag and length ",nl]),
+ emit([" %%-------------------------------------------------",nl]),
+
+ asn1ct_name:new(tlv),
+ emit([{curr,tlv},
+ " = ?RT_BER:match_tags(",{prev,tlv},",TagIn), ",nl]),
+ asn1ct_name:new(v),
+
+ emit(["["]),
+
+ InnerType = asn1ct_gen:get_inner(Cont#type.def),
+ ContName = case asn1ct_gen:type(InnerType) of
+ Atom when atom(Atom) -> Atom;
+ _ -> TypeNameSuffix
+ end,
+%% fix me
+ ObjFun =
+ case D#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _ ->
+ []
+ end,
+ gen_dec_line(Erules,TypeName,ContName,[],Cont,mandatory,ObjFun),
+ %% gen_dec_line_sof(Erules,Typename,ContName,Cont,ObjFun),
+ emit([" || ",{curr,v}," <- ",{curr,tlv},"].",nl,nl,nl]).
+
+
+gen_encode_sof_components(Erules,Typename,SeqOrSetOf,Cont)
+ when record(Cont,type)->
+
+ {Objfun,Objfun_novar,EncObj} =
+ case Cont#type.tablecinf of
+ [{objfun,_}|_R] ->
+ {", ObjFun",", _",{no_attr,"ObjFun"}};
+ _ ->
+ {"","",false}
+ end,
+ emit(["'enc_",asn1ct_gen:list2name(Typename),
+ "_components'([]",Objfun_novar,", AccBytes, AccLen) -> ",nl]),
+
+ case catch lists:member(der,get(encoding_options)) of
+ true ->
+ emit([indent(3),
+ "{?RT_BER:dynamicsort_SETOF(AccBytes),AccLen};",nl,nl]);
+ _ ->
+ emit([indent(3),"{lists:reverse(AccBytes),AccLen};",nl,nl])
+ end,
+ emit(["'enc_",asn1ct_gen:list2name(Typename),
+ "_components'([H|T]",Objfun,",AccBytes, AccLen) ->",nl]),
+ TypeNameSuffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,Cont#type.def),
+ gen_enc_line(Erules,Typename,TypeNameSuffix,Cont,"H",3,
+ mandatory,"{EncBytes,EncLen} = ",EncObj),
+ emit([",",nl]),
+ emit([indent(3),"'enc_",asn1ct_gen:list2name(Typename),
+ "_components'(T",Objfun,","]),
+ emit(["[EncBytes|AccBytes], AccLen + EncLen).",nl,nl]).
+
+%%============================================================================
+%% Encode/decode CHOICE
+%%
+%%============================================================================
+
+gen_encode_choice(Erules,Typename,D) when record(D,type) ->
+ ChoiceTag = D#type.tag,
+ {'CHOICE',CompList} = D#type.def,
+ Ext = extensible(CompList),
+ CompList1 = case CompList of
+ {Rl,El} -> Rl ++ El;
+ _ -> CompList
+ end,
+ gen_enc_choice(Erules,Typename,ChoiceTag,CompList1,Ext),
+ emit([nl,nl]).
+
+gen_decode_choice(Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ asn1ct_name:new(bytes),
+ ChoiceTag = D#type.tag,
+ {'CHOICE',CompList} = D#type.def,
+ Ext = extensible(CompList),
+ CompList1 = case CompList of
+ {Rl,El} -> Rl ++ El;
+ _ -> CompList
+ end,
+ gen_dec_choice(Erules,Typename,ChoiceTag,CompList1,Ext),
+ emit([".",nl]).
+
+
+%%============================================================================
+%% Encode SEQUENCE
+%%
+%%============================================================================
+
+gen_enc_sequence_call(Erules,TopType,[#'ComponentType'{name=Cname,typespec=Type,prop=Prop}|Rest],Pos,Ext,EncObj) ->
+ asn1ct_name:new(encBytes),
+ asn1ct_name:new(encLen),
+ Element =
+ case TopType of
+ ['EXTERNAL'] ->
+ io_lib:format("Cindex~w",[Pos]);
+ _ ->
+ io_lib:format("Cindex~w",[Pos])
+ end,
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ print_attribute_comment(InnerType,Pos,Cname,Prop),
+ gen_enc_line(Erules,TopType,Cname,Type,Element,3,Prop,EncObj),
+ emit([com,nl]),
+ gen_enc_sequence_call(Erules,TopType,Rest,Pos+1,Ext,EncObj);
+
+gen_enc_sequence_call(_Erules,_TopType,[],_Num,_,_) ->
+ true.
+
+%%============================================================================
+%% Decode SEQUENCE
+%%
+%%============================================================================
+
+gen_dec_sequence_call(Erules,TopType,CompList,Ext,DecObjInf) ->
+ gen_dec_sequence_call1(Erules,TopType, CompList, 1, Ext,DecObjInf,[],[]).
+
+
+gen_dec_sequence_call1(Erules,TopType,[#'ComponentType'{name=Cname,typespec=Type,prop=Prop,tags=Tags}|Rest],Num,Ext,DecObjInf,LeadingAttrAcc,ArgsAcc) ->
+ {LA,PostponedDec} =
+ gen_dec_component(Erules,TopType,Cname,Tags,Type,Num,Prop,
+ Ext,DecObjInf),
+ case Rest of
+ [] ->
+ {LA ++ LeadingAttrAcc,PostponedDec ++ ArgsAcc};
+ _ ->
+ emit([com,nl]),
+ asn1ct_name:new(bytes),
+ gen_dec_sequence_call1(Erules,TopType,Rest,Num+1,Ext,DecObjInf,
+ LA++LeadingAttrAcc,PostponedDec++ArgsAcc)
+ end;
+
+gen_dec_sequence_call1(_Erules,_TopType,[],1,_,_,_,_) ->
+ no_terms.
+
+
+%%----------------------------
+%%SEQUENCE mandatory
+%%----------------------------
+
+gen_dec_component(Erules,TopType,Cname,CTags,Type,Pos,Prop,Ext,DecObjInf) ->
+ InnerType =
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=OCFTType} -> OCFTType;
+ _ -> asn1ct_gen:get_inner(Type#type.def)
+ end,
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info) of
+% no ->
+% asn1ct_gen:get_inner(Type#type.def);
+% _ ->
+% Type#type.def
+% end,
+ Prop1 = case {Prop,Ext} of
+ {mandatory,{ext,Epos,_}} when Pos >= Epos ->
+ 'OPTIONAL';
+ _ ->
+ Prop
+ end,
+ print_attribute_comment(InnerType,Pos,Cname,Prop1),
+ asn1ct_name:new(term),
+ emit_term_tlv(Prop1,InnerType,DecObjInf),
+ asn1ct_name:new(rb),
+ PostponedDec =
+ gen_dec_line(Erules,TopType,Cname,CTags,Type,Prop1,DecObjInf),
+ asn1ct_name:new(v),
+ asn1ct_name:new(tlv),
+ asn1ct_name:new(form),
+ PostponedDec.
+
+
+emit_term_tlv({'DEFAULT',_},InnerType,DecObjInf) ->
+ emit_term_tlv(opt_or_def,InnerType,DecObjInf);
+emit_term_tlv('OPTIONAL',InnerType,DecObjInf) ->
+ emit_term_tlv(opt_or_def,InnerType,DecObjInf);
+emit_term_tlv(Prop,{typefield,_},DecObjInf) ->
+ emit_term_tlv(Prop,type_or_object_field,DecObjInf);
+emit_term_tlv(Prop,{objectfield,_,_},DecObjInf) ->
+ emit_term_tlv(Prop,type_or_object_field,DecObjInf);
+emit_term_tlv(opt_or_def,type_or_object_field,_) ->
+ asn1ct_name:new(tmpterm),
+ emit(["{",{curr,tmpterm},",",{curr,tlv},"} = "]);
+emit_term_tlv(opt_or_def,_,_) ->
+ emit(["{",{curr,term},",",{curr,tlv},"} = "]);
+emit_term_tlv(_,type_or_object_field,false) ->
+ emit(["[",{curr,v},"|",{curr,tlv},"] = ",{prev,tlv},", ",nl,
+ {curr,term}," = "]);
+emit_term_tlv(_,type_or_object_field,_) ->
+ asn1ct_name:new(tmpterm),
+ emit(["[",{curr,v},"|",{curr,tlv},"] = ",{prev,tlv},", ",nl]),
+ emit([nl," ",{curr,tmpterm}," = "]);
+emit_term_tlv(mandatory,_,_) ->
+ emit(["[",{curr,v},"|",{curr,tlv},"] = ",{prev,tlv},", ",nl,
+ {curr,term}," = "]).
+
+
+gen_dec_set_cases(_Erules,_TopType,[],Pos) ->
+ Pos;
+gen_dec_set_cases(Erules,TopType,[Comp|RestComps],Pos) ->
+ Name = Comp#'ComponentType'.name,
+ Type = Comp#'ComponentType'.typespec,
+ CTags = Comp#'ComponentType'.tags,
+
+ emit([indent(6),"%",Name,nl]),
+ Tags = case Type#type.tag of
+ [] -> % this is a choice without explicit tag
+ [(?ASN1CT_GEN_BER:decode_class(T1class) bsl 10) + T1number||
+ {T1class,T1number} <- CTags];
+ [FirstTag|_] ->
+ [(?ASN1CT_GEN_BER:decode_class(FirstTag#tag.class) bsl 10) + FirstTag#tag.number]
+ end,
+% emit([indent(6),"%Tags: ",Tags,nl]),
+% emit([indent(6),"%Type#type.tag: ",Type#type.tag,nl]),
+ CaseFun = fun(TagList=[H|T],Fun,N) ->
+ Semicolon = case TagList of
+ [_Tag1,_|_] -> [";",nl];
+ _ -> ""
+ end,
+ emit(["TTlv = {",H,",_} ->",nl]),
+ emit([indent(4),"{",Pos,", TTlv}",Semicolon]),
+ Fun(T,Fun,N+1);
+ ([],_,0) ->
+ true;
+ ([],_,_) ->
+ emit([";",nl])
+ end,
+ CaseFun(Tags,CaseFun,0),
+%% emit([";",nl]),
+ gen_dec_set_cases(Erules,TopType,RestComps,Pos+1).
+
+
+
+%%---------------------------------------------
+%% Encode CHOICE
+%%---------------------------------------------
+%% for BER we currently do care (a little) if the choice has an EXTENSIONMARKER
+
+
+gen_enc_choice(Erules,TopType,Tag,CompList,_Ext) ->
+ gen_enc_choice1(Erules,TopType,Tag,CompList,_Ext).
+
+gen_enc_choice1(Erules,TopType,_Tag,CompList,_Ext) ->
+ asn1ct_name:clear(),
+ emit([" {EncBytes,EncLen} = case element(1,Val) of",nl]),
+ gen_enc_choice2(Erules,TopType,CompList),
+ emit([nl," end,",nl,nl]),
+
+ emit(["?RT_BER:encode_tags(TagIn, EncBytes, EncLen).",nl]).
+
+
+gen_enc_choice2(Erules,TopType,[H1|T]) when record(H1,'ComponentType') ->
+ Cname = H1#'ComponentType'.name,
+ Type = H1#'ComponentType'.typespec,
+ emit([" ",{asis,Cname}," ->",nl]),
+ {Encobj,Assign} =
+ case {Type#type.def,asn1ct_gen:get_constraint(Type#type.constraint,
+ componentrelation)} of
+ {#'ObjectClassFieldType'{},{componentrelation,_,_}} ->
+ asn1ct_name:new(tmpBytes),
+ asn1ct_name:new(encBytes),
+ asn1ct_name:new(encLen),
+ Emit = ["{",{curr,tmpBytes},", _} = "],
+ {{no_attr,"ObjFun"},Emit};
+ _ ->
+ {false,[]}
+ end,
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info) of
+% no ->
+% {false,[]};
+% _ ->
+% asn1ct_name:new(tmpBytes),
+% asn1ct_name:new(encBytes),
+% asn1ct_name:new(encLen),
+% Emit = ["{",{curr,tmpBytes},", _} = "],
+% {{no_attr,"ObjFun"},Emit}
+% end,
+ gen_enc_line(Erules,TopType,Cname,Type,"element(2,Val)",9,
+ mandatory,Assign,Encobj),
+ case Encobj of
+ false -> ok;
+ _ ->
+ emit([",",nl,indent(9),"{",{curr,encBytes},", ",
+ {curr,encLen},"}"])
+ end,
+ emit([";",nl]),
+ case T of
+ [] ->
+ emit([indent(6), "Else -> ",nl,
+ indent(9),"exit({error,{asn1,{invalid_choice_type,Else}}})"]);
+ _ ->
+ true
+ end,
+ gen_enc_choice2(Erules,TopType,T);
+
+gen_enc_choice2(_Erules,_TopType,[]) ->
+ true.
+
+
+
+
+%%--------------------------------------------
+%% Decode CHOICE
+%%--------------------------------------------
+
+gen_dec_choice(Erules,TopType, _ChTag, CompList, Ext) ->
+ asn1ct_name:clear(),
+ asn1ct_name:new(tlv),
+ emit([{curr,tlv},
+ " = ?RT_BER:match_tags(",{prev,tlv},",TagIn), ",nl]),
+ asn1ct_name:new(tlv),
+ asn1ct_name:new(v),
+ emit(["case (case ",{prev,tlv},
+ " of [Ctemp",{prev,tlv},"] -> Ctemp",{prev,tlv},
+ "; _ -> ",{prev,tlv}," end)"," of",nl]),
+ asn1ct_name:new(tagList),
+ asn1ct_name:new(choTags),
+ asn1ct_name:new(res),
+ gen_dec_choice_cases(Erules,TopType,CompList),
+ emit([indent(6), {curr,else}," -> ",nl]),
+ case Ext of
+ noext ->
+ emit([indent(9),"exit({error,{asn1,{invalid_choice_tag,",
+ {curr,else},"}}})",nl]);
+ _ ->
+ emit([indent(9),"{asn1_ExtAlt, ?RT_BER:encode(",{curr,else},")}",nl])
+ end,
+ emit([indent(3),"end",nl]),
+ asn1ct_name:new(tag),
+ asn1ct_name:new(else).
+
+
+gen_dec_choice_cases(_Erules,_TopType, []) ->
+ ok;
+gen_dec_choice_cases(Erules,TopType, [H|T]) ->
+ Cname = H#'ComponentType'.name,
+ Type = H#'ComponentType'.typespec,
+ Prop = H#'ComponentType'.prop,
+ Tags = Type#type.tag,
+ Fcases = fun([{T1class,T1number}|Tail],Fun) ->
+ emit([indent(4),{curr,v}," = {",
+ (?ASN1CT_GEN_BER:decode_class(T1class) bsl 10) +
+ T1number,",_} -> ",nl]),
+ emit([indent(8),"{",{asis,Cname},", "]),
+ gen_dec_line(Erules,TopType,Cname,[],Type,Prop,false),
+ emit(["};",nl,nl]),
+ Fun(Tail,Fun);
+ ([],_) ->
+ ok
+ end,
+ emit([nl,"%% '",Cname,"'",nl]),
+ case {Tags,asn1ct:get_gen_state_field(namelist)} of
+ {[],_} -> % choice without explicit tags
+ Fcases(H#'ComponentType'.tags,Fcases);
+ {[FirstT|_RestT],[{Cname,undecoded}|Names]} ->
+ DecTag=(?ASN1CT_GEN_BER:decode_class(FirstT#tag.class) bsl 10) +
+ FirstT#tag.number,
+ asn1ct:add_generated_refed_func({[Cname|TopType],undecoded,
+ [DecTag],Type}),
+ asn1ct:update_gen_state(namelist,Names),
+ emit([indent(4),{curr,res}," = ",
+ match_tag(ber_bin,{FirstT#tag.class,FirstT#tag.number}),
+ " -> ",nl]),
+ emit([indent(8),"{",{asis,Cname},", {'",
+ asn1ct_gen:list2name([Cname|TopType]),"',",
+ {curr,res},"}};",nl,nl]);
+ {[FirstT|RestT],_} ->
+ emit([indent(4),"{",
+ (?ASN1CT_GEN_BER:decode_class(FirstT#tag.class) bsl 10) +
+ FirstT#tag.number,", ",{curr,v},"} -> ",nl]),
+ emit([indent(8),"{",{asis,Cname},", "]),
+ gen_dec_line(Erules,TopType,Cname,[],Type#type{tag=RestT},Prop,false),
+ emit(["};",nl,nl])
+ end,
+ gen_dec_choice_cases(Erules,TopType, T).
+
+
+
+%%---------------------------------------
+%% Generate the encode/decode code
+%%---------------------------------------
+
+gen_enc_line(Erules,TopType,Cname,
+ Type=#type{constraint=[{componentrelation,_,_}],
+ def=#'ObjectClassFieldType'{type={typefield,_}}},
+ Element,Indent,OptOrMand=mandatory,EncObj)
+ when list(Element) ->
+ asn1ct_name:new(tmpBytes),
+ gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,
+ ["{",{curr,tmpBytes},",_} = "],EncObj);
+gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,EncObj)
+ when list(Element) ->
+ gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,
+ ["{",{curr,encBytes},",",{curr,encLen},"} = "],EncObj).
+
+gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,Assign,EncObj)
+ when list(Element) ->
+ IndDeep = indent(Indent),
+ Tag = lists:reverse([?ASN1CT_GEN_BER:encode_tag_val(
+ ?ASN1CT_GEN_BER:decode_class(X#tag.class),
+ X#tag.form,
+ X#tag.number)
+ || X <- Type#type.tag]),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ WhatKind = asn1ct_gen:type(InnerType),
+ emit(IndDeep),
+ emit(Assign),
+ gen_optormand_case(OptOrMand,Erules,TopType,Cname,Type,InnerType,WhatKind,
+ Element),
+ case {Type,asn1ct_gen:get_constraint(Type#type.constraint,
+ componentrelation)} of
+% #type{constraint=[{tableconstraint_info,RefedFieldName}],
+% def={typefield,_}} ->
+ {#type{def=#'ObjectClassFieldType'{type={typefield,_},
+ fieldname=RefedFieldName}},
+ {componentrelation,_,_}} ->
+ {_LeadingAttrName,Fun} = EncObj,
+ case RefedFieldName of
+ {notype,T} ->
+ throw({error,{notype,type_from_object,T}});
+ {Name,RestFieldNames} when atom(Name) ->
+ case OptOrMand of
+ mandatory -> ok;
+ _ ->
+% emit(["{",{curr,tmpBytes},",",{curr,tmpLen},
+ emit(["{",{curr,tmpBytes},",_ } = "])
+% "} = "])
+ end,
+ emit([Fun,"(",{asis,Name},", ",Element,", ",
+ {asis,RestFieldNames},"),",nl]),
+ emit(IndDeep),
+ case OptOrMand of
+ mandatory ->
+ emit(["{",{curr,encBytes},",",{curr,encLen},
+ "} = "]),
+ emit(["?RT_BER:encode_open_type(",{curr,tmpBytes},
+ ",",{asis,Tag},")"]);
+ _ ->
+% emit(["{",{next,tmpBytes},", _} = "]),
+ emit(["{",{next,tmpBytes},",",{curr,tmpLen},
+ "} = "]),
+ emit(["?RT_BER:encode_open_type(",{curr,tmpBytes},
+ ",",{asis,Tag},"),",nl]),
+ emit(IndDeep),
+ emit(["{",{next,tmpBytes},", ",{curr,tmpLen},"}"])
+ end;
+ _ ->
+ throw({asn1,{'internal error'}})
+ end;
+ {{#'ObjectClassFieldType'{type={objectfield,PrimFieldName1,
+ PFNList}},_},
+ {componentrelation,_,_}} ->
+ %% this is when the dotted list in the FieldName has more
+ %% than one element
+ {_LeadingAttrName,Fun} = EncObj,
+ emit(["?RT_BER:encode_open_type(",Fun,"(",{asis,PrimFieldName1},
+ ", ",Element,", ",{asis,PFNList},"))"]);
+ _ ->
+ case WhatKind of
+ {primitive,bif} ->
+ EncType =
+ case Type#type.def of
+ #'ObjectClassFieldType'{type={fixedtypevaluefield,_,Btype}} ->
+ Btype;
+ _ ->
+ Type
+ end,
+ ?ASN1CT_GEN_BER:gen_encode_prim(ber,EncType,{asis,Tag},
+ Element);
+ {notype,_} ->
+ emit(["'enc_",InnerType,"'(",Element,", ",{asis,Tag},")"]);
+ 'ASN1_OPEN_TYPE' ->
+ case Type#type.def of
+ #'ObjectClassFieldType'{} -> %Open Type
+ ?ASN1CT_GEN_BER:gen_encode_prim(ber,#type{def='ASN1_OPEN_TYPE'},{asis,Tag},Element);
+ _ ->
+ ?ASN1CT_GEN_BER:gen_encode_prim(ber,Type,
+ {asis,Tag},
+ Element)
+ end;
+ _ ->
+ {EncFunName, _EncMod, _EncFun} =
+ mkfuncname(TopType,Cname,WhatKind,"enc_"),
+ case {WhatKind,Type#type.tablecinf,EncObj} of
+ {{constructed,bif},[{objfun,_}|_R],{_,Fun}} ->
+ emit([EncFunName,"(",Element,", ",{asis,Tag},
+ ", ",Fun,")"]);
+ _ ->
+ emit([EncFunName,"(",Element,", ",{asis,Tag},")"])
+ end
+ end
+ end,
+ case OptOrMand of
+ mandatory -> true;
+ _ ->
+ emit([nl,indent(7),"end"])
+ end.
+
+gen_optormand_case(mandatory,_Erules,_TopType,_Cname,_Type,_InnerType,_WhatKind,
+ _Element) ->
+ ok;
+gen_optormand_case('OPTIONAL',Erules,_TopType,_Cname,_Type,_InnerType,_WhatKind,
+ Element) ->
+ emit([" case ",Element," of",nl]),
+ emit([indent(9),"asn1_NOVALUE -> {",
+ empty_lb(Erules),",0};",nl]),
+ emit([indent(9),"_ ->",nl,indent(12)]);
+gen_optormand_case({'DEFAULT',DefaultValue},Erules,TopType,Cname,Type,
+ InnerType,WhatKind,Element) ->
+ CurrMod = get(currmod),
+ case catch lists:member(der,get(encoding_options)) of
+ true ->
+ emit(" case catch "),
+ asn1ct_gen:gen_check_call(TopType,Cname,Type,InnerType,
+ WhatKind,{asis,DefaultValue},
+ Element),
+ emit([" of",nl]),
+ emit([indent(12),"true -> {[],0};",nl]);
+ _ ->
+ emit([" case ",Element," of",nl]),
+ emit([indent(9),"asn1_DEFAULT -> {",
+ empty_lb(Erules),
+ ",0};",nl]),
+ case DefaultValue of
+ #'Externalvaluereference'{module=CurrMod,
+ value=V} ->
+ emit([indent(9),"?",{asis,V}," -> {",
+ empty_lb(Erules),",0};",nl]);
+ _ ->
+ emit([indent(9),{asis,
+ DefaultValue}," -> {",
+ empty_lb(Erules),",0};",nl])
+ end
+ end,
+ emit([indent(9),"_ ->",nl,indent(12)]).
+
+
+
+gen_dec_line(Erules,TopType,Cname,CTags,Type,OptOrMand,DecObjInf) ->
+ BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(v)),
+ Tag =
+ [(?ASN1CT_GEN_BER:decode_class(X#tag.class) bsl 10) + X#tag.number ||
+ X <- Type#type.tag],
+ ChoiceTags =
+ [(?ASN1CT_GEN_BER:decode_class(Class) bsl 10) + Number||
+ {Class,Number} <- CTags],
+ InnerType =
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=OCFTType} ->
+ OCFTType;
+ _ ->
+ asn1ct_gen:get_inner(Type#type.def)
+ end,
+ PostpDec =
+ case OptOrMand of
+ mandatory ->
+ gen_dec_call(InnerType,Erules,TopType,Cname,Type,
+ BytesVar,Tag,
+ mandatory,", mandatory, ",DecObjInf,OptOrMand);
+ _ -> %optional or default or a mandatory component after an extensionmark
+ {FirstTag,RestTag} =
+ case Tag of
+ [] ->
+ {ChoiceTags,[]};
+ [Ft|Rt] ->
+ {Ft,Rt}
+ end,
+ emit(["case ",{prev,tlv}," of",nl]),
+ PostponedDec =
+ case Tag of
+ [] when length(ChoiceTags) > 0 -> % a choice without explicit tag
+ Fcases =
+ fun(FirstTag1) ->
+ emit(["[",{curr,v}," = {",{asis,FirstTag1},
+ ",_}|Temp",
+ {curr,tlv},
+ "] ->",nl]),
+ emit([indent(4),"{"]),
+ Pdec=
+ gen_dec_call(InnerType,Erules,
+ TopType,Cname,Type,
+ BytesVar,RestTag,
+ mandatory,
+ ", mandatory, ",
+ DecObjInf,OptOrMand),
+
+ emit([", Temp",{curr,tlv},"}"]),
+ emit([";",nl]),
+ Pdec
+ end,
+ hd([Fcases(TmpTag)|| TmpTag <- FirstTag]);
+
+ [] -> % an open type without explicit tag
+ emit(["[",{curr,v},"|Temp",{curr,tlv},"] ->",nl]),
+ emit([indent(4),"{"]),
+ Pdec=
+ gen_dec_call(InnerType,Erules,TopType,Cname,
+ Type,BytesVar,RestTag,mandatory,
+ ", mandatory, ",DecObjInf,
+ OptOrMand),
+
+ emit([", Temp",{curr,tlv},"}"]),
+ emit([";",nl]),
+ Pdec;
+
+ _ ->
+ emit(["[{",{asis,FirstTag},
+ ",",{curr,v},"}|Temp",
+ {curr,tlv},
+ "] ->",nl]),
+ emit([indent(4),"{"]),
+ Pdec=
+ gen_dec_call(InnerType,Erules,TopType,Cname,
+ Type,BytesVar,RestTag,mandatory,
+ ", mandatory, ",DecObjInf,
+ OptOrMand),
+
+ emit([", Temp",{curr,tlv},"}"]),
+ emit([";",nl]),
+ Pdec
+ end,
+
+ emit([indent(4),"_ ->",nl]),
+ case OptOrMand of
+ {'DEFAULT', Def} ->
+ emit([indent(8),"{",{asis,Def},",",{prev,tlv},"}",nl]);
+ 'OPTIONAL' ->
+ emit([indent(8),"{ asn1_NOVALUE, ",{prev,tlv},"}",nl])
+ end,
+ emit(["end"]),
+ PostponedDec
+ end,
+ case DecObjInf of
+ {Cname,ObjSet} -> % this must be the component were an object is
+ %% choosen from the object set according to the table
+ %% constraint.
+ {[{ObjSet,Cname,asn1ct_gen:mk_var(asn1ct_name:curr(term))}],
+ PostpDec};
+ _ -> {[],PostpDec}
+ end.
+
+gen_dec_call({typefield,_},_,_,_Cname,Type,BytesVar,Tag,_,_,false,_) ->
+ %% this in case of a choice with typefield components
+ asn1ct_name:new(reason),
+ asn1ct_name:new(opendec),
+ asn1ct_name:new(tmpterm),
+ asn1ct_name:new(tmptlv),
+
+ {FirstPFName,RestPFName} =
+% asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info),
+ (Type#type.def)#'ObjectClassFieldType'.fieldname,
+ emit([nl,indent(6),"begin",nl]),
+% emit([indent(9),{curr,opendec}," = ?RT_BER:decode_open_type(",
+ emit([indent(9),{curr,tmptlv}," = ?RT_BER:decode_open_type(",
+ BytesVar,",",{asis,Tag},"),",nl]),
+% emit([indent(9),"{",{curr,tmptlv},",_} = ?RT_BER:decode(",
+% {curr,opendec},"),",nl]),
+
+ emit([indent(9),"case (catch ObjFun(",{asis,FirstPFName},
+ ", ",{curr,tmptlv},", ",{asis,RestPFName},
+ ")) of", nl]),%% ??? What about Tag
+ emit([indent(12),"{'EXIT',",{curr,reason},"} ->",nl]),
+ emit([indent(15),"exit({'Type not ",
+ "compatible with table constraint', ",{curr,reason},"});",nl]),
+ emit([indent(12),{curr,tmpterm}," ->",nl]),
+ emit([indent(15),{curr,tmpterm},nl]),
+ emit([indent(9),"end",nl,indent(6),"end",nl]),
+ [];
+gen_dec_call({typefield,_},_,_,Cname,Type,BytesVar,Tag,_,_,_DecObjInf,OptOrMandComp) ->
+ emit(["?RT_BER:decode_open_type(",BytesVar,",",{asis,Tag},")"]),
+ RefedFieldName =
+% asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info),
+ (Type#type.def)#'ObjectClassFieldType'.fieldname,
+ [{Cname,RefedFieldName,asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+ asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),Tag,OptOrMandComp}];
+gen_dec_call({objectfield,PrimFieldName,PFNList},_,_,Cname,_,BytesVar,Tag,_,_,_,OptOrMandComp) ->
+ emit(["?RT_BER:decode_open_type(",BytesVar,",",{asis,Tag},")"]),
+ [{Cname,{PrimFieldName,PFNList},asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+ asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),Tag,OptOrMandComp}];
+gen_dec_call(InnerType,Erules,TopType,Cname,Type,BytesVar,Tag,PrimOptOrMand,
+ OptOrMand,DecObjInf,_) ->
+ WhatKind = asn1ct_gen:type(InnerType),
+ gen_dec_call1(WhatKind,InnerType,Erules,TopType,Cname,Type,BytesVar,Tag,
+ PrimOptOrMand,OptOrMand),
+ case DecObjInf of
+ {Cname,{_,OSet,UniqueFName,ValIndex}} ->
+ Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+ ValueMatch = value_match(ValIndex,Term),
+ emit([",",nl,"ObjFun = 'getdec_",OSet,"'(",
+% {asis,UniqueFName},", ",{curr,term},")"]);
+ {asis,UniqueFName},", ",ValueMatch,")"]);
+ _ ->
+ ok
+ end,
+ [].
+gen_dec_call1({primitive,bif},InnerType,Erules,TopType,Cname,Type,BytesVar,
+ Tag,OptOrMand,_) ->
+ case {asn1ct:get_gen_state_field(namelist),InnerType} of
+ {[{Cname,undecoded}|Rest],_} ->
+ asn1ct:add_generated_refed_func({[Cname|TopType],undecoded,
+ Tag,Type}),
+ asn1ct:update_gen_state(namelist,Rest),
+% emit(["?RT_BER:match_tags(",BytesVar,",",{asis,Tag},")"]);
+ emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',",
+ BytesVar,"}"]);
+ {_,{fixedtypevaluefield,_,Btype}} ->
+ ?ASN1CT_GEN_BER:gen_dec_prim(Erules,Btype,BytesVar,Tag,[],
+ ?PRIMITIVE,OptOrMand);
+ _ ->
+ ?ASN1CT_GEN_BER:gen_dec_prim(Erules,Type,BytesVar,Tag,[],
+ ?PRIMITIVE,OptOrMand)
+ end;
+gen_dec_call1('ASN1_OPEN_TYPE',_InnerType,Erules,TopType,Cname,Type,BytesVar,
+ Tag,OptOrMand,_) ->
+ case {asn1ct:get_gen_state_field(namelist),Type#type.def} of
+ {[{Cname,undecoded}|Rest],_} ->
+ asn1ct:add_generated_refed_func({[Cname|TopType],undecoded,
+ Tag,Type}),
+ asn1ct:update_gen_state(namelist,Rest),
+ emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',",
+ BytesVar,"}"]);
+% emit(["?RT_BER:match_tags(",BytesVar,",",{asis,Tag},")"]);
+ {_,#'ObjectClassFieldType'{type=OpenType}} ->
+ ?ASN1CT_GEN_BER:gen_dec_prim(Erules,#type{def=OpenType},
+ BytesVar,Tag,[],
+ ?PRIMITIVE,OptOrMand);
+ _ ->
+ ?ASN1CT_GEN_BER:gen_dec_prim(Erules,Type,BytesVar,Tag,[],
+ ?PRIMITIVE,OptOrMand)
+ end;
+gen_dec_call1(WhatKind,_,_Erules,TopType,Cname,Type,BytesVar,
+ Tag,_,_OptOrMand) ->
+ case asn1ct:get_gen_state_field(namelist) of
+ [{Cname,undecoded}|Rest] ->
+ asn1ct:add_generated_refed_func({[Cname|TopType],undecoded,
+ Tag,Type}),
+ asn1ct:update_gen_state(namelist,Rest),
+ emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',",
+ BytesVar,"}"]);
+ _ ->
+% {DecFunName, _DecMod, _DecFun} =
+% case {asn1ct:get_gen_state_field(namelist),WhatKind} of
+ EmitDecFunCall =
+ fun(FuncName) ->
+ case {WhatKind,Type#type.tablecinf} of
+ {{constructed,bif},[{objfun,_}|_Rest]} ->
+ emit([FuncName,"(",BytesVar,", ",{asis,Tag},
+ ", ObjFun)"]);
+ _ ->
+ emit([FuncName,"(",BytesVar,", ",{asis,Tag},")"])
+ end
+ end,
+ case asn1ct:get_gen_state_field(namelist) of
+ [{Cname,List}|Rest] when list(List) ->
+ case WhatKind of
+ #'Externaltypereference'{} ->
+ %%io:format("gen_dec_call1 1:~n~p~n~n",[WhatKind]),
+ asn1ct:add_tobe_refed_func({WhatKind,List});
+ _ ->
+ %%io:format("gen_dec_call1 2:~n~p~n~n",[[Cname|TopType]]),
+ asn1ct:add_tobe_refed_func({[Cname|TopType],
+ List})
+ end,
+ asn1ct:update_gen_state(namelist,Rest),
+ Prefix=asn1ct:get_gen_state_field(prefix),
+ {DecFunName,_,_}=
+ mkfuncname(TopType,Cname,WhatKind,Prefix),
+ EmitDecFunCall(DecFunName);
+ [{Cname,parts}|Rest] ->
+ asn1ct:update_gen_state(namelist,Rest),
+ asn1ct:get_gen_state_field(prefix),
+ %% This is to prepare SEQUENCE OF value in
+ %% partial incomplete decode for a later
+ %% part-decode, i.e. skip %% the tag.
+ asn1ct:add_generated_refed_func({[Cname|TopType],
+ parts,
+ [],Type}),
+ emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',"]),
+ EmitDecFunCall("?RT_BER:match_tags"),
+ emit("}");
+ _ ->
+ {DecFunName,_,_}=
+ mkfuncname(TopType,Cname,WhatKind,"dec_"),
+ EmitDecFunCall(DecFunName)
+ end
+% case {WhatKind,Type#type.tablecinf} of
+% {{constructed,bif},[{objfun,_}|_Rest]} ->
+% emit([DecFunName,"(",BytesVar,", ",{asis,Tag},
+% ", ObjFun)"]);
+% _ ->
+% emit([DecFunName,"(",BytesVar,", ",{asis,Tag},")"])
+% end
+ end.
+
+
+%%------------------------------------------------------
+%% General and special help functions (not exported)
+%%------------------------------------------------------
+
+
+indent(N) ->
+ lists:duplicate(N,32). % 32 = space
+
+mkcindexlist([H,T1|T], Sep) -> % Sep is a string e.g ", " or "+ "
+ emit(["Cindex",H,Sep]),
+ mkcindexlist([T1|T], Sep);
+mkcindexlist([H|T], Sep) ->
+ emit(["Cindex",H]),
+ mkcindexlist(T, Sep);
+mkcindexlist([], _) ->
+ true.
+
+mkcindexlist(L) ->
+ mkcindexlist(L,", ").
+
+
+mkvlist([H,T1|T], Sep) -> % Sep is a string e.g ", " or "+ "
+ emit([{var,H},Sep]),
+ mkvlist([T1|T], Sep);
+mkvlist([H|T], Sep) ->
+ emit([{var,H}]),
+ mkvlist(T, Sep);
+mkvlist([], _) ->
+ true.
+
+mkvlist(L) ->
+ mkvlist(L,", ").
+
+mkvplus(L) ->
+ mkvlist(L," + ").
+
+extensible(CompList) when list(CompList) ->
+ noext;
+extensible({RootList,ExtList}) ->
+ {ext,length(RootList)+1,length(ExtList)}.
+
+
+print_attribute_comment(InnerType,Pos,Cname,Prop) ->
+ CommentLine = "%%-------------------------------------------------",
+ emit([nl,CommentLine]),
+ case InnerType of
+ {typereference,_,Name} ->
+ emit([nl,"%% attribute ",Cname,"(",Pos,") with type ",Name]);
+ {'Externaltypereference',_,XModule,Name} ->
+ emit([nl,"%% attribute ",Cname,"(",Pos,") External ",XModule,":",Name]);
+ _ ->
+ emit([nl,"%% attribute ",Cname,"(",Pos,") with type ",InnerType])
+ end,
+ case Prop of
+ mandatory ->
+ continue;
+ {'DEFAULT', Def} ->
+ emit([" DEFAULT = ",{asis,Def}]);
+ 'OPTIONAL' ->
+ emit([" OPTIONAL"])
+ end,
+ emit([nl,CommentLine,nl]).
+
+
+
+mkfuncname(TopType,Cname,WhatKind,Prefix) ->
+ CurrMod = get(currmod),
+ case WhatKind of
+ #'Externaltypereference'{module=CurrMod,type=EType} ->
+ F = lists:concat(["'",Prefix,EType,"'"]),
+ {F, "?MODULE", F};
+ #'Externaltypereference'{module=Mod,type=EType} ->
+ {lists:concat(["'",Mod,"':'",Prefix,EType,"'"]),Mod,
+ lists:concat(["'",Prefix,EType,"'"])};
+ {constructed,bif} ->
+ F = lists:concat(["'",Prefix,asn1ct_gen:list2name([Cname|TopType]),"'"]),
+ {F, "?MODULE", F}
+ end.
+
+empty_lb(ber) ->
+ "[]";
+empty_lb(ber_bin) ->
+ "<<>>";
+empty_lb(ber_bin_v2) ->
+ "<<>>".
+
+value_match(Index,Value) when atom(Value) ->
+ value_match(Index,atom_to_list(Value));
+value_match([],Value) ->
+ Value;
+value_match([{VI,_}|VIs],Value) ->
+ value_match1(Value,VIs,lists:concat(["element(",VI,","]),1).
+value_match1(Value,[],Acc,Depth) ->
+ Acc ++ Value ++ lists:concat(lists:duplicate(Depth,")"));
+value_match1(Value,[{VI,_}|VIs],Acc,Depth) ->
+ value_match1(Value,VIs,Acc++lists:concat(["element(",VI,","]),Depth+1).
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_constructed_per.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_constructed_per.erl
new file mode 100644
index 0000000000..a21c38f8a8
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_constructed_per.erl
@@ -0,0 +1,1234 @@
+% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_constructed_per.erl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-module(asn1ct_constructed_per).
+
+-export([gen_encode_sequence/3]).
+-export([gen_decode_sequence/3]).
+-export([gen_encode_set/3]).
+-export([gen_decode_set/3]).
+-export([gen_encode_sof/4]).
+-export([gen_decode_sof/4]).
+-export([gen_encode_choice/3]).
+-export([gen_decode_choice/3]).
+
+-include("asn1_records.hrl").
+%-compile(export_all).
+
+-import(asn1ct_gen, [emit/1,demit/1]).
+
+
+%% ENCODE GENERATOR FOR SEQUENCE TYPE ** **********
+
+
+gen_encode_set(Erules,TypeName,D) ->
+ gen_encode_constructed(Erules,TypeName,D).
+
+gen_encode_sequence(Erules,TypeName,D) ->
+ gen_encode_constructed(Erules,TypeName,D).
+
+gen_encode_constructed(Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ asn1ct_name:new(term),
+ asn1ct_name:new(bytes),
+ {CompList,TableConsInfo} =
+ case D#type.def of
+ #'SEQUENCE'{tablecinf=TCI,components=CL} ->
+ {CL,TCI};
+ #'SET'{tablecinf=TCI,components=CL} ->
+ {CL,TCI}
+ end,
+ case Typename of
+ ['EXTERNAL'] ->
+ emit({{var,asn1ct_name:next(val)},
+ " = asn1rt_check:transform_to_EXTERNAL1990(",
+ {var,asn1ct_name:curr(val)},"),",nl}),
+ asn1ct_name:new(val);
+ _ ->
+ ok
+ end,
+ case {Optionals = optionals(CompList),CompList} of
+ {[],EmptyCL} when EmptyCL == {[],[]};EmptyCL == [] ->
+ emit(["%%Variable setting just to eliminate ",
+ "compiler warning for unused vars!",nl,
+ "_Val = ",{var,asn1ct_name:curr(val)},",",nl]);
+ {[],_} ->
+ emit([{var,asn1ct_name:next(val)}," = ?RT_PER:list_to_record("]),
+ emit(["'",asn1ct_gen:list2rname(Typename),"'"]),
+ emit([", ",{var,asn1ct_name:curr(val)},"),",nl]);
+ _ ->
+ Fixoptcall =
+ case Erules of
+ per -> ",Opt} = ?RT_PER:fixoptionals2(";
+ _ -> ",Opt} = ?RT_PER:fixoptionals("
+ end,
+ emit({"{",{var,asn1ct_name:next(val)},Fixoptcall,
+ {asis,Optionals},",",length(Optionals),
+ ",",{var,asn1ct_name:curr(val)},"),",nl})
+ end,
+ asn1ct_name:new(val),
+ Ext = extensible(CompList),
+ case Ext of
+ {ext,_,NumExt} when NumExt > 0 ->
+ emit(["Extensions = ?RT_PER:fixextensions(",{asis,Ext},
+ ", ",{curr,val},"),",nl]);
+ _ -> true
+ end,
+ EncObj =
+ case TableConsInfo of
+ #simpletableattributes{usedclassfield=Used,
+ uniqueclassfield=Unique} when Used /= Unique ->
+ false;
+ %% ObjectSet, name of the object set in constraints
+ %%
+ %%{ObjectSet,AttrN,N,UniqueFieldName} -> %% N is index of attribute that determines constraint
+ #simpletableattributes{objectsetname=ObjectSet,
+ c_name=AttrN,
+ c_index=N,
+ usedclassfield=UniqueFieldName,
+ uniqueclassfield=UniqueFieldName,
+ valueindex=ValueIndex
+ } -> %% N is index of attribute that determines constraint
+ OSDef =
+ case ObjectSet of
+ {Module,OSName} ->
+ asn1_db:dbget(Module,OSName);
+ OSName ->
+ asn1_db:dbget(get(currmod),OSName)
+ end,
+ case (OSDef#typedef.typespec)#'ObjectSet'.gen of
+ true ->
+ ObjectEncode =
+ asn1ct_gen:un_hyphen_var(lists:concat(['Obj',AttrN])),
+ emit([ObjectEncode," = ",nl]),
+ emit([" 'getenc_",ObjectSet,"'(",
+ {asis,UniqueFieldName},", ",nl]),
+ El = make_element(N+1,asn1ct_gen:mk_var(asn1ct_name:curr(val)),AttrN),
+ Indent = 12 + length(atom_to_list(ObjectSet)),
+ case ValueIndex of
+ [] ->
+ emit([indent(Indent),El,"),",nl]);
+ _ ->
+ emit([indent(Indent),"value_match(",
+ {asis,ValueIndex},",",El,")),",nl]),
+ notice_value_match()
+ end,
+ {AttrN,ObjectEncode};
+ _ ->
+ false
+ end;
+ _ ->
+ case D#type.tablecinf of
+ [{objfun,_}|_] ->
+ %% when the simpletableattributes was at an outer
+ %% level and the objfun has been passed through the
+ %% function call
+ {"got objfun through args","ObjFun"};
+ _ ->
+ false
+ end
+ end,
+ emit({"[",nl}),
+ MaybeComma1 =
+ case Ext of
+ {ext,_Pos,NumExt2} when NumExt2 > 0 ->
+ emit({"?RT_PER:setext(Extensions =/= [])"}),
+ ", ";
+ {ext,_Pos,_} ->
+ emit({"?RT_PER:setext(false)"}),
+ ", ";
+ _ ->
+ ""
+ end,
+ MaybeComma2 =
+ case optionals(CompList) of
+ [] -> MaybeComma1;
+ _ ->
+ emit(MaybeComma1),
+ emit("Opt"),
+ {",",nl}
+ end,
+ gen_enc_components_call(Typename,CompList,MaybeComma2,EncObj,Ext),
+ emit({"].",nl}).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% generate decode function for SEQUENCE and SET
+%%
+gen_decode_set(Erules,Typename,D) ->
+ gen_decode_constructed(Erules,Typename,D).
+
+gen_decode_sequence(Erules,Typename,D) ->
+ gen_decode_constructed(Erules,Typename,D).
+
+gen_decode_constructed(_Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ {CompList,TableConsInfo} =
+ case D#type.def of
+ #'SEQUENCE'{tablecinf=TCI,components=CL} ->
+ {CL,TCI};
+ #'SET'{tablecinf=TCI,components=CL} ->
+ {CL,TCI}
+ end,
+ Ext = extensible(CompList),
+ MaybeComma1 = case Ext of
+ {ext,_Pos,_NumExt} ->
+ gen_dec_extension_value("Bytes"),
+ {",",nl};
+ _ ->
+ ""
+ end,
+ Optionals = optionals(CompList),
+ MaybeComma2 = case Optionals of
+ [] -> MaybeComma1;
+ _ ->
+ Bcurr = asn1ct_name:curr(bytes),
+ Bnext = asn1ct_name:next(bytes),
+ emit(MaybeComma1),
+ GetoptCall = "} = ?RT_PER:getoptionals2(",
+ emit({"{Opt,",{var,Bnext},GetoptCall,
+ {var,Bcurr},",",{asis,length(Optionals)},")"}),
+ asn1ct_name:new(bytes),
+ ", "
+ end,
+ {DecObjInf,UniqueFName,ValueIndex} =
+ case TableConsInfo of
+%% {ObjectSet,AttrN,N,UniqueFieldName} ->%% N is index of attribute that determines constraint
+ #simpletableattributes{objectsetname=ObjectSet,
+ c_name=AttrN,
+ usedclassfield=UniqueFieldName,
+ uniqueclassfield=UniqueFieldName,
+ valueindex=ValIndex} ->
+%% {AttrN,ObjectSet};
+ F = fun(#'ComponentType'{typespec=CT})->
+ case {CT#type.constraint,CT#type.tablecinf} of
+ {[],[{objfun,_}|_R]} -> true;
+ _ -> false
+ end
+ end,
+ case lists:any(F,CompList) of
+ true -> % when component relation constraint establish
+ %% relation from a component to another components
+ %% subtype component
+ {{AttrN,{deep,ObjectSet,UniqueFieldName,ValIndex}},
+ UniqueFieldName,ValIndex};
+ false ->
+ {{AttrN,ObjectSet},UniqueFieldName,ValIndex}
+ end;
+ _ ->
+ case D#type.tablecinf of
+ [{objfun,_}|_] ->
+ {{"got objfun through args","ObjFun"},false,false};
+ _ ->
+ {false,false,false}
+ end
+ end,
+ {AccTerm,AccBytes} =
+ gen_dec_components_call(Typename,CompList,MaybeComma2,DecObjInf,Ext,length(Optionals)),
+ case asn1ct_name:all(term) of
+ [] -> emit(MaybeComma2); % no components at all
+ _ -> emit({com,nl})
+ end,
+ case {AccTerm,AccBytes} of
+ {[],[]} ->
+ ok;
+ {_,[]} ->
+ ok;
+ {[{ObjSet,LeadingAttr,Term}],ListOfOpenTypes} ->
+ DecObj = asn1ct_gen:un_hyphen_var(lists:concat(['DecObj',LeadingAttr,Term])),
+ ValueMatch = value_match(ValueIndex,Term),
+ emit({DecObj," =",nl," 'getdec_",ObjSet,"'(",
+% {asis,UniqueFName},", ",Term,"),",nl}),
+ {asis,UniqueFName},", ",ValueMatch,"),",nl}),
+ gen_dec_listofopentypes(DecObj,ListOfOpenTypes,false)
+ end,
+ %% we don't return named lists any more Cnames = mkcnamelist(CompList),
+ demit({"Result = "}), %dbg
+ %% return value as record
+ case Typename of
+ ['EXTERNAL'] ->
+ emit({" OldFormat={'",asn1ct_gen:list2rname(Typename),
+ "'"}),
+ mkvlist(asn1ct_name:all(term)),
+ emit({"},",nl}),
+ emit({" ASN11994Format =",nl,
+ " asn1rt_check:transform_to_EXTERNAL1994",
+ "(OldFormat),",nl}),
+ emit(" {ASN11994Format,");
+ _ ->
+ emit(["{{'",asn1ct_gen:list2rname(Typename),"'"]),
+ mkvlist(asn1ct_name:all(term)),
+ emit("},")
+ end,
+ emit({{var,asn1ct_name:curr(bytes)},"}"}),
+ emit({".",nl,nl}).
+
+gen_dec_listofopentypes(_,[],_) ->
+ emit(nl);
+gen_dec_listofopentypes(DecObj,[{_Cname,{FirstPFN,PFNList},Term,TmpTerm,Prop}|Rest],_Update) ->
+
+% asn1ct_name:new(term),
+ asn1ct_name:new(tmpterm),
+ asn1ct_name:new(reason),
+
+ emit([Term," = ",nl]),
+
+ N = case Prop of
+ mandatory -> 0;
+ 'OPTIONAL' ->
+ emit_opt_or_mand_check(asn1_NOVALUE,TmpTerm),
+ 6;
+ {'DEFAULT',Val} ->
+ emit_opt_or_mand_check(Val,TmpTerm),
+ 6
+ end,
+
+ emit([indent(N+3),"case (catch ",DecObj,"(",
+ {asis,FirstPFN},", ",TmpTerm,", telltype,",{asis,PFNList},")) of",nl]),
+ emit([indent(N+6),"{'EXIT', ",{curr,reason},"} ->",nl]),
+%% emit({indent(9),"throw({runtime_error,{","'Type not compatible with table constraint'",",",Term,"}});",nl}),
+ emit([indent(N+9),"exit({'Type not compatible with table constraint',",
+ {curr,reason},"});",nl]),
+ emit([indent(N+6),"{",{curr,tmpterm},",_} ->",nl]),
+ emit([indent(N+9),{curr,tmpterm},nl]),
+
+ case Prop of
+ mandatory ->
+ emit([indent(N+3),"end,",nl]);
+ _ ->
+ emit([indent(N+3),"end",nl,
+ indent(3),"end,",nl])
+ end,
+ gen_dec_listofopentypes(DecObj,Rest,true).
+
+
+emit_opt_or_mand_check(Val,Term) ->
+ emit([indent(3),"case ",Term," of",nl,
+ indent(6),{asis,Val}," ->",{asis,Val},";",nl,
+ indent(6),"_ ->",nl]).
+
+%% ENCODE GENERATOR FOR THE CHOICE TYPE *******
+%% assume Val = {Alternative,AltType}
+%% generate
+%%[
+%% ?RT_PER:set_choice(element(1,Val),Altnum,Altlist,ext),
+%%case element(1,Val) of
+%% alt1 ->
+%% encode_alt1(element(2,Val));
+%% alt2 ->
+%% encode_alt2(element(2,Val))
+%%end
+%%].
+
+gen_encode_choice(_Erules,Typename,D) when record(D,type) ->
+ {'CHOICE',CompList} = D#type.def,
+ emit({"[",nl}),
+ Ext = extensible(CompList),
+ gen_enc_choice(Typename,CompList,Ext),
+ emit({nl,"].",nl}).
+
+gen_decode_choice(_Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ asn1ct_name:new(bytes),
+ {'CHOICE',CompList} = D#type.def,
+ Ext = extensible(CompList),
+ gen_dec_choice(Typename,CompList,Ext),
+ emit({".",nl}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Encode generator for SEQUENCE OF type
+
+
+gen_encode_sof(_Erules,Typename,SeqOrSetOf,D) when record(D,type) ->
+ asn1ct_name:start(),
+% Val = [Component]
+% ?RT_PER:encode_length(length(Val)),
+% lists:
+ {_SeqOrSetOf,ComponentType} = D#type.def,
+ emit({"[",nl}),
+ SizeConstraint =
+ case asn1ct_gen:get_constraint(D#type.constraint,
+ 'SizeConstraint') of
+ no -> undefined;
+ Range -> Range
+ end,
+ ObjFun =
+ case D#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _->
+ ""
+ end,
+ emit({nl,indent(3),"?RT_PER:encode_length(",
+ {asis,SizeConstraint},
+ ",length(Val)),",nl}),
+ emit({indent(3),"'enc_",asn1ct_gen:list2name(Typename),
+ "_components'(Val",ObjFun,", [])"}),
+ emit({nl,"].",nl}),
+ NewComponentType =
+ case ComponentType#type.def of
+ {'ENUMERATED',_,Component}->
+ ComponentType#type{def={'ENUMERATED',Component}};
+ _ -> ComponentType
+ end,
+ gen_encode_sof_components(Typename,SeqOrSetOf,NewComponentType).
+
+gen_decode_sof(_Erules,Typename,SeqOrSetOf,D) when record(D,type) ->
+ asn1ct_name:start(),
+% Val = [Component]
+% ?RT_PER:encode_length(length(Val)),
+% lists:
+ {_SeqOrSetOf,ComponentType} = D#type.def,
+ SizeConstraint =
+ case asn1ct_gen:get_constraint(D#type.constraint,
+ 'SizeConstraint') of
+ no -> undefined;
+ Range -> Range
+ end,
+ ObjFun =
+ case D#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _ ->
+ ""
+ end,
+ emit({nl,"{Num,Bytes1} = ?RT_PER:decode_length(Bytes,",{asis,SizeConstraint},"),",nl}),
+ emit({"'dec_",asn1ct_gen:list2name(Typename),
+ "_components'(Num, Bytes1, telltype",ObjFun,", []).",nl}),
+ NewComponentType =
+ case ComponentType#type.def of
+ {'ENUMERATED',_,Component}->
+ ComponentType#type{def={'ENUMERATED',Component}};
+ _ -> ComponentType
+ end,
+ gen_decode_sof_components(Typename,SeqOrSetOf,NewComponentType).
+
+gen_encode_sof_components(Typename,SeqOrSetOf,Cont) ->
+ {ObjFun,ObjFun_Var} =
+ case Cont#type.tablecinf of
+ [{objfun,_}|_R] ->
+ {", ObjFun",", _"};
+ _ ->
+ {"",""}
+ end,
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"_components'([]",
+ ObjFun_Var,", Acc) -> lists:reverse(Acc);",nl,nl}),
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"_components'([H|T]",
+ ObjFun,", Acc) ->",nl}),
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"_components'(T"}),
+ emit({ObjFun,", ["}),
+ %% the component encoder
+ Constructed_Suffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,
+ Cont#type.def),
+
+ Conttype = asn1ct_gen:get_inner(Cont#type.def),
+ Currmod = get(currmod),
+ Ctgenmod = list_to_atom(lists:concat(["asn1ct_gen_",per,
+ asn1ct_gen:rt2ct_suffix()])),
+ case asn1ct_gen:type(Conttype) of
+ {primitive,bif} ->
+ gen_encode_prim_wrapper(Ctgenmod,per,Cont,false,"H");
+% Ctgenmod:gen_encode_prim(per,Cont,false,"H");
+ {constructed,bif} ->
+ NewTypename = [Constructed_Suffix|Typename],
+ emit({"'enc_",asn1ct_gen:list2name(NewTypename),"'(H",
+ ObjFun,")",nl,nl});
+ #'Externaltypereference'{module=Currmod,type=Ename} ->
+ emit({"'enc_",Ename,"'(H)",nl,nl});
+ #'Externaltypereference'{module=EMod,type=EType} ->
+ emit({"'",EMod,"':'enc_",EType,"'(H)",nl,nl});
+ _ ->
+ emit({"'enc_",Conttype,"'(H)",nl,nl})
+ end,
+ emit({" | Acc]).",nl}).
+
+gen_decode_sof_components(Typename,SeqOrSetOf,Cont) ->
+ {ObjFun,ObjFun_Var} =
+ case Cont#type.tablecinf of
+ [{objfun,_}|_R] ->
+ {", ObjFun",", _"};
+ _ ->
+ {"",""}
+ end,
+ emit({"'dec_",asn1ct_gen:list2name(Typename),
+ "_components'(0, Bytes, _",ObjFun_Var,", Acc) ->",nl,
+ indent(3),"{lists:reverse(Acc), Bytes};",nl}),
+ emit({"'dec_",asn1ct_gen:list2name(Typename),
+ "_components'(Num, Bytes, _",ObjFun,", Acc) ->",nl}),
+ emit({indent(3),"{Term,Remain} = "}),
+ Constructed_Suffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,
+ Cont#type.def),
+ Conttype = asn1ct_gen:get_inner(Cont#type.def),
+ Ctgenmod = list_to_atom(lists:concat(["asn1ct_gen_",per,
+ asn1ct_gen:rt2ct_suffix()])),
+ case asn1ct_gen:type(Conttype) of
+ {primitive,bif} ->
+ Ctgenmod:gen_dec_prim(per,Cont,"Bytes"),
+ emit({com,nl});
+ {constructed,bif} ->
+ NewTypename = [Constructed_Suffix|Typename],
+ emit({"'dec_",asn1ct_gen:list2name(NewTypename),
+ "'(Bytes, telltype",ObjFun,"),",nl});
+ #typereference{val=Dname} ->
+ emit({"'dec_",Dname,"'(Bytes,telltype),",nl});
+ #'Externaltypereference'{module=EMod,type=EType} ->
+ emit({"'",EMod,"':'dec_",EType,"'(Bytes,telltype),",nl});
+ _ ->
+ emit({"'dec_",Conttype,"'(Bytes,telltype),",nl})
+ end,
+ emit({indent(3),"'dec_",asn1ct_gen:list2name(Typename),
+ "_components'(Num-1, Remain, telltype",ObjFun,", [Term|Acc]).",nl}).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% General and special help functions (not exported)
+
+mkvlist([H|T]) ->
+ emit(","),
+ mkvlist2([H|T]);
+mkvlist([]) ->
+ true.
+mkvlist2([H,T1|T]) ->
+ emit({{var,H},","}),
+ mkvlist2([T1|T]);
+mkvlist2([H|T]) ->
+ emit({{var,H}}),
+ mkvlist2(T);
+mkvlist2([]) ->
+ true.
+
+extensible(CompList) when list(CompList) ->
+ noext;
+extensible({RootList,ExtList}) ->
+ {ext,length(RootList)+1,length(ExtList)}.
+
+gen_dec_extension_value(_) ->
+ emit({"{Ext,",{next,bytes},"} = ?RT_PER:getext(",{curr,bytes},")"}),
+ asn1ct_name:new(bytes).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Produce a list with positions (in the Value record) where
+%% there are optional components, start with 2 because first element
+%% is the record name
+
+optionals({L,_Ext}) -> optionals(L,[],2);
+optionals(L) -> optionals(L,[],2).
+
+optionals([{'EXTENSIONMARK',_,_}|Rest],Acc,Pos) ->
+ optionals(Rest,Acc,Pos); % optionals in extension are currently not handled
+optionals([#'ComponentType'{prop='OPTIONAL'}|Rest],Acc,Pos) ->
+ optionals(Rest,[Pos|Acc],Pos+1);
+optionals([#'ComponentType'{prop={'DEFAULT',_}}|Rest],Acc,Pos) ->
+ optionals(Rest,[Pos|Acc],Pos+1);
+optionals([#'ComponentType'{}|Rest],Acc,Pos) ->
+ optionals(Rest,Acc,Pos+1);
+optionals([],Acc,_) ->
+ lists:reverse(Acc).
+
+
+gen_enc_components_call(TopType,{CompList,ExtList},MaybeComma,DynamicEnc,Ext) ->
+ %% The type has extensionmarker
+ Rpos = gen_enc_components_call1(TopType,CompList,1,MaybeComma,DynamicEnc,noext),
+ case Ext of
+ {ext,_,ExtNum} when ExtNum > 0 ->
+ emit([nl,
+ ",Extensions",nl]);
+ _ -> true
+ end,
+ %handle extensions
+ gen_enc_components_call1(TopType,ExtList,Rpos,MaybeComma,DynamicEnc,Ext);
+gen_enc_components_call(TopType, CompList, MaybeComma, DynamicEnc, Ext) ->
+ %% The type has no extensionmarker
+ gen_enc_components_call1(TopType,CompList,1,MaybeComma,DynamicEnc,Ext).
+
+gen_enc_components_call1(TopType,
+ [C=#'ComponentType'{name=Cname,typespec=Type,prop=Prop}|Rest],
+ Tpos,
+ MaybeComma, DynamicEnc, Ext) ->
+
+ put(component_type,{true,C}),
+ %% information necessary in asn1ct_gen_per_rt2ct:gen_encode_prim
+
+ Pos = case Ext of
+ noext -> Tpos;
+ {ext,Epos,_Enum} -> Tpos - Epos + 1
+ end,
+ emit(MaybeComma),
+ case Prop of
+ 'OPTIONAL' ->
+ gen_enc_component_optional(TopType,Cname,Type,Tpos,DynamicEnc,Ext);
+ {'DEFAULT',_DefVal} ->
+ gen_enc_component_default(TopType,Cname,Type,Tpos,DynamicEnc,Ext);
+ _ ->
+ case Ext of
+ {ext,ExtPos,_} when Tpos >= ExtPos ->
+ gen_enc_component_optional(TopType,Cname,Type,Tpos,DynamicEnc,Ext);
+ _ ->
+ gen_enc_component_mandatory(TopType,Cname,Type,Tpos,DynamicEnc,Ext)
+ end
+ end,
+
+ erase(component_type),
+
+ case Rest of
+ [] ->
+ Pos+1;
+ _ ->
+ emit({com,nl}),
+ gen_enc_components_call1(TopType,Rest,Tpos+1,"",DynamicEnc,Ext)
+ end;
+gen_enc_components_call1(_TopType,[],Pos,_,_,_) ->
+ Pos.
+
+gen_enc_component_default(TopType,Cname,Type,Pos,DynamicEnc,Ext) ->
+% Element = io_lib:format("?RT_PER:cindex(~w,Val1,~w)",[Pos+1,Cname]),
+ Element = make_element(Pos+1,"Val1",Cname),
+ emit({"case ",Element," of",nl}),
+% case Ext of
+% {ext,ExtPos,_} when Pos >= ExtPos ->
+% emit({"asn1_NOEXTVALUE -> [];",nl});
+% _ ->
+ emit({"asn1_DEFAULT -> [];",nl}),
+% end,
+ asn1ct_name:new(tmpval),
+ emit({{curr,tmpval}," ->",nl}),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ emit({nl,"%% attribute number ",Pos," with type ",
+ InnerType,nl}),
+ NextElement = asn1ct_gen:mk_var(asn1ct_name:curr(tmpval)),
+ gen_enc_line(TopType,Cname,Type,NextElement, Pos,DynamicEnc,Ext),
+ emit({nl,"end"}).
+
+gen_enc_component_optional(TopType,Cname,Type,Pos,DynamicEnc,Ext) ->
+% Element = io_lib:format("?RT_PER:cindex(~w,Val1,~w)",[Pos+1,Cname]),
+ Element = make_element(Pos+1,"Val1",Cname),
+ emit({"case ",Element," of",nl}),
+% case Ext of
+% {ext,ExtPos,_} when Pos >= ExtPos ->
+% emit({"asn1_NOEXTVALUE -> [];",nl});
+% _ ->
+ emit({"asn1_NOVALUE -> [];",nl}),
+% end,
+ asn1ct_name:new(tmpval),
+ emit({{curr,tmpval}," ->",nl}),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ emit({nl,"%% attribute number ",Pos," with type ",
+ InnerType,nl}),
+ NextElement = asn1ct_gen:mk_var(asn1ct_name:curr(tmpval)),
+ gen_enc_line(TopType,Cname,Type,NextElement, Pos,DynamicEnc,Ext),
+ emit({nl,"end"}).
+
+gen_enc_component_mandatory(TopType,Cname,Type,Pos,DynamicEnc,Ext) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ emit({nl,"%% attribute number ",Pos," with type ",
+ InnerType,nl}),
+ gen_enc_line(TopType,Cname,Type,[],Pos,DynamicEnc,Ext).
+
+gen_enc_line(TopType, Cname, Type, [], Pos,DynamicEnc,Ext) ->
+% Element = io_lib:format("?RT_PER:cindex(~w,~s,~w)",[Pos+1,asn1ct_gen:mk_var(asn1ct_name:curr(val)),Cname]),
+ Element = make_element(Pos+1,asn1ct_gen:mk_var(asn1ct_name:curr(val)),Cname),
+ gen_enc_line(TopType,Cname,Type,Element, Pos,DynamicEnc,Ext);
+gen_enc_line(TopType,Cname,Type,Element, Pos,DynamicEnc,Ext) ->
+ Ctgenmod = list_to_atom(lists:concat(["asn1ct_gen_",per,
+ asn1ct_gen:rt2ct_suffix()])),
+ Atype =
+ case Type of
+ #type{def=#'ObjectClassFieldType'{type=InnerType}} ->
+ InnerType;
+ _ ->
+ asn1ct_gen:get_inner(Type#type.def)
+ end,
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info) of
+% no ->
+% asn1ct_gen:get_inner(Type#type.def);
+% _ ->
+% Type#type.def
+% end,
+ case Ext of
+ {ext,Ep1,_} when Pos >= Ep1 ->
+ emit(["?RT_PER:encode_open_type(dummy,?RT_PER:complete("]);
+ _ -> true
+ end,
+ case Atype of
+ {typefield,_} ->
+ case DynamicEnc of
+ {_LeadingAttrName,Fun} ->
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% componentrelation) of
+ case (Type#type.def)#'ObjectClassFieldType'.fieldname of
+ {notype,T} ->
+ throw({error,{notype,type_from_object,T}});
+ {Name,RestFieldNames} when atom(Name) ->
+ emit({"?RT_PER:encode_open_type([],?RT_PER:complete(",nl}),
+ emit({" ",Fun,"(",{asis,Name},", ",
+ Element,", ",{asis,RestFieldNames},")))"});
+ Other ->
+ throw({asn1,{'internal error',Other}})
+ end
+ end;
+ {objectfield,PrimFieldName1,PFNList} ->
+ case DynamicEnc of
+ {_LeadingAttrName,Fun} ->
+ emit({"?RT_PER:encode_open_type([],"
+ "?RT_PER:complete(",nl}),
+ emit({" ",Fun,"(",{asis,PrimFieldName1},
+ ", ",Element,", ",{asis,PFNList},")))"})
+ end;
+ _ ->
+ CurrMod = get(currmod),
+ case asn1ct_gen:type(Atype) of
+ #'Externaltypereference'{module=Mod,type=EType} when
+ (CurrMod==Mod) ->
+ emit({"'enc_",EType,"'(",Element,")"});
+ #'Externaltypereference'{module=Mod,type=EType} ->
+ emit({"'",Mod,"':'enc_",
+ EType,"'(",Element,")"});
+ #typereference{val=Ename} ->
+ emit({"'enc_",Ename,"'(",Element,")"});
+ {notype,_} ->
+ emit({"'enc_",Atype,"'(",Element,")"});
+ {primitive,bif} ->
+ EncType =
+ case Atype of
+ {fixedtypevaluefield,_,Btype} ->
+ Btype;
+ _ ->
+ Type
+ end,
+ gen_encode_prim_wrapper(Ctgenmod,per,EncType,
+ false,Element);
+% Ctgenmod:gen_encode_prim(per,EncType,
+% false,Element);
+ 'ASN1_OPEN_TYPE' ->
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=OpenType} ->
+ gen_encode_prim_wrapper(Ctgenmod,per,
+ #type{def=OpenType},
+ false,Element);
+ _ ->
+ gen_encode_prim_wrapper(Ctgenmod,per,Type,
+ false,Element)
+ end;
+% Ctgenmod:gen_encode_prim(per,Type,
+% false,Element);
+ {constructed,bif} ->
+ NewTypename = [Cname|TopType],
+ case {Type#type.tablecinf,DynamicEnc} of
+ {[{objfun,_}|_R],{_,EncFun}} ->
+%% emit({"?RT_PER:encode_open_type([],",
+%% "?RT_PER:complete(",nl}),
+ emit({"'enc_",
+ asn1ct_gen:list2name(NewTypename),
+ "'(",Element,", ",EncFun,")"});
+ _ ->
+ emit({"'enc_",
+ asn1ct_gen:list2name(NewTypename),
+ "'(",Element,")"})
+ end
+ end
+ end,
+ case Ext of
+ {ext,Ep2,_} when Pos >= Ep2 ->
+ emit(["))"]);
+ _ -> true
+ end.
+
+gen_dec_components_call(TopType,{CompList,ExtList},MaybeComma,DecInfObj,Ext,NumberOfOptionals) ->
+ %% The type has extensionmarker
+ {Rpos,AccTerm,AccBytes} =
+ gen_dec_components_call1(TopType, CompList, 1, 1, MaybeComma,DecInfObj,
+ noext,[],[],NumberOfOptionals),
+ emit([",",nl,"{Extensions,",{next,bytes},"} = "]),
+ emit(["?RT_PER:getextension(Ext,",{curr,bytes},"),",nl]),
+ asn1ct_name:new(bytes),
+ {_Epos,AccTermE,AccBytesE} =
+ gen_dec_components_call1(TopType,ExtList,Rpos, 1, "",DecInfObj,Ext,[],[],NumberOfOptionals),
+ case ExtList of
+ [] -> true;
+ _ -> emit([",",nl])
+ end,
+ emit([{next,bytes},"= ?RT_PER:skipextensions(",{curr,bytes},",",
+ length(ExtList)+1,",Extensions)",nl]),
+ asn1ct_name:new(bytes),
+ {AccTerm++AccTermE,AccBytes++AccBytesE};
+
+gen_dec_components_call(TopType,CompList,MaybeComma,DecInfObj,Ext,NumberOfOptionals) ->
+ %% The type has no extensionmarker
+ {_,AccTerm,AccBytes} =
+ gen_dec_components_call1(TopType, CompList, 1, 1,MaybeComma,DecInfObj,Ext,[],[],NumberOfOptionals),
+ {AccTerm,AccBytes}.
+
+
+gen_dec_components_call1(TopType,
+ [C=#'ComponentType'{name=Cname,typespec=Type,prop=Prop}|Rest],
+ Tpos,OptPos,MaybeComma,DecInfObj,Ext,AccTerm,AccBytes,NumberOfOptionals) ->
+ Pos = case Ext of
+ noext -> Tpos;
+ {ext,Epos,_Enum} -> Tpos - Epos + 1
+ end,
+ emit(MaybeComma),
+%% asn1ct_name:new(term),
+ InnerType =
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=InType} ->
+ InType;
+ Def ->
+ asn1ct_gen:get_inner(Def)
+ end,
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info) of
+% no ->
+% asn1ct_gen:get_inner(Type#type.def);
+% _ ->
+% Type#type.def
+% end,
+ case InnerType of
+ #'Externaltypereference'{type=T} ->
+ emit({nl,"%% attribute number ",Tpos," with type ",
+ T,nl});
+ IT when tuple(IT) ->
+ emit({nl,"%% attribute number ",Tpos," with type ",
+ element(2,IT),nl});
+ _ ->
+ emit({nl,"%% attribute number ",Tpos," with type ",
+ InnerType,nl})
+ end,
+
+ case InnerType of
+ {typefield,_} ->
+ asn1ct_name:new(term),
+ asn1ct_name:new(tmpterm),
+ emit({"{",{curr,tmpterm},", ",{next,bytes},"} = "});
+ {objectfield,_,_} ->
+ asn1ct_name:new(term),
+ asn1ct_name:new(tmpterm),
+ emit({"{",{curr,tmpterm},", ",{next,bytes},"} = "});
+ _ ->
+ asn1ct_name:new(term),
+ emit({"{",{curr,term},",",{next,bytes},"} = "})
+ end,
+
+ NewOptPos =
+ case {Ext,Prop} of
+ {noext,mandatory} -> OptPos; % generate nothing
+ {noext,_} ->
+ Element = io_lib:format("Opt band (1 bsl ~w)",[NumberOfOptionals - OptPos]),
+ emit({"case ",Element," of",nl}),
+ emit({"_Opt",OptPos," when _Opt",OptPos," > 0 ->"}),
+ OptPos+1;
+ _ ->
+ emit(["case Extensions of",nl]),
+ emit(["_ when size(Extensions) >= ",Pos,",element(",Pos,",Extensions) == 1 ->",nl])
+ end,
+ put(component_type,{true,C}),
+ {TermVar,BytesVar} = gen_dec_line(TopType,Cname,Type,Tpos,DecInfObj,Ext),
+ erase(component_type),
+ case {Ext,Prop} of
+ {noext,mandatory} -> true; % generate nothing
+ {noext,_} ->
+ emit([";",nl,"0 ->"]),
+ gen_dec_component_no_val(TopType,Cname,Type,Prop,Tpos,Ext);
+ _ ->
+ emit([";",nl,"_ ->",nl]),
+ gen_dec_component_no_val(TopType,Cname,Type,Prop,Tpos,Ext)
+ end,
+ case {Ext,Prop} of
+ {noext,mandatory} -> true; % generate nothing
+ {noext,_} ->
+ emit([nl,"end"]);
+ _ ->
+ emit([nl,"end"])
+
+ end,
+ asn1ct_name:new(bytes),
+ case Rest of
+ [] ->
+ {Pos+1,AccTerm++TermVar,AccBytes++BytesVar};
+ _ ->
+ emit({com,nl}),
+ gen_dec_components_call1(TopType,Rest,Tpos+1,NewOptPos,"",DecInfObj,Ext,
+ AccTerm++TermVar,AccBytes++BytesVar,NumberOfOptionals)
+ end;
+
+gen_dec_components_call1(_TopType,[],Pos,_OptPos,_,_,_,AccTerm,AccBytes,_NumberOfOptionals) ->
+ {Pos,AccTerm,AccBytes}.
+
+
+%%gen_dec_component_no_val(TopType,Cname,Type,_,Pos,{ext,Ep,Enum}) when Pos >= Ep ->
+%% emit({"{asn1_NOEXTVALUE,",{curr,bytes},"}",nl});
+gen_dec_component_no_val(_,_,_,{'DEFAULT',DefVal},_,_) ->
+ emit(["{",{asis,DefVal},",",{curr,bytes},"}",nl]);
+gen_dec_component_no_val(_,_,_,'OPTIONAL',_,_) ->
+ emit({"{asn1_NOVALUE,",{curr,bytes},"}",nl});
+gen_dec_component_no_val(_,_,_,mandatory,_,{ext,_,_}) ->
+ emit({"{asn1_NOVALUE,",{curr,bytes},"}",nl}).
+
+
+gen_dec_line(TopType,Cname,Type,Pos,DecInfObj,Ext) ->
+ Ctgenmod = list_to_atom(lists:concat(["asn1ct_gen_",per,
+ asn1ct_gen:rt2ct_suffix()])),
+ Atype =
+ case Type of
+ #type{def=#'ObjectClassFieldType'{type=InnerType}} ->
+ InnerType;
+ _ ->
+ asn1ct_gen:get_inner(Type#type.def)
+ end,
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info) of
+% no ->
+% asn1ct_gen:get_inner(Type#type.def);
+% _ ->
+% Type#type.def
+% end,
+ BytesVar0 = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
+ BytesVar = case Ext of
+ {ext,Ep,_} when Pos >= Ep ->
+ emit(["begin",nl,"{TmpVal",Pos,",Trem",Pos,
+ "}=?RT_PER:decode_open_type(",
+ {curr,bytes},",[]),",nl,
+ "{TmpValx",Pos,",_}="]),
+ io_lib:format("TmpVal~p",[Pos]);
+ _ -> BytesVar0
+ end,
+ SaveBytes =
+ case Atype of
+ {typefield,_} ->
+ case DecInfObj of
+ false -> % This is in a choice with typefield components
+ {Name,RestFieldNames} =
+ (Type#type.def)#'ObjectClassFieldType'.fieldname,
+% asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info),
+ asn1ct_name:new(tmpterm),
+ asn1ct_name:new(reason),
+ emit([indent(2),"{",{curr,tmpterm},", ",{next,bytes},
+ "} = ?RT_PER:decode_open_type(",{curr,bytes},
+ ", []),",nl]),
+ emit([indent(2),"case (catch ObjFun(",
+ {asis,Name},
+ ",",{curr,tmpterm},",telltype,",
+ {asis,RestFieldNames},")) of", nl]),
+ emit([indent(4),"{'EXIT',",{curr,reason},"} ->",nl]),
+ emit([indent(6),"exit({'Type not ",
+ "compatible with table constraint', ",
+ {curr,reason},"});",nl]),
+ asn1ct_name:new(tmpterm),
+ emit([indent(4),"{",{curr,tmpterm},", _} ->",nl]),
+ emit([indent(6),"{",Cname,", {",{curr,tmpterm},", ",
+ {next,bytes},"}}",nl]),
+ emit([indent(2),"end"]),
+ [];
+ {"got objfun through args","ObjFun"} ->
+ %% this is when the generated code gots the
+ %% objfun though arguments on function
+ %% invocation.
+ {Name,RestFieldNames} =
+ (Type#type.def)#'ObjectClassFieldType'.fieldname,
+ emit(["?RT_PER:decode_open_type(",{curr,bytes},
+ ", []),",nl]),
+ emit([{curr,term}," =",nl,
+ " case (catch ObjFun(",{asis,Name},",",
+ {curr,tmpterm},",telltype,",
+ {asis,RestFieldNames},")) of", nl]),
+ emit([" {'EXIT',",{curr,reason},"} ->",nl]),
+ emit([indent(6),"exit({'Type not ",
+ "compatible with table constraint', ",
+ {curr,reason},"});",nl]),
+ asn1ct_name:new(tmpterm),
+ emit([indent(4),"{",{curr,tmpterm},", _} ->",nl]),
+ emit([indent(6),{curr,tmpterm},nl]),
+ emit([indent(2),"end"]),
+ [];
+ _ ->
+ emit({"?RT_PER:decode_open_type(",{curr,bytes},
+ ", [])"}),
+ RefedFieldName =
+ (Type#type.def)#'ObjectClassFieldType'.fieldname,
+% asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info),
+ [{Cname,RefedFieldName,
+ asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+ asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),
+ get_components_prop()}]
+ end;
+ {objectfield,PrimFieldName1,PFNList} ->
+ emit({"?RT_PER:decode_open_type(",{curr,bytes},", [])"}),
+ [{Cname,{PrimFieldName1,PFNList},
+ asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+ asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),
+ get_components_prop()}];
+ _ ->
+ CurrMod = get(currmod),
+ case asn1ct_gen:type(Atype) of
+ #'Externaltypereference'{module=CurrMod,type=EType} ->
+ emit({"'dec_",EType,"'(",BytesVar,",telltype)"});
+ #'Externaltypereference'{module=Mod,type=EType} ->
+ emit({"'",Mod,"':'dec_",EType,"'(",BytesVar,
+ ",telltype)"});
+ {primitive,bif} ->
+ case Atype of
+ {fixedtypevaluefield,_,Btype} ->
+ Ctgenmod:gen_dec_prim(per,Btype,
+ BytesVar);
+ _ ->
+ Ctgenmod:gen_dec_prim(per,Type,
+ BytesVar)
+ end;
+ 'ASN1_OPEN_TYPE' ->
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=OpenType} ->
+ Ctgenmod:gen_dec_prim(per,#type{def=OpenType},
+ BytesVar);
+ _ ->
+ Ctgenmod:gen_dec_prim(per,Type,
+ BytesVar)
+ end;
+ #typereference{val=Dname} ->
+ emit({"'dec_",Dname,"'(",BytesVar,",telltype)"});
+ {notype,_} ->
+ emit({"'dec_",Atype,"'(",BytesVar,",telltype)"});
+ {constructed,bif} ->
+ NewTypename = [Cname|TopType],
+ case Type#type.tablecinf of
+ [{objfun,_}|_R] ->
+ emit({"'dec_",asn1ct_gen:list2name(NewTypename),
+ "'(",BytesVar,", telltype, ObjFun)"});
+ _ ->
+ emit({"'dec_",asn1ct_gen:list2name(NewTypename),
+ "'(",BytesVar,", telltype)"})
+ end
+ end,
+ case DecInfObj of
+ {Cname,{_,OSet,UniqueFName,ValIndex}} ->
+ Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+ ValueMatch = value_match(ValIndex,Term),
+ emit({",",nl,"ObjFun = 'getdec_",OSet,"'(",
+ {asis,UniqueFName},", ",ValueMatch,")"});
+ _ ->
+ ok
+ end,
+ []
+ end,
+ case Ext of
+ {ext,Ep2,_} when Pos >= Ep2 ->
+ emit([", {TmpValx",Pos,",Trem",Pos,"}",nl,"end"]);
+ _ -> true
+ end,
+ %% Prepare return value
+ case DecInfObj of
+ {Cname,ObjSet} ->
+ {[{ObjSet,Cname,asn1ct_gen:mk_var(asn1ct_name:curr(term))}],
+ SaveBytes};
+ _ ->
+ {[],SaveBytes}
+ end.
+
+gen_enc_choice(TopType,CompList,Ext) ->
+ gen_enc_choice_tag(CompList, [], Ext),
+ emit({com,nl}),
+ emit({"case element(1,Val) of",nl}),
+ gen_enc_choice2(TopType, CompList, Ext),
+ emit({nl,"end"}).
+
+gen_enc_choice_tag({C1,C2},_,_) ->
+ N1 = get_name_list(C1),
+ N2 = get_name_list(C2),
+ emit(["?RT_PER:set_choice(element(1,Val),",
+ {asis,{N1,N2}},", ",{asis,{length(N1),length(N2)}},")"]);
+gen_enc_choice_tag(C,_,_) ->
+ N = get_name_list(C),
+ emit(["?RT_PER:set_choice(element(1,Val),",
+ {asis,N},", ",{asis,length(N)},")"]).
+
+get_name_list(L) ->
+ get_name_list(L,[]).
+
+get_name_list([#'ComponentType'{name=Name}|T], Acc) ->
+ get_name_list(T,[Name|Acc]);
+get_name_list([], Acc) ->
+ lists:reverse(Acc).
+
+%gen_enc_choice_tag([H|T],Acc,Ext) when record(H,'ComponentType') ->
+% gen_enc_choice_tag(T,[H#'ComponentType'.name|Acc],Ext);
+%gen_enc_choice_tag([H|T],Acc,Ext) -> % skip EXTENSIONMARK
+% gen_enc_choice_tag(T,Acc,Ext);
+%gen_enc_choice_tag([],Acc,Ext) ->
+% Length = length(Acc),
+% emit({"?RT_PER:set_choice(element(1,Val),",{asis,Length},",",
+% {asis,lists:reverse(Acc)},",",{asis,Ext},")"}),
+% Length.
+
+gen_enc_choice2(TopType, {L1,L2}, Ext) ->
+ gen_enc_choice2(TopType, L1 ++ L2, 0, Ext);
+gen_enc_choice2(TopType, L, Ext) ->
+ gen_enc_choice2(TopType, L, 0, Ext).
+
+gen_enc_choice2(TopType,[H1,H2|T], Pos, Ext)
+when record(H1,'ComponentType'), record(H2,'ComponentType') ->
+ Cname = H1#'ComponentType'.name,
+ Type = H1#'ComponentType'.typespec,
+ EncObj =
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info) of
+% no ->
+% false;
+% _ ->
+% {no_attr,"ObjFun"}
+% end,
+ case asn1ct_gen:get_constraint(Type#type.constraint,
+ componentrelation) of
+ no -> false;
+ _ -> {no_attr,"ObjFun"}
+ end,
+ emit({{asis,Cname}," ->",nl}),
+ gen_enc_line(TopType,Cname,Type,"element(2,Val)", Pos+1,EncObj,Ext),
+ emit({";",nl}),
+ gen_enc_choice2(TopType,[H2|T], Pos+1, Ext);
+gen_enc_choice2(TopType,[H1|T], Pos, Ext) when record(H1,'ComponentType') ->
+ Cname = H1#'ComponentType'.name,
+ Type = H1#'ComponentType'.typespec,
+ EncObj =
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info) of
+% no ->
+% false;
+% _ ->
+% {no_attr,"ObjFun"}
+% end,
+ case asn1ct_gen:get_constraint(Type#type.constraint,
+ componentrelation) of
+ no -> false;
+ _ -> {no_attr,"ObjFun"}
+ end,
+ emit({{asis,H1#'ComponentType'.name}," ->",nl}),
+ gen_enc_line(TopType,Cname,Type,"element(2,Val)", Pos+1,EncObj,Ext),
+ gen_enc_choice2(TopType,T, Pos+1, Ext);
+gen_enc_choice2(_,[], _, _) ->
+ true.
+
+gen_dec_choice(TopType,CompList,{ext,Pos,NumExt}) ->
+ emit({"{Ext,",{curr,bytes},"} = ?RT_PER:getbit(Bytes),",nl}),
+ asn1ct_name:new(bytes),
+ gen_dec_choice1(TopType,CompList,{ext,Pos,NumExt});
+gen_dec_choice(TopType,CompList,noext) ->
+ gen_dec_choice1(TopType,CompList,noext).
+
+gen_dec_choice1(TopType,CompList,noext) ->
+ emit({"{Choice,",{curr,bytes},
+ "} = ?RT_PER:getchoice(",{prev,bytes},",",
+ length(CompList),", 0),",nl}),
+ emit({"{Cname,{Val,NewBytes}} = case Choice of",nl}),
+ gen_dec_choice2(TopType,CompList,noext),
+ emit({nl,"end,",nl}),
+ emit({nl,"{{Cname,Val},NewBytes}"});
+gen_dec_choice1(TopType,{RootList,ExtList},Ext) ->
+ NewList = RootList ++ ExtList,
+ gen_dec_choice1(TopType, NewList, Ext);
+gen_dec_choice1(TopType,CompList,{ext,ExtPos,ExtNum}) ->
+ emit({"{Choice,",{curr,bytes},
+ "} = ?RT_PER:getchoice(",{prev,bytes},",",
+ length(CompList)-ExtNum,",Ext ),",nl}),
+ emit({"{Cname,{Val,NewBytes}} = case Choice + Ext*",ExtPos-1," of",nl}),
+ gen_dec_choice2(TopType,CompList,{ext,ExtPos,ExtNum}),
+ emit([";",nl,"_ -> {asn1_ExtAlt, ?RT_PER:decode_open_type(",{curr,bytes},",[])}"]),
+ emit({nl,"end,",nl}),
+ emit({nl,"{{Cname,Val},NewBytes}"}).
+
+
+gen_dec_choice2(TopType,L,Ext) ->
+ gen_dec_choice2(TopType,L,0,Ext).
+
+gen_dec_choice2(TopType,[H1,H2|T],Pos,Ext)
+when record(H1,'ComponentType'), record(H2,'ComponentType') ->
+ Cname = H1#'ComponentType'.name,
+ Type = H1#'ComponentType'.typespec,
+ case Type#type.def of
+ #'ObjectClassFieldType'{type={typefield,_}} ->
+ emit({Pos," -> ",nl}),
+ wrap_gen_dec_line(H1,TopType,Cname,Type,Pos+1,false,Ext),
+ emit({";",nl});
+ _ ->
+ emit({Pos," -> {",{asis,Cname},",",nl}),
+ wrap_gen_dec_line(H1,TopType,Cname,Type,Pos+1,false,Ext),
+ emit({"};",nl})
+ end,
+ gen_dec_choice2(TopType,[H2|T],Pos+1,Ext);
+gen_dec_choice2(TopType,[H1,_H2|T],Pos,Ext) when record(H1,'ComponentType') ->
+ gen_dec_choice2(TopType,[H1|T],Pos,Ext); % skip extensionmark
+gen_dec_choice2(TopType,[H1|T],Pos,Ext) when record(H1,'ComponentType') ->
+ Cname = H1#'ComponentType'.name,
+ Type = H1#'ComponentType'.typespec,
+ case Type#type.def of
+ #'ObjectClassFieldType'{type={typefield,_}} ->
+ emit({Pos," -> ",nl}),
+ wrap_gen_dec_line(H1,TopType,Cname,Type,Pos+1,false,Ext);
+ _ ->
+ emit({Pos," -> {",{asis,Cname},",",nl}),
+ wrap_gen_dec_line(H1,TopType,Cname,Type,Pos+1,false,Ext),
+ emit("}")
+ end,
+ gen_dec_choice2(TopType,[T],Pos+1);
+gen_dec_choice2(TopType,[_|T],Pos,Ext) ->
+ gen_dec_choice2(TopType,T,Pos,Ext);% skip extensionmark
+gen_dec_choice2(_,[],Pos,_) ->
+ Pos.
+
+indent(N) ->
+ lists:duplicate(N,32). % 32 = space
+
+gen_encode_prim_wrapper(CtgenMod,Erule,Cont,DoTag,Value) ->
+% put(component_type,true), % add more info in component_type
+ CtgenMod:gen_encode_prim(Erule,Cont,DoTag,Value).
+% erase(component_type).
+
+make_element(I,Val,Cname) ->
+ case lists:member(optimize,get(encoding_options)) of
+ false ->
+ io_lib:format("?RT_PER:cindex(~w,~s,~w)",[I,Val,Cname]);
+ _ ->
+ io_lib:format("element(~w,~s)",[I,Val])
+ end.
+
+wrap_gen_dec_line(C,TopType,Cname,Type,Pos,DIO,Ext) ->
+ put(component_type,{true,C}),
+ gen_dec_line(TopType,Cname,Type,Pos,DIO,Ext),
+ erase(component_type).
+
+get_components_prop() ->
+ case get(component_type) of
+ undefined ->
+ mandatory;
+ {true,#'ComponentType'{prop=Prop}} -> Prop
+ end.
+
+
+value_match(Index,Value) when atom(Value) ->
+ value_match(Index,atom_to_list(Value));
+value_match([],Value) ->
+ Value;
+value_match([{VI,_}|VIs],Value) ->
+ value_match1(Value,VIs,lists:concat(["element(",VI,","]),1).
+value_match1(Value,[],Acc,Depth) ->
+ Acc ++ Value ++ lists:concat(lists:duplicate(Depth,")"));
+value_match1(Value,[{VI,_}|VIs],Acc,Depth) ->
+ value_match1(Value,VIs,Acc++lists:concat(["element(",VI,","]),Depth+1).
+
+notice_value_match() ->
+ Module = get(currmod),
+ put(value_match,{true,Module}).
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen.erl
new file mode 100644
index 0000000000..5d2f7a13bd
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen.erl
@@ -0,0 +1,1664 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_gen.erl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-module(asn1ct_gen).
+
+-include("asn1_records.hrl").
+%%-compile(export_all).
+-export([pgen_exports/3,
+ pgen_hrl/4,
+ gen_head/3,
+ demit/1,
+ emit/1,
+ fopen/2,
+ get_inner/1,type/1,def_to_tag/1,prim_bif/1,
+ type_from_object/1,
+ get_typefromobject/1,get_fieldcategory/2,
+ get_classfieldcategory/2,
+ list2name/1,
+ list2rname/1,
+ constructed_suffix/2,
+ unify_if_string/1,
+ gen_check_call/7,
+ get_constraint/2,
+ insert_once/2,
+ rt2ct_suffix/1,rt2ct_suffix/0]).
+-export([pgen/4,pgen_module/5,mk_var/1, un_hyphen_var/1]).
+-export([gen_encode_constructed/4,gen_decode_constructed/4]).
+
+%% pgen(Erules, Module, TypeOrVal)
+%% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module
+%% .hrl file is only generated if necessary
+%% Erules = per | ber | ber_bin | per_bin
+%% Module = atom()
+%% TypeOrVal = {TypeList,ValueList}
+%% TypeList = ValueList = [atom()]
+
+pgen(OutFile,Erules,Module,TypeOrVal) ->
+ pgen_module(OutFile,Erules,Module,TypeOrVal,true).
+
+
+pgen_module(OutFile,Erules,Module,TypeOrVal,Indent) ->
+ put(outfile,OutFile),
+ HrlGenerated = asn1ct_gen:pgen_hrl(Erules,Module,TypeOrVal,Indent),
+ asn1ct_name:start(),
+ ErlFile = lists:concat([OutFile,".erl"]),
+ Fid = asn1ct_gen:fopen(ErlFile,write),
+ put(gen_file_out,Fid),
+ asn1ct_gen:gen_head(Erules,Module,HrlGenerated),
+ pgen_exports(Erules,Module,TypeOrVal),
+ pgen_dispatcher(Erules,Module,TypeOrVal),
+ pgen_info(Erules,Module),
+ pgen_typeorval(wrap_ber(Erules),Module,TypeOrVal),
+ pgen_partial_incomplete_decode(Erules),
+% gen_vars(asn1_db:mod_to_vars(Module)),
+% gen_tag_table(AllTypes),
+ file:close(Fid),
+ io:format("--~p--~n",[{generated,ErlFile}]).
+
+
+pgen_typeorval(Erules,Module,{Types,Values,_Ptypes,_Classes,Objects,ObjectSets}) ->
+ pgen_types(Erules,Module,Types),
+ pgen_values(Erules,Module,Values),
+ pgen_objects(Erules,Module,Objects),
+ pgen_objectsets(Erules,Module,ObjectSets),
+ case catch lists:member(der,get(encoding_options)) of
+ true ->
+ pgen_check_defaultval(Erules,Module);
+ _ -> ok
+ end,
+ pgen_partial_decode(Erules,Module).
+
+pgen_values(_,_,[]) ->
+ true;
+pgen_values(Erules,Module,[H|T]) ->
+ Valuedef = asn1_db:dbget(Module,H),
+ gen_value(Valuedef),
+ pgen_values(Erules,Module,T).
+
+pgen_types(_,Module,[]) ->
+ gen_value_match(Module),
+ true;
+pgen_types(Erules,Module,[H|T]) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ asn1ct_name:clear(),
+ Typedef = asn1_db:dbget(Module,H),
+ Rtmod:gen_encode(Erules,Typedef),
+ asn1ct_name:clear(),
+ Rtmod:gen_decode(Erules,Typedef),
+ pgen_types(Erules,Module,T).
+
+pgen_objects(_,_,[]) ->
+ true;
+pgen_objects(Erules,Module,[H|T]) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ asn1ct_name:clear(),
+ Typedef = asn1_db:dbget(Module,H),
+ Rtmod:gen_obj_code(Erules,Module,Typedef),
+ pgen_objects(Erules,Module,T).
+
+pgen_objectsets(_,_,[]) ->
+ true;
+pgen_objectsets(Erules,Module,[H|T]) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ asn1ct_name:clear(),
+ TypeDef = asn1_db:dbget(Module,H),
+ Rtmod:gen_objectset_code(Erules,TypeDef),
+ pgen_objectsets(Erules,Module,T).
+
+pgen_check_defaultval(Erules,Module) ->
+ CheckObjects = ets:tab2list(check_functions),
+ case get(asndebug) of
+ true ->
+ FileName = lists:concat([Module,'.table']),
+ {ok,IoDevice} = file:open(FileName,[write]),
+ Fun =
+ fun(X)->
+ io:format(IoDevice,"~n~n************~n~n~p~n~n*****"
+ "********~n~n",[X])
+ end,
+ lists:foreach(Fun,CheckObjects),
+ file:close(IoDevice);
+ _ -> ok
+ end,
+ gen_check_defaultval(Erules,Module,CheckObjects).
+
+pgen_partial_decode(Erules,Module) ->
+ pgen_partial_inc_dec(Erules,Module),
+ pgen_partial_dec(Erules,Module).
+
+pgen_partial_inc_dec(Erules,Module) ->
+% io:format("Start partial incomplete decode gen?~n"),
+ case asn1ct:get_gen_state_field(inc_type_pattern) of
+ undefined ->
+% io:format("Partial incomplete decode gen not started: ~w~n",[asn1ct:get_gen_state_field(active)]),
+ ok;
+% [] ->
+% ok;
+ ConfList ->
+ PatternLists=lists:map(fun({_,P}) -> P end,ConfList),
+ pgen_partial_inc_dec1(Erules,Module,PatternLists),
+ gen_partial_inc_dec_refed_funcs(Erules)
+ end.
+
+%% pgen_partial_inc_dec1 generates a function of the toptype in each
+%% of the partial incomplete decoded types.
+pgen_partial_inc_dec1(Erules,Module,[P|Ps]) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ TopTypeName = asn1ct:partial_inc_dec_toptype(P),
+ TypeDef=asn1_db:dbget(Module,TopTypeName),
+ asn1ct_name:clear(),
+ asn1ct:update_gen_state(namelist,P),
+ asn1ct:update_gen_state(active,true),
+ asn1ct:update_gen_state(prefix,"dec-inc-"),
+ Rtmod:gen_decode(Erules,TypeDef),
+%% asn1ct:update_gen_state(namelist,tl(P)), %%
+ gen_dec_part_inner_constr(Erules,TypeDef,[TopTypeName]),
+ pgen_partial_inc_dec1(Erules,Module,Ps);
+pgen_partial_inc_dec1(_,_,[]) ->
+ ok.
+
+gen_partial_inc_dec_refed_funcs(Erule) when Erule == ber_bin_v2 ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erule),
+ rt2ct_suffix(Erule)])),
+ case asn1ct:next_refed_func() of
+ [] ->
+ ok;
+ {#'Externaltypereference'{module=M,type=Name},Pattern} ->
+ TypeDef = asn1_db:dbget(M,Name),
+ asn1ct:update_gen_state(namelist,Pattern),
+ Rtmod:gen_inc_decode(Erule,TypeDef),
+ gen_dec_part_inner_constr(Erule,TypeDef,[Name]),
+ gen_partial_inc_dec_refed_funcs(Erule);
+ _ ->
+ gen_partial_inc_dec_refed_funcs(Erule)
+ end;
+gen_partial_inc_dec_refed_funcs(_) ->
+ ok.
+
+pgen_partial_dec(_Erules,_Module) ->
+ ok. %%%% implement later
+
+%% generate code for all inner types that are called from the top type
+%% of the partial incomplete decode
+gen_dec_part_inner_constr(Erules,TypeDef,TypeName) ->
+ Def = TypeDef#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ case InnerType of
+ 'SET' ->
+ #'SET'{components=Components} = Def#type.def,
+ gen_dec_part_inner_types(Erules,Components,TypeName);
+ %% Continue generate the inner of each component
+ 'SEQUENCE' ->
+ #'SEQUENCE'{components=Components} = Def#type.def,
+ gen_dec_part_inner_types(Erules,Components,TypeName);
+ 'CHOICE' ->
+ {_,Components} = Def#type.def,
+ gen_dec_part_inner_types(Erules,Components,TypeName);
+ 'SEQUENCE OF' ->
+ %% this and next case must be the last component in the
+ %% partial decode chain here. Not likely that this occur.
+ {_,Type} = Def#type.def,
+ NameSuffix = constructed_suffix(InnerType,Type#type.def),
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ asn1ct_name:clear(),
+ Rtmod:gen_decode(Erules,[NameSuffix|TypeName],Type);
+%% gen_types(Erules,[NameSuffix|Typename],Type);
+ 'SET OF' ->
+ {_,Type} = Def#type.def,
+ NameSuffix = constructed_suffix(InnerType,Type#type.def),
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ asn1ct_name:clear(),
+ Rtmod:gen_decode(Erules,[NameSuffix|TypeName],Type);
+ _ ->
+ ok
+ end.
+
+gen_dec_part_inner_types(Erules,[ComponentType|Rest],TypeName) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ asn1ct_name:clear(),
+ Rtmod:gen_decode(Erules,TypeName,ComponentType),
+ gen_dec_part_inner_types(Erules,Rest,TypeName);
+gen_dec_part_inner_types(Erules,{Comps1,Comps2},TypeName)
+ when list(Comps1),list(Comps2) ->
+ gen_dec_part_inner_types(Erules,Comps1 ++ Comps2,TypeName);
+gen_dec_part_inner_types(_,[],_) ->
+ ok.
+
+
+pgen_partial_incomplete_decode(Erule) ->
+ case asn1ct:get_gen_state_field(active) of
+ true ->
+ pgen_partial_incomplete_decode1(Erule),
+ asn1ct:reset_gen_state();
+ _ ->
+ ok
+ end.
+pgen_partial_incomplete_decode1(ber_bin_v2) ->
+ case asn1ct:read_config_data(partial_incomplete_decode) of
+ undefined ->
+ ok;
+ Data ->
+ lists:foreach(fun emit_partial_incomplete_decode/1,Data)
+ end,
+ GeneratedFs= asn1ct:get_gen_state_field(gen_refed_funcs),
+% io:format("GeneratedFs :~n~p~n",[GeneratedFs]),
+ gen_part_decode_funcs(GeneratedFs,0);
+pgen_partial_incomplete_decode1(_) -> ok.
+
+emit_partial_incomplete_decode({FuncName,TopTypeName,Pattern}) ->
+ emit([{asis,FuncName},"(Bytes) ->",nl,
+ " decode_partial_incomplete(",{asis,TopTypeName},",Bytes,",{asis,Pattern},").",nl]);
+emit_partial_incomplete_decode(D) ->
+ throw({error,{asn1,{"bad data in asn1config file",D}}}).
+
+gen_part_decode_funcs([Data={Name,_,_,Type}|GeneratedFs],N) ->
+ InnerType =
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=OCFTType} ->
+ OCFTType;
+ _ ->
+ get_inner(Type#type.def)
+ end,
+ WhatKind = type(InnerType),
+ TypeName=list2name(Name),
+ if
+ N > 0 -> emit([";",nl]);
+ true -> ok
+ end,
+ emit(["decode_inc_disp('",TypeName,"',Data) ->",nl]),
+ gen_part_decode_funcs(WhatKind,TypeName,Data),
+ gen_part_decode_funcs(GeneratedFs,N+1);
+gen_part_decode_funcs([_H|T],N) ->
+ gen_part_decode_funcs(T,N);
+gen_part_decode_funcs([],N) ->
+ if
+ N > 0 ->
+ .emit([".",nl]);
+ true ->
+ ok
+ end.
+
+gen_part_decode_funcs(#'Externaltypereference'{module=M,type=T},
+ _TypeName,Data) ->
+ #typedef{typespec=TS} = asn1_db:dbget(M,T),
+ InnerType =
+ case TS#type.def of
+ #'ObjectClassFieldType'{type=OCFTType} ->
+ OCFTType;
+ _ ->
+ get_inner(TS#type.def)
+ end,
+ WhatKind = type(InnerType),
+ gen_part_decode_funcs(WhatKind,[T],Data);
+gen_part_decode_funcs({constructed,bif},TypeName,
+ {_Name,parts,Tag,_Type}) ->
+ emit([" case Data of",nl,
+ " L when list(L) ->",nl,
+ " 'dec_",TypeName,"'(lists:map(fun(X)->element(1,?RT_BER:decode(X)) end,L),",{asis,Tag},");",nl,
+ " _ ->",nl,
+ " [Res] = 'dec_",TypeName,"'([Data],",{asis,Tag},"),",nl,
+ " Res",nl,
+ " end"]);
+gen_part_decode_funcs(WhatKind,_TypeName,{_Name,parts,_Tag,_Type}) ->
+ throw({error,{asn1,{"only SEQUENCE OF/SET OF may have the partial incomplete directive 'parts'.",WhatKind}}});
+gen_part_decode_funcs({constructed,bif},TypeName,
+ {_Name,undecoded,Tag,_Type}) ->
+ emit([" 'dec_",TypeName,"'(Data,",{asis,Tag},")"]);
+gen_part_decode_funcs({primitive,bif},_TypeName,
+ {_Name,undecoded,Tag,Type}) ->
+ % Argument no 6 is 0, i.e. bit 6 for primitive encoding.
+ asn1ct_gen_ber_bin_v2:gen_dec_prim(ber_bin_v2,Type,"Data",Tag,[],0,", mandatory, ");
+gen_part_decode_funcs(WhatKind,_TypeName,{_,Directive,_,_}) ->
+ throw({error,{asn1,{"Not implemented yet",WhatKind," partial incomplete directive:",Directive}}}).
+
+gen_types(Erules,Tname,{RootList,ExtList}) when list(RootList) ->
+ gen_types(Erules,Tname,RootList),
+ gen_types(Erules,Tname,ExtList);
+gen_types(Erules,Tname,[{'EXTENSIONMARK',_,_}|Rest]) ->
+ gen_types(Erules,Tname,Rest);
+gen_types(Erules,Tname,[ComponentType|Rest]) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ asn1ct_name:clear(),
+ Rtmod:gen_encode(Erules,Tname,ComponentType),
+ asn1ct_name:clear(),
+ Rtmod:gen_decode(Erules,Tname,ComponentType),
+ gen_types(Erules,Tname,Rest);
+gen_types(_,_,[]) ->
+ true;
+gen_types(Erules,Tname,Type) when record(Type,type) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ asn1ct_name:clear(),
+ Rtmod:gen_encode(Erules,Tname,Type),
+ asn1ct_name:clear(),
+ Rtmod:gen_decode(Erules,Tname,Type).
+
+gen_value_match(Module) ->
+ case get(value_match) of
+ {true,Module} ->
+ emit(["value_match([{Index,Cname}|Rest],Value) ->",nl,
+ " Value2 =",nl,
+ " case element(Index,Value) of",nl,
+ " {Cname,Val2} -> Val2;",nl,
+ " X -> X",nl,
+ " end,",nl,
+ " value_match(Rest,Value2);",nl,
+ "value_match([],Value) ->",nl,
+ " Value.",nl]);
+ _ -> ok
+ end,
+ put(value_match,undefined).
+
+gen_check_defaultval(Erules,Module,[{Name,Type}|Rest]) ->
+ gen_check_func(Name,Type),
+ gen_check_defaultval(Erules,Module,Rest);
+gen_check_defaultval(_,_,[]) ->
+ ok.
+
+gen_check_func(Name,FType = #type{def=Def}) ->
+ emit({Name,"(V,asn1_DEFAULT) ->",nl," true;",nl}),
+ emit({Name,"(V,V) ->",nl," true;",nl}),
+ emit({Name,"(V,{_,V}) ->",nl," true;",nl}),
+ case Def of
+ {'SEQUENCE OF',Type} ->
+ gen_check_sof(Name,'SEQOF',Type);
+ {'SET OF',Type} ->
+ gen_check_sof(Name,'SETOF',Type);
+ #'SEQUENCE'{components=Components} ->
+ gen_check_sequence(Name,Components);
+ #'SET'{components=Components} ->
+ gen_check_sequence(Name,Components);
+ {'CHOICE',Components} ->
+ gen_check_choice(Name,Components);
+ #'Externaltypereference'{type=T} ->
+ emit({Name,"(DefaultValue,Value) ->",nl}),
+ emit({" ",list2name([T,check]),"(DefaultValue,Value).",nl});
+ MaybePrim ->
+ InnerType = get_inner(MaybePrim),
+ case type(InnerType) of
+ {primitive,bif} ->
+ emit({Name,"(DefaultValue,Value) ->",nl," "}),
+ gen_prim_check_call(InnerType,"DefaultValue","Value",
+ FType),
+ emit({".",nl,nl});
+ _ ->
+ throw({asn1_error,{unknown,type,MaybePrim}})
+ end
+ end.
+
+gen_check_sof(Name,SOF,Type) ->
+ NewName = list2name([sorted,Name]),
+ emit({Name,"(V1,V2) ->",nl}),
+ emit({" ",NewName,"(lists:sort(V1),lists:sort(V2)).",nl,nl}),
+ emit({NewName,"([],[]) ->",nl," true;",nl}),
+ emit({NewName,"([DV|DVs],[V|Vs]) ->",nl," "}),
+ InnerType = get_inner(Type#type.def),
+ case type(InnerType) of
+ {primitive,bif} ->
+ gen_prim_check_call(InnerType,"DV","V",Type),
+ emit({",",nl});
+ {constructed,bif} ->
+ emit({list2name([SOF,Name]),"(DV, V),",nl});
+ #'Externaltypereference'{type=T} ->
+ emit({list2name([T,check]),"(DV,V),",nl})
+ end,
+ emit({" ",NewName,"(DVs,Vs).",nl,nl}).
+
+gen_check_sequence(Name,Components) ->
+ emit({Name,"(DefaultValue,Value) ->",nl}),
+ gen_check_sequence(Name,Components,1).
+gen_check_sequence(Name,[#'ComponentType'{name=N,typespec=Type}|Cs],Num) ->
+ InnerType = get_inner(Type#type.def),
+% NthDefV = lists:concat(["lists:nth(",Num,",DefaultValue)"]),
+ NthDefV = ["element(",Num+1,",DefaultValue)"],
+% NthV = lists:concat(["lists:nth(",Num,",Value)"]),
+ NthV = ["element(",Num+1,",Value)"],
+ gen_check_func_call(Name,Type,InnerType,NthDefV,NthV,N),
+ case Cs of
+ [] ->
+ emit({".",nl,nl});
+ _ ->
+ emit({",",nl}),
+ gen_check_sequence(Name,Cs,Num+1)
+ end;
+gen_check_sequence(_,[],_) ->
+ ok.
+
+gen_check_choice(Name,CList=[#'ComponentType'{}|_Cs]) ->
+ emit({Name,"({Id,DefaultValue},{Id,Value}) ->",nl}),
+ emit({" case Id of",nl}),
+ gen_check_choice_components(Name,CList,1).
+
+gen_check_choice_components(_,[],_)->
+ ok;
+gen_check_choice_components(Name,[#'ComponentType'{name=N,typespec=Type}|
+ Cs],Num) ->
+ Ind6 = " ",
+ InnerType = get_inner(Type#type.def),
+% DefVal = ["element(2,lists:nth(",Num,",DefaultValue))"],
+ emit({Ind6,N," ->",nl,Ind6}),
+ gen_check_func_call(Name,Type,InnerType,{var,"defaultValue"},
+ {var,"value"},N),
+ case Cs of
+ [] ->
+ emit({nl," end.",nl,nl});
+ _ ->
+ emit({";",nl}),
+ gen_check_choice_components(Name,Cs,Num+1)
+ end.
+
+gen_check_func_call(Name,Type,InnerType,DefVal,Val,N) ->
+ case type(InnerType) of
+ {primitive,bif} ->
+ emit(" "),
+ gen_prim_check_call(InnerType,DefVal,Val,Type);
+ #'Externaltypereference'{type=T} ->
+ emit({" ",list2name([T,check]),"(",DefVal,",",Val,")"});
+ _ ->
+ emit({" ",list2name([N,Name]),"(",DefVal,",",Val,")"})
+ end.
+
+
+%% VARIOUS GENERATOR STUFF
+%% *************************************************
+%%**************************************************
+
+mk_var(X) when atom(X) ->
+ list_to_atom(mk_var(atom_to_list(X)));
+
+mk_var([H|T]) ->
+ [H-32|T].
+
+%% Since hyphens are allowed in ASN.1 names, it may occur in a
+%% variable to. Turn a hyphen into a under-score sign.
+un_hyphen_var(X) when atom(X) ->
+ list_to_atom(un_hyphen_var(atom_to_list(X)));
+un_hyphen_var([45|T]) ->
+ [95|un_hyphen_var(T)];
+un_hyphen_var([H|T]) ->
+ [H|un_hyphen_var(T)];
+un_hyphen_var([]) ->
+ [].
+
+%% Generate value functions ***************
+%% ****************************************
+%% Generates a function 'V'/0 for each Value V defined in the ASN.1 module
+%% the function returns the value in an Erlang representation which can be
+%% used as input to the runtime encode functions
+
+gen_value(Value) when record(Value,valuedef) ->
+%% io:format(" ~w ",[Value#valuedef.name]),
+ emit({"'",Value#valuedef.name,"'() ->",nl}),
+ V = Value#valuedef.value,
+ emit([{asis,V},".",nl,nl]).
+
+gen_encode_constructed(Erules,Typename,InnerType,D) when record(D,type) ->
+
+ Rtmod = list_to_atom(lists:concat(["asn1ct_constructed_",erule(Erules)])),
+ case InnerType of
+ 'SET' ->
+ Rtmod:gen_encode_set(Erules,Typename,D),
+ #'SET'{components=Components} = D#type.def,
+ gen_types(Erules,Typename,Components);
+ 'SEQUENCE' ->
+ Rtmod:gen_encode_sequence(Erules,Typename,D),
+ #'SEQUENCE'{components=Components} = D#type.def,
+ gen_types(Erules,Typename,Components);
+ 'CHOICE' ->
+ Rtmod:gen_encode_choice(Erules,Typename,D),
+ {_,Components} = D#type.def,
+ gen_types(Erules,Typename,Components);
+ 'SEQUENCE OF' ->
+ Rtmod:gen_encode_sof(Erules,Typename,InnerType,D),
+ {_,Type} = D#type.def,
+ NameSuffix = asn1ct_gen:constructed_suffix(InnerType,Type#type.def),
+ gen_types(Erules,[NameSuffix|Typename],Type);
+ 'SET OF' ->
+ Rtmod:gen_encode_sof(Erules,Typename,InnerType,D),
+ {_,Type} = D#type.def,
+ NameSuffix = asn1ct_gen:constructed_suffix(InnerType,Type#type.def),
+ gen_types(Erules,[NameSuffix|Typename],Type);
+ _ ->
+ exit({nyi,InnerType})
+ end;
+gen_encode_constructed(Erules,Typename,InnerType,D)
+ when record(D,typedef) ->
+ gen_encode_constructed(Erules,Typename,InnerType,D#typedef.typespec).
+
+gen_decode_constructed(Erules,Typename,InnerType,D) when record(D,type) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_constructed_",erule(Erules)])),
+ asn1ct:step_in_constructed(), %% updates namelist for incomplete
+ %% partial decode
+ case InnerType of
+ 'SET' ->
+ Rtmod:gen_decode_set(Erules,Typename,D);
+ 'SEQUENCE' ->
+ Rtmod:gen_decode_sequence(Erules,Typename,D);
+ 'CHOICE' ->
+ Rtmod:gen_decode_choice(Erules,Typename,D);
+ 'SEQUENCE OF' ->
+ Rtmod:gen_decode_sof(Erules,Typename,InnerType,D);
+ 'SET OF' ->
+ Rtmod:gen_decode_sof(Erules,Typename,InnerType,D);
+ _ ->
+ exit({nyi,InnerType})
+ end;
+
+
+gen_decode_constructed(Erules,Typename,InnerType,D) when record(D,typedef) ->
+ gen_decode_constructed(Erules,Typename,InnerType,D#typedef.typespec).
+
+
+pgen_exports(Erules,_Module,{Types,Values,_,_,Objects,ObjectSets}) ->
+ emit({"-export([encoding_rule/0]).",nl}),
+ case Types of
+ [] -> ok;
+ _ ->
+ emit({"-export([",nl}),
+ case Erules of
+ ber ->
+ gen_exports1(Types,"enc_",2);
+ ber_bin ->
+ gen_exports1(Types,"enc_",2);
+ ber_bin_v2 ->
+ gen_exports1(Types,"enc_",2);
+ _ ->
+ gen_exports1(Types,"enc_",1)
+ end,
+ emit({"-export([",nl}),
+ gen_exports1(Types,"dec_",2),
+ case Erules of
+ ber ->
+ emit({"-export([",nl}),
+ gen_exports1(Types,"dec_",3);
+ ber_bin ->
+ emit({"-export([",nl}),
+ gen_exports1(Types,"dec_",3);
+ ber_bin_v2 ->
+ emit({"-export([",nl}),
+ gen_exports1(Types,"dec_",2);
+ _ -> ok
+ end
+ end,
+ case Values of
+ [] -> ok;
+ _ ->
+ emit({"-export([",nl}),
+ gen_exports1(Values,"",0)
+ end,
+ case Objects of
+ [] -> ok;
+ _ ->
+ case erule(Erules) of
+ per ->
+ emit({"-export([",nl}),
+ gen_exports1(Objects,"enc_",3),
+ emit({"-export([",nl}),
+ gen_exports1(Objects,"dec_",4);
+ ber_bin_v2 ->
+ emit({"-export([",nl}),
+ gen_exports1(Objects,"enc_",3),
+ emit({"-export([",nl}),
+ gen_exports1(Objects,"dec_",3);
+ _ ->
+ emit({"-export([",nl}),
+ gen_exports1(Objects,"enc_",4),
+ emit({"-export([",nl}),
+ gen_exports1(Objects,"dec_",4)
+ end
+ end,
+ case ObjectSets of
+ [] -> ok;
+ _ ->
+ emit({"-export([",nl}),
+ gen_exports1(ObjectSets,"getenc_",2),
+ emit({"-export([",nl}),
+ gen_exports1(ObjectSets,"getdec_",2)
+ end,
+ emit({"-export([info/0]).",nl}),
+ gen_partial_inc_decode_exports(),
+ emit({nl,nl}).
+
+gen_exports1([F1,F2|T],Prefix,Arity) ->
+ emit({"'",Prefix,F1,"'/",Arity,com,nl}),
+ gen_exports1([F2|T],Prefix,Arity);
+gen_exports1([Flast|_T],Prefix,Arity) ->
+ emit({"'",Prefix,Flast,"'/",Arity,nl,"]).",nl,nl}).
+
+gen_partial_inc_decode_exports() ->
+ case {asn1ct:read_config_data(partial_incomplete_decode),
+ asn1ct:get_gen_state_field(inc_type_pattern)} of
+ {undefined,_} ->
+ ok;
+ {_,undefined} ->
+ ok;
+ {Data,_} ->
+ gen_partial_inc_decode_exports(Data),
+ emit("-export([decode_part/2]).")
+ end.
+gen_partial_inc_decode_exports([]) ->
+ ok;
+gen_partial_inc_decode_exports([{Name,_,_}|Rest]) ->
+ emit(["-export([",Name,"/1"]),
+ gen_partial_inc_decode_exports1(Rest);
+gen_partial_inc_decode_exports([_|Rest]) ->
+ gen_partial_inc_decode_exports(Rest).
+
+gen_partial_inc_decode_exports1([]) ->
+ emit(["]).",nl]);
+gen_partial_inc_decode_exports1([{Name,_,_}|Rest]) ->
+ emit([", ",Name,"/1"]),
+ gen_partial_inc_decode_exports1(Rest);
+gen_partial_inc_decode_exports1([_|Rest]) ->
+ gen_partial_inc_decode_exports1(Rest).
+
+pgen_dispatcher(Erules,_Module,{[],_Values,_,_,_Objects,_ObjectSets}) ->
+ emit(["encoding_rule() ->",nl]),
+ emit([{asis,Erules},".",nl,nl]);
+pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) ->
+ emit(["-export([encode/2,decode/2,encode_disp/2,decode_disp/2]).",nl,nl]),
+ emit(["encoding_rule() ->",nl]),
+ emit([" ",{asis,Erules},".",nl,nl]),
+ Call = case Erules of
+ per -> "?RT_PER:complete(encode_disp(Type,Data))";
+ per_bin -> "?RT_PER:complete(encode_disp(Type,Data))";
+ ber -> "encode_disp(Type,Data)";
+ ber_bin -> "encode_disp(Type,Data)";
+ ber_bin_v2 -> "encode_disp(Type,Data)"
+ end,
+ EncWrap = case Erules of
+ ber -> "wrap_encode(Bytes)";
+ _ -> "Bytes"
+ end,
+ emit(["encode(Type,Data) ->",nl,
+ "case catch ",Call," of",nl,
+ " {'EXIT',{error,Reason}} ->",nl,
+ " {error,Reason};",nl,
+ " {'EXIT',Reason} ->",nl,
+ " {error,{asn1,Reason}};",nl,
+ " {Bytes,_Len} ->",nl,
+ " {ok,",EncWrap,"};",nl,
+ " Bytes ->",nl,
+ " {ok,",EncWrap,"}",nl,
+ "end.",nl,nl]),
+
+ case Erules of
+ ber_bin_v2 ->
+ emit(["decode(Type,Data0) ->",nl]),
+ emit(["{Data,_RestBin} = ?RT_BER:decode(Data0",driver_parameter(),"),",nl]);
+ _ ->
+ emit(["decode(Type,Data) ->",nl])
+ end,
+ DecWrap = case Erules of
+ ber -> "wrap_decode(Data)";
+ _ -> "Data"
+ end,
+
+ emit(["case catch decode_disp(Type,",DecWrap,") of",nl,
+ " {'EXIT',{error,Reason}} ->",nl,
+ " {error,Reason};",nl,
+ " {'EXIT',Reason} ->",nl,
+ " {error,{asn1,Reason}};",nl]),
+ case Erules of
+ ber_bin_v2 ->
+ emit([" Result ->",nl,
+ " {ok,Result}",nl]);
+ _ ->
+ emit([" {X,_Rest} ->",nl,
+ " {ok,X};",nl,
+ " {X,_Rest,_Len} ->",nl,
+ " {ok,X}",nl])
+ end,
+ emit(["end.",nl,nl]),
+
+ gen_decode_partial_incomplete(Erules),
+
+ case Types of
+ [] -> ok;
+ _ ->
+ case Erules of
+ ber ->
+ gen_dispatcher(Types,"encode_disp","enc_",",[]"),
+ gen_dispatcher(Types,"decode_disp","dec_",",mandatory");
+ ber_bin ->
+ gen_dispatcher(Types,"encode_disp","enc_",",[]"),
+ gen_dispatcher(Types,"decode_disp","dec_",",mandatory");
+ ber_bin_v2 ->
+ gen_dispatcher(Types,"encode_disp","enc_",""),
+ gen_dispatcher(Types,"decode_disp","dec_",""),
+ gen_partial_inc_dispatcher();
+ _PerOrPer_bin ->
+ gen_dispatcher(Types,"encode_disp","enc_",""),
+ gen_dispatcher(Types,"decode_disp","dec_",",mandatory")
+ end,
+ emit([nl])
+ end,
+ case Erules of
+ ber ->
+ gen_wrapper();
+ _ -> ok
+ end,
+ emit({nl,nl}).
+
+
+gen_decode_partial_incomplete(Erule) when Erule == ber;Erule==ber_bin;
+ Erule==ber_bin_v2 ->
+ case {asn1ct:read_config_data(partial_incomplete_decode),
+ asn1ct:get_gen_state_field(inc_type_pattern)} of
+ {undefined,_} ->
+ ok;
+ {_,undefined} ->
+ ok;
+ _ ->
+ case Erule of
+ ber_bin_v2 ->
+ EmitCaseClauses =
+ fun() ->
+ emit([" {'EXIT',{error,Reason}} ->",nl,
+ " {error,Reason};",nl,
+ " {'EXIT',Reason} ->",nl,
+ " {error,{asn1,Reason}};",nl,
+ " Result ->",nl,
+ " {ok,Result}",nl,
+ " end.",nl,nl])
+ end,
+ emit(["decode_partial_incomplete(Type,Data0,",
+ "Pattern) ->",nl]),
+ emit([" {Data,_RestBin} =",nl,
+ " ?RT_BER:decode_primitive_",
+ "incomplete(Pattern,Data0),",nl,
+ " case catch decode_partial_inc_disp(Type,",
+ "Data) of",nl]),
+ EmitCaseClauses(),
+ emit(["decode_part(Type,Data0) ->",nl,
+ " {Data,_RestBin} = ?RT_BER:decode(Data0),",nl,
+ " case catch decode_inc_disp(Type,Data) of",nl]),
+ EmitCaseClauses();
+ _ -> ok % add later
+ end
+ end;
+gen_decode_partial_incomplete(_Erule) ->
+ ok.
+
+gen_partial_inc_dispatcher() ->
+ case {asn1ct:read_config_data(partial_incomplete_decode),
+ asn1ct:get_gen_state_field(inc_type_pattern)} of
+ {undefined,_} ->
+ ok;
+ {_,undefined} ->
+ ok;
+ {Data,_} ->
+ gen_partial_inc_dispatcher(Data)
+ end.
+gen_partial_inc_dispatcher([{_FuncName,TopType,_Pattern}|Rest]) ->
+ emit(["decode_partial_inc_disp(",{asis,TopType},",Data) ->",nl,
+ " ",{asis,list_to_atom(lists:concat([dec,"-inc-",TopType]))},
+ "(Data);",nl]),
+ gen_partial_inc_dispatcher(Rest);
+gen_partial_inc_dispatcher([]) ->
+ emit(["decode_partial_inc_disp(Type,_Data) ->",nl,
+ " exit({error,{asn1,{undefined_type,Type}}}).",nl]).
+
+driver_parameter() ->
+ Options = get(encoding_options),
+ case lists:member(driver,Options) of
+ true ->
+ ",driver";
+ _ -> ""
+ end.
+
+gen_wrapper() ->
+ emit(["wrap_encode(Bytes) when list(Bytes) ->",nl,
+ " binary_to_list(list_to_binary(Bytes));",nl,
+ "wrap_encode(Bytes) when binary(Bytes) ->",nl,
+ " binary_to_list(Bytes);",nl,
+ "wrap_encode(Bytes) -> Bytes.",nl,nl]),
+ emit(["wrap_decode(Bytes) when list(Bytes) ->",nl,
+ " list_to_binary(Bytes);",nl,
+ "wrap_decode(Bytes) -> Bytes.",nl]).
+
+gen_dispatcher([F1,F2|T],FuncName,Prefix,ExtraArg) ->
+ emit([FuncName,"('",F1,"',Data) -> '",Prefix,F1,"'(Data",ExtraArg,")",";",nl]),
+ gen_dispatcher([F2|T],FuncName,Prefix,ExtraArg);
+gen_dispatcher([Flast|_T],FuncName,Prefix,ExtraArg) ->
+ emit([FuncName,"('",Flast,"',Data) -> '",Prefix,Flast,"'(Data",ExtraArg,")",";",nl]),
+ emit([FuncName,"(","Type",",_Data) -> exit({error,{asn1,{undefined_type,Type}}}).",nl,nl,nl]).
+
+pgen_info(_Erules,Module) ->
+ Options = get(encoding_options),
+ emit({"info() ->",nl,
+ " [{vsn,'",asn1ct:vsn(),"'},",
+ " {module,'",Module,"'},",
+ " {options,",io_lib:format("~p",[Options]),"}].",nl}).
+
+open_hrl(OutFile,Module) ->
+ File = lists:concat([OutFile,".hrl"]),
+ Fid = fopen(File,write),
+ put(gen_file_out,Fid),
+ gen_hrlhead(Module).
+
+%% EMIT functions ************************
+%% ***************************************
+
+ % debug generation
+demit(Term) ->
+ case get(asndebug) of
+ true -> emit(Term);
+ _ ->true
+ end.
+
+ % always generation
+
+emit({external,_M,T}) ->
+ emit(T);
+
+emit({prev,Variable}) when atom(Variable) ->
+ emit({var,asn1ct_name:prev(Variable)});
+
+emit({next,Variable}) when atom(Variable) ->
+ emit({var,asn1ct_name:next(Variable)});
+
+emit({curr,Variable}) when atom(Variable) ->
+ emit({var,asn1ct_name:curr(Variable)});
+
+emit({var,Variable}) when atom(Variable) ->
+ [Head|V] = atom_to_list(Variable),
+ emit([Head-32|V]);
+
+emit({var,Variable}) ->
+ [Head|V] = Variable,
+ emit([Head-32|V]);
+
+emit({asis,What}) ->
+ format(get(gen_file_out),"~w",[What]);
+
+emit(nl) ->
+ nl(get(gen_file_out));
+
+emit(com) ->
+ emit(",");
+
+emit(tab) ->
+ put_chars(get(gen_file_out)," ");
+
+emit(What) when integer(What) ->
+ put_chars(get(gen_file_out),integer_to_list(What));
+
+emit(What) when list(What), integer(hd(What)) ->
+ put_chars(get(gen_file_out),What);
+
+emit(What) when atom(What) ->
+ put_chars(get(gen_file_out),atom_to_list(What));
+
+emit(What) when tuple(What) ->
+ emit_parts(tuple_to_list(What));
+
+emit(What) when list(What) ->
+ emit_parts(What);
+
+emit(X) ->
+ exit({'cant emit ',X}).
+
+emit_parts([]) -> true;
+emit_parts([H|T]) ->
+ emit(H),
+ emit_parts(T).
+
+format(undefined,X,Y) ->
+ io:format(X,Y);
+format(X,Y,Z) ->
+ io:format(X,Y,Z).
+
+nl(undefined) -> io:nl();
+nl(X) -> io:nl(X).
+
+put_chars(undefined,X) ->
+ io:put_chars(X);
+put_chars(Y,X) ->
+ io:put_chars(Y,X).
+
+fopen(F, Mode) ->
+ case file:open(F, [Mode]) of
+ {ok, Fd} ->
+ Fd;
+ {error, Reason} ->
+ io:format("** Can't open file ~p ~n", [F]),
+ exit({error,Reason})
+ end.
+
+pgen_hrl(Erules,Module,TypeOrVal,_Indent) ->
+ put(currmod,Module),
+ {Types,Values,Ptypes,_,_,_} = TypeOrVal,
+ Ret =
+ case pgen_hrltypes(Erules,Module,Ptypes++Types,0) of
+ 0 ->
+ case Values of
+ [] ->
+ 0;
+ _ ->
+ open_hrl(get(outfile),get(currmod)),
+ pgen_macros(Erules,Module,Values),
+ 1
+ end;
+ X ->
+ pgen_macros(Erules,Module,Values),
+ X
+ end,
+ case Ret of
+ 0 ->
+ 0;
+ Y ->
+ Fid = get(gen_file_out),
+ file:close(Fid),
+ io:format("--~p--~n",
+ [{generated,lists:concat([get(outfile),".hrl"])}]),
+ Y
+ end.
+
+pgen_macros(_,_,[]) ->
+ true;
+pgen_macros(Erules,Module,[H|T]) ->
+ Valuedef = asn1_db:dbget(Module,H),
+ gen_macro(Valuedef),
+ pgen_macros(Erules,Module,T).
+
+pgen_hrltypes(_,_,[],NumRecords) ->
+ NumRecords;
+pgen_hrltypes(Erules,Module,[H|T],NumRecords) ->
+% io:format("records = ~p~n",NumRecords),
+ Typedef = asn1_db:dbget(Module,H),
+ AddNumRecords = gen_record(Typedef,NumRecords),
+ pgen_hrltypes(Erules,Module,T,NumRecords+AddNumRecords).
+
+
+%% Generates a macro for value Value defined in the ASN.1 module
+gen_macro(Value) when record(Value,valuedef) ->
+ emit({"-define('",Value#valuedef.name,"', ",
+ {asis,Value#valuedef.value},").",nl}).
+
+%% Generate record functions **************
+%% Generates an Erlang record for each named and unnamed SEQUENCE and SET in the ASN.1
+%% module. If no SEQUENCE or SET is found there is no .hrl file generated
+
+
+gen_record(Tdef,NumRecords) when record(Tdef,typedef) ->
+ Name = [Tdef#typedef.name],
+ Type = Tdef#typedef.typespec,
+ gen_record(type,Name,Type,NumRecords);
+
+gen_record(Tdef,NumRecords) when record(Tdef,ptypedef) ->
+ Name = [Tdef#ptypedef.name],
+ Type = Tdef#ptypedef.typespec,
+ gen_record(ptype,Name,Type,NumRecords).
+
+gen_record(TorPtype,Name,[#'ComponentType'{name=Cname,typespec=Type}|T],Num) ->
+ Num2 = gen_record(TorPtype,[Cname|Name],Type,Num),
+ gen_record(TorPtype,Name,T,Num2);
+gen_record(TorPtype,Name,{Clist1,Clist2},Num) when list(Clist1), list(Clist2) ->
+ gen_record(TorPtype,Name,Clist1++Clist2,Num);
+gen_record(TorPtype,Name,[_|T],Num) -> % skip EXTENSIONMARK
+ gen_record(TorPtype,Name,T,Num);
+gen_record(_TorPtype,_Name,[],Num) ->
+ Num;
+
+gen_record(TorPtype,Name,Type,Num) when record(Type,type) ->
+ Def = Type#type.def,
+ Rec = case Def of
+ Seq when record(Seq,'SEQUENCE') ->
+ case Seq#'SEQUENCE'.pname of
+ false ->
+ {record,Seq#'SEQUENCE'.components};
+ _Pname when TorPtype == type ->
+ false;
+ _ ->
+ {record,Seq#'SEQUENCE'.components}
+ end;
+ Set when record(Set,'SET') ->
+ case Set#'SET'.pname of
+ false ->
+ {record,Set#'SET'.components};
+ _Pname when TorPtype == type ->
+ false;
+ _ ->
+ {record,Set#'SET'.components}
+ end;
+% {'SET',{_,_CompList}} ->
+% {record,_CompList};
+ {'CHOICE',_CompList} -> {inner,Def};
+ {'SEQUENCE OF',_CompList} -> {['SEQOF'|Name],Def};
+ {'SET OF',_CompList} -> {['SETOF'|Name],Def};
+ _ -> false
+ end,
+ case Rec of
+ false -> Num;
+ {record,CompList} ->
+ case Num of
+ 0 -> open_hrl(get(outfile),get(currmod));
+ _ -> true
+ end,
+ emit({"-record('",list2name(Name),"',{",nl}),
+ RootList = case CompList of
+ _ when list(CompList) ->
+ CompList;
+ {_Rl,_} -> _Rl
+ end,
+ gen_record2(Name,'SEQUENCE',RootList),
+ NewCompList =
+ case CompList of
+ {CompList1,[]} ->
+ emit({"}). % with extension mark",nl,nl}),
+ CompList1;
+ {Tr,ExtensionList2} ->
+ case Tr of
+ [] -> true;
+ _ -> emit({",",nl})
+ end,
+ emit({"%% with extensions",nl}),
+ gen_record2(Name, 'SEQUENCE', ExtensionList2,
+ "", ext),
+ emit({"}).",nl,nl}),
+ Tr ++ ExtensionList2;
+ _ ->
+ emit({"}).",nl,nl}),
+ CompList
+ end,
+ gen_record(TorPtype,Name,NewCompList,Num+1);
+ {inner,{'CHOICE', CompList}} ->
+ gen_record(TorPtype,Name,CompList,Num);
+ {NewName,{_, CompList}} ->
+ gen_record(TorPtype,NewName,CompList,Num)
+ end;
+gen_record(_,_,_,NumRecords) -> % skip CLASS etc for now.
+ NumRecords.
+
+gen_head(Erules,Mod,Hrl) ->
+ {Rtmac,Rtmod} = case Erules of
+ per ->
+ emit({"%% Generated by the Erlang ASN.1 PER-"
+ "compiler version:",asn1ct:vsn(),nl}),
+ {"RT_PER",?RT_PER};
+ ber ->
+ emit({"%% Generated by the Erlang ASN.1 BER-"
+ "compiler version:",asn1ct:vsn(),nl}),
+ {"RT_BER",?RT_BER_BIN};
+ per_bin ->
+ emit({"%% Generated by the Erlang ASN.1 BER-"
+ "compiler version, utilizing bit-syntax:",
+ asn1ct:vsn(),nl}),
+ %% temporary code to enable rt2ct optimization
+ Options = get(encoding_options),
+ case lists:member(optimize,Options) of
+ true -> {"RT_PER","asn1rt_per_bin_rt2ct"};
+ _ ->
+ {"RT_PER",?RT_PER_BIN}
+ end;
+ ber_bin ->
+ emit({"%% Generated by the Erlang ASN.1 BER-"
+ "compiler version, utilizing bit-syntax:",
+ asn1ct:vsn(),nl}),
+ {"RT_BER",?RT_BER_BIN};
+ ber_bin_v2 ->
+ emit({"%% Generated by the Erlang ASN.1 BER_V2-"
+ "compiler version, utilizing bit-syntax:",
+ asn1ct:vsn(),nl}),
+ {"RT_BER","asn1rt_ber_bin_v2"}
+ end,
+ emit({"%% Purpose: encoder and decoder to the types in mod ",Mod,nl,nl}),
+ emit({"-module('",Mod,"').",nl}),
+ put(currmod,Mod),
+ %emit({"-compile(export_all).",nl}),
+ case Hrl of
+ 0 -> true;
+ _ ->
+ emit({"-include(\"",Mod,".hrl\").",nl})
+ end,
+ emit(["-define('",Rtmac,"',",Rtmod,").",nl]).
+
+
+gen_hrlhead(Mod) ->
+ emit({"%% Generated by the Erlang ASN.1 compiler version:",asn1ct:vsn(),nl}),
+ emit({"%% Purpose: Erlang record definitions for each named and unnamed",nl}),
+ emit({"%% SEQUENCE and SET, and macro definitions for each value",nl}),
+ emit({"%% definition,in module ",Mod,nl,nl}),
+ emit({nl,nl}).
+
+gen_record2(Name,SeqOrSet,Comps) ->
+ gen_record2(Name,SeqOrSet,Comps,"",noext).
+
+gen_record2(_Name,_SeqOrSet,[],_Com,_Extension) ->
+ true;
+gen_record2(Name,SeqOrSet,[{'EXTENSIONMARK',_,_}|T],Com,Extension) ->
+ gen_record2(Name,SeqOrSet,T,Com,Extension);
+gen_record2(_Name,_SeqOrSet,[H],Com,Extension) ->
+ #'ComponentType'{name=Cname} = H,
+ emit(Com),
+ emit({asis,Cname}),
+ gen_record_default(H, Extension);
+gen_record2(Name,SeqOrSet,[H|T],Com, Extension) ->
+ #'ComponentType'{name=Cname} = H,
+ emit(Com),
+ emit({asis,Cname}),
+ gen_record_default(H, Extension),
+% emit(", "),
+ gen_record2(Name,SeqOrSet,T,", ", Extension).
+
+%gen_record_default(C, ext) ->
+% emit(" = asn1_NOEXTVALUE");
+gen_record_default(#'ComponentType'{prop='OPTIONAL'}, _)->
+ emit(" = asn1_NOVALUE");
+gen_record_default(#'ComponentType'{prop={'DEFAULT',_}}, _)->
+ emit(" = asn1_DEFAULT");
+gen_record_default(_, _) ->
+ true.
+
+gen_check_call(TopType,Cname,Type,InnerType,WhatKind,DefaultValue,Element) ->
+ case WhatKind of
+ {primitive,bif} ->
+ gen_prim_check_call(InnerType,DefaultValue,Element,Type);
+ #'Externaltypereference'{module=M,type=T} ->
+ %% generate function call
+ Name = list2name([T,check]),
+ emit({"'",Name,"'(",DefaultValue,", ",Element,")"}),
+ %% insert in ets table and do look ahead check
+ Typedef = asn1_db:dbget(M,T),
+ RefType = Typedef#typedef.typespec,
+ InType = asn1ct_gen:get_inner(RefType#type.def),
+ case insert_once(check_functions,{Name,RefType}) of
+ true ->
+ lookahead_innertype([T],InType,RefType);
+% case asn1ct_gen:type(InType) of
+% {constructed,bif} ->
+% lookahead_innertype([T],InType,RefType);
+% #'Externaltypereference'{type=TNew} ->
+% lookahead_innertype([TNew],InType,RefType);
+% _ ->
+% ok
+% end;
+ _ ->
+ ok
+ end;
+ {constructed,bif} ->
+ NameList = [Cname|TopType],
+ Name = list2name(NameList ++ [check]),
+ emit({"'",Name,"'(",DefaultValue,", ",Element,")"}),
+ ets:insert(check_functions,{Name,Type}),
+ %% Must look for check functions in InnerType,
+ %% that may be referenced or internal defined
+ %% constructed types not used elsewhere.
+ lookahead_innertype(NameList,InnerType,Type)
+ end.
+
+gen_prim_check_call(PrimType,DefaultValue,Element,Type) ->
+ case unify_if_string(PrimType) of
+ 'BOOLEAN' ->
+ emit({"asn1rt_check:check_bool(",DefaultValue,", ",
+ Element,")"});
+ 'INTEGER' ->
+ NNL =
+ case Type#type.def of
+ {_,NamedNumberList} -> NamedNumberList;
+ _ -> []
+ end,
+ emit({"asn1rt_check:check_int(",DefaultValue,", ",
+ Element,", ",{asis,NNL},")"});
+ 'BIT STRING' ->
+ {_,NBL} = Type#type.def,
+ emit({"asn1rt_check:check_bitstring(",DefaultValue,", ",
+ Element,", ",{asis,NBL},")"});
+ 'OCTET STRING' ->
+ emit({"asn1rt_check:check_octetstring(",DefaultValue,", ",
+ Element,")"});
+ 'NULL' ->
+ emit({"asn1rt_check:check_null(",DefaultValue,", ",
+ Element,")"});
+ 'OBJECT IDENTIFIER' ->
+ emit({"asn1rt_check:check_objectidentifier(",DefaultValue,
+ ", ",Element,")"});
+ 'ObjectDescriptor' ->
+ emit({"asn1rt_check:check_objectdescriptor(",DefaultValue,
+ ", ",Element,")"});
+ 'REAL' ->
+ emit({"asn1rt_check:check_real(",DefaultValue,
+ ", ",Element,")"});
+ 'ENUMERATED' ->
+ {_,Enumerations} = Type#type.def,
+ emit({"asn1rt_check:check_enum(",DefaultValue,
+ ", ",Element,", ",{asis,Enumerations},")"});
+ restrictedstring ->
+ emit({"asn1rt_check:check_restrictedstring(",DefaultValue,
+ ", ",Element,")"})
+ end.
+
+%% lokahead_innertype/3 traverses Type and checks if check functions
+%% have to be generated, i.e. for all constructed or referenced types.
+lookahead_innertype(Name,'SEQUENCE',Type) ->
+ Components = (Type#type.def)#'SEQUENCE'.components,
+ lookahead_components(Name,Components);
+lookahead_innertype(Name,'SET',Type) ->
+ Components = (Type#type.def)#'SET'.components,
+ lookahead_components(Name,Components);
+lookahead_innertype(Name,'CHOICE',Type) ->
+ {_,Components} = Type#type.def,
+ lookahead_components(Name,Components);
+lookahead_innertype(Name,'SEQUENCE OF',SeqOf) ->
+ lookahead_sof(Name,'SEQOF',SeqOf);
+lookahead_innertype(Name,'SET OF',SeqOf) ->
+ lookahead_sof(Name,'SETOF',SeqOf);
+lookahead_innertype(_Name,#'Externaltypereference'{module=M,type=T},_) ->
+ Typedef = asn1_db:dbget(M,T),
+ RefType = Typedef#typedef.typespec,
+ InType = asn1ct_gen:get_inner(RefType#type.def),
+ case type(InType) of
+ {constructed,bif} ->
+ NewName = list2name([T,check]),
+ case insert_once(check_functions,{NewName,RefType}) of
+ true ->
+ lookahead_innertype([T],InType,RefType);
+ _ ->
+ ok
+ end;
+ #'Externaltypereference'{} ->
+ NewName = list2name([T,check]),
+ case insert_once(check_functions,{NewName,RefType}) of
+ true ->
+ lookahead_innertype([T],InType,RefType);
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end;
+% case insert_once(check_functions,{list2name(Name++[check]),Type}) of
+% true ->
+% InnerType = asn1ct_gen:get_inner(Type#type.def),
+% case asn1ct_gen:type(InnerType) of
+% {constructed,bif} ->
+% lookahead_innertype([T],InnerType,Type);
+% #'Externaltypereference'{type=TNew} ->
+% lookahead_innertype([TNew],InnerType,Type);
+% _ ->
+% ok
+% end;
+% _ ->
+% ok
+% end;
+lookahead_innertype(_,_,_) ->
+ ok.
+
+lookahead_components(_,[]) -> ok;
+lookahead_components(Name,[C|Cs]) ->
+ #'ComponentType'{name=Cname,typespec=Type} = C,
+ InType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InType) of
+ {constructed,bif} ->
+ case insert_once(check_functions,
+ {list2name([Cname|Name] ++ [check]),Type}) of
+ true ->
+ lookahead_innertype([Cname|Name],InType,Type);
+ _ ->
+ ok
+ end;
+ #'Externaltypereference'{module=RefMod,type=RefName} ->
+ Typedef = asn1_db:dbget(RefMod,RefName),
+ RefType = Typedef#typedef.typespec,
+ case insert_once(check_functions,{list2name([RefName,check]),
+ RefType}) of
+ true ->
+ lookahead_innertype([RefName],InType,RefType);
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end,
+ lookahead_components(Name,Cs).
+
+lookahead_sof(Name,SOF,SOFType) ->
+ Type = case SOFType#type.def of
+ {_,_Type} -> _Type;
+ _Type -> _Type
+ end,
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ %% this is if a constructed type is defined in
+ %% the SEQUENCE OF type
+ NameList = [SOF|Name],
+ insert_once(check_functions,
+ {list2name(NameList ++ [check]),Type}),
+ lookahead_innertype(NameList,InnerType,Type);
+ #'Externaltypereference'{module=M,type=T} ->
+ Typedef = asn1_db:dbget(M,T),
+ RefType = Typedef#typedef.typespec,
+ InType = get_inner(RefType#type.def),
+ case insert_once(check_functions,
+ {list2name([T,check]),RefType}) of
+ true ->
+ lookahead_innertype([T],InType,RefType);
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end.
+
+
+insert_once(Table,Object) ->
+ case ets:lookup(Table,element(1,Object)) of
+ [] ->
+ ets:insert(Table,Object); %returns true
+ _ -> false
+ end.
+
+unify_if_string(PrimType) ->
+ case PrimType of
+ 'NumericString' ->
+ restrictedstring;
+ 'PrintableString' ->
+ restrictedstring;
+ 'TeletexString' ->
+ restrictedstring;
+ 'VideotexString' ->
+ restrictedstring;
+ 'IA5String' ->
+ restrictedstring;
+ 'UTCTime' ->
+ restrictedstring;
+ 'GeneralizedTime' ->
+ restrictedstring;
+ 'GraphicString' ->
+ restrictedstring;
+ 'VisibleString' ->
+ restrictedstring;
+ 'GeneralString' ->
+ restrictedstring;
+ 'UniversalString' ->
+ restrictedstring;
+ 'BMPString' ->
+ restrictedstring;
+ Other -> Other
+ end.
+
+
+
+
+
+get_inner(A) when atom(A) -> A;
+get_inner(Ext) when record(Ext,'Externaltypereference') -> Ext;
+get_inner(Tref) when record(Tref,typereference) -> Tref;
+get_inner({fixedtypevaluefield,_,Type}) ->
+ if
+ record(Type,type) ->
+ get_inner(Type#type.def);
+ true ->
+ get_inner(Type)
+ end;
+get_inner({typefield,TypeName}) ->
+ TypeName;
+get_inner(#'ObjectClassFieldType'{type=Type}) ->
+% get_inner(Type);
+ Type;
+get_inner(T) when tuple(T) ->
+ case element(1,T) of
+ Tuple when tuple(Tuple),element(1,Tuple) == objectclass ->
+ case catch(lists:last(element(2,T))) of
+ {valuefieldreference,FieldName} ->
+ get_fieldtype(element(2,Tuple),FieldName);
+ {typefieldreference,FieldName} ->
+ get_fieldtype(element(2,Tuple),FieldName);
+ {'EXIT',Reason} ->
+ throw({asn1,{'internal error in get_inner/1',Reason}})
+ end;
+ _ -> element(1,T)
+ end.
+
+
+
+
+
+type(X) when record(X,'Externaltypereference') ->
+ X;
+type(X) when record(X,typereference) ->
+ X;
+type('ASN1_OPEN_TYPE') ->
+ 'ASN1_OPEN_TYPE';
+type({fixedtypevaluefield,_Name,Type}) when record(Type,type) ->
+ type(get_inner(Type#type.def));
+type({typefield,_}) ->
+ 'ASN1_OPEN_TYPE';
+type(X) ->
+ %% io:format("asn1_types:type(~p)~n",[X]),
+ case catch type2(X) of
+ {'EXIT',_} ->
+ {notype,X};
+ Normal ->
+ Normal
+ end.
+
+type2(X) ->
+ case prim_bif(X) of
+ true ->
+ {primitive,bif};
+ false ->
+ case construct_bif(X) of
+ true ->
+ {constructed,bif};
+ false ->
+ {undefined,user}
+ end
+ end.
+
+prim_bif(X) ->
+ lists:member(X,['INTEGER' ,
+ 'ENUMERATED',
+ 'OBJECT IDENTIFIER',
+ 'ANY',
+ 'NULL',
+ 'BIT STRING' ,
+ 'OCTET STRING' ,
+ 'ObjectDescriptor',
+ 'NumericString',
+ 'TeletexString',
+ 'VideotexString',
+ 'UTCTime',
+ 'GeneralizedTime',
+ 'GraphicString',
+ 'VisibleString',
+ 'GeneralString',
+ 'PrintableString',
+ 'IA5String',
+ 'UniversalString',
+ 'BMPString',
+ 'ENUMERATED',
+ 'BOOLEAN']).
+
+construct_bif(T) ->
+ lists:member(T,['SEQUENCE' ,
+ 'SEQUENCE OF' ,
+ 'CHOICE' ,
+ 'SET' ,
+ 'SET OF']).
+
+def_to_tag(#tag{class=Class,number=Number}) ->
+ {Class,Number};
+def_to_tag(#'ObjectClassFieldType'{type=Type}) ->
+ case Type of
+ T when tuple(T),element(1,T)==fixedtypevaluefield ->
+ {'UNIVERSAL',get_inner(Type)};
+ _ ->
+ []
+ end;
+def_to_tag(Def) ->
+ {'UNIVERSAL',get_inner(Def)}.
+
+
+%% Information Object Class
+
+type_from_object(X) ->
+ case (catch lists:last(element(2,X))) of
+ {'EXIT',_} ->
+ {notype,X};
+ Normal ->
+ Normal
+ end.
+
+
+get_fieldtype([],_FieldName)->
+ {no_type,no_name};
+get_fieldtype([Field|Rest],FieldName) ->
+ case element(2,Field) of
+ FieldName ->
+ case element(1,Field) of
+ fixedtypevaluefield ->
+ {element(1,Field),FieldName,element(3,Field)};
+ _ ->
+ {element(1,Field),FieldName}
+ end;
+ _ ->
+ get_fieldtype(Rest,FieldName)
+ end.
+
+get_fieldcategory([],_FieldName) ->
+ no_cat;
+get_fieldcategory([Field|Rest],FieldName) ->
+ case element(2,Field) of
+ FieldName ->
+ element(1,Field);
+ _ ->
+ get_fieldcategory(Rest,FieldName)
+ end.
+
+get_typefromobject(Type) when record(Type,type) ->
+ case Type#type.def of
+ {{objectclass,_,_},TypeFrObj} when list(TypeFrObj) ->
+ {_,FieldName} = lists:last(TypeFrObj),
+ FieldName;
+ _ ->
+ {no_field}
+ end.
+
+get_classfieldcategory(Type,FieldName) ->
+ case (catch Type#type.def) of
+ {{obejctclass,Fields,_},_} ->
+ get_fieldcategory(Fields,FieldName);
+ {'EXIT',_} ->
+ no_cat;
+ _ ->
+ no_cat
+ end.
+%% Information Object Class
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Convert a list of name parts to something that can be output by emit
+%%
+%% used to output function names in generated code.
+
+list2name(L) ->
+ NewL = list2name1(L),
+ lists:concat(lists:reverse(NewL)).
+
+list2name1([{ptype,H1},H2|T]) ->
+ [H1,"_",list2name([H2|T])];
+list2name1([H1,H2|T]) ->
+ [H1,"_",list2name([H2|T])];
+list2name1([{ptype,H}|_T]) ->
+ [H];
+list2name1([H|_T]) ->
+ [H];
+list2name1([]) ->
+ [].
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Convert a list of name parts to something that can be output by emit
+%% stops at {ptype,Pname} i.e Pname whill be the first part of the name
+%% used to output record names in generated code.
+
+list2rname(L) ->
+ NewL = list2rname1(L),
+ lists:concat(lists:reverse(NewL)).
+
+list2rname1([{ptype,H1},_H2|_T]) ->
+ [H1];
+list2rname1([H1,H2|T]) ->
+ [H1,"_",list2name([H2|T])];
+list2rname1([{ptype,H}|_T]) ->
+ [H];
+list2rname1([H|_T]) ->
+ [H];
+list2rname1([]) ->
+ [].
+
+
+
+constructed_suffix(_,#'SEQUENCE'{pname=Ptypename}) when Ptypename =/= false ->
+ {ptype, Ptypename};
+constructed_suffix(_,#'SET'{pname=Ptypename}) when Ptypename =/= false ->
+ {ptype,Ptypename};
+constructed_suffix('SEQUENCE OF',_) ->
+ 'SEQOF';
+constructed_suffix('SET OF',_) ->
+ 'SETOF'.
+
+erule(ber) ->
+ ber;
+erule(ber_bin) ->
+ ber;
+erule(ber_bin_v2) ->
+ ber_bin_v2;
+erule(per) ->
+ per;
+erule(per_bin) ->
+ per.
+
+wrap_ber(ber) ->
+ ber_bin;
+wrap_ber(Erule) ->
+ Erule.
+
+rt2ct_suffix() ->
+ Options = get(encoding_options),
+ case {lists:member(optimize,Options),lists:member(per_bin,Options)} of
+ {true,true} -> "_rt2ct";
+ _ -> ""
+ end.
+rt2ct_suffix(per_bin) ->
+ Options = get(encoding_options),
+ case lists:member(optimize,Options) of
+ true -> "_rt2ct";
+ _ -> ""
+ end;
+rt2ct_suffix(_) -> "".
+
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V;
+ {value,Cnstr} ->
+ Cnstr
+ end.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen_ber.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen_ber.erl
new file mode 100644
index 0000000000..765745dc13
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen_ber.erl
@@ -0,0 +1,1525 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_gen_ber.erl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-module(asn1ct_gen_ber).
+
+%% Generate erlang module which handles (PER) encode and decode for
+%% all types in an ASN.1 module
+
+-include("asn1_records.hrl").
+
+-export([pgen/4]).
+-export([decode_class/1, decode_type/1]).
+-export([add_removed_bytes/0]).
+-export([gen_encode/2,gen_encode/3,gen_decode/2,gen_decode/3]).
+-export([gen_encode_prim/4]).
+-export([gen_dec_prim/8]).
+-export([gen_objectset_code/2, gen_obj_code/3]).
+-export([re_wrap_erule/1]).
+-export([unused_var/2]).
+
+-import(asn1ct_gen, [emit/1,demit/1]).
+
+ % the encoding of class of tag bits 8 and 7
+-define(UNIVERSAL, 0).
+-define(APPLICATION, 16#40).
+-define(CONTEXT, 16#80).
+-define(PRIVATE, 16#C0).
+
+ % primitive or constructed encoding % bit 6
+-define(PRIMITIVE, 0).
+-define(CONSTRUCTED, 2#00100000).
+
+
+-define(T_ObjectDescriptor, ?UNIVERSAL bor ?PRIMITIVE bor 7).
+ % restricted character string types
+-define(T_NumericString, ?UNIVERSAL bor ?PRIMITIVE bor 18). %can be constructed
+-define(T_PrintableString, ?UNIVERSAL bor ?PRIMITIVE bor 19). %can be constructed
+-define(T_TeletexString, ?UNIVERSAL bor ?PRIMITIVE bor 20). %can be constructed
+-define(T_VideotexString, ?UNIVERSAL bor ?PRIMITIVE bor 21). %can be constructed
+-define(T_IA5String, ?UNIVERSAL bor ?PRIMITIVE bor 22). %can be constructed
+-define(T_GraphicString, ?UNIVERSAL bor ?PRIMITIVE bor 25). %can be constructed
+-define(T_VisibleString, ?UNIVERSAL bor ?PRIMITIVE bor 26). %can be constructed
+-define(T_GeneralString, ?UNIVERSAL bor ?PRIMITIVE bor 27). %can be constructed
+
+%% pgen(Erules, Module, TypeOrVal)
+%% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module
+%% .hrl file is only generated if necessary
+%% Erules = per | ber
+%% Module = atom()
+%% TypeOrVal = {TypeList,ValueList,PTypeList}
+%% TypeList = ValueList = [atom()]
+
+pgen(OutFile,Erules,Module,TypeOrVal) ->
+ asn1ct_gen:pgen_module(OutFile,Erules,Module,TypeOrVal,true).
+
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Generate ENCODING
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% encode #{typedef, {pos, name, typespec}}
+%%===============================================================================
+
+gen_encode(Erules,Type) when record(Type,typedef) ->
+ gen_encode_user(Erules,Type).
+
+%%===============================================================================
+%% encode #{type, {tag, def, constraint}}
+%%===============================================================================
+
+gen_encode(Erules,Typename,Type) when record(Type,type) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ ObjFun =
+ case lists:keysearch(objfun,1,Type#type.tablecinf) of
+ {value,{_,_Name}} ->
+ ", ObjFun";
+ false ->
+ ""
+ end,
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ emit([nl,nl,nl,"%%================================"]),
+ emit([nl,"%% ",asn1ct_gen:list2name(Typename)]),
+ emit([nl,"%%================================",nl]),
+ case lists:member(InnerType,['SET','SEQUENCE']) of
+ true ->
+ case get(asn_keyed_list) of
+ true ->
+ CompList =
+ case Type#type.def of
+ #'SEQUENCE'{components=Cl} -> Cl;
+ #'SET'{components=Cl} -> Cl
+ end,
+ emit([nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, TagIn",ObjFun,
+ ") when list(Val) ->",nl]),
+ emit([" 'enc_",asn1ct_gen:list2name(Typename),
+ "'(?RT_BER:fixoptionals(",
+ {asis,optionals(CompList)},
+ ",Val), TagIn",ObjFun,");",nl,nl]);
+ _ -> true
+ end;
+ _ ->
+ emit([nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'({'",asn1ct_gen:list2name(Typename),
+ "',Val}, TagIn",ObjFun,") ->",nl]),
+ emit([" 'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, TagIn",ObjFun,");",nl,nl])
+ end,
+ emit(["'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, TagIn",ObjFun,") ->",nl," "]),
+ asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
+ _ ->
+ true
+ end;
+
+%%===============================================================================
+%% encode ComponentType
+%%===============================================================================
+
+gen_encode(Erules,Tname,{'ComponentType',_Pos,Cname,Type,_,_}) ->
+ NewTname = [Cname|Tname],
+ %% The tag is set to [] to avoid that it is
+ %% taken into account twice, both as a component/alternative (passed as
+ %% argument to the encode decode function and within the encode decode
+ %% function it self.
+ NewType = Type#type{tag=[]},
+ gen_encode(Erules,NewTname,NewType).
+
+gen_encode_user(Erules,D) when record(D,typedef) ->
+ Typename = [D#typedef.name],
+ Type = D#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ OTag = Type#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ emit([nl,nl,"%%================================"]),
+ emit([nl,"%% ",Typename]),
+ emit([nl,"%%================================",nl]),
+ case lists:member(InnerType,['SET','SEQUENCE']) of
+ true ->
+ case get(asn_keyed_list) of
+ true ->
+ CompList =
+ case Type#type.def of
+ #'SEQUENCE'{components=Cl} -> Cl;
+ #'SET'{components=Cl} -> Cl
+ end,
+
+ emit([nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, TagIn) when list(Val) ->",nl]),
+ emit([" 'enc_",asn1ct_gen:list2name(Typename),
+ "'(?RT_BER:fixoptionals(",
+ {asis,optionals(CompList)},
+ ",Val), TagIn);",nl,nl]);
+ _ -> true
+ end;
+ _ ->
+ emit({nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'({'",asn1ct_gen:list2name(Typename),"',Val}, TagIn) ->",nl}),
+ emit({" 'enc_",asn1ct_gen:list2name(Typename),"'(Val, TagIn);",nl,nl})
+ end,
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"'(",
+ unused_var("Val",Type#type.def),", TagIn) ->",nl}),
+ CurrentMod = get(currmod),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,D);
+ {primitive,bif} ->
+ asn1ct_gen_ber:gen_encode_prim(ber,Type,["TagIn ++ ",
+ {asis,Tag}],"Val"),
+ emit([".",nl]);
+ #typereference{val=Ename} ->
+ emit([" 'enc_",Ename,"'(Val, TagIn ++ ",{asis,Tag},").",nl]);
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'enc_",Etype,"'(Val, TagIn ++ ",
+ {asis,Tag},").",nl]);
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'enc_",Etype,"'(Val, TagIn ++ ",
+ {asis,Tag},").",nl]);
+ 'ASN1_OPEN_TYPE' ->
+ emit(["%% OPEN TYPE",nl]),
+ asn1ct_gen_ber:gen_encode_prim(ber,
+ Type#type{def='ASN1_OPEN_TYPE'},
+ ["TagIn ++ ",
+ {asis,Tag}],"Val"),
+ emit([".",nl])
+ end.
+
+unused_var(Var,#'SEQUENCE'{components=Cl}) ->
+ unused_var1(Var,Cl);
+unused_var(Var,#'SET'{components=Cl}) ->
+ unused_var1(Var,Cl);
+unused_var(Var,_) ->
+ Var.
+unused_var1(Var,Cs) when Cs == []; Cs == {[],[]} ->
+ lists:concat(["_",Var]);
+unused_var1(Var,_) ->
+ Var.
+
+unused_optormand_var(Var,Def) ->
+ case asn1ct_gen:type(asn1ct_gen:get_inner(Def)) of
+ 'ASN1_OPEN_TYPE' ->
+ lists:concat(["_",Var]);
+ _ ->
+ Var
+ end.
+
+
+gen_encode_prim(_Erules,D,DoTag,Value) when record(D,type) ->
+
+%%% Currently not used for BER (except for BitString) and therefore replaced
+%%% with [] as a placeholder
+ BitStringConstraint = D#type.constraint,
+ Constraint = [],
+ asn1ct_name:new(enumval),
+ case D#type.def of
+ 'BOOLEAN' ->
+ emit_encode_func('boolean',Value,DoTag);
+ 'INTEGER' ->
+ emit_encode_func('integer',Constraint,Value,DoTag);
+ {'INTEGER',NamedNumberList} ->
+ emit_encode_func('integer',Constraint,Value,
+ NamedNumberList,DoTag);
+ {'ENUMERATED',NamedNumberList={_,_}} ->
+
+ emit(["case (case ",Value," of {asn1_enum,_}->",Value,";{_,_}->element(2,",Value,");_->",
+ Value," end) of",nl]),
+ emit_enc_enumerated_cases(NamedNumberList,DoTag);
+ {'ENUMERATED',NamedNumberList} ->
+
+ emit(["case (case ",Value," of {_,_}->element(2,",Value,");_->",
+ Value," end) of",nl]),
+ emit_enc_enumerated_cases(NamedNumberList,DoTag);
+
+ {'BIT STRING',NamedNumberList} ->
+ emit_encode_func('bit_string',BitStringConstraint,Value,
+ NamedNumberList,DoTag);
+ 'ANY' ->
+ emit_encode_func('open_type', Value,DoTag);
+ 'NULL' ->
+ emit_encode_func('null',Value,DoTag);
+ 'OBJECT IDENTIFIER' ->
+ emit_encode_func("object_identifier",Value,DoTag);
+ 'ObjectDescriptor' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_ObjectDescriptor,DoTag);
+ 'OCTET STRING' ->
+ emit_encode_func('octet_string',Constraint,Value,DoTag);
+ 'NumericString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_NumericString,DoTag);
+ 'TeletexString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_TeletexString,DoTag);
+ 'VideotexString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_VideotexString,DoTag);
+ 'GraphicString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_GraphicString,DoTag);
+ 'VisibleString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_VisibleString,DoTag);
+ 'GeneralString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_GeneralString,DoTag);
+ 'PrintableString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_PrintableString,DoTag);
+ 'IA5String' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_IA5String,DoTag);
+ 'UniversalString' ->
+ emit_encode_func('universal_string',Constraint,Value,DoTag);
+ 'BMPString' ->
+ emit_encode_func('BMP_string',Constraint,Value,DoTag);
+ 'UTCTime' ->
+ emit_encode_func('utc_time',Constraint,Value,DoTag);
+ 'GeneralizedTime' ->
+ emit_encode_func('generalized_time',Constraint,Value,DoTag);
+ 'ASN1_OPEN_TYPE' ->
+ emit_encode_func('open_type', Value,DoTag);
+ XX ->
+ exit({'can not encode' ,XX})
+ end.
+
+
+emit_encode_func(Name,Value,Tags) when atom(Name) ->
+ emit_encode_func(atom_to_list(Name),Value,Tags);
+emit_encode_func(Name,Value,Tags) ->
+ Fname = "?RT_BER:encode_" ++ Name,
+ emit([Fname,"(",Value,", ",Tags,")"]).
+
+emit_encode_func(Name,Constraint,Value,Tags) when atom(Name) ->
+ emit_encode_func(atom_to_list(Name),Constraint,Value,Tags);
+emit_encode_func(Name,Constraint,Value,Tags) ->
+ Fname = "?RT_BER:encode_" ++ Name,
+ emit([Fname,"(",{asis,Constraint},", ",Value,", ",Tags,")"]).
+
+emit_encode_func(Name,Constraint,Value,Asis,Tags) when atom(Name) ->
+ emit_encode_func(atom_to_list(Name),Constraint,Value,Asis,Tags);
+emit_encode_func(Name,Constraint,Value,Asis,Tags) ->
+ Fname = "?RT_BER:encode_" ++ Name,
+ emit([Fname,"(",{asis,Constraint},", ",Value,
+ ", ",{asis,Asis},
+ ", ",Tags,")"]).
+
+emit_enc_enumerated_cases({L1,L2}, Tags) ->
+ emit_enc_enumerated_cases(L1++L2, Tags, ext);
+emit_enc_enumerated_cases(L, Tags) ->
+ emit_enc_enumerated_cases(L, Tags, noext).
+
+emit_enc_enumerated_cases([{EnumName,EnumVal},H2|T], Tags, Ext) ->
+ emit([{asis,EnumName}," -> ?RT_BER:encode_enumerated(",EnumVal,",",Tags,");",nl]),
+%% emit(["'",{asis,EnumName},"' -> ?RT_BER:encode_enumerated(",EnumVal,",",Tags,");",nl]),
+ emit_enc_enumerated_cases([H2|T], Tags, Ext);
+emit_enc_enumerated_cases([{EnumName,EnumVal}], Tags, Ext) ->
+ emit([{asis,EnumName}," -> ?RT_BER:encode_enumerated(",EnumVal,",",Tags,")"]),
+%% emit(["'",{asis,EnumName},"' -> ?RT_BER:encode_enumerated(",EnumVal,",",Tags,")"]),
+ case Ext of
+ noext -> emit([";",nl]);
+ ext ->
+ emit([";",nl,"{asn1_enum,",{curr,enumval},"} -> ",
+ "?RT_BER:encode_enumerated(",{curr,enumval},",",Tags,");",nl]),
+ asn1ct_name:new(enumval)
+ end,
+ emit([{curr,enumval}," -> exit({error,{asn1, {enumerated_not_in_range,",{curr, enumval},"}}})"]),
+ emit([nl,"end"]).
+
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Generate DECODING
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% decode #{typedef, {pos, name, typespec}}
+%%===============================================================================
+
+gen_decode(Erules,Type) when record(Type,typedef) ->
+ D = Type,
+ emit({nl,nl}),
+ emit({"'dec_",Type#typedef.name,"'(Bytes, OptOrMand) ->",nl}),
+ emit({" 'dec_",Type#typedef.name,"'(Bytes, OptOrMand, []).",nl,nl}),
+ emit({"'dec_",Type#typedef.name,"'(Bytes, ",
+ unused_optormand_var("OptOrMand",(Type#typedef.typespec)#type.def),", TagIn) ->",nl}),
+ dbdec(Type#typedef.name),
+ gen_decode_user(Erules,D).
+
+
+%%===============================================================================
+%% decode #{type, {tag, def, constraint}}
+%%===============================================================================
+
+gen_decode(Erules,Tname,Type) when record(Type,type) ->
+ Typename = Tname,
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ ObjFun =
+ case Type#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _ ->
+ ""
+ end,
+ emit({"'dec_",asn1ct_gen:list2name(Typename),"'(Bytes, OptOrMand, TagIn",ObjFun,") ->",nl}),
+ dbdec(Typename),
+ asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,Type);
+ _ ->
+ true
+ end;
+
+
+%%===============================================================================
+%% decode ComponentType
+%%===============================================================================
+
+gen_decode(Erules,Tname,{'ComponentType',_Pos,Cname,Type,_,_}) ->
+ NewTname = [Cname|Tname],
+ %% The tag is set to [] to avoid that it is
+ %% taken into account twice, both as a component/alternative (passed as
+ %% argument to the encode decode function and within the encode decode
+ %% function it self.
+ NewType = Type#type{tag=[]},
+ gen_decode(Erules,NewTname,NewType).
+
+
+gen_decode_user(Erules,D) when record(D,typedef) ->
+ Typename = [D#typedef.name],
+ Def = D#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ InnerTag = Def#type.tag ,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- InnerTag],
+ case asn1ct_gen:type(InnerType) of
+ 'ASN1_OPEN_TYPE' ->
+ BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
+ asn1ct_name:new(len),
+ gen_dec_prim(Erules, Def#type{def='ASN1_OPEN_TYPE'},
+ BytesVar, Tag, "TagIn",no_length,
+ ?PRIMITIVE,"OptOrMand"),
+ emit({".",nl,nl});
+ {primitive,bif} ->
+ BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
+ asn1ct_name:new(len),
+ gen_dec_prim(Erules, Def, BytesVar, Tag, "TagIn",no_length,
+ ?PRIMITIVE,"OptOrMand"),
+ emit({".",nl,nl});
+ {constructed,bif} ->
+ asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,D);
+ TheType ->
+ DecFunName = mkfuncname(TheType,dec),
+ emit({DecFunName,"(",{curr,bytes},
+ ", OptOrMand, TagIn++",{asis,Tag},")"}),
+ emit({".",nl,nl})
+ end.
+
+
+gen_dec_prim(Erules,Att,BytesVar,DoTag,TagIn,Length,_Form,OptOrMand) ->
+ Typename = Att#type.def,
+%% Currently not used for BER replaced with [] as place holder
+%% Constraint = Att#type.constraint,
+%% Constraint = [],
+ Constraint =
+ case get_constraint(Att#type.constraint,'SizeConstraint') of
+ no -> [];
+ Tc -> Tc
+ end,
+ ValueRange =
+ case get_constraint(Att#type.constraint,'ValueRange') of
+ no -> [];
+ Tv -> Tv
+ end,
+ SingleValue =
+ case get_constraint(Att#type.constraint,'SingleValue') of
+ no -> [];
+ Sv -> Sv
+ end,
+ AsBin = case get(binary_strings) of
+ true -> "_as_bin";
+ _ -> ""
+ end,
+ NewTypeName = case Typename of
+ 'ANY' -> 'ASN1_OPEN_TYPE';
+ _ -> Typename
+ end,
+ DoLength =
+ case NewTypeName of
+ 'BOOLEAN'->
+ emit({"?RT_BER:decode_boolean(",BytesVar,","}),
+ false;
+ 'INTEGER' ->
+ emit({"?RT_BER:decode_integer(",BytesVar,",",
+ {asis,int_constr(SingleValue,ValueRange)},","}),
+ false;
+ {'INTEGER',NamedNumberList} ->
+ emit({"?RT_BER:decode_integer(",BytesVar,",",
+ {asis,int_constr(SingleValue,ValueRange)},",",
+ {asis,NamedNumberList},","}),
+ false;
+ {'ENUMERATED',NamedNumberList} ->
+ emit({"?RT_BER:decode_enumerated(",BytesVar,",",
+ {asis,Constraint},",",
+ {asis,NamedNumberList},","}),
+ false;
+ {'BIT STRING',NamedNumberList} ->
+ case get(compact_bit_string) of
+ true ->
+ emit({"?RT_BER:decode_compact_bit_string(",
+ BytesVar,",",{asis,Constraint},",",
+ {asis,NamedNumberList},","});
+ _ ->
+ emit({"?RT_BER:decode_bit_string(",BytesVar,",",
+ {asis,Constraint},",",
+ {asis,NamedNumberList},","})
+ end,
+ true;
+ 'NULL' ->
+ emit({"?RT_BER:decode_null(",BytesVar,","}),
+ false;
+ 'OBJECT IDENTIFIER' ->
+ emit({"?RT_BER:decode_object_identifier(",BytesVar,","}),
+ false;
+ 'ObjectDescriptor' ->
+ emit({"?RT_BER:decode_restricted_string(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_ObjectDescriptor},","}),
+ true;
+ 'OCTET STRING' ->
+ emit({"?RT_BER:decode_octet_string",AsBin,"(",BytesVar,",",{asis,Constraint},","}),
+ true;
+ 'NumericString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_NumericString},","}),true;
+ 'TeletexString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_TeletexString},","}),
+ true;
+ 'VideotexString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_VideotexString},","}),
+ true;
+ 'GraphicString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_GraphicString},","})
+ ,true;
+ 'VisibleString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_VisibleString},","}),
+ true;
+ 'GeneralString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_GeneralString},","}),
+ true;
+ 'PrintableString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_PrintableString},","}),
+ true;
+ 'IA5String' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_IA5String},","}),
+ true;
+ 'UniversalString' ->
+ emit({"?RT_BER:decode_universal_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","}),
+ true;
+ 'BMPString' ->
+ emit({"?RT_BER:decode_BMP_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","}),
+ true;
+ 'UTCTime' ->
+ emit({"?RT_BER:decode_utc_time",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","}),
+ true;
+ 'GeneralizedTime' ->
+ emit({"?RT_BER:decode_generalized_time",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","}),
+ true;
+ 'ASN1_OPEN_TYPE' ->
+ emit(["?RT_BER:decode_open_type(",re_wrap_erule(Erules),",",
+ BytesVar,","]),
+ false;
+ Other ->
+ exit({'can not decode' ,Other})
+ end,
+
+ NewLength = case DoLength of
+ true -> [", ", Length];
+ false -> ""
+ end,
+ NewOptOrMand = case OptOrMand of
+ _ when list(OptOrMand) -> OptOrMand;
+ mandatory -> {asis,mandatory};
+ _ -> {asis,opt_or_default}
+ end,
+ case {TagIn,NewTypeName} of
+ {[],'ASN1_OPEN_TYPE'} ->
+ emit([{asis,DoTag},")"]);
+ {_,'ASN1_OPEN_TYPE'} ->
+ emit([TagIn,"++",{asis,DoTag},")"]);
+ {[],_} ->
+ emit([{asis,DoTag},NewLength,", ",NewOptOrMand,")"]);
+ _ when list(TagIn) ->
+ emit([TagIn,"++",{asis,DoTag},NewLength,", ",NewOptOrMand,")"])
+ end.
+
+
+int_constr([],[]) ->
+ [];
+int_constr([],ValueRange) ->
+ ValueRange;
+int_constr(SingleValue,[]) ->
+ SingleValue;
+int_constr(SV,VR) ->
+ [SV,VR].
+
+%% Object code generating for encoding and decoding
+%% ------------------------------------------------
+
+gen_obj_code(Erules,_Module,Obj) when record(Obj,typedef) ->
+ ObjName = Obj#typedef.name,
+ Def = Obj#typedef.typespec,
+ #'Externaltypereference'{module=M,type=ClName} = Def#'Object'.classname,
+ Class = asn1_db:dbget(M,ClName),
+
+ {object,_,Fields} = Def#'Object'.def,
+ emit({nl,nl,nl,"%%================================"}),
+ emit({nl,"%% ",ObjName}),
+ emit({nl,"%%================================",nl}),
+ EncConstructed =
+ gen_encode_objectfields(ClName,get_class_fields(Class),
+ ObjName,Fields,[]),
+ emit(nl),
+ gen_encode_constr_type(Erules,EncConstructed),
+ emit(nl),
+ DecConstructed =
+ gen_decode_objectfields(ClName,get_class_fields(Class),
+ ObjName,Fields,[]),
+ emit(nl),
+ gen_decode_constr_type(Erules,DecConstructed);
+gen_obj_code(_Erules,_Module,Obj) when record(Obj,pobjectdef) ->
+ ok.
+
+
+gen_encode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Args) ->
+ emit(["'enc_",ObjName,"'(",{asis,Name},
+ ", ",Args,", _RestPrimFieldName) ->",nl])
+ end,
+% emit(["'enc_",ObjName,"'(",{asis,Name},
+% ", Val, TagIn, _RestPrimFieldName) ->",nl]),
+ MaybeConstr=
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} -> %% this case is illegal
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_, _"),
+ emit([" {[],0}"]),
+ [];
+ {false,{'DEFAULT',DefaultType}} ->
+ EmitFuncClause("Val, TagIn"),
+ gen_encode_default_call(ClassName,Name,DefaultType);
+ {{Name,TypeSpec},_} ->
+ %% A specified field owerwrites any 'DEFAULT' or
+ %% 'OPTIONAL' field in the class
+ EmitFuncClause("Val, TagIn"),
+ gen_encode_field_call(ObjName,Name,TypeSpec)
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_encode_objectfields(ClassName,Rest,ObjName,ObjectFields,
+ MaybeConstr++ConstrAcc);
+gen_encode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Args) ->
+ emit(["'enc_",ObjName,"'(",{asis,Name},
+ ", ",Args,") ->",nl])
+ end,
+% emit(["'enc_",ObjName,"'(",{asis,Name},
+% ", Val, TagIn, [H|T]) ->",nl]),
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} ->
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_,_,_"),
+ emit([" exit({error,{'use of missing field in object', ",Name,
+ "}})"]);
+ {false,{'DEFAULT',_DefaultObject}} ->
+ exit({error,{asn1,{"not implemented yet",Name}}});
+ {{Name,TypeSpec},_} ->
+ EmitFuncClause(" Val, TagIn, [H|T]"),
+ case TypeSpec#typedef.name of
+ {ExtMod,TypeName} ->
+ emit({indent(3),"'",ExtMod,"':'enc_",TypeName,
+ "'(H, Val, TagIn, T)"});
+ TypeName ->
+ emit({indent(3),"'enc_",TypeName,"'(H, Val, TagIn, T)"})
+ end
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_encode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc);
+gen_encode_objectfields(ClassName,[_|Cs],O,OF,Acc) ->
+ gen_encode_objectfields(ClassName,Cs,O,OF,Acc);
+gen_encode_objectfields(_,[],_,_,Acc) ->
+ Acc.
+
+
+% gen_encode_objectfields(Class,ObjName,[{FieldName,Type}|Rest],ConstrAcc) ->
+% Fields = Class#objectclass.fields,
+% MaybeConstr=
+% case is_typefield(Fields,FieldName) of
+% true ->
+% Def = Type#typedef.typespec,
+% OTag = Def#type.tag,
+% Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+% emit({"'enc_",ObjName,"'(",{asis,FieldName},
+% ", Val, TagIn, RestPrimFieldName) ->",nl}),
+% CAcc=
+% case Type#typedef.name of
+% {primitive,bif} ->
+% gen_encode_prim(ber,Def,["TagIn ++ ",{asis,Tag}],
+% "Val"),
+% [];
+% {constructed,bif} ->
+% %%InnerType = asn1ct_gen:get_inner(Def#type.def),
+% %%asn1ct_gen:gen_encode_constructed(ber,[ObjName],
+% %% InnerType,Def);
+% emit({" 'enc_",ObjName,'_',FieldName,
+% "'(Val, TagIn ++ ",{asis,Tag},")"}),
+% [{['enc_',ObjName,'_',FieldName],Def}];
+% {ExtMod,TypeName} ->
+% emit({" '",ExtMod,"':'enc_",TypeName,
+% "'(Val, TagIn ++ ",{asis,Tag},")"}),
+% [];
+% TypeName ->
+% emit({" 'enc_",TypeName,"'(Val, TagIn ++ ",
+% {asis,Tag},")"}),
+% []
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% CAcc;
+% {false,objectfield} ->
+% emit({"'enc_",ObjName,"'(",{asis,FieldName},
+% ", Val, TagIn, [H|T]) ->",nl}),
+% case Type#typedef.name of
+% {ExtMod,TypeName} ->
+% emit({indent(3),"'",ExtMod,"':'enc_",TypeName,
+% "'(H, Val, TagIn, T)"});
+% TypeName ->
+% emit({indent(3),"'enc_",TypeName,"'(H, Val, TagIn, T)"})
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% [];
+% {false,_} -> []
+% end,
+% gen_encode_objectfields(Class,ObjName,Rest,MaybeConstr ++ ConstrAcc);
+% gen_encode_objectfields(C,O,[H|T],Acc) ->
+% gen_encode_objectfields(C,O,T,Acc);
+% gen_encode_objectfields(_,_,[],Acc) ->
+% Acc.
+
+% gen_encode_constr_type([{Name,Def}|Rest]) ->
+% emit({Name,"(Val,TagIn) ->",nl}),
+% InnerType = asn1ct_gen:get_inner(Def#type.def),
+% asn1ct_gen:gen_encode_constructed(ber,Name,InnerType,Def),
+% gen_encode_constr_type(Rest);
+gen_encode_constr_type(Erules,[TypeDef|Rest]) when record(TypeDef,typedef) ->
+ case is_already_generated(enc,TypeDef#typedef.name) of
+ true -> ok;
+ _ -> gen_encode_user(Erules,TypeDef)
+ end,
+ gen_encode_constr_type(Erules,Rest);
+gen_encode_constr_type(_,[]) ->
+ ok.
+
+gen_encode_field_call(ObjName,FieldName,Type) ->
+ Def = Type#typedef.typespec,
+ OTag = Def#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ case Type#typedef.name of
+ {primitive,bif} -> %%tag should be the primitive tag
+ gen_encode_prim(ber,Def,["TagIn ++ ",{asis,Tag}],
+ "Val"),
+ [];
+ {constructed,bif} ->
+ emit({" 'enc_",ObjName,'_',FieldName,
+ "'(Val, TagIn ++",{asis,Tag},")"}),
+ [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+ {ExtMod,TypeName} ->
+ emit({" '",ExtMod,"':'enc_",TypeName,
+ "'(Val, TagIn ++ ",{asis,Tag},")"}),
+ [];
+ TypeName ->
+ emit({" 'enc_",TypeName,"'(Val, TagIn ++ ",{asis,Tag},")"}),
+ []
+ end.
+
+gen_encode_default_call(ClassName,FieldName,Type) ->
+ CurrentMod = get(currmod),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ OTag = Type#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+%% asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
+ emit([" 'enc_",ClassName,'_',FieldName,"'(Bytes, TagIn ++ ",
+ {asis,Tag},")"]),
+ [#typedef{name=list_to_atom(lists:concat([ClassName,'_',FieldName])),
+ typespec=Type}];
+ {primitive,bif} ->
+ gen_encode_prim(ber,Type,["TagIn ++ ",{asis,Tag}],"Val"),
+ [];
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'enc_",Etype,"'(Val, TagIn ++ ",{asis,Tag},")",nl]),
+ [];
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'enc_",Etype,"'(Val, TagIn ++ ",{asis,Tag},")",nl]),
+ []
+ end.
+
+
+
+gen_decode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Args) ->
+ emit(["'dec_",ObjName,"'(",{asis,Name},
+ ", ",Args,"_) ->",nl])
+ end,
+% emit(["'dec_",ObjName,"'(",{asis,Name},
+% ", Bytes, TagIn, RestPrimFieldName) ->",nl]),
+ MaybeConstr=
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} -> %% this case is illegal
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_, _,"),
+ emit([" asn1_NOVALUE"]),
+ [];
+ {false,{'DEFAULT',DefaultType}} ->
+ EmitFuncClause("Bytes, TagIn,"),
+ gen_decode_default_call(ClassName,Name,"Bytes",DefaultType);
+ {{Name,TypeSpec},_} ->
+ %% A specified field owerwrites any 'DEFAULT' or
+ %% 'OPTIONAL' field in the class
+ EmitFuncClause("Bytes, TagIn,"),
+ gen_decode_field_call(ObjName,Name,"Bytes",TypeSpec)
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,MaybeConstr++ConstrAcc);
+gen_decode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Args) ->
+ emit(["'dec_",ObjName,"'(",{asis,Name},
+ ", ",Args,") ->",nl])
+ end,
+% emit(["'dec_",ObjName,"'(",{asis,Name},
+% ", Bytes,TagIn,[H|T]) ->",nl]),
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} ->
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_,_,_"),
+ emit([" exit({error,{'illegal use of missing field in object', ",Name,
+ "}})"]);
+ {false,{'DEFAULT',_DefaultObject}} ->
+ exit({error,{asn1,{"not implemented yet",Name}}});
+ {{Name,TypeSpec},_} ->
+ EmitFuncClause("Bytes,TagIn,[H|T]"),
+ case TypeSpec#typedef.name of
+ {ExtMod,TypeName} ->
+ emit({indent(3),"'",ExtMod,"':'dec_",TypeName,
+ "'(H, Bytes, TagIn, T)"});
+ TypeName ->
+ emit({indent(3),"'dec_",TypeName,"'(H, Bytes, TagIn, T)"})
+ end
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc);
+gen_decode_objectfields(CN,[_|Cs],O,OF,CAcc) ->
+ gen_decode_objectfields(CN,Cs,O,OF,CAcc);
+gen_decode_objectfields(_,[],_,_,CAcc) ->
+ CAcc.
+
+
+
+% gen_decode_objectfields(Erules,Class,ObjName,[{FieldName,Type}|Rest],ConstrAcc) ->
+% Fields = Class#objectclass.fields,
+% MaybeConstr =
+% case is_typefield(Fields,FieldName) of
+% true ->
+% Def = Type#typedef.typespec,
+% emit({"'dec_",ObjName,"'(",{asis,FieldName},
+% ", Bytes, TagIn, RestPrimFieldName) ->",nl}),
+% OTag = Def#type.tag,
+% Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+% Prop =
+% case get_optionalityspec(Fields,FieldName) of
+% 'OPTIONAL' -> opt_or_default;
+% {'DEFAULT',_} -> opt_or_default;
+% _ -> mandatory
+% end,
+% CAcc =
+% case Type#typedef.name of
+% {primitive,bif} ->
+% gen_dec_prim(Erules,Def,"Bytes",Tag,"TagIn",no_length,
+% ?PRIMITIVE,Prop),
+% [];
+% {constructed,bif} ->
+% emit({" 'dec_",ObjName,'_',FieldName,"'(Bytes,",
+% {asis,Prop},", TagIn ++ ",{asis,Tag},")"}),
+% [{['dec_',ObjName,'_',FieldName],Def}];
+% {ExtMod,TypeName} ->
+% emit({" '",ExtMod,"':'dec_",TypeName,"'(Bytes, ",
+% {asis,Prop},", TagIn ++ ",{asis,Tag},")"}),
+% [];
+% TypeName ->
+% emit({" 'dec_",TypeName,"'(Bytes, ",{asis,Prop},
+% ", TagIn ++ ",{asis,Tag},")"}),
+% []
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% CAcc;
+% {false,objectfield} ->
+% emit({"'dec_",ObjName,"'(",{asis,FieldName},
+% ", Bytes, TagIn, [H|T]) ->",nl}),
+% case Type#typedef.name of
+% {ExtMod,TypeName} ->
+% emit({indent(3),"'",ExtMod,"':'dec_",TypeName,
+% "'(H, Bytes, TagIn, T)"});
+% TypeName ->
+% emit({indent(3),"'dec_",TypeName,
+% "'(H, Bytes, TagIn, T)"})
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% [];
+% {false,_} ->
+% []
+% end,
+% gen_decode_objectfields(Erules,Class,ObjName,Rest,MaybeConstr ++ ConstrAcc);
+% gen_decode_objectfields(Erules,C,O,[H|T],CAcc) ->
+% gen_decode_objectfields(Erules,C,O,T,CAcc);
+% gen_decode_objectfields(_,_,_,[],CAcc) ->
+% CAcc.
+
+gen_decode_constr_type(Erules,[{Name,Def}|Rest]) ->
+%% emit({Name,"(Bytes, OptOrMand) ->",nl}),
+%% emit({" ",Name,"(Bytes, OptOrMand, []).",nl,nl}),
+ emit({Name,"(Bytes, OptOrMand, TagIn) ->",nl}),
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ asn1ct_gen:gen_decode_constructed(ber,Name,InnerType,Def),
+ gen_decode_constr_type(Erules,Rest);
+gen_decode_constr_type(Erules,[TypeDef|Rest]) when record(TypeDef,typedef) ->
+ case is_already_generated(dec,TypeDef#typedef.name) of
+ true -> ok;
+ _ ->
+ gen_decode(Erules,TypeDef)
+ end,
+ gen_decode_constr_type(Erules,Rest);
+gen_decode_constr_type(_,[]) ->
+ ok.
+
+gen_decode_field_call(ObjName,FieldName,Bytes,Type) ->
+ Def = Type#typedef.typespec,
+ OTag = Def#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ case Type#typedef.name of
+ {primitive,bif} -> %%tag should be the primitive tag
+ gen_dec_prim(ber,Def,Bytes,Tag,"TagIn",no_length,
+ ?PRIMITIVE,opt_or_default),
+ [];
+ {constructed,bif} ->
+ emit({" 'dec_",ObjName,'_',FieldName,
+ "'(",Bytes,",opt_or_default, TagIn ++ ",{asis,Tag},")"}),
+ [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+ {ExtMod,TypeName} ->
+ emit({" '",ExtMod,"':'dec_",TypeName,
+ "'(",Bytes,", opt_or_default,TagIn ++ ",{asis,Tag},")"}),
+ [];
+ TypeName ->
+ emit({" 'dec_",TypeName,"'(",Bytes,
+ ", opt_or_default,TagIn ++ ",{asis,Tag},")"}),
+ []
+ end.
+
+gen_decode_default_call(ClassName,FieldName,Bytes,Type) ->
+ CurrentMod = get(currmod),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ OTag = Type#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ emit([" 'dec_",ClassName,'_',FieldName,"'(",Bytes,
+ ",opt_or_default, TagIn ++ ",{asis,Tag},")"]),
+ [#typedef{name=list_to_atom(lists:concat([ClassName,'_',FieldName])),
+ typespec=Type}];
+ {primitive,bif} ->
+ gen_dec_prim(ber,Type,Bytes,Tag,"TagIn",no_length,
+ ?PRIMITIVE,opt_or_default),
+ [];
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'dec_",Etype,"'(",Bytes,
+ " ,opt_or_default, TagIn ++ ",{asis,Tag},")",nl]),
+ [];
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'dec_",Etype,"'(",Bytes,
+ ", opt_or_defualt, TagIn ++ ",{asis,Tag},")",nl]),
+ []
+ end.
+
+
+more_genfields([]) ->
+ false;
+more_genfields([Field|Fields]) ->
+ case element(1,Field) of
+ typefield ->
+ true;
+ objectfield ->
+ true;
+ _ ->
+ more_genfields(Fields)
+ end.
+
+
+
+%% Object Set code generating for encoding and decoding
+%% ----------------------------------------------------
+gen_objectset_code(Erules,ObjSet) ->
+ ObjSetName = ObjSet#typedef.name,
+ Def = ObjSet#typedef.typespec,
+% {ClassName,ClassDef} = Def#'ObjectSet'.class,
+ #'Externaltypereference'{module=ClassModule,
+ type=ClassName} = Def#'ObjectSet'.class,
+ ClassDef = asn1_db:dbget(ClassModule,ClassName),
+ UniqueFName = Def#'ObjectSet'.uniquefname,
+ Set = Def#'ObjectSet'.set,
+ emit({nl,nl,nl,"%%================================"}),
+ emit({nl,"%% ",ObjSetName}),
+ emit({nl,"%%================================",nl}),
+ case ClassName of
+ {_Module,ExtClassName} ->
+ gen_objset_code(Erules,ObjSetName,UniqueFName,Set,
+ ExtClassName,ClassDef);
+ _ ->
+ gen_objset_code(Erules,ObjSetName,UniqueFName,Set,
+ ClassName,ClassDef)
+ end,
+ emit(nl).
+
+gen_objset_code(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassDef)->
+ ClassFields = (ClassDef#classdef.typespec)#objectclass.fields,
+ InternalFuncs=gen_objset_enc(ObjSetName,UniqueFName,Set,ClassName,ClassFields,1,[]),
+ gen_objset_dec(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassFields,1),
+ gen_internal_funcs(Erules,InternalFuncs).
+
+%% gen_objset_enc iterates over the objects of the object set
+gen_objset_enc(_,{unique,undefined},_,_,_,_,_) ->
+ %% There is no unique field in the class of this object set
+ %% don't bother about the constraint
+ [];
+gen_objset_enc(ObjSName,UniqueName,
+ [{ObjName,Val,Fields},T|Rest],ClName,ClFields,NthObj,Acc)->
+ emit({"'getenc_",ObjSName,"'(",{asis,UniqueName},",",{asis,Val},") ->",nl}),
+ {InternalFunc,NewNthObj}=
+ case ObjName of
+ no_name ->
+ gen_inlined_enc_funs(Fields,ClFields,ObjSName,NthObj);
+ _Other ->
+ emit({" fun 'enc_",ObjName,"'/4"}),
+ {[],NthObj}
+ end,
+ emit({";",nl}),
+ gen_objset_enc(ObjSName,UniqueName,[T|Rest],ClName,ClFields,
+ NewNthObj,InternalFunc ++ Acc);
+gen_objset_enc(ObjSetName,UniqueName,
+ [{ObjName,Val,Fields}],_ClName,ClFields,NthObj,Acc) ->
+ emit({"'getenc_",ObjSetName,"'(",{asis,UniqueName},",",{asis,Val},") ->",nl}),
+ {InternalFunc,_}=
+ case ObjName of
+ no_name ->
+ gen_inlined_enc_funs(Fields,ClFields,ObjSetName,NthObj);
+ _Other ->
+ emit({" fun 'enc_",ObjName,"'/4"}),
+ {[],NthObj}
+ end,
+ emit({".",nl,nl}),
+ InternalFunc ++ Acc;
+%% See X.681 Annex E for the following case
+gen_objset_enc(ObjSetName,_UniqueName,['EXTENSIONMARK'],
+ _ClName,_ClFields,_NthObj,Acc) ->
+ emit({"'getenc_",ObjSetName,"'(_, _) ->",nl}),
+ emit({indent(3),"fun(_Attr, Val, _TagIn, _RestPrimFieldName) ->",nl}),
+ emit({indent(6),"Len = case Val of",nl,indent(9),
+ "Bin when binary(Bin) -> size(Bin);",nl,indent(9),
+ "_ -> length(Val)",nl,indent(6),"end,"}),
+ emit({indent(6),"{Val,Len}",nl}),
+ emit({indent(3),"end.",nl,nl}),
+ Acc;
+gen_objset_enc(_,_,[],_,_,_,Acc) ->
+ Acc.
+
+%% gen_inlined_enc_funs for each object iterates over all fields of a
+%% class, and for each typefield it checks if the object has that
+%% field and emits the proper code.
+gen_inlined_enc_funs(Fields,[{typefield,Name,_}|Rest],ObjSetName,
+ NthObj) ->
+ InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({indent(3),"fun(Type, Val, TagIn, _RestPrimFieldName) ->",nl,
+ indent(6),"case Type of",nl}),
+ {Ret,N} = emit_inner_of_fun(Type,InternalDefFunName),
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+N,Ret);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({indent(3),"fun(Type, Val, TagIn, _RestPrimFieldName) ->",nl,
+ indent(6),"case Type of",nl}),
+ emit({indent(9),{asis,Name}," ->",nl}),
+ {Ret,N} = emit_inner_of_fun(Type,InternalDefFunName),
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+N,Ret);
+ false ->
+ gen_inlined_enc_funs(Fields,Rest,ObjSetName,NthObj)
+ end;
+gen_inlined_enc_funs(Fields,[_H|Rest],ObjSetName,NthObj) ->
+ gen_inlined_enc_funs(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_enc_funs(_,[],_,NthObj) ->
+ {[],NthObj}.
+
+gen_inlined_enc_funs1(Fields,[{typefield,Name,_}|Rest],ObjSetName,
+ NthObj,Acc) ->
+ InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
+ {Acc2,NAdd}=
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({";",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ {Ret++Acc,N};
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({";",nl,indent(9),{asis,Name}," ->",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ {Ret++Acc,N};
+ false ->
+ {Acc,0}
+ end,
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+NAdd,Acc2);
+gen_inlined_enc_funs1(Fields,[_H|Rest],ObjSetName,NthObj,Acc)->
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj,Acc);
+gen_inlined_enc_funs1(_,[],_,NthObj,Acc) ->
+ emit({nl,indent(6),"end",nl}),
+ emit({indent(3),"end"}),
+ {Acc,NthObj}.
+
+
+emit_inner_of_fun(TDef = #typedef{name={ExtMod,Name},typespec=Type},
+ InternalDefFunName) ->
+ OTag = Type#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ case {ExtMod,Name} of
+ {primitive,bif} ->
+ emit(indent(12)),
+ gen_encode_prim(ber,Type,["TagIn ++ ",{asis,Tag}],"Val"),
+ {[],0};
+ {constructed,bif} ->
+ emit([indent(12),"'enc_",
+ InternalDefFunName,"'(Val,TagIn ++ ",
+ {asis,Tag},")"]),
+ {[TDef#typedef{name=InternalDefFunName}],1};
+ _ ->
+ emit({indent(12),"'",ExtMod,"':'enc_",Name,"'(Val, TagIn ++ ",
+ {asis,Tag},")"}),
+ {[],0}
+ end;
+emit_inner_of_fun(#typedef{name=Name,typespec=Type},_) ->
+ OTag = Type#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ emit({indent(12),"'enc_",Name,"'(Val, TagIn ++ ",{asis,Tag},")"}),
+ {[],0};
+emit_inner_of_fun(Type,_) when record(Type,type) ->
+ CurrMod = get(currmod),
+ OTag = Type#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ case Type#type.def of
+ Def when atom(Def) ->
+ emit({indent(9),Def," ->",nl,indent(12)}),
+ gen_encode_prim(ber,Type,["TagIn ++ ",{asis,Tag}],"Val");
+ TRef when record(TRef,typereference) ->
+ T = TRef#typereference.val,
+ emit({indent(9),T," ->",nl,indent(12),"'enc_",T,
+ "'(Val, TagIn ++ ",{asis,Tag},")"});
+ #'Externaltypereference'{module=CurrMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),"'enc_",T,
+ "'(Val, TagIn ++ ",{asis,Tag},")"});
+ #'Externaltypereference'{module=ExtMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),ExtMod,":'enc_",
+ T,"'(Val, TagIn ++ ",{asis,Tag},")"})
+ end,
+ {[],0}.
+
+indent(N) ->
+ lists:duplicate(N,32). % 32 = space
+
+
+gen_objset_dec(_,_,{unique,undefined},_,_,_,_) ->
+ %% There is no unique field in the class of this object set
+ %% don't bother about the constraint
+ ok;
+gen_objset_dec(Erules,ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest],
+ ClName,ClFields,NthObj)->
+ emit({"'getdec_",ObjSName,"'(",{asis,UniqueName},",",{asis,Val},
+ ") ->",nl}),
+ NewNthObj=
+ case ObjName of
+ no_name ->
+ gen_inlined_dec_funs(Erules,Fields,ClFields,ObjSName,
+ NthObj);
+ _Other ->
+ emit({" fun 'dec_",ObjName,"'/4"}),
+ NthObj
+ end,
+ emit({";",nl}),
+ gen_objset_dec(Erules,ObjSName,UniqueName,[T|Rest],ClName,ClFields,
+ NewNthObj);
+gen_objset_dec(Erules,ObjSetName,UniqueName,[{ObjName,Val,Fields}],_ClName,
+ ClFields,NthObj) ->
+ emit({"'getdec_",ObjSetName,"'(",{asis,UniqueName},",",{asis,Val},") ->",nl}),
+ case ObjName of
+ no_name ->
+ gen_inlined_dec_funs(Erules,Fields,ClFields,ObjSetName,
+ NthObj);
+ _Other ->
+ emit({" fun 'dec_",ObjName,"'/4"})
+ end,
+ emit({".",nl,nl});
+gen_objset_dec(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,_ClFields,
+ _NthObj) ->
+ emit({"'getdec_",ObjSetName,"'(_, _) ->",nl}),
+ emit({indent(3),"fun(_, Bytes, _, _) ->",nl}),
+ emit({indent(6),"Len = case Bytes of",nl,indent(9),
+ "Bin when binary(Bin) -> size(Bin);",nl,indent(9),
+ "_ -> length(Bytes)",nl,indent(6),"end,"}),
+ emit({indent(6),"{Bytes,[],Len}",nl}),
+ emit({indent(3),"end.",nl,nl}),
+ ok;
+gen_objset_dec(_,_,_,[],_,_,_) ->
+ ok.
+
+gen_inlined_dec_funs(Erules,Fields,[{typefield,Name,Prop}|Rest],
+ ObjSetName,NthObj) ->
+ DecProp = case Prop of
+ 'OPTIONAL' -> opt_or_default;
+ {'DEFAULT',_} -> opt_or_default;
+ _ -> mandatory
+ end,
+ InternalDefFunName = [NthObj,Name,ObjSetName],
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({indent(3),"fun(Type, Bytes, TagIn, _RestPrimFieldName) ->",
+ nl,indent(6),"case Type of",nl}),
+ N=emit_inner_of_decfun(Erules,Type,DecProp,InternalDefFunName),
+ gen_inlined_dec_funs1(Erules,Fields,Rest,ObjSetName,NthObj+N);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({indent(3),"fun(Type, Bytes, TagIn, _RestPrimFieldName) ->",
+ nl,indent(6),"case Type of",nl}),
+ emit({indent(9),{asis,Name}," ->",nl}),
+ N=emit_inner_of_decfun(Erules,Type,DecProp,InternalDefFunName),
+ gen_inlined_dec_funs1(Erules,Fields,Rest,ObjSetName,NthObj+N);
+ false ->
+ gen_inlined_dec_funs(Erules,Fields,Rest,ObjSetName,NthObj)
+ end;
+gen_inlined_dec_funs(Erules,Fields,[_H|Rest],ObjSetName,NthObj) ->
+ gen_inlined_dec_funs(Erules,Fields,Rest,ObjSetName,NthObj);
+gen_inlined_dec_funs(_,_,[],_,NthObj) ->
+ NthObj.
+
+gen_inlined_dec_funs1(Erules,Fields,[{typefield,Name,Prop}|Rest],
+ ObjSetName,NthObj) ->
+ DecProp = case Prop of
+ 'OPTIONAL' -> opt_or_default;
+ {'DEFAULT',_} -> opt_or_default;
+ _ -> mandatory
+ end,
+ InternalDefFunName = [NthObj,Name,ObjSetName],
+ N=
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({";",nl}),
+ emit_inner_of_decfun(Erules,Type,DecProp,InternalDefFunName);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({";",nl,indent(9),{asis,Name}," ->",nl}),
+ emit_inner_of_decfun(Erules,Type,DecProp,InternalDefFunName);
+ false ->
+ 0
+ end,
+ gen_inlined_dec_funs1(Erules,Fields,Rest,ObjSetName,NthObj+N);
+gen_inlined_dec_funs1(Erules,Fields,[_H|Rest],ObjSetName,NthObj)->
+ gen_inlined_dec_funs1(Erules,Fields,Rest,ObjSetName,NthObj);
+gen_inlined_dec_funs1(_,_,[],_,NthObj) ->
+ emit({nl,indent(6),"end",nl}),
+ emit({indent(3),"end"}),
+ NthObj.
+
+emit_inner_of_decfun(Erules,#typedef{name={ExtName,Name},typespec=Type},
+ Prop,InternalDefFunName) ->
+ OTag = Type#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ case {ExtName,Name} of
+ {primitive,bif} ->
+ emit(indent(12)),
+ gen_dec_prim(Erules,Type,"Bytes",Tag,"TagIn",no_length,
+ ?PRIMITIVE,Prop),
+ 0;
+ {constructed,bif} ->
+ emit({indent(12),"'dec_",
+ asn1ct_gen:list2name(InternalDefFunName),"'(Bytes, ",Prop,
+ ", TagIn ++ ",{asis,Tag},")"}),
+ 1;
+ _ ->
+ emit({indent(12),"'",ExtName,"':'dec_",Name,"'(Bytes, ",Prop,
+ ", TagIn ++ ",{asis,Tag},")"}),
+ 0
+ end;
+emit_inner_of_decfun(_,#typedef{name=Name,typespec=Type},Prop,_) ->
+ OTag = Type#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ emit({indent(12),"'dec_",Name,"'(Bytes, ",Prop,", TagIn ++ ",
+ {asis,Tag},")"}),
+ 0;
+emit_inner_of_decfun(Erules,Type,Prop,_) when record(Type,type) ->
+ OTag = Type#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ CurrMod = get(currmod),
+ Def = Type#type.def,
+ InnerType = asn1ct_gen:get_inner(Def),
+ WhatKind = asn1ct_gen:type(InnerType),
+ case WhatKind of
+ {primitive,bif} ->
+ emit({indent(9),Def," ->",nl,indent(12)}),
+ gen_dec_prim(Erules,Type,"Bytes",Tag,"TagIn",no_length,
+ ?PRIMITIVE,Prop);
+% TRef when record(TRef,typereference) ->
+% T = TRef#typereference.val,
+% emit({indent(9),T," ->",nl,indent(12),"'dec_",T,"'(Val)"});
+ #'Externaltypereference'{module=CurrMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),"'dec_",T,
+ "'(Bytes, ",Prop,", TagIn ++ ",{asis,Tag},")"});
+ #'Externaltypereference'{module=ExtMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),ExtMod,":'dec_",
+ T,"'(Bytes, ",Prop,", TagIn ++ ",{asis,Tag},")"})
+ end,
+ 0.
+
+
+gen_internal_funcs(_,[]) ->
+ ok;
+gen_internal_funcs(Erules,[TypeDef|Rest]) ->
+ gen_encode_user(Erules,TypeDef),
+ emit({"'dec_",TypeDef#typedef.name,"'(Bytes, ",
+ unused_optormand_var("OptOrMand",(TypeDef#typedef.typespec)#type.def),", TagIn) ->",nl}),
+ gen_decode_user(Erules,TypeDef),
+ gen_internal_funcs(Erules,Rest).
+
+
+dbdec(Type) ->
+ demit({"io:format(\"decoding: ",{asis,Type},"~w~n\",[Bytes]),",nl}).
+
+
+decode_class('UNIVERSAL') ->
+ ?UNIVERSAL;
+decode_class('APPLICATION') ->
+ ?APPLICATION;
+decode_class('CONTEXT') ->
+ ?CONTEXT;
+decode_class('PRIVATE') ->
+ ?PRIVATE.
+
+decode_type('BOOLEAN') -> 1;
+decode_type('INTEGER') -> 2;
+decode_type('BIT STRING') -> 3;
+decode_type('OCTET STRING') -> 4;
+decode_type('NULL') -> 5;
+decode_type('OBJECT IDENTIFIER') -> 6;
+decode_type('OBJECT DESCRIPTOR') -> 7;
+decode_type('EXTERNAL') -> 8;
+decode_type('REAL') -> 9;
+decode_type('ENUMERATED') -> 10;
+decode_type('EMBEDDED_PDV') -> 11;
+decode_type('SEQUENCE') -> 16;
+decode_type('SEQUENCE OF') -> 16;
+decode_type('SET') -> 17;
+decode_type('SET OF') -> 17;
+decode_type('NumericString') -> 18;
+decode_type('PrintableString') -> 19;
+decode_type('TeletexString') -> 20;
+decode_type('VideotexString') -> 21;
+decode_type('IA5String') -> 22;
+decode_type('UTCTime') -> 23;
+decode_type('GeneralizedTime') -> 24;
+decode_type('GraphicString') -> 25;
+decode_type('VisibleString') -> 26;
+decode_type('GeneralString') -> 27;
+decode_type('UniversalString') -> 28;
+decode_type('BMPString') -> 30;
+decode_type('CHOICE') -> 'CHOICE'; % choice gets the tag from the actual alternative
+decode_type(Else) -> exit({error,{asn1,{unrecognized_type,Else}}}).
+
+add_removed_bytes() ->
+ asn1ct_name:delete(rb),
+ add_removed_bytes(asn1ct_name:all(rb)).
+
+add_removed_bytes([H,T1|T]) ->
+ emit({{var,H},"+"}),
+ add_removed_bytes([T1|T]);
+add_removed_bytes([H|T]) ->
+ emit({{var,H}}),
+ add_removed_bytes(T);
+add_removed_bytes([]) ->
+ true.
+
+mkfuncname(WhatKind,DecOrEnc) ->
+ case WhatKind of
+ #'Externaltypereference'{module=Mod,type=EType} ->
+ CurrMod = get(currmod),
+ case CurrMod of
+ Mod ->
+ lists:concat(["'",DecOrEnc,"_",EType,"'"]);
+ _ ->
+% io:format("CurrMod: ~p, Mod: ~p~n",[CurrMod,Mod]),
+ lists:concat(["'",Mod,"':'",DecOrEnc,"_",EType,"'"])
+ end;
+ #'typereference'{val=EType} ->
+ lists:concat(["'",DecOrEnc,"_",EType,"'"]);
+ 'ASN1_OPEN_TYPE' ->
+ lists:concat(["'",DecOrEnc,"_",WhatKind,"'"])
+
+ end.
+
+optionals(L) -> optionals(L,[],1).
+
+optionals([{'EXTENSIONMARK',_,_}|Rest],Acc,Pos) ->
+ optionals(Rest,Acc,Pos); % optionals in extension are currently not handled
+optionals([#'ComponentType'{name=Name,prop='OPTIONAL'}|Rest],Acc,Pos) ->
+ optionals(Rest,[{Name,Pos}|Acc],Pos+1);
+optionals([#'ComponentType'{name=Name,prop={'DEFAULT',_}}|Rest],Acc,Pos) ->
+ optionals(Rest,[{Name,Pos}|Acc],Pos+1);
+optionals([#'ComponentType'{}|Rest],Acc,Pos) ->
+ optionals(Rest,Acc,Pos+1);
+optionals([],Acc,_) ->
+ lists:reverse(Acc).
+
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V
+ end.
+
+%% if the original option was ber and it has been wrapped to ber_bin
+%% turn it back to ber
+re_wrap_erule(ber_bin) ->
+ case get(encoding_options) of
+ Options when list(Options) ->
+ case lists:member(ber,Options) of
+ true -> ber;
+ _ -> ber_bin
+ end;
+ _ -> ber_bin
+ end;
+re_wrap_erule(Erule) ->
+ Erule.
+
+is_already_generated(Operation,Name) ->
+ case get(class_default_type) of
+ undefined ->
+ put(class_default_type,[{Operation,Name}]),
+ false;
+ GeneratedList ->
+ case lists:member({Operation,Name},GeneratedList) of
+ true ->
+ true;
+ false ->
+ put(class_default_type,[{Operation,Name}|GeneratedList]),
+ false
+ end
+ end.
+
+get_class_fields(#classdef{typespec=ObjClass}) ->
+ ObjClass#objectclass.fields;
+get_class_fields(#objectclass{fields=Fields}) ->
+ Fields;
+get_class_fields(_) ->
+ [].
+
+get_object_field(Name,ObjectFields) ->
+ case lists:keysearch(Name,1,ObjectFields) of
+ {value,Field} -> Field;
+ false -> false
+ end.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen_ber_bin_v2.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen_ber_bin_v2.erl
new file mode 100644
index 0000000000..89530d4017
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen_ber_bin_v2.erl
@@ -0,0 +1,1562 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_gen_ber_bin_v2.erl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-module(asn1ct_gen_ber_bin_v2).
+
+%% Generate erlang module which handles (PER) encode and decode for
+%% all types in an ASN.1 module
+
+-include("asn1_records.hrl").
+
+-export([pgen/4]).
+-export([decode_class/1, decode_type/1]).
+-export([add_removed_bytes/0]).
+-export([gen_encode/2,gen_encode/3,gen_decode/2,gen_decode/3]).
+-export([gen_encode_prim/4]).
+-export([gen_dec_prim/7]).
+-export([gen_objectset_code/2, gen_obj_code/3]).
+-export([encode_tag_val/3]).
+-export([gen_inc_decode/2]).
+
+-import(asn1ct_gen, [emit/1,demit/1]).
+
+ % the encoding of class of tag bits 8 and 7
+-define(UNIVERSAL, 0).
+-define(APPLICATION, 16#40).
+-define(CONTEXT, 16#80).
+-define(PRIVATE, 16#C0).
+
+ % primitive or constructed encoding % bit 6
+-define(PRIMITIVE, 0).
+-define(CONSTRUCTED, 2#00100000).
+
+
+-define(T_ObjectDescriptor, ?UNIVERSAL bor ?PRIMITIVE bor 7).
+ % restricted character string types
+-define(T_NumericString, ?UNIVERSAL bor ?PRIMITIVE bor 18). %can be constructed
+-define(T_PrintableString, ?UNIVERSAL bor ?PRIMITIVE bor 19). %can be constructed
+-define(T_TeletexString, ?UNIVERSAL bor ?PRIMITIVE bor 20). %can be constructed
+-define(T_VideotexString, ?UNIVERSAL bor ?PRIMITIVE bor 21). %can be constructed
+-define(T_IA5String, ?UNIVERSAL bor ?PRIMITIVE bor 22). %can be constructed
+-define(T_GraphicString, ?UNIVERSAL bor ?PRIMITIVE bor 25). %can be constructed
+-define(T_VisibleString, ?UNIVERSAL bor ?PRIMITIVE bor 26). %can be constructed
+-define(T_GeneralString, ?UNIVERSAL bor ?PRIMITIVE bor 27). %can be constructed
+
+%% pgen(Erules, Module, TypeOrVal)
+%% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module
+%% .hrl file is only generated if necessary
+%% Erules = per | ber
+%% Module = atom()
+%% TypeOrVal = {TypeList,ValueList,PTypeList}
+%% TypeList = ValueList = [atom()]
+
+pgen(OutFile,Erules,Module,TypeOrVal) ->
+ asn1ct_gen:pgen_module(OutFile,Erules,Module,TypeOrVal,true).
+
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Generate ENCODING
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% encode #{typedef, {pos, name, typespec}}
+%%===============================================================================
+
+gen_encode(Erules,Type) when record(Type,typedef) ->
+ gen_encode_user(Erules,Type).
+
+%%===============================================================================
+%% encode #{type, {tag, def, constraint}}
+%%===============================================================================
+
+gen_encode(Erules,Typename,Type) when record(Type,type) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ ObjFun =
+ case lists:keysearch(objfun,1,Type#type.tablecinf) of
+ {value,{_,_Name}} ->
+ ", ObjFun";
+ false ->
+ ""
+ end,
+
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ emit([nl,nl,nl,"%%================================"]),
+ emit([nl,"%% ",asn1ct_gen:list2name(Typename)]),
+ emit([nl,"%%================================",nl]),
+ case length(Typename) of
+ 1 -> % top level type
+ emit(["'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val",ObjFun,") ->",nl]),
+ emit([" 'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, ", {asis,lists:reverse(Type#type.tag)},ObjFun,").",nl,nl]);
+ _ -> % embedded type with constructed name
+ true
+ end,
+ case lists:member(InnerType,['SET','SEQUENCE']) of
+ true ->
+ case get(asn_keyed_list) of
+ true ->
+ CompList =
+ case Type#type.def of
+ #'SEQUENCE'{components=Cl} -> Cl;
+ #'SET'{components=Cl} -> Cl
+ end,
+ emit([nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, TagIn",ObjFun,
+ ") when list(Val) ->",nl]),
+ emit([" 'enc_",asn1ct_gen:list2name(Typename),
+ "'(?RT_BER:fixoptionals(",
+ {asis,optionals(CompList)},
+ ",Val), TagIn",ObjFun,");",nl,nl]);
+ _ -> true
+ end;
+ _ ->
+ emit([nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'({'",asn1ct_gen:list2name(Typename),
+ "',Val}, TagIn",ObjFun,") ->",nl]),
+ emit([" 'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, TagIn",ObjFun,");",nl,nl])
+ end,
+ emit(["'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, TagIn",ObjFun,") ->",nl," "]),
+ asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
+ _ ->
+ true
+ end;
+
+%%===============================================================================
+%% encode ComponentType
+%%===============================================================================
+
+gen_encode(Erules,Tname,{'ComponentType',_Pos,Cname,Type,_Prop,_Tags}) ->
+ NewTname = [Cname|Tname],
+ %% The tag is set to [] to avoid that it is
+ %% taken into account twice, both as a component/alternative (passed as
+ %% argument to the encode decode function and within the encode decode
+ %% function it self.
+ NewType = Type#type{tag=[]},
+ gen_encode(Erules,NewTname,NewType).
+
+gen_encode_user(Erules,D) when record(D,typedef) ->
+ Typename = [D#typedef.name],
+ Type = D#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ OTag = Type#type.tag,
+ Tag = [encode_tag_val(decode_class(X#tag.class),X#tag.form,X#tag.number)|| X <- OTag],
+ emit([nl,nl,"%%================================"]),
+ emit([nl,"%% ",Typename]),
+ emit([nl,"%%================================",nl]),
+ emit(["'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val",") ->",nl]),
+ emit([" 'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, ", {asis,lists:reverse(Tag)},").",nl,nl]),
+
+ case lists:member(InnerType,['SET','SEQUENCE']) of
+ true ->
+ case get(asn_keyed_list) of
+ true ->
+ CompList =
+ case Type#type.def of
+ #'SEQUENCE'{components=Cl} -> Cl;
+ #'SET'{components=Cl} -> Cl
+ end,
+
+ emit([nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, TagIn) when list(Val) ->",nl]),
+ emit([" 'enc_",asn1ct_gen:list2name(Typename),
+ "'(?RT_BER:fixoptionals(",
+ {asis,optionals(CompList)},
+ ",Val), TagIn);",nl,nl]);
+ _ -> true
+ end;
+ _ ->
+ emit({nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'({'",asn1ct_gen:list2name(Typename),"',Val}, TagIn) ->",nl}),
+ emit({" 'enc_",asn1ct_gen:list2name(Typename),"'(Val, TagIn);",nl,nl})
+ end,
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val, TagIn) ->",nl}),
+ CurrentMod = get(currmod),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,D);
+ {primitive,bif} ->
+ gen_encode_prim(ber,Type,"TagIn","Val"),
+ emit([".",nl]);
+ #typereference{val=Ename} ->
+ emit([" 'enc_",Ename,"'(Val, TagIn).",nl]);
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'enc_",Etype,"'(Val, TagIn).",nl]);
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'enc_",Etype,"'(Val, TagIn).",nl]);
+ 'ASN1_OPEN_TYPE' ->
+ emit(["%% OPEN TYPE",nl]),
+ gen_encode_prim(ber,
+ Type#type{def='ASN1_OPEN_TYPE'},
+ "TagIn","Val"),
+ emit([".",nl])
+ end.
+
+gen_encode_prim(_Erules,D,DoTag,Value) when record(D,type) ->
+
+%%% Constraint is currently not used for BER (except for BitString) and therefore replaced
+%%% with [] as a placeholder
+ BitStringConstraint = D#type.constraint,
+ Constraint = [],
+ asn1ct_name:new(enumval),
+ case D#type.def of
+ 'BOOLEAN' ->
+ emit_encode_func('boolean',Value,DoTag);
+ 'INTEGER' ->
+ emit_encode_func('integer',Constraint,Value,DoTag);
+ {'INTEGER',NamedNumberList} ->
+ emit_encode_func('integer',Constraint,Value,
+ NamedNumberList,DoTag);
+ {'ENUMERATED',NamedNumberList={_,_}} ->
+
+ emit(["case (case ",Value," of {asn1_enum,_}->",Value,";{_,_}->element(2,",Value,");_->",
+ Value," end) of",nl]),
+ emit_enc_enumerated_cases(NamedNumberList,DoTag);
+ {'ENUMERATED',NamedNumberList} ->
+
+ emit(["case (case ",Value," of {_,_}->element(2,",Value,");_->",
+ Value," end) of",nl]),
+ emit_enc_enumerated_cases(NamedNumberList,DoTag);
+
+ {'BIT STRING',NamedNumberList} ->
+ emit_encode_func('bit_string',BitStringConstraint,Value,
+ NamedNumberList,DoTag);
+ 'ANY' ->
+ emit_encode_func('open_type', Value,DoTag);
+ 'NULL' ->
+ emit_encode_func('null',Value,DoTag);
+ 'OBJECT IDENTIFIER' ->
+ emit_encode_func("object_identifier",Value,DoTag);
+ 'ObjectDescriptor' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_ObjectDescriptor,DoTag);
+ 'OCTET STRING' ->
+ emit_encode_func('octet_string',Constraint,Value,DoTag);
+ 'NumericString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_NumericString,DoTag);
+ 'TeletexString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_TeletexString,DoTag);
+ 'VideotexString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_VideotexString,DoTag);
+ 'GraphicString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_GraphicString,DoTag);
+ 'VisibleString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_VisibleString,DoTag);
+ 'GeneralString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_GeneralString,DoTag);
+ 'PrintableString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_PrintableString,DoTag);
+ 'IA5String' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_IA5String,DoTag);
+ 'UniversalString' ->
+ emit_encode_func('universal_string',Constraint,Value,DoTag);
+ 'BMPString' ->
+ emit_encode_func('BMP_string',Constraint,Value,DoTag);
+ 'UTCTime' ->
+ emit_encode_func('utc_time',Constraint,Value,DoTag);
+ 'GeneralizedTime' ->
+ emit_encode_func('generalized_time',Constraint,Value,DoTag);
+ 'ASN1_OPEN_TYPE' ->
+ emit_encode_func('open_type', Value,DoTag);
+ XX ->
+ exit({'can not encode' ,XX})
+ end.
+
+
+emit_encode_func(Name,Value,Tags) when atom(Name) ->
+ emit_encode_func(atom_to_list(Name),Value,Tags);
+emit_encode_func(Name,Value,Tags) ->
+ Fname = "?RT_BER:encode_" ++ Name,
+ emit([Fname,"(",Value,", ",Tags,")"]).
+
+emit_encode_func(Name,Constraint,Value,Tags) when atom(Name) ->
+ emit_encode_func(atom_to_list(Name),Constraint,Value,Tags);
+emit_encode_func(Name,Constraint,Value,Tags) ->
+ Fname = "?RT_BER:encode_" ++ Name,
+ emit([Fname,"(",{asis,Constraint},", ",Value,", ",Tags,")"]).
+
+emit_encode_func(Name,Constraint,Value,Asis,Tags) when atom(Name) ->
+ emit_encode_func(atom_to_list(Name),Constraint,Value,Asis,Tags);
+emit_encode_func(Name,Constraint,Value,Asis,Tags) ->
+ Fname = "?RT_BER:encode_" ++ Name,
+ emit([Fname,"(",{asis,Constraint},", ",Value,
+ ", ",{asis,Asis},
+ ", ",Tags,")"]).
+
+emit_enc_enumerated_cases({L1,L2}, Tags) ->
+ emit_enc_enumerated_cases(L1++L2, Tags, ext);
+emit_enc_enumerated_cases(L, Tags) ->
+ emit_enc_enumerated_cases(L, Tags, noext).
+
+emit_enc_enumerated_cases([{EnumName,EnumVal},H2|T], Tags, Ext) ->
+ emit([{asis,EnumName}," -> ?RT_BER:encode_enumerated(",EnumVal,",",Tags,");",nl]),
+%% emit(["'",{asis,EnumName},"' -> ?RT_BER:encode_enumerated(",EnumVal,",",Tags,");",nl]),
+ emit_enc_enumerated_cases([H2|T], Tags, Ext);
+emit_enc_enumerated_cases([{EnumName,EnumVal}], Tags, Ext) ->
+ emit([{asis,EnumName}," -> ?RT_BER:encode_enumerated(",EnumVal,",",Tags,")"]),
+%% emit(["'",{asis,EnumName},"' -> ?RT_BER:encode_enumerated(",EnumVal,",",Tags,")"]),
+ case Ext of
+ noext -> emit([";",nl]);
+ ext ->
+ emit([";",nl,"{asn1_enum,",{curr,enumval},"} -> ",
+ "?RT_BER:encode_enumerated(",{curr,enumval},",",Tags,");",nl]),
+ asn1ct_name:new(enumval)
+ end,
+ emit([{curr,enumval}," -> exit({error,{asn1, {enumerated_not_in_range,",{curr, enumval},"}}})"]),
+ emit([nl,"end"]).
+
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Generate DECODING
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% decode #{typedef, {pos, name, typespec}}
+%%===============================================================================
+
+gen_decode(Erules,Type) when record(Type,typedef) ->
+ Def = Type#typedef.typespec,
+ InnerTag = Def#type.tag ,
+
+ Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number || X <- InnerTag],
+
+ Prefix =
+ case {asn1ct:get_gen_state_field(active),
+ asn1ct:get_gen_state_field(prefix)} of
+ {true,Pref} -> Pref;
+ _ -> "dec_"
+ end,
+ emit({nl,nl}),
+ emit(["'",Prefix,Type#typedef.name,"'(Tlv) ->",nl]),
+ emit([" '",Prefix,Type#typedef.name,"'(Tlv, ",{asis,Tag},").",nl,nl]),
+ emit(["'",Prefix,Type#typedef.name,"'(Tlv, TagIn) ->",nl]),
+ dbdec(Type#typedef.name),
+ gen_decode_user(Erules,Type).
+
+gen_inc_decode(Erules,Type) when record(Type,typedef) ->
+ Prefix = asn1ct:get_gen_state_field(prefix),
+ emit({nl,nl}),
+ emit(["'",Prefix,Type#typedef.name,"'(Tlv, TagIn) ->",nl]),
+ gen_decode_user(Erules,Type).
+
+%%===============================================================================
+%% decode #{type, {tag, def, constraint}}
+%%===============================================================================
+
+%% This gen_decode is called by the gen_decode/3 that decodes
+%% ComponentType and the type of a SEQUENCE OF/SET OF.
+gen_decode(Erules,Tname,Type) when record(Type,type) ->
+ Typename = Tname,
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ Prefix =
+ case asn1ct:get_gen_state_field(active) of
+ true -> "'dec-inc-";
+ _ -> "'dec_"
+ end,
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ ObjFun =
+ case Type#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _ ->
+ ""
+ end,
+ emit([Prefix,asn1ct_gen:list2name(Typename),"'(Tlv, TagIn",ObjFun,") ->",nl]),
+ dbdec(Typename),
+ asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,Type);
+ Rec when record(Rec,'Externaltypereference') ->
+ case {Typename,asn1ct:get_gen_state_field(namelist)} of
+ {[Cname|_],[{Cname,_}|_]} -> %%
+ %% This referenced type must only be generated
+ %% once as incomplete partial decode. Therefore we
+ %% have to check whether this function already is
+ %% generated.
+ case asn1ct:is_function_generated(Typename) of
+ true ->
+ ok;
+ _ ->
+ asn1ct:generated_refed_func(Typename),
+ #'Externaltypereference'{module=M,type=Name}=Rec,
+ TypeDef = asn1_db:dbget(M,Name),
+ gen_decode(Erules,TypeDef)
+ end;
+ _ ->
+ true
+ end;
+ _ ->
+ true
+ end;
+
+
+%%===============================================================================
+%% decode ComponentType
+%%===============================================================================
+
+gen_decode(Erules,Tname,{'ComponentType',_Pos,Cname,Type,_Prop,_Tags}) ->
+ NewTname = [Cname|Tname],
+ %% The tag is set to [] to avoid that it is
+ %% taken into account twice, both as a component/alternative (passed as
+ %% argument to the encode decode function and within the encode decode
+ %% function it self.
+ NewType = Type#type{tag=[]},
+ case {asn1ct:get_gen_state_field(active),
+ asn1ct:get_tobe_refed_func(NewTname)} of
+ {true,{_,NameList}} ->
+ asn1ct:update_gen_state(namelist,NameList),
+ %% remove to gen_refed_funcs list from tobe_refed_funcs later
+ gen_decode(Erules,NewTname,NewType);
+ {No,_} when No == false; No == undefined ->
+ gen_decode(Erules,NewTname,NewType);
+ _ ->
+ ok
+ end.
+
+
+gen_decode_user(Erules,D) when record(D,typedef) ->
+ Typename = [D#typedef.name],
+ Def = D#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ BytesVar = "Tlv",
+ case asn1ct_gen:type(InnerType) of
+ 'ASN1_OPEN_TYPE' ->
+ asn1ct_name:new(len),
+ gen_dec_prim(ber, Def#type{def='ASN1_OPEN_TYPE'},
+ BytesVar,{string,"TagIn"}, [] ,
+ ?PRIMITIVE,"OptOrMand"),
+ emit({".",nl,nl});
+ {primitive,bif} ->
+ asn1ct_name:new(len),
+ gen_dec_prim(ber, Def, BytesVar,{string,"TagIn"},[] ,
+ ?PRIMITIVE,"OptOrMand"),
+ emit([".",nl,nl]);
+ {constructed,bif} ->
+ asn1ct:update_namelist(D#typedef.name),
+ asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,D);
+ TheType ->
+ DecFunName = mkfuncname(TheType,dec),
+ emit([DecFunName,"(",BytesVar,
+ ", TagIn)"]),
+ emit([".",nl,nl])
+ end.
+
+
+gen_dec_prim(_Erules,Att,BytesVar,DoTag,_TagIn,_Form,_OptOrMand) ->
+ Typename = Att#type.def,
+%% Currently not used for BER replaced with [] as place holder
+%% Constraint = Att#type.constraint,
+%% Constraint = [],
+ Constraint =
+ case get_constraint(Att#type.constraint,'SizeConstraint') of
+ no -> [];
+ Tc -> Tc
+ end,
+ ValueRange =
+ case get_constraint(Att#type.constraint,'ValueRange') of
+ no -> [];
+ Tv -> Tv
+ end,
+ SingleValue =
+ case get_constraint(Att#type.constraint,'SingleValue') of
+ no -> [];
+ Sv -> Sv
+ end,
+ AsBin = case get(binary_strings) of
+ true -> "_as_bin";
+ _ -> ""
+ end,
+ NewTypeName = case Typename of
+ 'ANY' -> 'ASN1_OPEN_TYPE';
+ _ -> Typename
+ end,
+% DoLength =
+ case NewTypeName of
+ 'BOOLEAN'->
+ emit({"?RT_BER:decode_boolean(",BytesVar,","}),
+ add_func({decode_boolean,2});
+ 'INTEGER' ->
+ emit({"?RT_BER:decode_integer(",BytesVar,",",
+ {asis,int_constr(SingleValue,ValueRange)},","}),
+ add_func({decode_integer,3});
+ {'INTEGER',NamedNumberList} ->
+ emit({"?RT_BER:decode_integer(",BytesVar,",",
+ {asis,int_constr(SingleValue,ValueRange)},",",
+ {asis,NamedNumberList},","}),
+ add_func({decode_integer,4});
+ {'ENUMERATED',NamedNumberList} ->
+ emit({"?RT_BER:decode_enumerated(",BytesVar,",",
+ {asis,Constraint},",",
+ {asis,NamedNumberList},","}),
+ add_func({decode_enumerated,4});
+ {'BIT STRING',NamedNumberList} ->
+ case get(compact_bit_string) of
+ true ->
+ emit({"?RT_BER:decode_compact_bit_string(",
+ BytesVar,",",{asis,Constraint},",",
+ {asis,NamedNumberList},","}),
+ add_func({decode_compact_bit_string,4});
+ _ ->
+ emit({"?RT_BER:decode_bit_string(",BytesVar,",",
+ {asis,Constraint},",",
+ {asis,NamedNumberList},","}),
+ add_func({decode_bit_string,4})
+ end;
+ 'NULL' ->
+ emit({"?RT_BER:decode_null(",BytesVar,","}),
+ add_func({decode_null,2});
+ 'OBJECT IDENTIFIER' ->
+ emit({"?RT_BER:decode_object_identifier(",BytesVar,","}),
+ add_func({decode_object_identifier,2});
+ 'ObjectDescriptor' ->
+ emit({"?RT_BER:decode_restricted_string(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_ObjectDescriptor},","}),
+ add_func({decode_restricted_string,4});
+ 'OCTET STRING' ->
+ emit({"?RT_BER:decode_octet_string",AsBin,"(",BytesVar,",",{asis,Constraint},","}),
+ add_func({decode_octet_string,3});
+ 'NumericString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_NumericString},","}),
+ add_func({decode_restricted_string,4});
+ 'TeletexString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_TeletexString},","}),
+ add_func({decode_restricted_string,4});
+ 'VideotexString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_VideotexString},","}),
+ add_func({decode_restricted_string,4});
+ 'GraphicString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_GraphicString},","}),
+ add_func({decode_restricted_string,4});
+ 'VisibleString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_VisibleString},","}),
+ add_func({decode_restricted_string,4});
+ 'GeneralString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_GeneralString},","}),
+ add_func({decode_restricted_string,4});
+ 'PrintableString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_PrintableString},","}),
+ add_func({decode_restricted_string,4});
+ 'IA5String' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_IA5String},","}),
+ add_func({decode_restricted_string,4}) ;
+ 'UniversalString' ->
+ emit({"?RT_BER:decode_universal_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","}),
+ add_func({decode_universal_string,3});
+ 'BMPString' ->
+ emit({"?RT_BER:decode_BMP_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","}),
+ add_func({decode_BMP_string,3});
+ 'UTCTime' ->
+ emit({"?RT_BER:decode_utc_time",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","}),
+ add_func({decode_utc_time,3});
+ 'GeneralizedTime' ->
+ emit({"?RT_BER:decode_generalized_time",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","}),
+ add_func({decode_generalized_time,3});
+ 'ASN1_OPEN_TYPE' ->
+ emit(["?RT_BER:decode_open_type_as_binary(",
+ BytesVar,","]),
+ add_func({decode_open_type_as_binary,2});
+ Other ->
+ exit({'can not decode' ,Other})
+ end,
+
+ case {DoTag,NewTypeName} of
+ {{string,TagStr},'ASN1_OPEN_TYPE'} ->
+ emit([TagStr,")"]);
+ {_,'ASN1_OPEN_TYPE'} ->
+ emit([{asis,DoTag},")"]);
+ {{string,TagStr},_} ->
+ emit([TagStr,")"]);
+ _ when list(DoTag) ->
+ emit([{asis,DoTag},")"])
+ end.
+
+
+int_constr([],[]) ->
+ [];
+int_constr([],ValueRange) ->
+ ValueRange;
+int_constr(SingleValue,[]) ->
+ SingleValue;
+int_constr(SV,VR) ->
+ [SV,VR].
+
+%% Object code generating for encoding and decoding
+%% ------------------------------------------------
+
+gen_obj_code(Erules,_Module,Obj) when record(Obj,typedef) ->
+ ObjName = Obj#typedef.name,
+ Def = Obj#typedef.typespec,
+ #'Externaltypereference'{module=M,type=ClName} = Def#'Object'.classname,
+ Class = asn1_db:dbget(M,ClName),
+ {object,_,Fields} = Def#'Object'.def,
+ emit({nl,nl,nl,"%%================================"}),
+ emit({nl,"%% ",ObjName}),
+ emit({nl,"%%================================",nl}),
+ EncConstructed =
+ gen_encode_objectfields(ClName,get_class_fields(Class),
+ ObjName,Fields,[]),
+ emit(nl),
+ gen_encode_constr_type(Erules,EncConstructed),
+ emit(nl),
+ DecConstructed =
+ gen_decode_objectfields(ClName,get_class_fields(Class),
+ ObjName,Fields,[]),
+ emit(nl),
+ gen_decode_constr_type(Erules,DecConstructed),
+ emit_tlv_format_function();
+gen_obj_code(_Erules,_Module,Obj) when record(Obj,pobjectdef) ->
+ ok.
+
+gen_encode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Arg) ->
+ emit(["'enc_",ObjName,"'(",{asis,Name},
+ ", ",Arg,", _RestPrimFieldName) ->",nl])
+ end,
+% emit(["'enc_",ObjName,"'(",{asis,Name},
+% ", Val, RestPrimFieldName) ->",nl]),
+ MaybeConstr=
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} -> %% this case is illegal
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_"),
+ emit([" {<<>>,0}"]),
+ [];
+ {false,{'DEFAULT',DefaultType}} ->
+ EmitFuncClause("Val"),
+ gen_encode_default_call(ClassName,Name,DefaultType);
+ {{Name,TypeSpec},_} ->
+ %% A specified field owerwrites any 'DEFAULT' or
+ %% 'OPTIONAL' field in the class
+ EmitFuncClause("Val"),
+ gen_encode_field_call(ObjName,Name,TypeSpec)
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_encode_objectfields(ClassName,Rest,ObjName,ObjectFields,
+ MaybeConstr++ConstrAcc);
+gen_encode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Args) ->
+ emit(["'enc_",ObjName,"'(",{asis,Name},
+ ", ",Args,") ->",nl])
+ end,
+% emit(["'enc_",ObjName,"'(",{asis,Name},
+% ", Val,[H|T]) ->",nl]),
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} ->
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_,_"),
+ emit([" exit({error,{'use of missing field in object', ",Name,
+ "}})"]);
+ {false,{'DEFAULT',_DefaultObject}} ->
+ exit({error,{asn1,{"not implemented yet",Name}}});
+ {{Name,TypeSpec},_} ->
+ EmitFuncClause(" Val, [H|T]"),
+ case TypeSpec#typedef.name of
+ {ExtMod,TypeName} ->
+ emit({indent(3),"'",ExtMod,"':'enc_",TypeName,
+ "'(H, Val, T)"});
+ TypeName ->
+ emit({indent(3),"'enc_",TypeName,"'(H, Val, T)"})
+ end
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_encode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc);
+
+% gen_encode_objectfields(Class,ObjName,[{FieldName,Type}|Rest],ConstrAcc) ->
+% Fields = Class#objectclass.fields,
+% MaybeConstr=
+% case is_typefield(Fields,FieldName) of
+% true ->
+% Def = Type#typedef.typespec,
+% emit({"'enc_",ObjName,"'(",{asis,FieldName},
+% ", Val, RestPrimFieldName) ->",nl}),
+% CAcc=
+% case Type#typedef.name of
+% {primitive,bif} -> %%tag should be the primitive tag
+% OTag = Def#type.tag,
+% Tag = [encode_tag_val(decode_class(X#tag.class),
+% X#tag.form,X#tag.number)||
+% X <- OTag],
+% gen_encode_prim(ber,Def,{asis,lists:reverse(Tag)},
+% "Val"),
+% [];
+% {constructed,bif} ->
+% emit({" 'enc_",ObjName,'_',FieldName,
+% "'(Val)"}),
+% [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+% {ExtMod,TypeName} ->
+% emit({" '",ExtMod,"':'enc_",TypeName,
+% "'(Val)"}),
+% [];
+% TypeName ->
+% emit({" 'enc_",TypeName,"'(Val)"}),
+% []
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% CAcc;
+% {false,objectfield} ->
+% emit({"'enc_",ObjName,"'(",{asis,FieldName},
+% ", Val,[H|T]) ->",nl}),
+% case Type#typedef.name of
+% {ExtMod,TypeName} ->
+% emit({indent(3),"'",ExtMod,"':'enc_",TypeName,
+% "'(H, Val, T)"});
+% TypeName ->
+% emit({indent(3),"'enc_",TypeName,"'(H, Val, T)"})
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% [];
+% {false,_} -> []
+% end,
+% gen_encode_objectfields(Class,ObjName,Rest,MaybeConstr ++ ConstrAcc);
+gen_encode_objectfields(ClassName,[_C|Cs],O,OF,Acc) ->
+ gen_encode_objectfields(ClassName,Cs,O,OF,Acc);
+gen_encode_objectfields(_,[],_,_,Acc) ->
+ Acc.
+
+% gen_encode_constr_type(Erules,[{Name,Def}|Rest]) ->
+% emit({Name,"(Val,TagIn) ->",nl}),
+% InnerType = asn1ct_gen:get_inner(Def#type.def),
+% asn1ct_gen:gen_encode_constructed(Erules,Name,InnerType,Def),
+% gen_encode_constr_type(Erules,Rest);
+gen_encode_constr_type(Erules,[TypeDef|Rest]) when record(TypeDef,typedef) ->
+ case is_already_generated(enc,TypeDef#typedef.name) of
+ true -> ok;
+ _ -> gen_encode_user(Erules,TypeDef)
+ end,
+ gen_encode_constr_type(Erules,Rest);
+gen_encode_constr_type(_,[]) ->
+ ok.
+
+gen_encode_field_call(ObjName,FieldName,Type) ->
+ Def = Type#typedef.typespec,
+ OTag = Def#type.tag,
+ Tag = [encode_tag_val(decode_class(X#tag.class),
+ X#tag.form,X#tag.number)||
+ X <- OTag],
+ case Type#typedef.name of
+ {primitive,bif} -> %%tag should be the primitive tag
+% OTag = Def#type.tag,
+% Tag = [encode_tag_val(decode_class(X#tag.class),
+% X#tag.form,X#tag.number)||
+% X <- OTag],
+ gen_encode_prim(ber,Def,{asis,lists:reverse(Tag)},
+ "Val"),
+ [];
+ {constructed,bif} ->
+ emit({" 'enc_",ObjName,'_',FieldName,
+ "'(Val,",{asis,Tag},")"}),
+ [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+ {ExtMod,TypeName} ->
+ emit({" '",ExtMod,"':'enc_",TypeName,
+ "'(Val,",{asis,Tag},")"}),
+ [];
+ TypeName ->
+ emit({" 'enc_",TypeName,"'(Val,",{asis,Tag},")"}),
+ []
+ end.
+
+gen_encode_default_call(ClassName,FieldName,Type) ->
+ CurrentMod = get(currmod),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ OTag = Type#type.tag,
+ Tag = [encode_tag_val(decode_class(X#tag.class),X#tag.form,X#tag.number)|| X <- OTag],
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+%% asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
+ emit([" 'enc_",ClassName,'_',FieldName,"'(Bytes)"]),
+ [#typedef{name=list_to_atom(lists:concat([ClassName,'_',FieldName])),
+ typespec=Type}];
+ {primitive,bif} ->
+ gen_encode_prim(ber,Type,{asis,lists:reverse(Tag)},"Val"),
+ [];
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'enc_",Etype,"'(Val, ",{asis,Tag},")",nl]),
+ [];
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'enc_",Etype,"'(Val, ",{asis,Tag},")",nl]),
+ []
+% 'ASN1_OPEN_TYPE' ->
+% emit(["%% OPEN TYPE",nl]),
+% gen_encode_prim(ber,
+% Type#type{def='ASN1_OPEN_TYPE'},
+% "TagIn","Val"),
+% emit([".",nl])
+ end.
+
+%%%%%%%%%%%%%%%%
+
+gen_decode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Arg) ->
+ emit(["'dec_",ObjName,"'(",{asis,Name},
+ ", ",Arg,",_) ->",nl])
+ end,
+% emit(["'dec_",ObjName,"'(",{asis,Name},
+% ", Bytes, RestPrimFieldName) ->",nl]),
+ MaybeConstr=
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} -> %% this case is illegal
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause(" _"),
+ emit([" asn1_NOVALUE"]),
+ [];
+ {false,{'DEFAULT',DefaultType}} ->
+ EmitFuncClause("Bytes"),
+ emit_tlv_format("Bytes"),
+ gen_decode_default_call(ClassName,Name,"Tlv",DefaultType);
+ {{Name,TypeSpec},_} ->
+ %% A specified field owerwrites any 'DEFAULT' or
+ %% 'OPTIONAL' field in the class
+ EmitFuncClause("Bytes"),
+ emit_tlv_format("Bytes"),
+ gen_decode_field_call(ObjName,Name,"Tlv",TypeSpec)
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,MaybeConstr++ConstrAcc);
+gen_decode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Args) ->
+ emit(["'dec_",ObjName,"'(",{asis,Name},
+ ", ",Args,") ->",nl])
+ end,
+% emit(["'dec_",ObjName,"'(",{asis,Name},
+% ", Bytes,[H|T]) ->",nl]),
+% emit_tlv_format("Bytes"),
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} ->
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_,_"),
+ emit([" exit({error,{'illegal use of missing field in object', ",Name,
+ "}})"]);
+ {false,{'DEFAULT',_DefaultObject}} ->
+ exit({error,{asn1,{"not implemented yet",Name}}});
+ {{Name,TypeSpec},_} ->
+ EmitFuncClause("Bytes,[H|T]"),
+% emit_tlv_format("Bytes"),
+ case TypeSpec#typedef.name of
+ {ExtMod,TypeName} ->
+ emit({indent(3),"'",ExtMod,"':'dec_",TypeName,
+ "'(H, Bytes, T)"});
+ TypeName ->
+ emit({indent(3),"'dec_",TypeName,"'(H, Bytes, T)"})
+ end
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc);
+gen_decode_objectfields(CN,[_C|Cs],O,OF,CAcc) ->
+ gen_decode_objectfields(CN,Cs,O,OF,CAcc);
+gen_decode_objectfields(_,[],_,_,CAcc) ->
+ CAcc.
+
+emit_tlv_format(Bytes) ->
+ notice_tlv_format_gen(), % notice for generating of tlv_format/1
+ emit([" Tlv = tlv_format(",Bytes,"),",nl]).
+
+notice_tlv_format_gen() ->
+ Module = get(currmod),
+% io:format("Noticed: ~p~n",[Module]),
+ case get(tlv_format) of
+ {done,Module} ->
+ ok;
+ _ -> % true or undefined
+ put(tlv_format,true)
+ end.
+
+emit_tlv_format_function() ->
+ Module = get(currmod),
+% io:format("Tlv formated: ~p",[Module]),
+ case get(tlv_format) of
+ true ->
+% io:format(" YES!~n"),
+ emit_tlv_format_function1(),
+ put(tlv_format,{done,Module});
+ _ ->
+% io:format(" NO!~n"),
+ ok
+ end.
+emit_tlv_format_function1() ->
+ emit(["tlv_format(Bytes) when binary(Bytes) ->",nl,
+ " {Tlv,_}=?RT_BER:decode(Bytes),",nl,
+ " Tlv;",nl,
+ "tlv_format(Bytes) ->",nl,
+ " Bytes.",nl]).
+
+
+gen_decode_constr_type(Erules,[{Name,Def}|Rest]) ->
+ emit([Name,"(Tlv, TagIn) ->",nl]),
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ asn1ct_gen:gen_decode_constructed(Erules,Name,InnerType,Def),
+ gen_decode_constr_type(Erules,Rest);
+gen_decode_constr_type(Erules,[TypeDef|Rest]) when record(TypeDef,typedef) ->
+ case is_already_generated(dec,TypeDef#typedef.name) of
+ true -> ok;
+ _ ->
+ gen_decode(Erules,TypeDef)
+ end,
+ gen_decode_constr_type(Erules,Rest);
+gen_decode_constr_type(_,[]) ->
+ ok.
+
+%%%%%%%%%%%
+gen_decode_field_call(ObjName,FieldName,Bytes,Type) ->
+ Def = Type#typedef.typespec,
+ OTag = Def#type.tag,
+ Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number ||
+ X <- OTag],
+ case Type#typedef.name of
+ {primitive,bif} -> %%tag should be the primitive tag
+ gen_dec_prim(ber,Def,Bytes,Tag,"TagIn",?PRIMITIVE,
+ opt_or_default),
+ [];
+ {constructed,bif} ->
+ emit({" 'dec_",ObjName,'_',FieldName,
+ "'(",Bytes,",",{asis,Tag},")"}),
+ [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+ {ExtMod,TypeName} ->
+ emit({" '",ExtMod,"':'dec_",TypeName,
+ "'(",Bytes,",",{asis,Tag},")"}),
+ [];
+ TypeName ->
+ emit({" 'dec_",TypeName,"'(",Bytes,",",{asis,Tag},")"}),
+ []
+ end.
+
+gen_decode_default_call(ClassName,FieldName,Bytes,Type) ->
+ CurrentMod = get(currmod),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ OTag = Type#type.tag,
+ Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number || X <- OTag],
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ emit([" 'dec_",ClassName,'_',FieldName,"'(",Bytes,",",
+ {asis,Tag},")"]),
+ [#typedef{name=list_to_atom(lists:concat([ClassName,'_',
+ FieldName])),
+ typespec=Type}];
+ {primitive,bif} ->
+ gen_dec_prim(ber,Type,Bytes,Tag,"TagIn",
+ ?PRIMITIVE,opt_or_default),
+ [];
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'dec_",Etype,"'(",Bytes, " ,",{asis,Tag},")",nl]),
+ [];
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'dec_",Etype,"'(",Bytes,", ",
+ {asis,Tag},")",nl]),
+ []
+% 'ASN1_OPEN_TYPE' ->
+% emit(["%% OPEN TYPE",nl]),
+% gen_encode_prim(ber,
+% Type#type{def='ASN1_OPEN_TYPE'},
+% "TagIn","Val"),
+% emit([".",nl])
+ end.
+%%%%%%%%%%%
+
+is_already_generated(Operation,Name) ->
+ case get(class_default_type) of
+ undefined ->
+ put(class_default_type,[{Operation,Name}]),
+ false;
+ GeneratedList ->
+ case lists:member({Operation,Name},GeneratedList) of
+ true ->
+ true;
+ false ->
+ put(class_default_type,[{Operation,Name}|GeneratedList]),
+ false
+ end
+ end.
+
+more_genfields([]) ->
+ false;
+more_genfields([Field|Fields]) ->
+ case element(1,Field) of
+ typefield ->
+ true;
+ objectfield ->
+ true;
+ _ ->
+ more_genfields(Fields)
+ end.
+
+
+
+
+%% Object Set code generating for encoding and decoding
+%% ----------------------------------------------------
+gen_objectset_code(Erules,ObjSet) ->
+ ObjSetName = ObjSet#typedef.name,
+ Def = ObjSet#typedef.typespec,
+% {ClassName,ClassDef} = Def#'ObjectSet'.class,
+ #'Externaltypereference'{module=ClassModule,
+ type=ClassName} = Def#'ObjectSet'.class,
+ ClassDef = asn1_db:dbget(ClassModule,ClassName),
+ UniqueFName = Def#'ObjectSet'.uniquefname,
+ Set = Def#'ObjectSet'.set,
+ emit({nl,nl,nl,"%%================================"}),
+ emit({nl,"%% ",ObjSetName}),
+ emit({nl,"%%================================",nl}),
+ case ClassName of
+ {_Module,ExtClassName} ->
+ gen_objset_code(Erules,ObjSetName,UniqueFName,Set,ExtClassName,ClassDef);
+ _ ->
+ gen_objset_code(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassDef)
+ end,
+ emit(nl).
+
+gen_objset_code(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassDef)->
+ ClassFields = get_class_fields(ClassDef),
+ InternalFuncs=gen_objset_enc(Erules,ObjSetName,UniqueFName,Set,
+ ClassName,ClassFields,1,[]),
+ gen_objset_dec(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassFields,1),
+ gen_internal_funcs(Erules,InternalFuncs).
+
+%% gen_objset_enc iterates over the objects of the object set
+gen_objset_enc(_,_,{unique,undefined},_,_,_,_,_) ->
+ %% There is no unique field in the class of this object set
+ %% don't bother about the constraint
+ [];
+gen_objset_enc(Erules,ObjSName,UniqueName,
+ [{ObjName,Val,Fields},T|Rest],ClName,ClFields,
+ NthObj,Acc)->
+ emit({"'getenc_",ObjSName,"'(",{asis,UniqueName},",",{asis,Val},
+ ") ->",nl}),
+ {InternalFunc,NewNthObj}=
+ case ObjName of
+ no_name ->
+ gen_inlined_enc_funs(Fields,ClFields,ObjSName,NthObj);
+ _ ->
+ emit({" fun 'enc_",ObjName,"'/3"}),
+ {[],NthObj}
+ end,
+ emit({";",nl}),
+ gen_objset_enc(Erules,ObjSName,UniqueName,[T|Rest],ClName,ClFields,
+ NewNthObj,InternalFunc ++ Acc);
+gen_objset_enc(_,ObjSetName,UniqueName,
+ [{ObjName,Val,Fields}],_ClName,ClFields,NthObj,Acc) ->
+ emit({"'getenc_",ObjSetName,"'(",{asis,UniqueName},",",
+ {asis,Val},") ->",nl}),
+ {InternalFunc,_} =
+ case ObjName of
+ no_name ->
+ gen_inlined_enc_funs(Fields,ClFields,ObjSetName,NthObj);
+ _ ->
+ emit({" fun 'enc_",ObjName,"'/3"}),
+ {[],NthObj}
+ end,
+ emit({".",nl,nl}),
+ InternalFunc ++ Acc;
+%% See X.681 Annex E for the following case
+gen_objset_enc(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
+ _ClFields,_NthObj,Acc) ->
+ emit({"'getenc_",ObjSetName,"'(_, _) ->",nl}),
+ emit({indent(3),"fun(_, Val, _RestPrimFieldName) ->",nl}),
+ emit({indent(6),"Len = case Val of",nl,indent(9),
+ "Bin when binary(Bin) -> size(Bin);",nl,indent(9),
+ "_ -> length(Val)",nl,indent(6),"end,"}),
+ emit({indent(6),"{Val,Len}",nl}),
+ emit({indent(3),"end.",nl,nl}),
+ Acc;
+gen_objset_enc(_,_,_,[],_,_,_,Acc) ->
+ Acc.
+
+%% gen_inlined_enc_funs for each object iterates over all fields of a
+%% class, and for each typefield it checks if the object has that
+%% field and emits the proper code.
+gen_inlined_enc_funs(Fields,[{typefield,Name,_}|Rest],
+ ObjSetName,NthObj) ->
+ InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({indent(3),"fun(Type, Val, _RestPrimFieldName) ->",nl,
+ indent(6),"case Type of",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+N,Ret);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({indent(3),"fun(Type, Val, _RestPrimFieldName) ->",nl,
+ indent(6),"case Type of",nl}),
+ emit({indent(9),{asis,Name}," ->",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+N,Ret);
+ false ->
+ gen_inlined_enc_funs(Fields,Rest,ObjSetName,NthObj)
+ end;
+gen_inlined_enc_funs(Fields,[_|Rest],ObjSetName,NthObj) ->
+ gen_inlined_enc_funs(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_enc_funs(_,[],_,NthObj) ->
+ {[],NthObj}.
+
+gen_inlined_enc_funs1(Fields,[{typefield,Name,_}|Rest],ObjSetName,
+ NthObj,Acc) ->
+ InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
+ {Acc2,NAdd}=
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({";",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ {Ret++Acc,N};
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({";",nl,indent(9),{asis,Name}," ->",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ {Ret++Acc,N};
+ false ->
+ {Acc,0}
+ end,
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+NAdd,Acc2);
+gen_inlined_enc_funs1(Fields,[_|Rest],ObjSetName,NthObj,Acc)->
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj,Acc);
+gen_inlined_enc_funs1(_,[],_,NthObj,Acc) ->
+ emit({nl,indent(6),"end",nl}),
+ emit({indent(3),"end"}),
+ {Acc,NthObj}.
+
+emit_inner_of_fun(TDef=#typedef{name={ExtMod,Name},typespec=Type},
+ InternalDefFunName) ->
+ OTag = Type#type.tag,
+ Tag = [encode_tag_val(decode_class(X#tag.class),X#tag.form,X#tag.number)|| X <- OTag],
+% remove Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ case {ExtMod,Name} of
+ {primitive,bif} ->
+ emit(indent(12)),
+ gen_encode_prim(ber,Type,[{asis,lists:reverse(Tag)}],"Val"),
+ {[],0};
+ {constructed,bif} ->
+ emit([indent(12),"'enc_",
+ InternalDefFunName,"'(Val)"]),
+ {[TDef#typedef{name=InternalDefFunName}],1};
+ _ ->
+ emit({indent(12),"'",ExtMod,"':'enc_",Name,"'(Val)"}),
+ {[],0}
+ end;
+emit_inner_of_fun(#typedef{name=Name},_) ->
+% OTag = Type#type.tag,
+% remove Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+% Tag = [encode_tag_val(decode_class(X#tag.class),X#tag.form,X#tag.number)|| X <- OTag],
+ emit({indent(12),"'enc_",Name,"'(Val)"}),
+ {[],0};
+emit_inner_of_fun(Type,_) when record(Type,type) ->
+ CurrMod = get(currmod),
+% OTag = Type#type.tag,
+% remove Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+% Tag = [encode_tag_val(decode_class(X#tag.class),X#tag.form,X#tag.number)|| X <- OTag],
+ case Type#type.def of
+ Def when atom(Def) ->
+ OTag = Type#type.tag,
+ Tag = [encode_tag_val(decode_class(X#tag.class),
+ X#tag.form,X#tag.number)||X <- OTag],
+ emit([indent(9),Def," ->",nl,indent(12)]),
+ gen_encode_prim(ber,Type,{asis,lists:reverse(Tag)},"Val");
+ TRef when record(TRef,typereference) ->
+ T = TRef#typereference.val,
+ emit([indent(9),T," ->",nl,indent(12),"'enc_",T,
+ "'(Val)"]);
+ #'Externaltypereference'{module=CurrMod,type=T} ->
+ emit([indent(9),T," ->",nl,indent(12),"'enc_",T,
+ "'(Val)"]);
+ #'Externaltypereference'{module=ExtMod,type=T} ->
+ emit([indent(9),T," ->",nl,indent(12),ExtMod,":'enc_",
+ T,"'(Val)"])
+ end,
+ {[],0}.
+
+indent(N) ->
+ lists:duplicate(N,32). % 32 = space
+
+
+gen_objset_dec(_,_,{unique,undefined},_,_,_,_) ->
+ %% There is no unique field in the class of this object set
+ %% don't bother about the constraint
+ ok;
+gen_objset_dec(Erules,ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest],
+ ClName,ClFields,NthObj)->
+ emit(["'getdec_",ObjSName,"'(",{asis,UniqueName},",",
+ {asis,Val},") ->",nl]),
+ NewNthObj=
+ case ObjName of
+ no_name ->
+ gen_inlined_dec_funs(Fields,ClFields,ObjSName,NthObj);
+ _ ->
+ emit([" fun 'dec_",ObjName,"'/3"]),
+ NthObj
+ end,
+ emit([";",nl]),
+ gen_objset_dec(Erules,ObjSName,UniqueName,[T|Rest],ClName,
+ ClFields,NewNthObj);
+gen_objset_dec(_,ObjSetName,UniqueName,[{ObjName,Val,Fields}],
+ _ClName,ClFields,NthObj) ->
+ emit(["'getdec_",ObjSetName,"'(",{asis,UniqueName},",",
+ {asis,Val},") ->",nl]),
+ case ObjName of
+ no_name ->
+ gen_inlined_dec_funs(Fields,ClFields,ObjSetName,NthObj);
+ _ ->
+ emit([" fun 'dec_",ObjName,"'/3"])
+ end,
+ emit([".",nl,nl]),
+ ok;
+gen_objset_dec(Erules,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
+ _ClFields,_NthObj) ->
+ emit(["'getdec_",ObjSetName,"'(_, _) ->",nl]),
+ emit([indent(2),"fun(_,Bytes, _RestPrimFieldName) ->",nl]),
+ case Erules of
+ ber_bin_v2 ->
+ emit([indent(4),"case Bytes of",nl,
+ indent(6),"Bin when binary(Bin) -> ",nl,
+ indent(8),"Bin;",nl,
+ indent(6),"_ ->",nl,
+ indent(8),"?RT_BER:encode(Bytes)",nl,
+ indent(4),"end",nl]);
+ _ ->
+ emit([indent(6),"Len = case Bytes of",nl,indent(9),
+ "Bin when binary(Bin) -> size(Bin);",nl,indent(9),
+ "_ -> length(Bytes)",nl,indent(6),"end,"]),
+ emit([indent(4),"{Bytes,[],Len}",nl])
+ end,
+ emit([indent(2),"end.",nl,nl]),
+ ok;
+gen_objset_dec(_,_,_,[],_,_,_) ->
+ ok.
+
+gen_inlined_dec_funs(Fields,[{typefield,Name,Prop}|Rest],
+ ObjSetName,NthObj) ->
+ DecProp = case Prop of
+ 'OPTIONAL' -> opt_or_default;
+ {'DEFAULT',_} -> opt_or_default;
+ _ -> mandatory
+ end,
+ InternalDefFunName = [NthObj,Name,ObjSetName],
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit([indent(3),"fun(Type, Bytes, _RestPrimFieldName) ->",
+ nl,indent(6),"case Type of",nl]),
+ N=emit_inner_of_decfun(Type,DecProp,InternalDefFunName),
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit([indent(3),"fun(Type, Bytes, _RestPrimFieldName) ->",
+ nl,indent(6),"case Type of",nl]),
+ emit([indent(9),{asis,Name}," ->",nl]),
+ N=emit_inner_of_decfun(Type,DecProp,InternalDefFunName),
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N);
+ false ->
+ gen_inlined_dec_funs(Fields,Rest,ObjSetName,NthObj)
+ end;
+gen_inlined_dec_funs(Fields,[_H|Rest],ObjSetName,NthObj) ->
+ gen_inlined_dec_funs(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_dec_funs(_,[],_,NthObj) ->
+ NthObj.
+
+gen_inlined_dec_funs1(Fields,[{typefield,Name,Prop}|Rest],
+ ObjSetName,NthObj) ->
+ DecProp = case Prop of
+ 'OPTIONAL' -> opt_or_default;
+ {'DEFAULT',_} -> opt_or_default;
+ _ -> mandatory
+ end,
+ InternalDefFunName = [NthObj,Name,ObjSetName],
+ N=
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit([";",nl]),
+ emit_inner_of_decfun(Type,DecProp,InternalDefFunName);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit([";",nl,indent(9),{asis,Name}," ->",nl]),
+ emit_inner_of_decfun(Type,DecProp,InternalDefFunName);
+ false ->
+ 0
+ end,
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N);
+gen_inlined_dec_funs1(Fields,[_|Rest],ObjSetName,NthObj)->
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_dec_funs1(_,[],_,NthObj) ->
+ emit([nl,indent(6),"end",nl]),
+ emit([indent(3),"end"]),
+ NthObj.
+
+emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type},Prop,
+ InternalDefFunName) ->
+ OTag = Type#type.tag,
+%% Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number || X <- OTag],
+ case {ExtName,Name} of
+ {primitive,bif} ->
+ emit(indent(12)),
+ gen_dec_prim(ber,Type,"Bytes",Tag,"TagIn",
+ ?PRIMITIVE,Prop),
+ 0;
+ {constructed,bif} ->
+ emit([indent(12),"'dec_",
+% asn1ct_gen:list2name(InternalDefFunName),"'(Bytes, ",Prop,
+% ", ",{asis,Tag},")"]),
+ asn1ct_gen:list2name(InternalDefFunName),"'(Bytes, ",
+ {asis,Tag},")"]),
+ 1;
+ _ ->
+ emit([indent(12),"'",ExtName,"':'dec_",Name,"'(Bytes)"]),
+ 0
+ end;
+emit_inner_of_decfun(#typedef{name=Name},_Prop,_) ->
+ emit([indent(12),"'dec_",Name,"'(Bytes)"]),
+ 0;
+emit_inner_of_decfun(Type,Prop,_) when record(Type,type) ->
+ OTag = Type#type.tag,
+%% Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number || X <- OTag],
+ CurrMod = get(currmod),
+ Def = Type#type.def,
+ InnerType = asn1ct_gen:get_inner(Def),
+ WhatKind = asn1ct_gen:type(InnerType),
+ case WhatKind of
+ {primitive,bif} ->
+ emit([indent(9),Def," ->",nl,indent(12)]),
+ gen_dec_prim(ber,Type,"Bytes",Tag,"TagIn",
+ ?PRIMITIVE,Prop);
+ #'Externaltypereference'{module=CurrMod,type=T} ->
+ emit([indent(9),T," ->",nl,indent(12),"'dec_",T,
+% "'(Bytes, ",Prop,")"]);
+ "'(Bytes)"]);
+ #'Externaltypereference'{module=ExtMod,type=T} ->
+ emit([indent(9),T," ->",nl,indent(12),ExtMod,":'dec_",
+% T,"'(Bytes, ",Prop,")"])
+ T,"'(Bytes)"])
+ end,
+ 0.
+
+gen_internal_funcs(_,[]) ->
+ ok;
+gen_internal_funcs(Erules,[TypeDef|Rest]) ->
+ gen_encode_user(Erules,TypeDef),
+ emit([nl,nl,"'dec_",TypeDef#typedef.name,
+% "'(Tlv, OptOrMand, TagIn) ->",nl]),
+ "'(Tlv, TagIn) ->",nl]),
+ gen_decode_user(Erules,TypeDef),
+ gen_internal_funcs(Erules,Rest).
+
+
+dbdec(Type) ->
+ demit({"io:format(\"decoding: ",{asis,Type},"~w~n\",[Bytes]),",nl}).
+
+
+decode_class('UNIVERSAL') ->
+ ?UNIVERSAL;
+decode_class('APPLICATION') ->
+ ?APPLICATION;
+decode_class('CONTEXT') ->
+ ?CONTEXT;
+decode_class('PRIVATE') ->
+ ?PRIVATE.
+
+decode_type('BOOLEAN') -> 1;
+decode_type('INTEGER') -> 2;
+decode_type('BIT STRING') -> 3;
+decode_type('OCTET STRING') -> 4;
+decode_type('NULL') -> 5;
+decode_type('OBJECT IDENTIFIER') -> 6;
+decode_type('OBJECT DESCRIPTOR') -> 7;
+decode_type('EXTERNAL') -> 8;
+decode_type('REAL') -> 9;
+decode_type('ENUMERATED') -> 10;
+decode_type('EMBEDDED_PDV') -> 11;
+decode_type('SEQUENCE') -> 16;
+decode_type('SEQUENCE OF') -> 16;
+decode_type('SET') -> 17;
+decode_type('SET OF') -> 17;
+decode_type('NumericString') -> 18;
+decode_type('PrintableString') -> 19;
+decode_type('TeletexString') -> 20;
+decode_type('VideotexString') -> 21;
+decode_type('IA5String') -> 22;
+decode_type('UTCTime') -> 23;
+decode_type('GeneralizedTime') -> 24;
+decode_type('GraphicString') -> 25;
+decode_type('VisibleString') -> 26;
+decode_type('GeneralString') -> 27;
+decode_type('UniversalString') -> 28;
+decode_type('BMPString') -> 30;
+decode_type('CHOICE') -> 'CHOICE'; % choice gets the tag from the actual alternative
+decode_type(Else) -> exit({error,{asn1,{unrecognized_type,Else}}}).
+
+add_removed_bytes() ->
+ asn1ct_name:delete(rb),
+ add_removed_bytes(asn1ct_name:all(rb)).
+
+add_removed_bytes([H,T1|T]) ->
+ emit({{var,H},"+"}),
+ add_removed_bytes([T1|T]);
+add_removed_bytes([H|T]) ->
+ emit({{var,H}}),
+ add_removed_bytes(T);
+add_removed_bytes([]) ->
+ true.
+
+mkfuncname(WhatKind,DecOrEnc) ->
+ case WhatKind of
+ #'Externaltypereference'{module=Mod,type=EType} ->
+ CurrMod = get(currmod),
+ case CurrMod of
+ Mod ->
+ lists:concat(["'",DecOrEnc,"_",EType,"'"]);
+ _ ->
+% io:format("CurrMod: ~p, Mod: ~p~n",[CurrMod,Mod]),
+ lists:concat(["'",Mod,"':'",DecOrEnc,"_",EType,"'"])
+ end;
+ #'typereference'{val=EType} ->
+ lists:concat(["'",DecOrEnc,"_",EType,"'"]);
+ 'ASN1_OPEN_TYPE' ->
+ lists:concat(["'",DecOrEnc,"_",WhatKind,"'"])
+
+ end.
+
+optionals(L) -> optionals(L,[],1).
+
+optionals([{'EXTENSIONMARK',_,_}|Rest],Acc,Pos) ->
+ optionals(Rest,Acc,Pos); % optionals in extension are currently not handled
+optionals([#'ComponentType'{name=Name,prop='OPTIONAL'}|Rest],Acc,Pos) ->
+ optionals(Rest,[{Name,Pos}|Acc],Pos+1);
+optionals([#'ComponentType'{name=Name,prop={'DEFAULT',_}}|Rest],Acc,Pos) ->
+ optionals(Rest,[{Name,Pos}|Acc],Pos+1);
+optionals([#'ComponentType'{}|Rest],Acc,Pos) ->
+ optionals(Rest,Acc,Pos+1);
+optionals([],Acc,_) ->
+ lists:reverse(Acc).
+
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V
+ end.
+
+
+get_class_fields(#classdef{typespec=ObjClass}) ->
+ ObjClass#objectclass.fields;
+get_class_fields(#objectclass{fields=Fields}) ->
+ Fields;
+get_class_fields(_) ->
+ [].
+
+get_object_field(Name,ObjectFields) ->
+ case lists:keysearch(Name,1,ObjectFields) of
+ {value,Field} -> Field;
+ false -> false
+ end.
+
+%%encode_tag(TagClass(?UNI, APP etc), Form (?PRIM etx), TagInteger) ->
+%% 8bit Int | binary
+encode_tag_val(Class, Form, TagNo) when (TagNo =< 30) ->
+ <<(Class bsr 6):2,(Form bsr 5):1,TagNo:5>>;
+
+encode_tag_val(Class, Form, TagNo) ->
+ {Octets,_Len} = mk_object_val(TagNo),
+ BinOct = list_to_binary(Octets),
+ <<(Class bsr 6):2, (Form bsr 5):1, 31:5,BinOct/binary>>.
+
+%%%%%%%%%%%
+%% mk_object_val(Value) -> {OctetList, Len}
+%% returns a Val as a list of octets, the 8 bit is allways set to one except
+%% for the last octet, where its 0
+%%
+
+
+mk_object_val(Val) when Val =< 127 ->
+ {[255 band Val], 1};
+mk_object_val(Val) ->
+ mk_object_val(Val bsr 7, [Val band 127], 1).
+mk_object_val(0, Ack, Len) ->
+ {Ack, Len};
+mk_object_val(Val, Ack, Len) ->
+ mk_object_val(Val bsr 7, [((Val band 127) bor 128) | Ack], Len + 1).
+
+add_func(F={_Func,_Arity}) ->
+ ets:insert(asn1_functab,{F}).
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen_per.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen_per.erl
new file mode 100644
index 0000000000..b5c70fd856
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen_per.erl
@@ -0,0 +1,1189 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_gen_per.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1ct_gen_per).
+
+%% Generate erlang module which handles (PER) encode and decode for
+%% all types in an ASN.1 module
+
+-include("asn1_records.hrl").
+%-compile(export_all).
+
+-export([pgen/4,gen_dec_prim/3,gen_encode_prim/4]).
+-export([gen_obj_code/3,gen_objectset_code/2]).
+-export([gen_decode/2, gen_decode/3]).
+-export([gen_encode/2, gen_encode/3]).
+-export([is_already_generated/2,more_genfields/1,get_class_fields/1,
+ get_object_field/2]).
+
+-import(asn1ct_gen, [emit/1,demit/1]).
+
+%% pgen(Erules, Module, TypeOrVal)
+%% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module
+%% .hrl file is only generated if necessary
+%% Erules = per | ber
+%% Module = atom()
+%% TypeOrVal = {TypeList,ValueList}
+%% TypeList = ValueList = [atom()]
+
+pgen(OutFile,Erules,Module,TypeOrVal) ->
+ asn1ct_gen:pgen_module(OutFile,Erules,Module,TypeOrVal,true).
+
+
+%% Generate ENCODING ******************************
+%%****************************************x
+
+
+gen_encode(Erules,Type) when record(Type,typedef) ->
+ gen_encode_user(Erules,Type).
+%% case Type#typedef.typespec of
+%% Def when record(Def,type) ->
+%% gen_encode_user(Erules,Type);
+%% Def when tuple(Def),(element(1,Def) == 'Object') ->
+%% gen_encode_object(Erules,Type);
+%% Other ->
+%% exit({error,{asn1,{unknown,Other}}})
+%% end.
+
+gen_encode(Erules,Typename,#'ComponentType'{name=Cname,typespec=Type}) ->
+ NewTypename = [Cname|Typename],
+ gen_encode(Erules,NewTypename,Type);
+
+gen_encode(Erules,Typename,Type) when record(Type,type) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ ObjFun =
+ case lists:keysearch(objfun,1,Type#type.tablecinf) of
+ {value,{_,_Name}} ->
+%% lists:concat([", ObjFun",Name]);
+ ", ObjFun";
+ false ->
+ ""
+ end,
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ case InnerType of
+ 'SET' ->
+ true;
+ 'SEQUENCE' ->
+ true;
+ _ ->
+ emit({nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'({'",asn1ct_gen:list2name(Typename),
+ "',Val}",ObjFun,") ->",nl}),
+ emit({"'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val",ObjFun,");",nl,nl})
+ end,
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val",ObjFun,
+ ") ->",nl}),
+ asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
+ _ ->
+ true
+ end.
+
+
+gen_encode_user(Erules,D) when record(D,typedef) ->
+ CurrMod = get(currmod),
+ Typename = [D#typedef.name],
+ Def = D#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ case InnerType of
+ 'SET' -> true;
+ 'SEQUENCE' -> true;
+ _ ->
+ emit({nl,"'enc_",asn1ct_gen:list2name(Typename),"'({'",asn1ct_gen:list2name(Typename),"',Val}) ->",nl}),
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val);",nl,nl})
+ end,
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val) ->",nl}),
+ case asn1ct_gen:type(InnerType) of
+ {primitive,bif} ->
+ gen_encode_prim(Erules,Def,"false"),
+ emit({".",nl});
+ 'ASN1_OPEN_TYPE' ->
+ gen_encode_prim(Erules,Def#type{def='ASN1_OPEN_TYPE'},"false"),
+ emit({".",nl});
+ {constructed,bif} ->
+ asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,D);
+ #'Externaltypereference'{module=CurrMod,type=Etype} ->
+ emit({"'enc_",Etype,"'(Val).",nl,nl});
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit({"'",Emod,"':'enc_",Etype,"'(Val).",nl,nl});
+ #typereference{val=Ename} ->
+ emit({"'enc_",Ename,"'(Val).",nl,nl});
+ {notype,_} ->
+ emit({"'enc_",InnerType,"'(Val).",nl,nl})
+ end.
+
+
+gen_encode_prim(Erules,D,DoTag) ->
+ Value = case asn1ct_name:active(val) of
+ true ->
+ asn1ct_gen:mk_var(asn1ct_name:curr(val));
+ false ->
+ "Val"
+ end,
+ gen_encode_prim(Erules,D,DoTag,Value).
+
+gen_encode_prim(_Erules,D,_DoTag,Value) when record(D,type) ->
+ Constraint = D#type.constraint,
+ case D#type.def of
+ 'INTEGER' ->
+ emit({"?RT_PER:encode_integer(", %fel
+ {asis,Constraint},",",Value,")"});
+ {'INTEGER',NamedNumberList} ->
+ emit({"?RT_PER:encode_integer(",
+ {asis,Constraint},",",Value,",",
+ {asis,NamedNumberList},")"});
+ {'ENUMERATED',{Nlist1,Nlist2}} ->
+ NewList = lists:concat([[{0,X}||{X,_} <- Nlist1],['EXT_MARK'],[{1,X}||{X,_} <- Nlist2]]),
+ NewC = [{'ValueRange',{0,length(Nlist1)-1}}],
+ emit(["case (case ",Value," of {_,_}->element(2,",Value,");_->",
+ Value," end) of",nl]),
+ emit_enc_enumerated_cases(NewC, NewList++[{asn1_enum,length(Nlist1)-1}], 0);
+ {'ENUMERATED',NamedNumberList} ->
+ NewList = [X||{X,_} <- NamedNumberList],
+ NewC = [{'ValueRange',{0,length(NewList)-1}}],
+ emit(["case (case ",Value," of {_,_}->element(2,",Value,");_->",
+ Value," end) of",nl]),
+ emit_enc_enumerated_cases(NewC, NewList, 0);
+ {'BIT STRING',NamedNumberList} ->
+ emit({"?RT_PER:encode_bit_string(",
+ {asis,Constraint},",",Value,",",
+ {asis,NamedNumberList},")"});
+ 'NULL' ->
+ emit({"?RT_PER:encode_null(",Value,")"});
+ 'OBJECT IDENTIFIER' ->
+ emit({"?RT_PER:encode_object_identifier(",Value,")"});
+ 'ObjectDescriptor' ->
+ emit({"?RT_PER:encode_ObjectDescriptor(",{asis,Constraint},
+ ",",Value,")"});
+ 'BOOLEAN' ->
+ emit({"?RT_PER:encode_boolean(",Value,")"});
+ 'OCTET STRING' ->
+ emit({"?RT_PER:encode_octet_string(",{asis,Constraint},",",Value,")"});
+ 'NumericString' ->
+ emit({"?RT_PER:encode_NumericString(",{asis,Constraint},",",Value,")"});
+ 'TeletexString' ->
+ emit({"?RT_PER:encode_TeletexString(",{asis,Constraint},",",Value,")"});
+ 'VideotexString' ->
+ emit({"?RT_PER:encode_VideotexString(",{asis,Constraint},",",Value,")"});
+ 'UTCTime' ->
+ emit({"?RT_PER:encode_VisibleString(",{asis,Constraint},",",Value,")"});
+ 'GeneralizedTime' ->
+ emit({"?RT_PER:encode_VisibleString(",{asis,Constraint},",",Value,")"});
+ 'GraphicString' ->
+ emit({"?RT_PER:encode_GraphicString(",{asis,Constraint},",",Value,")"});
+ 'VisibleString' ->
+ emit({"?RT_PER:encode_VisibleString(",{asis,Constraint},",",Value,")"});
+ 'GeneralString' ->
+ emit({"?RT_PER:encode_GeneralString(",{asis,Constraint},",",Value,")"});
+ 'PrintableString' ->
+ emit({"?RT_PER:encode_PrintableString(",{asis,Constraint},",",Value,")"});
+ 'IA5String' ->
+ emit({"?RT_PER:encode_IA5String(",{asis,Constraint},",",Value,")"});
+ 'BMPString' ->
+ emit({"?RT_PER:encode_BMPString(",{asis,Constraint},",",Value,")"});
+ 'UniversalString' ->
+ emit({"?RT_PER:encode_UniversalString(",{asis,Constraint},",",Value,")"});
+ 'ANY' ->
+ emit(["?RT_PER:encode_open_type(", {asis,Constraint}, ",",
+ Value, ")"]);
+ 'ASN1_OPEN_TYPE' ->
+ NewValue = case Constraint of
+ [#'Externaltypereference'{type=Tname}] ->
+ io_lib:format(
+ "?RT_PER:complete(enc_~s(~s))",[Tname,Value]);
+ [#type{def=#'Externaltypereference'{type=Tname}}] ->
+ io_lib:format(
+ "?RT_PER:complete(enc_~s(~s))",[Tname,Value]);
+ _ -> Value
+ end,
+ emit(["?RT_PER:encode_open_type(", {asis,Constraint}, ",",
+ NewValue, ")"]);
+ XX ->
+ exit({asn1_error,nyi,XX})
+ end.
+
+emit_enc_enumerated_cases(C, [H], Count) ->
+ emit_enc_enumerated_case(C, H, Count),
+ emit([";",nl,"EnumVal -> exit({error,{asn1, {enumerated_not_in_range, EnumVal}}})"]),
+ emit([nl,"end"]);
+emit_enc_enumerated_cases(C, ['EXT_MARK'|T], _Count) ->
+ emit_enc_enumerated_cases(C, T, 0);
+emit_enc_enumerated_cases(C, [H1,H2|T], Count) ->
+ emit_enc_enumerated_case(C, H1, Count),
+ emit([";",nl]),
+ emit_enc_enumerated_cases(C, [H2|T], Count+1).
+
+
+
+emit_enc_enumerated_case(_C, {asn1_enum,High}, _) ->
+ emit([
+ "{asn1_enum,EnumV} when integer(EnumV), EnumV > ",High," -> ",
+ "[{bit,1},?RT_PER:encode_small_number(EnumV)]"]);
+emit_enc_enumerated_case(_C, 'EXT_MARK', _Count) ->
+ true;
+emit_enc_enumerated_case(_C, {1,EnumName}, Count) ->
+ emit(["'",EnumName,"' -> [{bit,1},?RT_PER:encode_small_number(",Count,")]"]);
+emit_enc_enumerated_case(C, {0,EnumName}, Count) ->
+ emit(["'",EnumName,"' -> [{bit,0},?RT_PER:encode_integer(",{asis,C},", ",Count,")]"]);
+emit_enc_enumerated_case(C, EnumName, Count) ->
+ emit(["'",EnumName,"' -> ?RT_PER:encode_integer(",{asis,C},", ",Count,")"]).
+
+
+%% Object code generating for encoding and decoding
+%% ------------------------------------------------
+
+gen_obj_code(Erules,_Module,Obj) when record(Obj,typedef) ->
+ ObjName = Obj#typedef.name,
+ Def = Obj#typedef.typespec,
+ #'Externaltypereference'{module=Mod,type=ClassName} =
+ Def#'Object'.classname,
+ Class = asn1_db:dbget(Mod,ClassName),
+ {object,_,Fields} = Def#'Object'.def,
+ emit({nl,nl,nl,"%%================================"}),
+ emit({nl,"%% ",ObjName}),
+ emit({nl,"%%================================",nl}),
+ EncConstructed =
+ gen_encode_objectfields(ClassName,get_class_fields(Class),
+ ObjName,Fields,[]),
+ emit(nl),
+ gen_encode_constr_type(Erules,EncConstructed),
+ emit(nl),
+ DecConstructed =
+ gen_decode_objectfields(ClassName,get_class_fields(Class),
+ ObjName,Fields,[]),
+ emit(nl),
+ gen_decode_constr_type(Erules,DecConstructed),
+ emit(nl);
+gen_obj_code(_,_,Obj) when record(Obj,pobjectdef) ->
+ ok.
+
+
+gen_encode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(V) ->
+ emit(["'enc_",ObjName,"'(",{asis,Name},
+ ",",V,",_RestPrimFieldName) ->",nl])
+ end,
+% emit(["'enc_",ObjName,"'(",{asis,Name},
+% ", Val, _RestPrimFieldName) ->",nl]),
+ MaybeConstr =
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} -> %% this case is illegal
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_"),
+ emit(" []"),
+ [];
+ {false,{'DEFAULT',DefaultType}} ->
+ EmitFuncClause("Val"),
+ gen_encode_default_call(ClassName,Name,DefaultType);
+ {{Name,TypeSpec},_} ->
+ %% A specified field owerwrites any 'DEFAULT' or
+ %% 'OPTIONAL' field in the class
+ EmitFuncClause("Val"),
+ gen_encode_field_call(ObjName,Name,TypeSpec)
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_encode_objectfields(ClassName,Rest,ObjName,ObjectFields,
+ MaybeConstr++ConstrAcc);
+gen_encode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Attrs) ->
+ emit(["'enc_",ObjName,"'(",{asis,Name},
+ ",",Attrs,") ->",nl])
+ end,
+% emit(["'enc_",ObjName,"'(",{asis,Name},
+% ", Val,[H|T]) ->",nl]),
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} ->
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_,_"),
+ emit([" exit({error,{'use of missing field in object', ",Name,
+ "}})"]);
+ {false,{'DEFAULT',_DefaultObject}} ->
+ exit({error,{asn1,{"not implemented yet",Name}}});
+ {{Name,TypeSpec},_} ->
+ EmitFuncClause("Val,[H|T]"),
+ case TypeSpec#typedef.name of
+ {ExtMod,TypeName} ->
+ emit({indent(3),"'",ExtMod,"':'enc_",TypeName,
+ "'(H, Val, T)"});
+ TypeName ->
+ emit({indent(3),"'enc_",TypeName,"'(H, Val, T)"})
+ end
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_encode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc);
+gen_encode_objectfields(ClassName,[_C|Cs],O,OF,Acc) ->
+ gen_encode_objectfields(ClassName,Cs,O,OF,Acc);
+gen_encode_objectfields(_,[],_,_,Acc) ->
+ Acc.
+
+
+% gen_encode_objectfields(Class,ObjName,[{FieldName,Type}|Rest],ConstrAcc) ->
+% Fields = Class#objectclass.fields,
+
+% MaybeConstr =
+% case is_typefield(Fields,FieldName) of
+% true ->
+% Def = Type#typedef.typespec,
+% emit({"'enc_",ObjName,"'(",{asis,FieldName},
+% ", Val, Dummy) ->",nl}),
+
+% CAcc =
+% case Type#typedef.name of
+% {primitive,bif} ->
+% gen_encode_prim(per,Def,"false","Val"),
+% [];
+% {constructed,bif} ->
+% emit({" 'enc_",ObjName,'_',FieldName,
+% "'(Val)"}),
+% [{['enc_',ObjName,'_',FieldName],Def}];
+% {ExtMod,TypeName} ->
+% emit({" '",ExtMod,"':'enc_",TypeName,"'(Val)"}),
+% [];
+% TypeName ->
+% emit({" 'enc_",TypeName,"'(Val)"}),
+% []
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% CAcc;
+% {false,objectfield} ->
+% emit({"'enc_",ObjName,"'(",{asis,FieldName},
+% ", Val, [H|T]) ->",nl}),
+% case Type#typedef.name of
+% {ExtMod,TypeName} ->
+% emit({indent(3),"'",ExtMod,"':'enc_",TypeName,
+% "'(H, Val, T)"});
+% TypeName ->
+% emit({indent(3),"'enc_",TypeName,"'(H, Val, T)"})
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% [];
+% {false,_} -> []
+% end,
+% gen_encode_objectfields(Class,ObjName,Rest,MaybeConstr ++ ConstrAcc);
+% gen_encode_objectfields(C,O,[H|T],Acc) ->
+% gen_encode_objectfields(C,O,T,Acc);
+% gen_encode_objectfields(_,_,[],Acc) ->
+% Acc.
+
+% gen_encode_constr_type(Erules,[{Name,Def}|Rest]) ->
+% emit({Name,"(Val) ->",nl}),
+% InnerType = asn1ct_gen:get_inner(Def#type.def),
+% asn1ct_gen:gen_encode_constructed(Erules,Name,InnerType,Def),
+% gen_encode_constr_type(Erules,Rest);
+gen_encode_constr_type(Erules,[TypeDef|Rest]) when record(TypeDef,typedef) ->
+ case is_already_generated(enc,TypeDef#typedef.name) of
+ true -> ok;
+ _ ->
+ Name = lists:concat(["enc_",TypeDef#typedef.name]),
+ emit({Name,"(Val) ->",nl}),
+ Def = TypeDef#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ asn1ct_gen:gen_encode_constructed(Erules,Name,InnerType,Def),
+ gen_encode_constr_type(Erules,Rest)
+ end;
+gen_encode_constr_type(_,[]) ->
+ ok.
+
+gen_encode_field_call(ObjName,FieldName,Type) ->
+ Def = Type#typedef.typespec,
+ case Type#typedef.name of
+ {primitive,bif} ->
+ gen_encode_prim(per,Def,"false",
+ "Val"),
+ [];
+ {constructed,bif} ->
+ emit({" 'enc_",ObjName,'_',FieldName,
+ "'(Val)"}),
+ [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+ {ExtMod,TypeName} ->
+ emit({" '",ExtMod,"':'enc_",TypeName,
+ "'(Val)"}),
+ [];
+ TypeName ->
+ emit({" 'enc_",TypeName,"'(Val)"}),
+ []
+ end.
+
+gen_encode_default_call(ClassName,FieldName,Type) ->
+ CurrentMod = get(currmod),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+%% asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
+ emit([" 'enc_",ClassName,'_',FieldName,"'(Val)"]),
+ [#typedef{name=list_to_atom(lists:concat([ClassName,'_',FieldName])),
+ typespec=Type}];
+ {primitive,bif} ->
+ gen_encode_prim(per,Type,"false","Val"),
+ [];
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'enc_",Etype,"'(Val)",nl]),
+ [];
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'enc_",Etype,"'(Val)",nl]),
+ []
+ end.
+
+
+gen_decode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Bytes) ->
+ emit(["'dec_",ObjName,"'(",{asis,Name},",",Bytes,
+ ",_,_RestPrimFieldName) ->",nl])
+ end,
+ MaybeConstr=
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} -> %% this case is illegal
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_"),
+ emit([" asn1_NOVALUE"]),
+ [];
+ {false,{'DEFAULT',DefaultType}} ->
+ EmitFuncClause("Bytes"),
+ gen_decode_default_call(ClassName,Name,"Bytes",DefaultType);
+ {{Name,TypeSpec},_} ->
+ %% A specified field owerwrites any 'DEFAULT' or
+ %% 'OPTIONAL' field in the class
+ EmitFuncClause("Bytes"),
+ gen_decode_field_call(ObjName,Name,"Bytes",TypeSpec)
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,MaybeConstr++ConstrAcc);
+gen_decode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Attrs) ->
+ emit(["'dec_",ObjName,"'(",{asis,Name},
+ ",",Attrs,") ->",nl])
+ end,
+% emit(["'dec_",ObjName,"'(",{asis,Name},
+% ", Bytes,_,[H|T]) ->",nl]),
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} ->
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_,_,_"),
+ emit([" exit({error,{'illegal use of missing field in object', ",Name,
+ "}})"]);
+ {false,{'DEFAULT',_DefaultObject}} ->
+ exit({error,{asn1,{"not implemented yet",Name}}});
+ {{Name,TypeSpec},_} ->
+ EmitFuncClause("Bytes,_,[H|T]"),
+ case TypeSpec#typedef.name of
+ {ExtMod,TypeName} ->
+ emit({indent(3),"'",ExtMod,"':'dec_",TypeName,
+ "'(H, Bytes, telltype, T)"});
+ TypeName ->
+ emit({indent(3),"'dec_",TypeName,"'(H, Bytes, telltype, T)"})
+ end
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc);
+gen_decode_objectfields(CN,[_C|Cs],O,OF,CAcc) ->
+ gen_decode_objectfields(CN,Cs,O,OF,CAcc);
+gen_decode_objectfields(_,[],_,_,CAcc) ->
+ CAcc.
+
+
+% gen_decode_objectfields(Class,ObjName,[{FieldName,Type}|Rest],ConstrAcc) ->
+% Fields = Class#objectclass.fields,
+
+% MaybeConstr =
+% case is_typefield(Fields,FieldName) of
+% true ->
+% Def = Type#typedef.typespec,
+% emit({"'dec_",ObjName,"'(",{asis,FieldName},
+% ", Val, Telltype, RestPrimFieldName) ->",nl}),
+
+% CAcc =
+% case Type#typedef.name of
+% {primitive,bif} ->
+% gen_dec_prim(per,Def,"Val"),
+% [];
+% {constructed,bif} ->
+% emit({" 'dec_",ObjName,'_',FieldName,
+% "'(Val, Telltype)"}),
+% [{['dec_',ObjName,'_',FieldName],Def}];
+% {ExtMod,TypeName} ->
+% emit({" '",ExtMod,"':'dec_",TypeName,
+% "'(Val, Telltype)"}),
+% [];
+% TypeName ->
+% emit({" 'dec_",TypeName,"'(Val, Telltype)"}),
+% []
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% CAcc;
+% {false,objectfield} ->
+% emit({"'dec_",ObjName,"'(",{asis,FieldName},
+% ", Val, Telltype, [H|T]) ->",nl}),
+% case Type#typedef.name of
+% {ExtMod,TypeName} ->
+% emit({indent(3),"'",ExtMod,"':'dec_",TypeName,
+% "'(H, Val, Telltype, T)"});
+% TypeName ->
+% emit({indent(3),"'dec_",TypeName,
+% "'(H, Val, Telltype, T)"})
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% [];
+% {false,_} ->
+% []
+% end,
+% gen_decode_objectfields(Class,ObjName,Rest,MaybeConstr ++ ConstrAcc);
+% gen_decode_objectfields(C,O,[H|T],CAcc) ->
+% gen_decode_objectfields(C,O,T,CAcc);
+% gen_decode_objectfields(_,_,[],CAcc) ->
+% CAcc.
+
+
+gen_decode_field_call(ObjName,FieldName,Bytes,Type) ->
+ Def = Type#typedef.typespec,
+ case Type#typedef.name of
+ {primitive,bif} ->
+ gen_dec_prim(per,Def,Bytes),
+ [];
+ {constructed,bif} ->
+ emit({" 'dec_",ObjName,'_',FieldName,
+ "'(",Bytes,",telltype)"}),
+ [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+ {ExtMod,TypeName} ->
+ emit({" '",ExtMod,"':'dec_",TypeName,
+ "'(",Bytes,", telltype)"}),
+ [];
+ TypeName ->
+ emit({" 'dec_",TypeName,"'(",Bytes,", telltype)"}),
+ []
+ end.
+
+gen_decode_default_call(ClassName,FieldName,Bytes,Type) ->
+ CurrentMod = get(currmod),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ emit([" 'dec_",ClassName,'_',FieldName,"'(",Bytes,", telltype)"]),
+ [#typedef{name=list_to_atom(lists:concat([ClassName,'_',FieldName])),
+ typespec=Type}];
+ {primitive,bif} ->
+ gen_dec_prim(per,Type,Bytes),
+ [];
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'dec_",Etype,"'(",Bytes,", telltype)",nl]),
+ [];
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'dec_",Etype,"'(",Bytes,", telltype)",nl]),
+ []
+ end.
+
+
+gen_decode_constr_type(Erules,[{Name,Def}|Rest]) ->
+ emit({Name,"(Bytes,_) ->",nl}),
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ asn1ct_gen:gen_decode_constructed(Erules,Name,InnerType,Def),
+ gen_decode_constr_type(Erules,Rest);
+gen_decode_constr_type(Erules,[TypeDef|Rest]) when record(TypeDef,typedef) ->
+ case is_already_generated(dec,TypeDef#typedef.name) of
+ true -> ok;
+ _ ->
+ gen_decode(Erules,TypeDef)
+ end,
+ gen_decode_constr_type(Erules,Rest);
+gen_decode_constr_type(_,[]) ->
+ ok.
+
+% more_genfields(Fields,[]) ->
+% false;
+% more_genfields(Fields,[{FieldName,_}|T]) ->
+% case is_typefield(Fields,FieldName) of
+% true -> true;
+% {false,objectfield} -> true;
+% {false,_} -> more_genfields(Fields,T)
+% end.
+
+more_genfields([]) ->
+ false;
+more_genfields([Field|Fields]) ->
+ case element(1,Field) of
+ typefield ->
+ true;
+ objectfield ->
+ true;
+ _ ->
+ more_genfields(Fields)
+ end.
+
+% is_typefield(Fields,FieldName) ->
+% case lists:keysearch(FieldName,2,Fields) of
+% {value,Field} ->
+% case element(1,Field) of
+% typefield ->
+% true;
+% Other ->
+% {false,Other}
+% end;
+% _ ->
+% false
+% end.
+%% Object Set code generating for encoding and decoding
+%% ----------------------------------------------------
+gen_objectset_code(Erules,ObjSet) ->
+ ObjSetName = ObjSet#typedef.name,
+ Def = ObjSet#typedef.typespec,
+%% {ClassName,ClassDef} = Def#'ObjectSet'.class,
+ #'Externaltypereference'{module=ClassModule,
+ type=ClassName} = Def#'ObjectSet'.class,
+ ClassDef = asn1_db:dbget(ClassModule,ClassName),
+ UniqueFName = Def#'ObjectSet'.uniquefname,
+ Set = Def#'ObjectSet'.set,
+ emit({nl,nl,nl,"%%================================"}),
+ emit({nl,"%% ",ObjSetName}),
+ emit({nl,"%%================================",nl}),
+ case ClassName of
+ {_Module,ExtClassName} ->
+ gen_objset_code(Erules,ObjSetName,UniqueFName,Set,
+ ExtClassName,ClassDef);
+ _ ->
+ gen_objset_code(Erules,ObjSetName,UniqueFName,Set,
+ ClassName,ClassDef)
+ end,
+ emit(nl).
+
+gen_objset_code(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassDef)->
+ ClassFields = (ClassDef#classdef.typespec)#objectclass.fields,
+ InternalFuncs=
+ gen_objset_enc(ObjSetName,UniqueFName,Set,ClassName,ClassFields,1,[]),
+ gen_objset_dec(ObjSetName,UniqueFName,Set,ClassName,ClassFields,1),
+ gen_internal_funcs(Erules,InternalFuncs).
+
+%% gen_objset_enc iterates over the objects of the object set
+gen_objset_enc(_,{unique,undefined},_,_,_,_,_) ->
+ %% There is no unique field in the class of this object set
+ %% don't bother about the constraint
+ [];
+gen_objset_enc(ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest],
+ ClName,ClFields,NthObj,Acc)->
+ emit({"'getenc_",ObjSName,"'(",{asis,UniqueName},",",{asis,Val},
+ ") ->",nl}),
+ {InternalFunc,NewNthObj}=
+ case ObjName of
+ no_name ->
+ gen_inlined_enc_funs(Fields,ClFields,ObjSName,NthObj);
+ _Other ->
+ emit({" fun 'enc_",ObjName,"'/3"}),
+ {[],0}
+ end,
+ emit({";",nl}),
+ gen_objset_enc(ObjSName,UniqueName,[T|Rest],ClName,ClFields,
+ NewNthObj,InternalFunc ++ Acc);
+gen_objset_enc(ObjSetName,UniqueName,
+ [{ObjName,Val,Fields}],_ClName,ClFields,NthObj,Acc) ->
+
+ emit({"'getenc_",ObjSetName,"'(",{asis,UniqueName},",",
+ {asis,Val},") ->",nl}),
+ {InternalFunc,_}=
+ case ObjName of
+ no_name ->
+ gen_inlined_enc_funs(Fields,ClFields,ObjSetName,NthObj);
+ _Other ->
+ emit({" fun 'enc_",ObjName,"'/3"}),
+ {[],NthObj}
+ end,
+ emit({".",nl,nl}),
+ InternalFunc++Acc;
+gen_objset_enc(ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
+ _ClFields,_NthObj,Acc) ->
+ emit({"'getenc_",ObjSetName,"'(_, _) ->",nl}),
+ emit({indent(3),"fun(_, Val, _) ->",nl}),
+ emit({indent(6),"[{octets,Val}]",nl}),
+ emit({indent(3),"end.",nl,nl}),
+ Acc;
+gen_objset_enc(_,_,[],_,_,_,Acc) ->
+ Acc.
+
+%% gen_inlined_enc_funs for each object iterates over all fields of a
+%% class, and for each typefield it checks if the object has that
+%% field and emits the proper code.
+gen_inlined_enc_funs(Fields,[{typefield,Name,_}|Rest],ObjSetName,NthObj) ->
+ InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({indent(3),"fun(Type, Val, _) ->",nl,
+ indent(6),"case Type of",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+N,Ret);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({indent(3),"fun(Type, Val, _) ->",nl,
+ indent(6),"case Type of",nl}),
+ emit({indent(9),{asis,Name}," ->",nl}),
+ {Ret,N} = emit_inner_of_fun(Type,InternalDefFunName),
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+N,Ret);
+ false ->
+ gen_inlined_enc_funs(Fields,Rest,ObjSetName,NthObj)
+ end;
+gen_inlined_enc_funs(Fields,[_H|Rest],ObjSetName,NthObj) ->
+ gen_inlined_enc_funs(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_enc_funs(_,[],_,NthObj) ->
+ {[],NthObj}.
+
+gen_inlined_enc_funs1(Fields,[{typefield,Name,_}|Rest],ObjSetName,
+ NthObj,Acc) ->
+ InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
+ {Acc2,NAdd}=
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({";",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ {Ret++Acc,N};
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({";",nl,indent(9),{asis,Name}," ->",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ {Ret++Acc,N};
+ false ->
+ {Acc,0}
+ end,
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+NAdd,Acc2);
+gen_inlined_enc_funs1(Fields,[_H|Rest],ObjSetName,NthObj,Acc)->
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj,Acc);
+gen_inlined_enc_funs1(_,[],_,NthObj,Acc) ->
+ emit({nl,indent(6),"end",nl}),
+ emit({indent(3),"end"}),
+ {Acc,NthObj}.
+
+emit_inner_of_fun(TDef=#typedef{name={ExtMod,Name},typespec=Type},
+ InternalDefFunName) ->
+ case {ExtMod,Name} of
+ {primitive,bif} ->
+ emit(indent(12)),
+ gen_encode_prim(per,Type,dotag,"Val"),
+ {[],0};
+ {constructed,bif} ->
+ emit([indent(12),"'enc_",
+ InternalDefFunName,"'(Val)"]),
+ {[TDef#typedef{name=InternalDefFunName}],1};
+ _ ->
+ emit({indent(12),"'",ExtMod,"':'enc_",Name,"'(Val)"}),
+ {[],0}
+ end;
+emit_inner_of_fun(#typedef{name=Name},_) ->
+ emit({indent(12),"'enc_",Name,"'(Val)"}),
+ {[],0};
+emit_inner_of_fun(Type,_) when record(Type,type) ->
+ CurrMod = get(currmod),
+ case Type#type.def of
+ Def when atom(Def) ->
+ emit({indent(9),Def," ->",nl,indent(12)}),
+ gen_encode_prim(erules,Type,dotag,"Val");
+ TRef when record(TRef,typereference) ->
+ T = TRef#typereference.val,
+ emit({indent(9),T," ->",nl,indent(12),"'enc_",T,"'(Val)"});
+ #'Externaltypereference'{module=CurrMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),"'enc_",T,"'(Val)"});
+ #'Externaltypereference'{module=ExtMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),ExtMod,":'enc_",
+ T,"'(Val)"})
+ end,
+ {[],0}.
+
+indent(N) ->
+ lists:duplicate(N,32). % 32 = space
+
+
+gen_objset_dec(_,{unique,undefined},_,_,_,_) ->
+ %% There is no unique field in the class of this object set
+ %% don't bother about the constraint
+ ok;
+gen_objset_dec(ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest],ClName,
+ ClFields,NthObj)->
+
+ emit({"'getdec_",ObjSName,"'(",{asis,UniqueName},",",{asis,Val},
+ ") ->",nl}),
+ NewNthObj=
+ case ObjName of
+ no_name ->
+ gen_inlined_dec_funs(Fields,ClFields,ObjSName,NthObj);
+ _Other ->
+ emit({" fun 'dec_",ObjName,"'/4"}),
+ NthObj
+ end,
+ emit({";",nl}),
+ gen_objset_dec(ObjSName,UniqueName,[T|Rest],ClName,ClFields,NewNthObj);
+gen_objset_dec(ObjSetName,UniqueName,[{ObjName,Val,Fields}],_ClName,
+ ClFields,NthObj) ->
+
+ emit({"'getdec_",ObjSetName,"'(",{asis,UniqueName},",",{asis,Val},
+ ") ->",nl}),
+ case ObjName of
+ no_name ->
+ gen_inlined_dec_funs(Fields,ClFields,ObjSetName,NthObj);
+ _Other ->
+ emit({" fun 'dec_",ObjName,"'/4"})
+ end,
+ emit({".",nl,nl}),
+ ok;
+gen_objset_dec(ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,_ClFields,
+ _NthObj) ->
+ emit({"'getdec_",ObjSetName,"'(_, _) ->",nl}),
+ emit({indent(3),"fun(Attr1, Bytes, _,_) ->",nl}),
+%% emit({indent(6),"?RT_PER:decode_open_type(Bytes,[])",nl}),
+ emit({indent(6),"{Bytes,Attr1}",nl}),
+ emit({indent(3),"end.",nl,nl}),
+ ok;
+gen_objset_dec(_,_,[],_,_,_) ->
+ ok.
+
+gen_inlined_dec_funs(Fields,[{typefield,Name,_}|Rest],
+ ObjSetName,NthObj) ->
+ InternalDefFunName = [NthObj,Name,ObjSetName],
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({indent(3),"fun(Type, Val, _, _) ->",nl,
+ indent(6),"case Type of",nl}),
+ N=emit_inner_of_decfun(Type,InternalDefFunName),
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({indent(3),"fun(Type, Val, _, _) ->",nl,
+ indent(6),"case Type of",nl}),
+ emit({indent(9),{asis,Name}," ->",nl}),
+ N=emit_inner_of_decfun(Type,InternalDefFunName),
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N);
+ false ->
+ gen_inlined_dec_funs(Fields,Rest,ObjSetName,NthObj)
+ end;
+gen_inlined_dec_funs(Fields,[_|Rest],ObjSetName,NthObj) ->
+ gen_inlined_dec_funs(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_dec_funs(_,[],_,NthObj) ->
+ NthObj.
+
+gen_inlined_dec_funs1(Fields,[{typefield,Name,_}|Rest],
+ ObjSetName,NthObj) ->
+ InternalDefFunName = [NthObj,Name,ObjSetName],
+ N=case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({";",nl}),
+ emit_inner_of_decfun(Type,InternalDefFunName);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({";",nl,indent(9),{asis,Name}," ->",nl}),
+ emit_inner_of_decfun(Type,InternalDefFunName);
+ false ->
+ 0
+ end,
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N);
+gen_inlined_dec_funs1(Fields,[_|Rest],ObjSetName,NthObj)->
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_dec_funs1(_,[],_,NthObj) ->
+ emit({nl,indent(6),"end",nl}),
+ emit({indent(3),"end"}),
+ NthObj.
+
+emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type},
+ InternalDefFunName) ->
+ case {ExtName,Name} of
+ {primitive,bif} ->
+ emit(indent(12)),
+ gen_dec_prim(per,Type,"Val"),
+ 0;
+ {constructed,bif} ->
+ emit({indent(12),"'dec_",
+ asn1ct_gen:list2name(InternalDefFunName),"'(Val)"}),
+ 1;
+ _ ->
+ emit({indent(12),"'",ExtName,"':'dec_",Name,"'(Val, telltype)"}),
+ 0
+ end;
+emit_inner_of_decfun(#typedef{name=Name},_) ->
+ emit({indent(12),"'dec_",Name,"'(Val, telltype)"}),
+ 0;
+emit_inner_of_decfun(Type,_) when record(Type,type) ->
+ CurrMod = get(currmod),
+ case Type#type.def of
+ Def when atom(Def) ->
+ emit({indent(9),Def," ->",nl,indent(12)}),
+ gen_dec_prim(erules,Type,"Val");
+ TRef when record(TRef,typereference) ->
+ T = TRef#typereference.val,
+ emit({indent(9),T," ->",nl,indent(12),"'dec_",T,"'(Val)"});
+ #'Externaltypereference'{module=CurrMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),"'dec_",T,"'(Val)"});
+ #'Externaltypereference'{module=ExtMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),ExtMod,":'dec_",
+ T,"'(Val)"})
+ end,
+ 0.
+
+
+gen_internal_funcs(_,[]) ->
+ ok;
+gen_internal_funcs(Erules,[TypeDef|Rest]) ->
+ gen_encode_user(Erules,TypeDef),
+ emit([nl,nl,"'dec_",TypeDef#typedef.name,"'(Bytes) ->",nl]),
+ gen_decode_user(Erules,TypeDef),
+ gen_internal_funcs(Erules,Rest).
+
+
+
+%% DECODING *****************************
+%%***************************************
+
+
+gen_decode(Erules,Type) when record(Type,typedef) ->
+ D = Type,
+ emit({nl,nl}),
+ emit({"'dec_",Type#typedef.name,"'(Bytes,_) ->",nl}),
+ dbdec(Type#typedef.name),
+ gen_decode_user(Erules,D).
+
+gen_decode(Erules,Tname,#'ComponentType'{name=Cname,typespec=Type}) ->
+ NewTname = [Cname|Tname],
+ gen_decode(Erules,NewTname,Type);
+
+gen_decode(Erules,Typename,Type) when record(Type,type) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ ObjFun =
+ case Type#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _ ->
+ ""
+ end,
+ emit({nl,"'dec_",asn1ct_gen:list2name(Typename),
+ "'(Bytes,_",ObjFun,") ->",nl}),
+ dbdec(Typename),
+ asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,Type);
+ _ ->
+ true
+ end.
+
+dbdec(Type) when list(Type)->
+ demit({"io:format(\"decoding: ",asn1ct_gen:list2name(Type),"~w~n\",[Bytes]),",nl});
+dbdec(Type) ->
+ demit({"io:format(\"decoding: ",{asis,Type},"~w~n\",[Bytes]),",nl}).
+
+gen_decode_user(Erules,D) when record(D,typedef) ->
+ CurrMod = get(currmod),
+ Typename = [D#typedef.name],
+ Def = D#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {primitive,bif} ->
+ gen_dec_prim(Erules,Def,"Bytes"),
+ emit({".",nl,nl});
+ 'ASN1_OPEN_TYPE' ->
+ gen_dec_prim(Erules,Def#type{def='ASN1_OPEN_TYPE'},"Bytes"),
+ emit({".",nl,nl});
+ {constructed,bif} ->
+ asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,D);
+ #typereference{val=Dname} ->
+ emit({"'dec_",Dname,"'(Bytes,telltype)"}),
+ emit({".",nl,nl});
+ #'Externaltypereference'{module=CurrMod,type=Etype} ->
+ emit({"'dec_",Etype,"'(Bytes,telltype).",nl,nl});
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit({"'",Emod,"':'dec_",Etype,"'(Bytes,telltype).",nl,nl});
+ Other ->
+ exit({error,{asn1,{unknown,Other}}})
+ end.
+
+
+gen_dec_prim(_Erules,Att,BytesVar) ->
+ Typename = Att#type.def,
+ Constraint = Att#type.constraint,
+ case Typename of
+ 'INTEGER' ->
+ emit({"?RT_PER:decode_integer(",BytesVar,",",
+ {asis,Constraint},")"});
+ {'INTEGER',NamedNumberList} ->
+ emit({"?RT_PER:decode_integer(",BytesVar,",",
+ {asis,Constraint},",",
+ {asis,NamedNumberList},")"});
+ {'BIT STRING',NamedNumberList} ->
+ case get(compact_bit_string) of
+ true ->
+ emit({"?RT_PER:decode_compact_bit_string(",
+ BytesVar,",",{asis,Constraint},",",
+ {asis,NamedNumberList},")"});
+ _ ->
+ emit({"?RT_PER:decode_bit_string(",BytesVar,",",
+ {asis,Constraint},",",
+ {asis,NamedNumberList},")"})
+ end;
+ 'NULL' ->
+ emit({"?RT_PER:decode_null(",
+ BytesVar,")"});
+ 'OBJECT IDENTIFIER' ->
+ emit({"?RT_PER:decode_object_identifier(",
+ BytesVar,")"});
+ 'ObjectDescriptor' ->
+ emit({"?RT_PER:decode_ObjectDescriptor(",
+ BytesVar,")"});
+ {'ENUMERATED',{NamedNumberList1,NamedNumberList2}} ->
+ NewTup = {list_to_tuple([X||{X,_} <- NamedNumberList1]),
+ list_to_tuple([X||{X,_} <- NamedNumberList2])},
+ NewC = [{'ValueRange',{0,size(element(1,NewTup))-1}}],
+ emit({"?RT_PER:decode_enumerated(",BytesVar,",",
+ {asis,NewC},",",
+ {asis,NewTup},")"});
+ {'ENUMERATED',NamedNumberList} ->
+ NewTup = list_to_tuple([X||{X,_} <- NamedNumberList]),
+ NewC = [{'ValueRange',{0,size(NewTup)-1}}],
+ emit({"?RT_PER:decode_enumerated(",BytesVar,",",
+ {asis,NewC},",",
+ {asis,NewTup},")"});
+ 'BOOLEAN'->
+ emit({"?RT_PER:decode_boolean(",BytesVar,")"});
+ 'OCTET STRING' ->
+ emit({"?RT_PER:decode_octet_string(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'NumericString' ->
+ emit({"?RT_PER:decode_NumericString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'TeletexString' ->
+ emit({"?RT_PER:decode_TeletexString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'VideotexString' ->
+ emit({"?RT_PER:decode_VideotexString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'UTCTime' ->
+ emit({"?RT_PER:decode_VisibleString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'GeneralizedTime' ->
+ emit({"?RT_PER:decode_VisibleString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'GraphicString' ->
+ emit({"?RT_PER:decode_GraphicString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'VisibleString' ->
+ emit({"?RT_PER:decode_VisibleString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'GeneralString' ->
+ emit({"?RT_PER:decode_GeneralString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'PrintableString' ->
+ emit({"?RT_PER:decode_PrintableString(",BytesVar,",",{asis,Constraint},")"});
+ 'IA5String' ->
+ emit({"?RT_PER:decode_IA5String(",BytesVar,",",{asis,Constraint},")"});
+ 'BMPString' ->
+ emit({"?RT_PER:decode_BMPString(",BytesVar,",",{asis,Constraint},")"});
+ 'UniversalString' ->
+ emit({"?RT_PER:decode_UniversalString(",BytesVar,",",{asis,Constraint},")"});
+ 'ANY' ->
+ emit(["?RT_PER:decode_open_type(",BytesVar,",",
+ {asis,Constraint}, ")"]);
+ 'ASN1_OPEN_TYPE' ->
+ case Constraint of
+ [#'Externaltypereference'{type=Tname}] ->
+ emit(["fun(FBytes) ->",nl,
+ " {XTerm,XBytes} = "]),
+ emit(["?RT_PER:decode_open_type(FBytes,[]),",nl]),
+ emit([" {YTerm,_} = dec_",Tname,"(XTerm,mandatory),",nl]),
+ emit([" {YTerm,XBytes} end(",BytesVar,")"]);
+ [#type{def=#'Externaltypereference'{type=Tname}}] ->
+ emit(["fun(FBytes) ->",nl,
+ " {XTerm,XBytes} = "]),
+ emit(["?RT_PER:decode_open_type(FBytes,[]),",nl]),
+ emit([" {YTerm,_} = dec_",Tname,"(XTerm,mandatory),",nl]),
+ emit([" {YTerm,XBytes} end(",BytesVar,")"]);
+ _ ->
+ emit(["?RT_PER:decode_open_type(",BytesVar,",[])"])
+ end;
+ Other ->
+ exit({'cant decode' ,Other})
+ end.
+
+
+is_already_generated(Operation,Name) ->
+ case get(class_default_type) of
+ undefined ->
+ put(class_default_type,[{Operation,Name}]),
+ false;
+ GeneratedList ->
+ case lists:member({Operation,Name},GeneratedList) of
+ true ->
+ true;
+ false ->
+ put(class_default_type,[{Operation,Name}|GeneratedList]),
+ false
+ end
+ end.
+
+get_class_fields(#classdef{typespec=ObjClass}) ->
+ ObjClass#objectclass.fields;
+get_class_fields(#objectclass{fields=Fields}) ->
+ Fields;
+get_class_fields(_) ->
+ [].
+
+
+get_object_field(Name,ObjectFields) ->
+ case lists:keysearch(Name,1,ObjectFields) of
+ {value,Field} -> Field;
+ false -> false
+ end.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen_per_rt2ct.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen_per_rt2ct.erl
new file mode 100644
index 0000000000..ddfa124048
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen_per_rt2ct.erl
@@ -0,0 +1,1811 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_gen_per_rt2ct.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1ct_gen_per_rt2ct).
+
+%% Generate erlang module which handles (PER) encode and decode for
+%% all types in an ASN.1 module
+
+-include("asn1_records.hrl").
+%-compile(export_all).
+
+-export([pgen/4,gen_dec_prim/3,gen_encode_prim/4]).
+-export([gen_obj_code/3,gen_objectset_code/2]).
+-export([gen_decode/2, gen_decode/3]).
+-export([gen_encode/2, gen_encode/3]).
+
+-import(asn1ct_gen, [emit/1,demit/1]).
+-import(asn1ct_gen_per, [is_already_generated/2,more_genfields/1,
+ get_class_fields/1,get_object_field/2]).
+
+%% pgen(Erules, Module, TypeOrVal)
+%% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module
+%% .hrl file is only generated if necessary
+%% Erules = per | ber
+%% Module = atom()
+%% TypeOrVal = {TypeList,ValueList}
+%% TypeList = ValueList = [atom()]
+
+pgen(OutFile,Erules,Module,TypeOrVal) ->
+ asn1ct_gen:pgen_module(OutFile,Erules,Module,TypeOrVal,true).
+
+
+%% Generate ENCODING ******************************
+%%****************************************x
+
+
+gen_encode(Erules,Type) when record(Type,typedef) ->
+ gen_encode_user(Erules,Type).
+
+gen_encode(Erules,Typename,#'ComponentType'{name=Cname,typespec=Type}) ->
+ NewTypename = [Cname|Typename],
+ gen_encode(Erules,NewTypename,Type);
+
+gen_encode(Erules,Typename,Type) when record(Type,type) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ ObjFun =
+ case lists:keysearch(objfun,1,Type#type.tablecinf) of
+ {value,{_,_Name}} ->
+ ", ObjFun";
+ false ->
+ ""
+ end,
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ case InnerType of
+ 'SET' ->
+ true;
+ 'SEQUENCE' ->
+ true;
+ _ ->
+ emit({nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'({'",asn1ct_gen:list2name(Typename),
+ "',Val}",ObjFun,") ->",nl}),
+ emit({"'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val",ObjFun,");",nl,nl})
+ end,
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val",ObjFun,
+ ") ->",nl}),
+ asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
+ _ ->
+ true
+ end.
+
+
+gen_encode_user(Erules,D) when record(D,typedef) ->
+ CurrMod = get(currmod),
+ Typename = [D#typedef.name],
+ Def = D#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ case InnerType of
+ 'SET' -> true;
+ 'SEQUENCE' -> true;
+ _ ->
+ emit({nl,"'enc_",asn1ct_gen:list2name(Typename),"'({'",asn1ct_gen:list2name(Typename),"',Val}) ->",nl}),
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val);",nl,nl})
+ end,
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val) ->",nl}),
+ case asn1ct_gen:type(InnerType) of
+ {primitive,bif} ->
+ gen_encode_prim(Erules,Def,"false"),
+ emit({".",nl});
+ 'ASN1_OPEN_TYPE' ->
+ gen_encode_prim(Erules,Def#type{def='ASN1_OPEN_TYPE'},"false"),
+ emit({".",nl});
+ {constructed,bif} ->
+ asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,D);
+ #'Externaltypereference'{module=CurrMod,type=Etype} ->
+ emit({"'enc_",Etype,"'(Val).",nl,nl});
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit({"'",Emod,"':'enc_",Etype,"'(Val).",nl,nl});
+ #typereference{val=Ename} ->
+ emit({"'enc_",Ename,"'(Val).",nl,nl});
+ {notype,_} ->
+ emit({"'enc_",InnerType,"'(Val).",nl,nl})
+ end.
+
+
+gen_encode_prim(Erules,D,DoTag) ->
+ Value = case asn1ct_name:active(val) of
+ true ->
+ asn1ct_gen:mk_var(asn1ct_name:curr(val));
+ false ->
+ "Val"
+ end,
+ gen_encode_prim(Erules,D,DoTag,Value).
+
+
+
+
+
+gen_encode_prim(_Erules,D,_DoTag,Value) when record(D,type) ->
+ Constraint = D#type.constraint,
+ case D#type.def of
+ 'INTEGER' ->
+ EffectiveConstr = effective_constraint(integer,Constraint),
+ emit([" %%INTEGER with effective constraint: ",
+ {asis,EffectiveConstr},nl]),
+ emit_enc_integer(EffectiveConstr,Value);
+ {'INTEGER',NamedNumberList} ->
+ EffectiveConstr = effective_constraint(integer,Constraint),
+ %% maybe an emit_enc_NNL_integer
+ emit([" %%INTEGER with effective constraint: ",
+ {asis,EffectiveConstr},nl]),
+ emit_enc_integer_NNL(EffectiveConstr,Value,NamedNumberList);
+ {'ENUMERATED',{Nlist1,Nlist2}} ->
+ NewList = lists:concat([[{0,X}||{X,_} <- Nlist1],['EXT_MARK'],[{1,X}||{X,_} <- Nlist2]]),
+ NewC = [{'ValueRange',{0,length(Nlist1)-1}}],
+ emit(["case (case ",Value," of {_,_}->element(2,",Value,");_->",
+ Value," end) of",nl]),
+ emit_enc_enumerated_cases(NewC, NewList++[{asn1_enum,length(Nlist1)-1}], 0);
+ {'ENUMERATED',NamedNumberList} ->
+ NewList = [X||{X,_} <- NamedNumberList],
+ NewC = effective_constraint(integer,
+ [{'ValueRange',
+ {0,length(NewList)-1}}]),
+ NewVal = enc_enum_cases(Value,NewList),
+ emit_enc_integer(NewC,NewVal);
+ {'BIT STRING',NamedNumberList} ->
+ EffectiveC = effective_constraint(bitstring,Constraint),
+ case EffectiveC of
+ 0 -> emit({"[]"});
+ _ ->
+ emit({"?RT_PER:encode_bit_string(",
+ {asis,EffectiveC},",",Value,",",
+ {asis,NamedNumberList},")"})
+ end;
+ 'NULL' ->
+ emit({"?RT_PER:encode_null(",Value,")"});
+ 'OBJECT IDENTIFIER' ->
+ emit({"?RT_PER:encode_object_identifier(",Value,")"});
+ 'ObjectDescriptor' ->
+ emit({"?RT_PER:encode_ObjectDescriptor(",{asis,Constraint},
+ ",",Value,")"});
+ 'BOOLEAN' ->
+% emit({"?RT_PER:encode_boolean(",Value,")"});
+ emit({"case ",Value," of",nl,
+% " true -> {bits,1,1};",nl,
+ " true -> [1];",nl,
+% " false -> {bits,1,0};",nl,
+ " false -> [0];",nl,
+ " _ -> exit({error,{asn1,{encode_boolean,",Value,"}}})",nl,
+ "end"});
+ 'OCTET STRING' ->
+ emit_enc_octet_string(Constraint,Value);
+
+ 'NumericString' ->
+ emit_enc_known_multiplier_string('NumericString',Constraint,Value);
+ 'TeletexString' ->
+ emit({"?RT_PER:encode_TeletexString(",{asis,Constraint},",",Value,")"});
+ 'VideotexString' ->
+ emit({"?RT_PER:encode_VideotexString(",{asis,Constraint},",",Value,")"});
+ 'UTCTime' ->
+ emit_enc_known_multiplier_string('VisibleString',Constraint,Value);
+ 'GeneralizedTime' ->
+ emit_enc_known_multiplier_string('VisibleString',Constraint,Value);
+ 'GraphicString' ->
+ emit({"?RT_PER:encode_GraphicString(",{asis,Constraint},",",Value,")"});
+ 'VisibleString' ->
+ emit_enc_known_multiplier_string('VisibleString',Constraint,Value);
+ 'GeneralString' ->
+ emit({"?RT_PER:encode_GeneralString(",{asis,Constraint},",",Value,")"});
+ 'PrintableString' ->
+ emit_enc_known_multiplier_string('PrintableString',Constraint,Value);
+ 'IA5String' ->
+ emit_enc_known_multiplier_string('IA5String',Constraint,Value);
+ 'BMPString' ->
+ emit_enc_known_multiplier_string('BMPString',Constraint,Value);
+ 'UniversalString' ->
+ emit_enc_known_multiplier_string('UniversalString',Constraint,Value);
+ 'ANY' ->
+ emit(["?RT_PER:encode_open_type(", {asis,Constraint}, ",",
+ Value, ")"]);
+ 'ASN1_OPEN_TYPE' ->
+ NewValue = case Constraint of
+ [#'Externaltypereference'{type=Tname}] ->
+ io_lib:format(
+ "?RT_PER:complete(enc_~s(~s))",[Tname,Value]);
+ [#type{def=#'Externaltypereference'{type=Tname}}] ->
+ io_lib:format(
+ "?RT_PER:complete(enc_~s(~s))",[Tname,Value]);
+ _ -> Value
+ end,
+ emit(["?RT_PER:encode_open_type(", {asis,Constraint}, ",",
+ NewValue, ")"]);
+ XX ->
+ exit({asn1_error,nyi,XX})
+ end.
+
+emit_enc_known_multiplier_string(StringType,C,Value) ->
+ SizeC =
+ case get_constraint(C,'SizeConstraint') of
+ L when list(L) -> {lists:min(L),lists:max(L)};
+ L -> L
+ end,
+ PAlphabC = get_constraint(C,'PermittedAlphabet'),
+ case {StringType,PAlphabC} of
+ {'UniversalString',{_,_}} ->
+ exit({error,{asn1,{'not implemented',"UniversalString with "
+ "PermittedAlphabet constraint"}}});
+ {'BMPString',{_,_}} ->
+ exit({error,{asn1,{'not implemented',"BMPString with "
+ "PermittedAlphabet constraint"}}});
+ _ -> ok
+ end,
+ NumBits = get_NumBits(C,StringType),
+ CharOutTab = get_CharOutTab(C,StringType),
+ %% NunBits and CharOutTab for chars_encode
+ emit_enc_k_m_string(StringType,SizeC,NumBits,CharOutTab,Value).
+
+emit_enc_k_m_string(_StringType,0,_NumBits,_CharOutTab,_Value) ->
+ emit({"[]"});
+emit_enc_k_m_string(StringType,SizeC,NumBits,CharOutTab,Value) ->
+ emit({"?RT_PER:encode_known_multiplier_string(",{asis,StringType},",",
+ {asis,SizeC},",",NumBits,",",{asis,CharOutTab},",",Value,")"}).
+
+emit_dec_known_multiplier_string(StringType,C,BytesVar) ->
+ SizeC = get_constraint(C,'SizeConstraint'),
+ PAlphabC = get_constraint(C,'PermittedAlphabet'),
+ case {StringType,PAlphabC} of
+ {'BMPString',{_,_}} ->
+ exit({error,{asn1,
+ {'not implemented',
+ "BMPString with PermittedAlphabet "
+ "constraint"}}});
+ _ ->
+ ok
+ end,
+ NumBits = get_NumBits(C,StringType),
+ CharInTab = get_CharInTab(C,StringType),
+ case SizeC of
+ 0 ->
+ emit({"{[],",BytesVar,"}"});
+ _ ->
+ emit({"?RT_PER:decode_known_multiplier_string(",
+ {asis,StringType},",",{asis,SizeC},",",NumBits,
+ ",",{asis,CharInTab},",",BytesVar,")"})
+ end.
+
+
+%% copied from run time module
+
+get_CharOutTab(C,StringType) ->
+ get_CharTab(C,StringType,out).
+
+get_CharInTab(C,StringType) ->
+ get_CharTab(C,StringType,in).
+
+get_CharTab(C,StringType,InOut) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ {'SingleValue',Sv} ->
+ get_CharTab2(C,StringType,hd(Sv),lists:max(Sv),Sv,InOut);
+ no ->
+ case StringType of
+ 'IA5String' ->
+ {0,16#7F,notab};
+ 'VisibleString' ->
+ get_CharTab2(C,StringType,16#20,16#7F,notab,InOut);
+ 'PrintableString' ->
+ Chars = lists:sort(
+ " '()+,-./0123456789:=?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"),
+ get_CharTab2(C,StringType,hd(Chars),lists:max(Chars),Chars,InOut);
+ 'NumericString' ->
+ get_CharTab2(C,StringType,16#20,$9," 0123456789",InOut);
+ 'UniversalString' ->
+ {0,16#FFFFFFFF,notab};
+ 'BMPString' ->
+ {0,16#FFFF,notab}
+ end
+ end.
+
+get_CharTab2(C,StringType,Min,Max,Chars,InOut) ->
+ BitValMax = (1 bsl get_NumBits(C,StringType))-1,
+ if
+ Max =< BitValMax ->
+ {0,Max,notab};
+ true ->
+ case InOut of
+ out ->
+ {Min,Max,create_char_tab(Min,Chars)};
+ in ->
+ {Min,Max,list_to_tuple(Chars)}
+ end
+ end.
+
+create_char_tab(Min,L) ->
+ list_to_tuple(create_char_tab(Min,L,0)).
+create_char_tab(Min,[Min|T],V) ->
+ [V|create_char_tab(Min+1,T,V+1)];
+create_char_tab(_Min,[],_V) ->
+ [];
+create_char_tab(Min,L,V) ->
+ [false|create_char_tab(Min+1,L,V)].
+
+get_NumBits(C,StringType) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ {'SingleValue',Sv} ->
+ charbits(length(Sv),aligned);
+ no ->
+ case StringType of
+ 'IA5String' ->
+ charbits(128,aligned); % 16#00..16#7F
+ 'VisibleString' ->
+ charbits(95,aligned); % 16#20..16#7E
+ 'PrintableString' ->
+ charbits(74,aligned); % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+ 'NumericString' ->
+ charbits(11,aligned); % $ ,"0123456789"
+ 'UniversalString' ->
+ 32;
+ 'BMPString' ->
+ 16
+ end
+ end.
+
+charbits(NumOfChars,aligned) ->
+ case charbits(NumOfChars) of
+ 1 -> 1;
+ 2 -> 2;
+ B when B =< 4 -> 4;
+ B when B =< 8 -> 8;
+ B when B =< 16 -> 16;
+ B when B =< 32 -> 32
+ end.
+
+charbits(NumOfChars) when NumOfChars =< 2 -> 1;
+charbits(NumOfChars) when NumOfChars =< 4 -> 2;
+charbits(NumOfChars) when NumOfChars =< 8 -> 3;
+charbits(NumOfChars) when NumOfChars =< 16 -> 4;
+charbits(NumOfChars) when NumOfChars =< 32 -> 5;
+charbits(NumOfChars) when NumOfChars =< 64 -> 6;
+charbits(NumOfChars) when NumOfChars =< 128 -> 7;
+charbits(NumOfChars) when NumOfChars =< 256 -> 8;
+charbits(NumOfChars) when NumOfChars =< 512 -> 9;
+charbits(NumOfChars) when NumOfChars =< 1024 -> 10;
+charbits(NumOfChars) when NumOfChars =< 2048 -> 11;
+charbits(NumOfChars) when NumOfChars =< 4096 -> 12;
+charbits(NumOfChars) when NumOfChars =< 8192 -> 13;
+charbits(NumOfChars) when NumOfChars =< 16384 -> 14;
+charbits(NumOfChars) when NumOfChars =< 32768 -> 15;
+charbits(NumOfChars) when NumOfChars =< 65536 -> 16;
+charbits(NumOfChars) when integer(NumOfChars) ->
+ 16 + charbits1(NumOfChars bsr 16).
+
+charbits1(0) ->
+ 0;
+charbits1(NumOfChars) ->
+ 1 + charbits1(NumOfChars bsr 1).
+
+%% copied from run time module
+
+emit_enc_octet_string(Constraint,Value) ->
+ case get_constraint(Constraint,'SizeConstraint') of
+ 0 ->
+ emit({" []"});
+ 1 ->
+ asn1ct_name:new(tmpval),
+ emit({" begin",nl}),
+ emit({" [",{curr,tmpval},"] = ",Value,",",nl}),
+% emit({" {bits,8,",{curr,tmpval},"}",nl}),
+ emit({" [10,8,",{curr,tmpval},"]",nl}),
+ emit(" end");
+ 2 ->
+ asn1ct_name:new(tmpval),
+ emit({" begin",nl}),
+ emit({" [",{curr,tmpval},",",{next,tmpval},"] = ",
+ Value,",",nl}),
+% emit({" [{bits,8,",{curr,tmpval},"},{bits,8,",
+% {next,tmpval},"}]",nl}),
+ emit({" [[10,8,",{curr,tmpval},"],[10,8,",
+ {next,tmpval},"]]",nl}),
+ emit(" end"),
+ asn1ct_name:new(tmpval);
+ Sv when integer(Sv),Sv =< 256 ->
+ asn1ct_name:new(tmpval),
+ emit({" begin",nl}),
+% emit({" case length(",Value,") == ",Sv," of",nl}),
+ emit({" case length(",Value,") of",nl}),
+ emit({" ",{curr,tmpval}," when ",{curr,tmpval}," == ",Sv," -> [2,20,",{curr,tmpval},",",Value,"];",nl}),
+ emit({" _ -> exit({error,{value_out_of_bounds,",Value,"}})",
+ nl," end",nl}),
+ emit(" end");
+ Sv when integer(Sv),Sv =< 65535 ->
+ asn1ct_name:new(tmpval),
+ emit({" begin",nl}),
+% emit({" case length(",Value,") == ",Sv," of",nl}),
+ emit({" case length(",Value,") of",nl}),
+% emit({" true -> [align,{octets,",Value,"}];",nl}),
+ emit({" ",{curr,tmpval}," when ",{curr,tmpval}," == ",Sv," -> [2,21,",{curr,tmpval},",",Value,"];",nl}),
+ emit({" _ -> exit({error,{value_out_of_bounds,",Value,"}})",
+ nl," end",nl}),
+ emit(" end");
+ C ->
+ emit({" ?RT_PER:encode_octet_string(",{asis,C},",false,",Value,")",nl})
+ end.
+
+emit_dec_octet_string(Constraint,BytesVar) ->
+ case get_constraint(Constraint,'SizeConstraint') of
+ 0 ->
+ emit({" {[],",BytesVar,"}",nl});
+ {_,0} ->
+ emit({" {[],",BytesVar,"}",nl});
+ C ->
+ emit({" ?RT_PER:decode_octet_string(",BytesVar,",",
+ {asis,C},",false)",nl})
+ end.
+
+emit_enc_integer_case(Value) ->
+ case get(component_type) of
+ {true,#'ComponentType'{prop=Prop}} ->
+ emit({" begin",nl}),
+ case Prop of
+ Opt when Opt=='OPTIONAL';
+ tuple(Opt),element(1,Opt)=='DEFAULT' ->
+ emit({" case ",Value," of",nl}),
+ ok;
+ _ ->
+ emit({" ",{curr,tmpval},"=",Value,",",nl}),
+ emit({" case ",{curr,tmpval}," of",nl}),
+ asn1ct_name:new(tmpval)
+ end;
+% asn1ct_name:new(tmpval);
+ _ ->
+ emit({" case ",Value," of ",nl})
+ end.
+emit_enc_integer_end_case() ->
+ case get(component_type) of
+ {true,_} ->
+ emit({nl," end"}); % end of begin ... end
+ _ -> ok
+ end.
+
+
+emit_enc_integer_NNL(C,Value,NNL) ->
+ EncVal = enc_integer_NNL_cases(Value,NNL),
+ emit_enc_integer(C,EncVal).
+
+enc_integer_NNL_cases(Value,NNL) ->
+ asn1ct_name:new(tmpval),
+ TmpVal = asn1ct_gen:mk_var(asn1ct_name:curr(tmpval)),
+ Cases=enc_integer_NNL_cases1(NNL),
+ lists:flatten(io_lib:format("(case ~s of "++Cases++
+ "~s when atom(~s)->exit({error,{asn1,{namednumber,~s}}});_->~s end)",[Value,TmpVal,TmpVal,TmpVal,Value])).
+
+enc_integer_NNL_cases1([{NNo,No}|Rest]) ->
+ io_lib:format("~w->~w;",[NNo,No])++enc_integer_NNL_cases1(Rest);
+enc_integer_NNL_cases1([]) ->
+ "".
+
+emit_enc_integer([{'SingleValue',Int}],Value) ->
+ asn1ct_name:new(tmpval),
+ emit_enc_integer_case(Value),% emit([" case ",Value," of",nl]),
+ emit([" ",Int," -> [];",nl]),
+ emit([" ",{curr,tmpval}," ->",nl]),
+ emit([" exit({error,{value_out_of_bounds,",{curr,tmpval},"}})",
+ nl," end",nl]),
+ emit_enc_integer_end_case();
+
+emit_enc_integer([{_,{Lb,Ub},_Range,{bits,NoBs}}],Value) -> % Range =< 255
+ asn1ct_name:new(tmpval),
+ emit_enc_integer_case(Value),
+ emit([" ",{curr,tmpval}," when ",{curr,tmpval},"=<",Ub,",",
+ {curr,tmpval},">=",Lb," ->",nl]),
+ emit([" [10,",NoBs,",",{curr,tmpval},"-",Lb,"];",nl]),
+ emit([" ",{curr,tmpval}," ->",nl]),
+ emit([" exit({error,{value_out_of_bounds,",{curr,tmpval},"}})",
+ nl," end",nl]),
+ emit_enc_integer_end_case();
+
+emit_enc_integer([{_,{Lb,Ub},Range,_}],Value) when Range =< 256 ->
+ asn1ct_name:new(tmpval),
+ emit_enc_integer_case(Value),
+ emit([" ",{curr,tmpval}," when ",{curr,tmpval},"=<",Ub,",",
+ {curr,tmpval},">=",Lb," ->",nl]),
+ emit([" [20,1,",{curr,tmpval},"-",Lb,"];",nl]),
+ emit([" ",{curr,tmpval}," ->",nl]),
+ emit([" exit({error,{value_out_of_bounds,",{curr,tmpval},"}})",
+ nl," end",nl]),
+ emit_enc_integer_end_case();
+
+emit_enc_integer([{_,{Lb,Ub},Range,_}],Value) when Range =< 65536 ->
+ asn1ct_name:new(tmpval),
+ emit_enc_integer_case(Value),
+ emit([" ",{curr,tmpval}," when ",{curr,tmpval},"=<",Ub,",",
+ {curr,tmpval},">=",Lb," ->",nl]),
+ emit([" [20,2,<<(",{curr,tmpval},"-",Lb,"):16>>];",nl]),
+ emit([" ",{curr,tmpval}," ->",nl]),
+ emit([" exit({error,{value_out_of_bounds,",{curr,tmpval},"}})",
+ nl," end",nl]),
+ emit_enc_integer_end_case();
+
+
+emit_enc_integer(C,Value) ->
+ emit({" ?RT_PER:encode_integer(",{asis,C},",",Value,")"}).
+
+
+
+
+enc_enum_cases(Value,NewList) ->
+ asn1ct_name:new(tmpval),
+ TmpVal = asn1ct_gen:mk_var(asn1ct_name:curr(tmpval)),
+ Cases=enc_enum_cases1(NewList),
+ lists:flatten(io_lib:format("(case ~s of "++Cases++
+ "~s ->exit({error,"
+ "{asn1,{enumerated,~s}}})"
+ " end)",
+ [Value,TmpVal,TmpVal])).
+enc_enum_cases1(NNL) ->
+ enc_enum_cases1(NNL,0).
+enc_enum_cases1([H|T],Index) ->
+ io_lib:format("~w->~w;",[H,Index])++enc_enum_cases1(T,Index+1);
+enc_enum_cases1([],_) ->
+ "".
+
+
+emit_enc_enumerated_cases(C, [H], Count) ->
+ emit_enc_enumerated_case(C, H, Count),
+ emit([";",nl,"EnumVal -> exit({error,{asn1, {enumerated_not_in_range, EnumVal}}})"]),
+ emit([nl,"end"]);
+emit_enc_enumerated_cases(C, ['EXT_MARK'|T], _Count) ->
+ emit_enc_enumerated_cases(C, T, 0);
+emit_enc_enumerated_cases(C, [H1,H2|T], Count) ->
+ emit_enc_enumerated_case(C, H1, Count),
+ emit([";",nl]),
+ emit_enc_enumerated_cases(C, [H2|T], Count+1).
+
+
+%% The function clauses matching on tuples with first element
+%% asn1_enum, 1 or 0 and the atom 'EXT_MARK' are for ENUMERATED
+%% with extension mark.
+emit_enc_enumerated_case(_C, {asn1_enum,High}, _) ->
+ %% ENUMERATED with extensionmark
+ %% value higher than the extension base and not
+ %% present in the extension range.
+ emit(["{asn1_enum,EnumV} when integer(EnumV), EnumV > ",High," -> ",
+ "[1,?RT_PER:encode_small_number(EnumV)]"]);
+emit_enc_enumerated_case(_C, 'EXT_MARK', _Count) ->
+ %% ENUMERATED with extensionmark
+ true;
+emit_enc_enumerated_case(_C, {1,EnumName}, Count) ->
+ %% ENUMERATED with extensionmark
+ %% values higher than extension root
+ emit(["'",EnumName,"' -> [1,?RT_PER:encode_small_number(",Count,")]"]);
+emit_enc_enumerated_case(C, {0,EnumName}, Count) ->
+ %% ENUMERATED with extensionmark
+ %% values within extension root
+ emit(["'",EnumName,"' -> [0,?RT_PER:encode_integer(",{asis,C},", ",Count,")]"]);
+
+%% This clause is invoked in case of an ENUMERATED without extension mark
+emit_enc_enumerated_case(_C, EnumName, Count) ->
+ emit(["'",EnumName,"' -> ",Count]).
+
+
+get_constraint([{Key,V}],Key) ->
+ V;
+get_constraint([],_) ->
+ no;
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V
+ end.
+
+get_constraints(L=[{Key,_}],Key) ->
+ L;
+get_constraints([],_) ->
+ [];
+get_constraints(C,Key) ->
+ {value,L} = keysearch_allwithkey(Key,1,C,[]),
+ L.
+
+keysearch_allwithkey(Key,Ix,C,Acc) ->
+ case lists:keysearch(Key,Ix,C) of
+ false ->
+ {value,Acc};
+ {value,T} ->
+ RestC = lists:delete(T,C),
+ keysearch_allwithkey(Key,Ix,RestC,[T|Acc])
+ end.
+
+%% effective_constraint(Type,C)
+%% Type = atom()
+%% C = [C1,...]
+%% C1 = {'SingleValue',SV} | {'ValueRange',VR} | {atom(),term()}
+%% SV = integer() | [integer(),...]
+%% VR = {Lb,Ub}
+%% Lb = 'MIN' | integer()
+%% Ub = 'MAX' | integer()
+%% Returns a single value if C only has a single value constraint, and no
+%% value range constraints, that constrains to a single value, otherwise
+%% returns a value range that has the lower bound set to the lowest value
+%% of all single values and lower bound values in C and the upper bound to
+%% the greatest value.
+effective_constraint(integer,[C={{_,_},_}|_Rest]) -> % extension
+ [C]; %% [C|effective_constraint(integer,Rest)]; XXX what is possible ???
+effective_constraint(integer,C) ->
+ SVs = get_constraints(C,'SingleValue'),
+ SV = effective_constr('SingleValue',SVs),
+ VRs = get_constraints(C,'ValueRange'),
+ VR = effective_constr('ValueRange',VRs),
+ CRange = greatest_common_range(SV,VR),
+ pre_encode(integer,CRange);
+effective_constraint(bitstring,C) ->
+% Constr=get_constraints(C,'SizeConstraint'),
+% case Constr of
+% [] -> no;
+% [{'SizeConstraint',Val}] -> Val;
+% Other -> Other
+% end;
+ get_constraint(C,'SizeConstraint');
+effective_constraint(Type,C) ->
+ io:format("Effective constraint for ~p, not implemented yet.~n",[Type]),
+ C.
+
+effective_constr(_,[]) ->
+ [];
+effective_constr('SingleValue',List) ->
+ SVList = lists:flatten(lists:map(fun(X)->element(2,X)end,List)),
+ case lists:usort(SVList) of
+ [N] ->
+ [{'SingleValue',N}];
+ L when list(L) ->
+ [{'ValueRange',{hd(L),lists:last(L)}}]
+ end;
+effective_constr('ValueRange',List) ->
+ LBs = lists:map(fun({_,{Lb,_}})-> Lb end,List),
+ UBs = lists:map(fun({_,{_,Ub}})-> Ub end,List),
+ Lb = least_Lb(LBs),
+ [{'ValueRange',{Lb,lists:max(UBs)}}].
+
+greatest_common_range([],VR) ->
+ VR;
+greatest_common_range(SV,[]) ->
+ SV;
+greatest_common_range([{_,Int}],[{_,{'MIN',Ub}}]) when integer(Int),
+ Int > Ub ->
+ [{'ValueRange',{'MIN',Int}}];
+greatest_common_range([{_,Int}],[{_,{Lb,Ub}}]) when integer(Int),
+ Int < Lb ->
+ [{'ValueRange',{Int,Ub}}];
+greatest_common_range([{_,Int}],VR=[{_,{_Lb,_Ub}}]) when integer(Int) ->
+ VR;
+greatest_common_range([{_,L}],[{_,{Lb,Ub}}]) when list(L) ->
+ Min = least_Lb([Lb|L]),
+ Max = greatest_Ub([Ub|L]),
+ [{'ValueRange',{Min,Max}}].
+
+
+least_Lb(L) ->
+ case lists:member('MIN',L) of
+ true -> 'MIN';
+ _ -> lists:min(L)
+ end.
+
+greatest_Ub(L) ->
+ case lists:member('MAX',L) of
+ true -> 'MAX';
+ _ -> lists:max(L)
+ end.
+
+% effective_constraint1('SingleValue',List) ->
+% SVList = lists:map(fun(X)->element(2,X)end,List),
+% sv_effective_constraint(hd(SVList),tl(SVList));
+% effective_constraint1('ValueRange',List) ->
+% VRList = lists:map(fun(X)->element(2,X)end,List),
+% vr_effective_constraint(lists:map(fun(X)->element(1,X)end,VRList),
+% lists:map(fun(X)->element(2,X)end,VRList)).
+
+%% vr_effective_constraint/2
+%% Gets all LowerEndPoints and UpperEndPoints as arguments
+%% Returns {'ValueRange',{Lb,Ub}} where Lb is the highest value of
+%% the LowerEndPoints and Ub is the lowest value of the UpperEndPoints,
+%% i.e. the intersection of all value ranges.
+% vr_effective_constraint(Mins,Maxs) ->
+% Lb=lists:foldl(fun(X,'MIN') when integer(X) -> X;
+% (X,'MIN') -> 'MIN';
+% (X,AccIn) when integer(X),X >= AccIn -> X;
+% (X,AccIn) -> AccIn
+% end,hd(Mins),tl(Mins)),
+% Ub = lists:min(Maxs),
+% {'ValueRange',{Lb,Ub}}.
+
+
+% sv_effective_constraint(SV,[]) ->
+% {'SingleValue',SV};
+% sv_effective_constraint([],_) ->
+% exit({error,{asn1,{illegal_single_value_constraint}}});
+% sv_effective_constraint(SV,[SV|Rest]) ->
+% sv_effective_constraint(SV,Rest);
+% sv_effective_constraint(Int,[SV|Rest]) when integer(Int),list(SV) ->
+% case lists:member(Int,SV) of
+% true ->
+% sv_effective_constraint(Int,Rest);
+% _ ->
+% exit({error,{asn1,{illegal_single_value_constraint}}})
+% end;
+% sv_effective_constraint(SV,[Int|Rest]) when integer(Int),list(SV) ->
+% case lists:member(Int,SV) of
+% true ->
+% sv_effective_constraint(Int,Rest);
+% _ ->
+% exit({error,{asn1,{illegal_single_value_constraint}}})
+% end;
+% sv_effective_constraint(SV1,[SV2|Rest]) when list(SV1),list(SV2) ->
+% sv_effective_constraint(common_set(SV1,SV2),Rest);
+% sv_effective_constraint(_,_) ->
+% exit({error,{asn1,{illegal_single_value_constraint}}}).
+
+%% common_set/2
+%% Two lists as input
+%% Returns the list with all elements that are common for both
+%% input lists
+% common_set(SV1,SV2) ->
+% lists:filter(fun(X)->lists:member(X,SV1) end,SV2).
+
+
+
+pre_encode(integer,[]) ->
+ [];
+pre_encode(integer,C=[{'SingleValue',_}]) ->
+ C;
+pre_encode(integer,C=[{'ValueRange',VR={Lb,Ub}}]) when integer(Lb),integer(Ub)->
+ Range = Ub-Lb+1,
+ if
+ Range =< 255 ->
+ NoBits = no_bits(Range),
+ [{'ValueRange',VR,Range,{bits,NoBits}}];
+ Range =< 256 ->
+ [{'ValueRange',VR,Range,{octets,1}}];
+ Range =< 65536 ->
+ [{'ValueRange',VR,Range,{octets,2}}];
+ true ->
+ C
+ end;
+pre_encode(integer,C) ->
+ C.
+
+no_bits(2) -> 1;
+no_bits(N) when N=<4 -> 2;
+no_bits(N) when N=<8 -> 3;
+no_bits(N) when N=<16 -> 4;
+no_bits(N) when N=<32 -> 5;
+no_bits(N) when N=<64 -> 6;
+no_bits(N) when N=<128 -> 7;
+no_bits(N) when N=<255 -> 8.
+
+%% Object code generating for encoding and decoding
+%% ------------------------------------------------
+
+gen_obj_code(Erules,_Module,Obj) when record(Obj,typedef) ->
+ ObjName = Obj#typedef.name,
+ Def = Obj#typedef.typespec,
+ #'Externaltypereference'{module=Mod,type=ClassName} =
+ Def#'Object'.classname,
+ Class = asn1_db:dbget(Mod,ClassName),
+ {object,_,Fields} = Def#'Object'.def,
+ emit({nl,nl,nl,"%%================================"}),
+ emit({nl,"%% ",ObjName}),
+ emit({nl,"%%================================",nl}),
+ EncConstructed =
+% gen_encode_objectfields(Class#classdef.typespec,ObjName,Fields,[]),
+ gen_encode_objectfields(ClassName,get_class_fields(Class),
+ ObjName,Fields,[]),
+ emit(nl),
+ gen_encode_constr_type(Erules,EncConstructed),
+ emit(nl),
+ DecConstructed =
+% gen_decode_objectfields(Class#classdef.typespec,ObjName,Fields,[]),
+ gen_decode_objectfields(ClassName,get_class_fields(Class),
+ ObjName,Fields,[]),
+ emit(nl),
+ gen_decode_constr_type(Erules,DecConstructed),
+ emit(nl);
+gen_obj_code(_Erules,_Module,Obj) when record(Obj,pobjectdef) ->
+ ok.
+
+gen_encode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(V) ->
+ emit(["'enc_",ObjName,"'(",{asis,Name},
+ ",",V,",_RestPrimFieldName) ->",nl])
+ end,
+% emit(["'enc_",ObjName,"'(",{asis,Name},
+% ", Val, RestPrimFieldName) ->",nl]),
+ MaybeConstr =
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} -> %% this case is illegal
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_"),
+ emit(" <<>>"),
+ [];
+ {false,{'DEFAULT',DefaultType}} ->
+ EmitFuncClause("Val"),
+ gen_encode_default_call(ClassName,Name,DefaultType);
+ {{Name,TypeSpec},_} ->
+ %% A specified field owerwrites any 'DEFAULT' or
+ %% 'OPTIONAL' field in the class
+ EmitFuncClause("Val"),
+ gen_encode_field_call(ObjName,Name,TypeSpec)
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_encode_objectfields(ClassName,Rest,ObjName,ObjectFields,
+ MaybeConstr++ConstrAcc);
+gen_encode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Attrs) ->
+ emit(["'enc_",ObjName,"'(",{asis,Name},
+ ",",Attrs,") ->",nl])
+ end,
+% emit(["'enc_",ObjName,"'(",{asis,Name},
+% ", Val,[H|T]) ->",nl]),
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} ->
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_,_"),
+ emit([" exit({error,{'use of missing field in object', ",Name,
+ "}})"]);
+ {false,{'DEFAULT',_DefaultObject}} ->
+ exit({error,{asn1,{"not implemented yet",Name}}});
+ {{Name,TypeSpec},_} ->
+ EmitFuncClause("Val,[H|T]"),
+ case TypeSpec#typedef.name of
+ {ExtMod,TypeName} ->
+ emit({indent(3),"'",ExtMod,"':'enc_",TypeName,
+ "'(H, Val, T)"});
+ TypeName ->
+ emit({indent(3),"'enc_",TypeName,"'(H, Val, T)"})
+ end
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_encode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc);
+gen_encode_objectfields(ClassName,[_C|Cs],O,OF,Acc) ->
+ gen_encode_objectfields(ClassName,Cs,O,OF,Acc);
+gen_encode_objectfields(_,[],_,_,Acc) ->
+ Acc.
+
+% gen_encode_objectfields(Class,ObjName,[{FieldName,Type}|Rest],ConstrAcc) ->
+% Fields = Class#objectclass.fields,
+
+% MaybeConstr =
+% case is_typefield(Fields,FieldName) of
+% true ->
+% Def = Type#typedef.typespec,
+% emit({"'enc_",ObjName,"'(",{asis,FieldName},
+% ", Val, Dummy) ->",nl}),
+
+% CAcc =
+% case Type#typedef.name of
+% {primitive,bif} ->
+% gen_encode_prim(per,Def,"false","Val"),
+% [];
+% {constructed,bif} ->
+% emit({" 'enc_",ObjName,'_',FieldName,
+% "'(Val)"}),
+% [{['enc_',ObjName,'_',FieldName],Def}];
+% {ExtMod,TypeName} ->
+% emit({" '",ExtMod,"':'enc_",TypeName,"'(Val)"}),
+% [];
+% TypeName ->
+% emit({" 'enc_",TypeName,"'(Val)"}),
+% []
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% CAcc;
+% {false,objectfield} ->
+% emit({"'enc_",ObjName,"'(",{asis,FieldName},
+% ", Val, [H|T]) ->",nl}),
+% case Type#typedef.name of
+% {ExtMod,TypeName} ->
+% emit({indent(3),"'",ExtMod,"':'enc_",TypeName,
+% "'(H, Val, T)"});
+% TypeName ->
+% emit({indent(3),"'enc_",TypeName,"'(H, Val, T)"})
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% [];
+% {false,_} -> []
+% end,
+% gen_encode_objectfields(Class,ObjName,Rest,MaybeConstr ++ ConstrAcc);
+% gen_encode_objectfields(C,O,[_|T],Acc) ->
+% gen_encode_objectfields(C,O,T,Acc);
+% gen_encode_objectfields(_,_,[],Acc) ->
+% Acc.
+
+gen_encode_constr_type(Erules,[TypeDef|Rest]) when record(TypeDef,typedef) ->
+ case is_already_generated(enc,TypeDef#typedef.name) of
+ true -> ok;
+ _ ->
+ Name = lists:concat(["enc_",TypeDef#typedef.name]),
+ emit({Name,"(Val) ->",nl}),
+ Def = TypeDef#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ asn1ct_gen:gen_encode_constructed(Erules,Name,InnerType,Def),
+ gen_encode_constr_type(Erules,Rest)
+ end;
+gen_encode_constr_type(_,[]) ->
+ ok.
+
+gen_encode_field_call(ObjName,FieldName,Type) ->
+ Def = Type#typedef.typespec,
+ case Type#typedef.name of
+ {primitive,bif} ->
+ gen_encode_prim(per,Def,"false",
+ "Val"),
+ [];
+ {constructed,bif} ->
+ emit({" 'enc_",ObjName,'_',FieldName,
+ "'(Val)"}),
+ [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+ {ExtMod,TypeName} ->
+ emit({" '",ExtMod,"':'enc_",TypeName,
+ "'(Val)"}),
+ [];
+ TypeName ->
+ emit({" 'enc_",TypeName,"'(Val)"}),
+ []
+ end.
+
+gen_encode_default_call(ClassName,FieldName,Type) ->
+ CurrentMod = get(currmod),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+%% asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
+ emit([" 'enc_",ClassName,'_',FieldName,"'(Val)"]),
+ [#typedef{name=list_to_atom(lists:concat([ClassName,'_',FieldName])),
+ typespec=Type}];
+ {primitive,bif} ->
+ gen_encode_prim(per,Type,"false","Val"),
+ [];
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'enc_",Etype,"'(Val)",nl]),
+ [];
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'enc_",Etype,"'(Val)",nl]),
+ []
+ end.
+
+
+
+gen_decode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Bytes) ->
+ emit(["'dec_",ObjName,"'(",{asis,Name},",",Bytes,
+ ",_,_RestPrimFieldName) ->",nl])
+ end,
+% emit(["'dec_",ObjName,"'(",{asis,Name},
+% ", Bytes, _, RestPrimFieldName) ->",nl]),
+ MaybeConstr=
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} -> %% this case is illegal
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_"),
+ emit([" asn1_NOVALUE"]),
+ [];
+ {false,{'DEFAULT',DefaultType}} ->
+ EmitFuncClause("Bytes"),
+ gen_decode_default_call(ClassName,Name,"Bytes",DefaultType);
+ {{Name,TypeSpec},_} ->
+ %% A specified field owerwrites any 'DEFAULT' or
+ %% 'OPTIONAL' field in the class
+ EmitFuncClause("Bytes"),
+ gen_decode_field_call(ObjName,Name,"Bytes",TypeSpec)
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,MaybeConstr++ConstrAcc);
+gen_decode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Attrs) ->
+ emit(["'dec_",ObjName,"'(",{asis,Name},
+ ",",Attrs,") ->",nl])
+ end,
+% emit(["'dec_",ObjName,"'(",{asis,Name},
+% ", Bytes,_,[H|T]) ->",nl]),
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} ->
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_,_,_"),
+ emit([" exit({error,{'illegal use of missing field in object', ",Name,
+ "}})"]);
+ {false,{'DEFAULT',_DefaultObject}} ->
+ exit({error,{asn1,{"not implemented yet",Name}}});
+ {{Name,TypeSpec},_} ->
+ EmitFuncClause("Bytes,_,[H|T]"),
+ case TypeSpec#typedef.name of
+ {ExtMod,TypeName} ->
+ emit({indent(3),"'",ExtMod,"':'dec_",TypeName,
+ "'(H, Bytes, telltype, T)"});
+ TypeName ->
+ emit({indent(3),"'dec_",TypeName,"'(H, Bytes, telltype, T)"})
+ end
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc);
+gen_decode_objectfields(CN,[_C|Cs],O,OF,CAcc) ->
+ gen_decode_objectfields(CN,Cs,O,OF,CAcc);
+gen_decode_objectfields(_,[],_,_,CAcc) ->
+ CAcc.
+
+
+gen_decode_field_call(ObjName,FieldName,Bytes,Type) ->
+ Def = Type#typedef.typespec,
+ case Type#typedef.name of
+ {primitive,bif} ->
+ gen_dec_prim(per,Def,Bytes),
+ [];
+ {constructed,bif} ->
+ emit({" 'dec_",ObjName,'_',FieldName,
+ "'(",Bytes,",telltype)"}),
+ [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+ {ExtMod,TypeName} ->
+ emit({" '",ExtMod,"':'dec_",TypeName,
+ "'(",Bytes,", telltype)"}),
+ [];
+ TypeName ->
+ emit({" 'dec_",TypeName,"'(",Bytes,", telltype)"}),
+ []
+ end.
+
+gen_decode_default_call(ClassName,FieldName,Bytes,Type) ->
+ CurrentMod = get(currmod),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ emit([" 'dec_",ClassName,'_',FieldName,"'(",Bytes,", telltype)"]),
+ [#typedef{name=list_to_atom(lists:concat([ClassName,'_',FieldName])),
+ typespec=Type}];
+ {primitive,bif} ->
+ gen_dec_prim(per,Type,Bytes),
+ [];
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'dec_",Etype,"'(",Bytes,", telltype)",nl]),
+ [];
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'dec_",Etype,"'(",Bytes,", telltype)",nl]),
+ []
+ end.
+
+%%%%%%%%%%%%%%%
+
+% gen_decode_objectfields(Class,ObjName,[{FieldName,Type}|Rest],ConstrAcc) ->
+% Fields = Class#objectclass.fields,
+
+% MaybeConstr =
+% case is_typefield(Fields,FieldName) of
+% true ->
+% Def = Type#typedef.typespec,
+% emit({"'dec_",ObjName,"'(",{asis,FieldName},
+% ", Val, Telltype, RestPrimFieldName) ->",nl}),
+
+% CAcc =
+% case Type#typedef.name of
+% {primitive,bif} ->
+% gen_dec_prim(per,Def,"Val"),
+% [];
+% {constructed,bif} ->
+% emit({" 'dec_",ObjName,'_',FieldName,
+% "'(Val, Telltype)"}),
+% [{['dec_',ObjName,'_',FieldName],Def}];
+% {ExtMod,TypeName} ->
+% emit({" '",ExtMod,"':'dec_",TypeName,
+% "'(Val, Telltype)"}),
+% [];
+% TypeName ->
+% emit({" 'dec_",TypeName,"'(Val, Telltype)"}),
+% []
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% CAcc;
+% {false,objectfield} ->
+% emit({"'dec_",ObjName,"'(",{asis,FieldName},
+% ", Val, Telltype, [H|T]) ->",nl}),
+% case Type#typedef.name of
+% {ExtMod,TypeName} ->
+% emit({indent(3),"'",ExtMod,"':'dec_",TypeName,
+% "'(H, Val, Telltype, T)"});
+% TypeName ->
+% emit({indent(3),"'dec_",TypeName,
+% "'(H, Val, Telltype, T)"})
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% [];
+% {false,_} ->
+% []
+% end,
+% gen_decode_objectfields(Class,ObjName,Rest,MaybeConstr ++ ConstrAcc);
+% gen_decode_objectfields(C,O,[_|T],CAcc) ->
+% gen_decode_objectfields(C,O,T,CAcc);
+% gen_decode_objectfields(_,_,[],CAcc) ->
+% CAcc.
+
+gen_decode_constr_type(Erules,[{Name,Def}|Rest]) ->
+ emit({Name,"(Bytes,_) ->",nl}),
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ asn1ct_gen:gen_decode_constructed(Erules,Name,InnerType,Def),
+ gen_decode_constr_type(Erules,Rest);
+gen_decode_constr_type(Erules,[TypeDef|Rest]) when record(TypeDef,typedef) ->
+ case is_already_generated(dec,TypeDef#typedef.name) of
+ true -> ok;
+ _ ->
+ gen_decode(Erules,TypeDef)
+ end,
+ gen_decode_constr_type(Erules,Rest);
+gen_decode_constr_type(_,[]) ->
+ ok.
+
+% is_typefield(Fields,FieldName) ->
+% case lists:keysearch(FieldName,2,Fields) of
+% {value,Field} ->
+% case element(1,Field) of
+% typefield ->
+% true;
+% Other ->
+% {false,Other}
+% end;
+% _ ->
+% false
+% end.
+%% Object Set code generating for encoding and decoding
+%% ----------------------------------------------------
+gen_objectset_code(Erules,ObjSet) ->
+ ObjSetName = ObjSet#typedef.name,
+ Def = ObjSet#typedef.typespec,
+%% {ClassName,ClassDef} = Def#'ObjectSet'.class,
+ #'Externaltypereference'{module=ClassModule,
+ type=ClassName} = Def#'ObjectSet'.class,
+ ClassDef = asn1_db:dbget(ClassModule,ClassName),
+ UniqueFName = Def#'ObjectSet'.uniquefname,
+ Set = Def#'ObjectSet'.set,
+ emit({nl,nl,nl,"%%================================"}),
+ emit({nl,"%% ",ObjSetName}),
+ emit({nl,"%%================================",nl}),
+ case ClassName of
+ {_Module,ExtClassName} ->
+ gen_objset_code(Erules,ObjSetName,UniqueFName,Set,
+ ExtClassName,ClassDef);
+ _ ->
+ gen_objset_code(Erules,ObjSetName,UniqueFName,Set,
+ ClassName,ClassDef)
+ end,
+ emit(nl).
+
+gen_objset_code(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassDef)->
+ ClassFields = (ClassDef#classdef.typespec)#objectclass.fields,
+ InternalFuncs=
+ gen_objset_enc(ObjSetName,UniqueFName,Set,ClassName,
+ ClassFields,1,[]),
+ gen_objset_dec(ObjSetName,UniqueFName,Set,ClassName,ClassFields,1),
+ gen_internal_funcs(Erules,InternalFuncs).
+
+gen_objset_enc(_,{unique,undefined},_,_,_,_,_) ->
+ %% There is no unique field in the class of this object set
+ %% don't bother about the constraint
+ [];
+gen_objset_enc(ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest],
+ ClName,ClFields,NthObj,Acc)->
+ emit({"'getenc_",ObjSName,"'(",{asis,UniqueName},",",
+ {asis,Val},") ->",nl}),
+ {InternalFunc,NewNthObj}=
+ case ObjName of
+ no_name ->
+ gen_inlined_enc_funs(Fields,ClFields,ObjSName,NthObj);
+ _ ->
+ emit({" fun 'enc_",ObjName,"'/3"}),
+ {[],NthObj}
+ end,
+ emit({";",nl}),
+ gen_objset_enc(ObjSName,UniqueName,[T|Rest],ClName,ClFields,
+ NewNthObj,InternalFunc++Acc);
+gen_objset_enc(ObjSetName,UniqueName,
+ [{ObjName,Val,Fields}],_ClName,ClFields,NthObj,Acc) ->
+
+ emit({"'getenc_",ObjSetName,"'(",{asis,UniqueName},",",
+ {asis,Val},") ->",nl}),
+ {InternalFunc,_}=
+ case ObjName of
+ no_name ->
+ gen_inlined_enc_funs(Fields,ClFields,ObjSetName,NthObj);
+ _ ->
+ emit({" fun 'enc_",ObjName,"'/3"}),
+ {[],NthObj}
+ end,
+ emit({".",nl,nl}),
+ InternalFunc++Acc;
+gen_objset_enc(ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
+ _ClFields,_NthObj,Acc) ->
+ emit({"'getenc_",ObjSetName,"'(_, _) ->",nl}),
+ emit({indent(3),"fun(_, Val, _) ->",nl}),
+ emit({indent(6),"Size = if",nl}),
+ emit({indent(9),"list(Val) -> length(Val);",nl}),
+ emit({indent(9),"true -> size(Val)",nl}),
+ emit({indent(6),"end,",nl}),
+ emit({indent(6),"if",nl}),
+ emit({indent(9),"Size < 256 ->",nl}),
+ emit({indent(12),"[20,Size,Val];",nl}),
+ emit({indent(9),"true ->",nl}),
+ emit({indent(12),"[21,<>,Val]",nl}),
+ emit({indent(6),"end",nl}),
+ emit({indent(3),"end.",nl,nl}),
+ Acc;
+gen_objset_enc(_,_,[],_,_,_,Acc) ->
+ Acc.
+
+%% gen_inlined_enc_funs for each object iterates over all fields of a
+%% class, and for each typefield it checks if the object has that
+%% field and emits the proper code.
+gen_inlined_enc_funs(Fields,[{typefield,Name,_}|Rest],ObjSetName,NthObj) ->
+ InternalDefFunName=asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({indent(3),"fun(Type, Val, _) ->",nl,
+ indent(6),"case Type of",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+N,Ret);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({indent(3),"fun(Type, Val, _) ->",nl,
+ indent(6),"case Type of",nl}),
+ emit({indent(9),{asis,Name}," ->",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+N,Ret);
+ false ->
+ gen_inlined_enc_funs(Fields,Rest,ObjSetName,NthObj)
+ end;
+gen_inlined_enc_funs(Fields,[_|Rest],ObjSetName,NthObj) ->
+ gen_inlined_enc_funs(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_enc_funs(_,[],_,NthObj) ->
+ {[],NthObj}.
+
+gen_inlined_enc_funs1(Fields,[{typefield,Name,_}|Rest],ObjSetName,
+ NthObj,Acc) ->
+ InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
+ {Acc2,NAdd}=
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({";",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ {Ret++Acc,N};
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({";",nl,indent(9),{asis,Name}," ->",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ {Ret++Acc,N};
+ false ->
+ {Acc,0}
+ end,
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+NAdd,Acc2);
+gen_inlined_enc_funs1(Fields,[_|Rest],ObjSetName,NthObj,Acc)->
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj,Acc);
+gen_inlined_enc_funs1(_,[],_,NthObj,Acc) ->
+ emit({nl,indent(6),"end",nl}),
+ emit({indent(3),"end"}),
+ {Acc,NthObj}.
+
+emit_inner_of_fun(TDef=#typedef{name={ExtMod,Name},typespec=Type},
+ InternalDefFunName) ->
+ case {ExtMod,Name} of
+ {primitive,bif} ->
+ emit(indent(12)),
+ gen_encode_prim(per,Type,dotag,"Val"),
+ {[],0};
+ {constructed,bif} ->
+ emit([indent(12),"'enc_",
+ InternalDefFunName,"'(Val)"]),
+ {[TDef#typedef{name=InternalDefFunName}],1};
+ _ ->
+ emit({indent(12),"'",ExtMod,"':'enc_",Name,"'(Val)"}),
+ {[],0}
+ end;
+emit_inner_of_fun(#typedef{name=Name},_) ->
+ emit({indent(12),"'enc_",Name,"'(Val)"}),
+ {[],0};
+emit_inner_of_fun(Type,_) when record(Type,type) ->
+ CurrMod = get(currmod),
+ case Type#type.def of
+ Def when atom(Def) ->
+ emit({indent(9),Def," ->",nl,indent(12)}),
+ gen_encode_prim(erules,Type,dotag,"Val");
+ TRef when record(TRef,typereference) ->
+ T = TRef#typereference.val,
+ emit({indent(9),T," ->",nl,indent(12),"'enc_",T,"'(Val)"});
+ #'Externaltypereference'{module=CurrMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),"'enc_",T,"'(Val)"});
+ #'Externaltypereference'{module=ExtMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),ExtMod,":'enc_",
+ T,"'(Val)"})
+ end,
+ {[],0}.
+
+indent(N) ->
+ lists:duplicate(N,32). % 32 = space
+
+
+gen_objset_dec(_,{unique,undefined},_,_,_,_) ->
+ %% There is no unique field in the class of this object set
+ %% don't bother about the constraint
+ ok;
+gen_objset_dec(ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest],ClName,
+ ClFields,NthObj)->
+
+ emit({"'getdec_",ObjSName,"'(",{asis,UniqueName},",",
+ {asis,Val},") ->",nl}),
+ NewNthObj=
+ case ObjName of
+ no_name ->
+ gen_inlined_dec_funs(Fields,ClFields,ObjSName,NthObj);
+ _ ->
+ emit({" fun 'dec_",ObjName,"'/4"}),
+ NthObj
+ end,
+ emit({";",nl}),
+ gen_objset_dec(ObjSName,UniqueName,[T|Rest],ClName,ClFields,NewNthObj);
+gen_objset_dec(ObjSetName,UniqueName,[{ObjName,Val,Fields}],_ClName,
+ ClFields,NthObj) ->
+
+ emit({"'getdec_",ObjSetName,"'(",{asis,UniqueName},",",
+ {asis,Val},") ->",nl}),
+ case ObjName of
+ no_name ->
+ gen_inlined_dec_funs(Fields,ClFields,ObjSetName,NthObj);
+ _ ->
+ emit({" fun 'dec_",ObjName,"'/4"})
+ end,
+ emit({".",nl,nl}),
+ ok;
+gen_objset_dec(ObjSetName,_,['EXTENSIONMARK'],_ClName,_ClFields,
+ _NthObj) ->
+ emit({"'getdec_",ObjSetName,"'(_, _) ->",nl}),
+ emit({indent(3),"fun(Attr1, Bytes, _, _) ->",nl}),
+ %% emit({indent(6),"?RT_PER:decode_open_type(Bytes,[])",nl}),
+ emit({indent(6),"{Bytes,Attr1}",nl}),
+ emit({indent(3),"end.",nl,nl}),
+ ok;
+gen_objset_dec(_,_,[],_,_,_) ->
+ ok.
+
+gen_inlined_dec_funs(Fields,[{typefield,Name,_}|Rest],
+ ObjSetName,NthObj) ->
+ InternalDefFunName = [NthObj,Name,ObjSetName],
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({indent(3),"fun(Type, Val, _, _) ->",nl,
+ indent(6),"case Type of",nl}),
+ N=emit_inner_of_decfun(Type,InternalDefFunName),
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({indent(3),"fun(Type, Val, _, _) ->",nl,
+ indent(6),"case Type of",nl}),
+ emit({indent(9),{asis,Name}," ->",nl}),
+ N=emit_inner_of_decfun(Type,InternalDefFunName),
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N);
+ false ->
+ gen_inlined_dec_funs(Fields,Rest,ObjSetName,NthObj)
+ end;
+gen_inlined_dec_funs(Fields,[_|Rest],ObjSetName,NthObj) ->
+ gen_inlined_dec_funs(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_dec_funs(_,[],_,NthObj) ->
+ NthObj.
+
+gen_inlined_dec_funs1(Fields,[{typefield,Name,_}|Rest],
+ ObjSetName,NthObj) ->
+ InternalDefFunName = [NthObj,Name,ObjSetName],
+ N=
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({";",nl}),
+ emit_inner_of_decfun(Type,InternalDefFunName);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({";",nl,indent(9),{asis,Name}," ->",nl}),
+ emit_inner_of_decfun(Type,InternalDefFunName);
+ false ->
+ 0
+ end,
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N);
+gen_inlined_dec_funs1(Fields,[_|Rest],ObjSetName,NthObj)->
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_dec_funs1(_,[],_,NthObj) ->
+ emit({nl,indent(6),"end",nl}),
+ emit({indent(3),"end"}),
+ NthObj.
+
+emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type},
+ InternalDefFunName) ->
+ case {ExtName,Name} of
+ {primitive,bif} ->
+ emit(indent(12)),
+ gen_dec_prim(per,Type,"Val"),
+ 0;
+ {constructed,bif} ->
+ emit({indent(12),"'dec_",
+ asn1ct_gen:list2name(InternalDefFunName),"'(Val)"}),
+ 1;
+ _ ->
+ emit({indent(12),"'",ExtName,"':'dec_",Name,
+ "'(Val, telltype)"}),
+ 0
+ end;
+emit_inner_of_decfun(#typedef{name=Name},_) ->
+ emit({indent(12),"'dec_",Name,"'(Val, telltype)"}),
+ 0;
+emit_inner_of_decfun(Type,_) when record(Type,type) ->
+ CurrMod = get(currmod),
+ case Type#type.def of
+ Def when atom(Def) ->
+ emit({indent(9),Def," ->",nl,indent(12)}),
+ gen_dec_prim(erules,Type,"Val");
+ TRef when record(TRef,typereference) ->
+ T = TRef#typereference.val,
+ emit({indent(9),T," ->",nl,indent(12),"'dec_",T,"'(Val)"});
+ #'Externaltypereference'{module=CurrMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),"'dec_",T,"'(Val)"});
+ #'Externaltypereference'{module=ExtMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),ExtMod,":'dec_",
+ T,"'(Val)"})
+ end,
+ 0.
+
+
+gen_internal_funcs(_Erules,[]) ->
+ ok;
+gen_internal_funcs(Erules,[TypeDef|Rest]) ->
+ gen_encode_user(Erules,TypeDef),
+ emit([nl,nl,"'dec_",TypeDef#typedef.name,"'(Bytes) ->",nl]),
+ gen_decode_user(Erules,TypeDef),
+ gen_internal_funcs(Erules,Rest).
+
+
+
+%% DECODING *****************************
+%%***************************************
+
+
+gen_decode(Erules,Type) when record(Type,typedef) ->
+ D = Type,
+ emit({nl,nl}),
+ emit({"'dec_",Type#typedef.name,"'(Bytes,_) ->",nl}),
+ dbdec(Type#typedef.name),
+ gen_decode_user(Erules,D).
+
+gen_decode(Erules,Tname,#'ComponentType'{name=Cname,typespec=Type}) ->
+ NewTname = [Cname|Tname],
+ gen_decode(Erules,NewTname,Type);
+
+gen_decode(Erules,Typename,Type) when record(Type,type) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ ObjFun =
+ case Type#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _ ->
+ ""
+ end,
+ emit({nl,"'dec_",asn1ct_gen:list2name(Typename),
+ "'(Bytes,_",ObjFun,") ->",nl}),
+ dbdec(Typename),
+ asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,Type);
+ _ ->
+ true
+ end.
+
+dbdec(Type) when list(Type)->
+ demit({"io:format(\"decoding: ",asn1ct_gen:list2name(Type),"~w~n\",[Bytes]),",nl});
+dbdec(Type) ->
+ demit({"io:format(\"decoding: ",{asis,Type},"~w~n\",[Bytes]),",nl}).
+
+gen_decode_user(Erules,D) when record(D,typedef) ->
+ CurrMod = get(currmod),
+ Typename = [D#typedef.name],
+ Def = D#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {primitive,bif} ->
+ gen_dec_prim(Erules,Def,"Bytes"),
+ emit({".",nl,nl});
+ 'ASN1_OPEN_TYPE' ->
+ gen_dec_prim(Erules,Def#type{def='ASN1_OPEN_TYPE'},"Bytes"),
+ emit({".",nl,nl});
+ {constructed,bif} ->
+ asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,D);
+ #typereference{val=Dname} ->
+ emit({"'dec_",Dname,"'(Bytes,telltype)"}),
+ emit({".",nl,nl});
+ #'Externaltypereference'{module=CurrMod,type=Etype} ->
+ emit({"'dec_",Etype,"'(Bytes,telltype).",nl,nl});
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit({"'",Emod,"':'dec_",Etype,"'(Bytes,telltype).",nl,nl});
+ Other ->
+ exit({error,{asn1,{unknown,Other}}})
+ end.
+
+
+
+gen_dec_prim(_Erules,Att,BytesVar) ->
+ Typename = Att#type.def,
+ Constraint = Att#type.constraint,
+ case Typename of
+ 'INTEGER' ->
+ EffectiveConstr = effective_constraint(integer,Constraint),
+ emit_dec_integer(EffectiveConstr,BytesVar);
+% emit({"?RT_PER:decode_integer(",BytesVar,",",
+% {asis,EffectiveConstr},")"});
+ {'INTEGER',NamedNumberList} ->
+ EffectiveConstr = effective_constraint(integer,Constraint),
+ emit_dec_integer(EffectiveConstr,BytesVar,NamedNumberList);
+% emit({"?RT_PER:decode_integer(",BytesVar,",",
+% {asis,EffectiveConstr},",",
+% {asis,NamedNumberList},")"});
+ {'BIT STRING',NamedNumberList} ->
+ case get(compact_bit_string) of
+ true ->
+ emit({"?RT_PER:decode_compact_bit_string(",
+ BytesVar,",",{asis,Constraint},",",
+ {asis,NamedNumberList},")"});
+ _ ->
+ emit({"?RT_PER:decode_bit_string(",BytesVar,",",
+ {asis,Constraint},",",
+ {asis,NamedNumberList},")"})
+ end;
+ 'NULL' ->
+ emit({"?RT_PER:decode_null(",
+ BytesVar,")"});
+ 'OBJECT IDENTIFIER' ->
+ emit({"?RT_PER:decode_object_identifier(",
+ BytesVar,")"});
+ 'ObjectDescriptor' ->
+ emit({"?RT_PER:decode_ObjectDescriptor(",
+ BytesVar,")"});
+ {'ENUMERATED',{NamedNumberList1,NamedNumberList2}} ->
+ NewTup = {list_to_tuple([X||{X,_} <- NamedNumberList1]),
+ list_to_tuple([X||{X,_} <- NamedNumberList2])},
+ NewC = [{'ValueRange',{0,size(element(1,NewTup))-1}}],
+ emit({"?RT_PER:decode_enumerated(",BytesVar,",",
+ {asis,NewC},",",
+ {asis,NewTup},")"});
+ {'ENUMERATED',NamedNumberList} ->
+ %NewTup = list_to_tuple([X||{X,Y} <- NamedNumberList]),
+ NewNNL = [X||{X,_} <- NamedNumberList],
+ NewC = effective_constraint(integer,
+ [{'ValueRange',{0,length(NewNNL)-1}}]),
+ emit_dec_enumerated(BytesVar,NewC,NewNNL);
+% emit({"?RT_PER:decode_enumerated(",BytesVar,",",
+% {asis,NewC},",",
+% {asis,NewTup},")"});
+ 'BOOLEAN'->
+ emit({"?RT_PER:decode_boolean(",BytesVar,")"});
+ 'OCTET STRING' ->
+ emit_dec_octet_string(Constraint,BytesVar);
+% emit({"?RT_PER:decode_octet_string(",BytesVar,",",
+% {asis,Constraint},")"});
+ 'NumericString' ->
+ emit_dec_known_multiplier_string('NumericString',
+ Constraint,BytesVar);
+% emit({"?RT_PER:decode_NumericString(",BytesVar,",",
+% {asis,Constraint},")"});
+ 'TeletexString' ->
+ emit({"?RT_PER:decode_TeletexString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'VideotexString' ->
+ emit({"?RT_PER:decode_VideotexString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'UTCTime' ->
+ emit_dec_known_multiplier_string('VisibleString',
+ Constraint,BytesVar);
+% emit({"?RT_PER:decode_VisibleString(",BytesVar,",",
+% {asis,Constraint},")"});
+ 'GeneralizedTime' ->
+ emit_dec_known_multiplier_string('VisibleString',
+ Constraint,BytesVar);
+% emit({"?RT_PER:decode_VisibleString(",BytesVar,",",
+% {asis,Constraint},")"});
+ 'GraphicString' ->
+ emit({"?RT_PER:decode_GraphicString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'VisibleString' ->
+ emit_dec_known_multiplier_string('VisibleString',
+ Constraint,BytesVar);
+% emit({"?RT_PER:decode_VisibleString(",BytesVar,",",
+% {asis,Constraint},")"});
+ 'GeneralString' ->
+ emit({"?RT_PER:decode_GeneralString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'PrintableString' ->
+ emit_dec_known_multiplier_string('PrintableString',
+ Constraint,BytesVar);
+% emit({"?RT_PER:decode_PrintableString(",BytesVar,",",{asis,Constraint},")"});
+ 'IA5String' ->
+ emit_dec_known_multiplier_string('IA5String',Constraint,BytesVar);
+% emit({"?RT_PER:decode_IA5String(",BytesVar,",",{asis,Constraint},")"});
+ 'BMPString' ->
+ emit_dec_known_multiplier_string('BMPString',Constraint,BytesVar);
+% emit({"?RT_PER:decode_BMPString(",BytesVar,",",{asis,Constraint},")"});
+ 'UniversalString' ->
+ emit_dec_known_multiplier_string('UniversalString',
+ Constraint,BytesVar);
+% emit({"?RT_PER:decode_UniversalString(",BytesVar,",",{asis,Constraint},")"});
+ 'ANY' ->
+ emit(["?RT_PER:decode_open_type(",BytesVar,",",
+ {asis,Constraint}, ")"]);
+ 'ASN1_OPEN_TYPE' ->
+ case Constraint of
+ [#'Externaltypereference'{type=Tname}] ->
+ emit(["fun(FBytes) ->",nl,
+ " {XTerm,XBytes} = "]),
+ emit(["?RT_PER:decode_open_type(FBytes,[]),",nl]),
+ emit([" {YTerm,_} = dec_",Tname,"(XTerm,mandatory),",nl]),
+ emit([" {YTerm,XBytes} end(",BytesVar,")"]);
+ [#type{def=#'Externaltypereference'{type=Tname}}] ->
+ emit(["fun(FBytes) ->",nl,
+ " {XTerm,XBytes} = "]),
+ emit(["?RT_PER:decode_open_type(FBytes,[]),",nl]),
+ emit([" {YTerm,_} = dec_",Tname,"(XTerm,mandatory),",nl]),
+ emit([" {YTerm,XBytes} end(",BytesVar,")"]);
+ _ ->
+ emit(["?RT_PER:decode_open_type(",BytesVar,",[])"])
+ end;
+ Other ->
+ exit({'cant decode' ,Other})
+ end.
+
+
+emit_dec_integer(C,BytesVar,NNL) ->
+ asn1ct_name:new(tmpterm),
+ asn1ct_name:new(buffer),
+ Tmpterm = asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),
+ Buffer = asn1ct_gen:mk_var(asn1ct_name:curr(buffer)),
+ emit({" begin {",{curr,tmpterm},",",{curr,buffer},"} = ",nl}),
+ emit_dec_integer(C,BytesVar),
+ emit({",",nl," case ",Tmpterm," of",nl}),
+ lists:map(fun({Name,Int})->emit({" ",Int," -> {",{asis,Name},",",
+ Buffer,"};",nl});
+ (_)-> exit({error,{asn1,{"error in named number list",NNL}}})
+ end,
+ NNL),
+ emit({" _ -> {",Tmpterm,",",Buffer,"}",nl}),
+ emit({" end",nl}), % end of case
+ emit(" end"). % end of begin
+
+emit_dec_integer([{'SingleValue',Int}],BytesVar) when integer(Int) ->
+ emit(["{",Int,",",BytesVar,"}"]);
+emit_dec_integer([{_,{Lb,_Ub},_Range,{BitsOrOctets,N}}],BytesVar) ->
+ GetBorO =
+ case BitsOrOctets of
+ bits -> "getbits";
+ _ -> "getoctets"
+ end,
+ asn1ct_name:new(tmpterm),
+ asn1ct_name:new(tmpremain),
+ emit({" begin",nl," {",{curr,tmpterm},",",{curr,tmpremain},"}=",
+ "?RT_PER:",GetBorO,"(",BytesVar,",",N,"),",nl}),
+ emit({" {",{curr,tmpterm},"+",Lb,",",{curr,tmpremain},"}",nl,
+ " end"});
+emit_dec_integer([{_,{'MIN',_}}],BytesVar) ->
+ emit({"?RT_PER:decode_unconstrained_number(",BytesVar,")"});
+emit_dec_integer([{_,{Lb,'MAX'}}],BytesVar) ->
+ emit({"?RT_PER:decode_semi_constrained_number(",BytesVar,",",Lb,")"});
+emit_dec_integer([{'ValueRange',VR={Lb,Ub}}],BytesVar) ->
+ Range = Ub-Lb+1,
+ emit({"?RT_PER:decode_constrained_number(",BytesVar,",",
+ {asis,VR},",",Range,")"});
+emit_dec_integer(C=[{Rc,_}],BytesVar) when tuple(Rc) ->
+ emit({"?RT_PER:decode_integer(",BytesVar,",",{asis,C},")"});
+emit_dec_integer(_,BytesVar) ->
+ emit({"?RT_PER:decode_unconstrained_number(",BytesVar,")"}).
+
+
+emit_dec_enumerated(BytesVar,C,NamedNumberList) ->
+ emit_dec_enumerated_begin(),% emits a begin if component
+ asn1ct_name:new(tmpterm),
+ Tmpterm = asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),
+ asn1ct_name:new(tmpremain),
+ Tmpremain = asn1ct_gen:mk_var(asn1ct_name:curr(tmpremain)),
+ emit({" {",{curr,tmpterm},",",{curr,tmpremain},"} =",nl}),
+ emit_dec_integer(C,BytesVar),
+ emit({",",nl," case ",Tmpterm," of "}),
+% Cases=lists:flatten(dec_enumerated_cases(NamedNumberList,asn1ct_gen:mk_var(asn1ct_name:curr(tmpremain)),0)),
+ Cases=lists:flatten(dec_enumerated_cases(NamedNumberList,Tmpremain,0)),
+ emit({Cases++"_->exit({error,{asn1,{decode_enumerated,{",Tmpterm,
+ ",",{asis,NamedNumberList},"}}}}) end",nl}),
+ emit_dec_enumerated_end().
+
+emit_dec_enumerated_begin() ->
+ case get(component_type) of
+ {true,_} ->
+ emit({" begin",nl});
+ _ -> ok
+ end.
+
+emit_dec_enumerated_end() ->
+ case get(component_type) of
+ {true,_} ->
+ emit(" end");
+ _ -> ok
+ end.
+
+% dec_enumerated_cases(NNL,Tmpremain,No) ->
+% Cases=dec_enumerated_cases1(NNL,Tmpremain,0),
+% lists:flatten(io_lib:format("(case ~s "++Cases++
+% "~s when atom(~s)->exit({error,{asn1,{namednumber,~s}}});_->~s end)",[Value,"TmpVal","TmpVal","TmpVal",Value])).
+
+dec_enumerated_cases([Name|Rest],Tmpremain,No) ->
+ io_lib:format("~w->{~w,~s};",[No,Name,Tmpremain])++
+ dec_enumerated_cases(Rest,Tmpremain,No+1);
+dec_enumerated_cases([],_,_) ->
+ "".
+
+
+% more_genfields(_Fields,[]) ->
+% false;
+% more_genfields(Fields,[{FieldName,_}|T]) ->
+% case is_typefield(Fields,FieldName) of
+% true -> true;
+% {false,objectfield} -> true;
+% {false,_} -> more_genfields(Fields,T)
+% end.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_name.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_name.erl
new file mode 100644
index 0000000000..1c7769998c
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_name.erl
@@ -0,0 +1,225 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_name.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1ct_name).
+
+%%-compile(export_all).
+-export([name_server_loop/1,
+ start/0,
+ stop/0,
+ push/1,
+ pop/1,
+ curr/1,
+ clear/0,
+ delete/1,
+ active/1,
+ prev/1,
+ next/1,
+ all/1,
+ new/1]).
+
+start() ->
+ start_server(asn1_ns, asn1ct_name,name_server_loop,[[]]).
+
+stop() -> stop_server(asn1_ns).
+
+name_server_loop(Vars) ->
+%% io:format("name -- ~w~n",[Vars]),
+ receive
+ {From,{current,Variable}} ->
+ From ! {asn1_ns,get_curr(Vars,Variable)},
+ name_server_loop(Vars);
+ {From,{pop,Variable}} ->
+ From ! {asn1_ns,done},
+ name_server_loop(pop_var(Vars,Variable));
+ {From,{push,Variable}} ->
+ From ! {asn1_ns,done},
+ name_server_loop(push_var(Vars,Variable));
+ {From,{delete,Variable}} ->
+ From ! {asn1_ns,done},
+ name_server_loop(delete_var(Vars,Variable));
+ {From,{new,Variable}} ->
+ From ! {asn1_ns,done},
+ name_server_loop(new_var(Vars,Variable));
+ {From,{prev,Variable}} ->
+ From ! {asn1_ns,get_prev(Vars,Variable)},
+ name_server_loop(Vars);
+ {From,{next,Variable}} ->
+ From ! {asn1_ns,get_next(Vars,Variable)},
+ name_server_loop(Vars);
+ {From,stop} ->
+ From ! {asn1_ns,stopped},
+ exit(normal)
+ end.
+
+active(V) ->
+ case curr(V) of
+ nil -> false;
+ _ -> true
+ end.
+
+req(Req) ->
+ asn1_ns ! {self(), Req},
+ receive {asn1_ns, Reply} -> Reply end.
+
+pop(V) -> req({pop,V}).
+push(V) -> req({push,V}).
+clear() -> req(stop), start().
+curr(V) -> req({current,V}).
+new(V) -> req({new,V}).
+delete(V) -> req({delete,V}).
+prev(V) ->
+ case req({prev,V}) of
+ none ->
+ exit('cant get prev of none');
+ Rep -> Rep
+ end.
+
+next(V) ->
+ case req({next,V}) of
+ none ->
+ exit('cant get next of none');
+ Rep -> Rep
+ end.
+
+all(V) ->
+ Curr = curr(V),
+ if Curr == V -> [];
+ true ->
+ lists:reverse(generate(V,last(Curr),[],0))
+ end.
+
+generate(V,Number,Res,Pos) ->
+ Ell = Pos+1,
+ if
+ Ell > Number ->
+ Res;
+ true ->
+ generate(V,Number,[list_to_atom(lists:concat([V,Ell]))|Res],Ell)
+ end.
+
+last(V) ->
+ last2(lists:reverse(atom_to_list(V))).
+
+last2(RevL) ->
+ list_to_integer(lists:reverse(get_digs(RevL))).
+
+
+get_digs([H|T]) ->
+ if
+ H < $9+1,
+ H > $0-1 ->
+ [H|get_digs(T)];
+ true ->
+ []
+ end.
+
+push_var(Vars,Variable) ->
+ case lists:keysearch(Variable,1,Vars) of
+ false ->
+ [{Variable,[0]}|Vars];
+ {value,{Variable,[Digit|Drest]}} ->
+ NewVars = lists:keydelete(Variable,1,Vars),
+ [{Variable,[Digit,Digit|Drest]}|NewVars]
+ end.
+
+pop_var(Vars,Variable) ->
+ case lists:keysearch(Variable,1,Vars) of
+ false ->
+ ok;
+ {value,{Variable,[_Dig]}} ->
+ lists:keydelete(Variable,1,Vars);
+ {value,{Variable,[_Dig|Digits]}} ->
+ NewVars = lists:keydelete(Variable,1,Vars),
+ [{Variable,Digits}|NewVars]
+ end.
+
+get_curr([],Variable) ->
+ Variable;
+get_curr([{Variable,[0|_Drest]}|_Tail],Variable) ->
+ Variable;
+get_curr([{Variable,[Digit|_Drest]}|_Tail],Variable) ->
+ list_to_atom(lists:concat([Variable,integer_to_list(Digit)]));
+
+get_curr([_|Tail],Variable) ->
+ get_curr(Tail,Variable).
+
+new_var(Vars,Variable) ->
+ case lists:keysearch(Variable,1,Vars) of
+ false ->
+ [{Variable,[1]}|Vars];
+ {value,{Variable,[Digit|Drest]}} ->
+ NewVars = lists:keydelete(Variable,1,Vars),
+ [{Variable,[Digit+1|Drest]}|NewVars]
+ end.
+
+delete_var(Vars,Variable) ->
+ case lists:keysearch(Variable,1,Vars) of
+ false ->
+ Vars;
+ {value,{Variable,[N]}} when N =< 1 ->
+ lists:keydelete(Variable,1,Vars);
+ {value,{Variable,[Digit|Drest]}} ->
+ case Digit of
+ 0 ->
+ Vars;
+ _ ->
+ NewVars = lists:keydelete(Variable,1,Vars),
+ [{Variable,[Digit-1|Drest]}|NewVars]
+ end
+ end.
+
+get_prev(Vars,Variable) ->
+ case lists:keysearch(Variable,1,Vars) of
+ false ->
+ none;
+ {value,{Variable,[Digit|_]}} when Digit =< 1 ->
+ Variable;
+ {value,{Variable,[Digit|_]}} when Digit > 1 ->
+ list_to_atom(lists:concat([Variable,
+ integer_to_list(Digit-1)]));
+ _ ->
+ none
+ end.
+
+get_next(Vars,Variable) ->
+ case lists:keysearch(Variable,1,Vars) of
+ false ->
+ list_to_atom(lists:concat([Variable,"1"]));
+ {value,{Variable,[Digit|_]}} when Digit >= 0 ->
+ list_to_atom(lists:concat([Variable,
+ integer_to_list(Digit+1)]));
+ _ ->
+ none
+ end.
+
+
+stop_server(Name) ->
+ stop_server(Name, whereis(Name)).
+stop_server(_Name, undefined) -> stopped;
+stop_server(Name, _Pid) ->
+ Name ! {self(), stop},
+ receive {Name, _} -> stopped end.
+
+
+start_server(Name,Mod,Fun,Args) ->
+ case whereis(Name) of
+ undefined ->
+ register(Name, spawn(Mod,Fun, Args));
+ _Pid ->
+ already_started
+ end.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_parser.yrl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_parser.yrl
new file mode 100644
index 0000000000..b2c1d70f6e
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_parser.yrl
@@ -0,0 +1,1162 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_parser.yrl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+Nonterminals
+ModuleDefinition ModuleIdentifier DefinitiveIdentifier DefinitiveObjIdComponentList
+DefinitiveObjIdComponent TagDefault ExtensionDefault
+ModuleBody Exports SymbolsExported Imports SymbolsImported
+SymbolsFromModuleList SymbolsFromModule GlobalModuleReference AssignedIdentifier SymbolList
+Symbol Reference AssignmentList Assignment
+ExtensionAndException
+ComponentTypeLists
+Externaltypereference Externalvaluereference DefinedType DefinedValue
+AbsoluteReference ItemSpec ItemId ComponentId TypeAssignment
+ValueAssignment
+% ValueSetTypeAssignment
+ValueSet
+Type BuiltinType NamedType ReferencedType
+Value ValueNotNull BuiltinValue ReferencedValue NamedValue
+% BooleanType
+BooleanValue IntegerType NamedNumberList NamedNumber SignedNumber
+% inlined IntegerValue
+EnumeratedType
+% inlined Enumerations
+Enumeration EnumerationItem
+% inlined EnumeratedValue
+% RealType
+RealValue NumericRealValue SpecialRealValue BitStringType
+% inlined BitStringValue
+IdentifierList
+% OctetStringType
+% inlined OctetStringValue
+% NullType NullValue
+SequenceType ComponentTypeList ComponentType
+% SequenceValue SequenceOfValue
+ComponentValueList SequenceOfType
+SAndSOfValue ValueList SetType
+% SetValue SetOfValue
+SetOfType
+ChoiceType
+% AlternativeTypeList made common with ComponentTypeList
+ChoiceValue
+AnyValue
+AnyDefBy
+SelectionType
+TaggedType Tag ClassNumber Class
+% redundant TaggedValue
+% EmbeddedPDVType EmbeddedPDVValue ExternalType ExternalValue ObjectIdentifierType
+ObjectIdentifierValue ObjIdComponentList ObjIdComponent
+% NameForm NumberForm NameAndNumberForm
+CharacterStringType
+RestrictedCharacterStringValue CharacterStringList
+% CharSyms CharsDefn
+Quadruple
+% Group Plane Row Cell
+Tuple
+% TableColumn TableRow
+% UnrestrictedCharacterString
+CharacterStringValue
+% UnrestrictedCharacterStringValue
+ConstrainedType Constraint ConstraintSpec TypeWithConstraint
+ElementSetSpecs ElementSetSpec
+%GeneralConstraint
+UserDefinedConstraint UserDefinedConstraintParameter
+UserDefinedConstraintParameters
+ExceptionSpec
+ExceptionIdentification
+Unions
+UnionMark
+UElems
+Intersections
+IntersectionElements
+IntersectionMark
+IElems
+Elements
+Elems
+SubTypeElements
+Exclusions
+LowerEndpoint
+UpperEndpoint
+LowerEndValue
+UpperEndValue
+TypeConstraints NamedConstraint PresenceConstraint
+
+ParameterizedTypeAssignment
+ParameterList
+Parameters
+Parameter
+ParameterizedType
+
+% X.681
+ObjectClassAssignment ObjectClass ObjectClassDefn
+FieldSpecs FieldSpec OptionalitySpec WithSyntaxSpec
+TokenOrGroupSpecs TokenOrGroupSpec
+SyntaxList OptionalGroup RequiredToken Word
+TypeOptionalitySpec
+ValueOrObjectOptSpec
+VSetOrOSetOptSpec
+ValueOptionalitySpec
+ObjectOptionalitySpec
+ValueSetOptionalitySpec
+ObjectSetOptionalitySpec
+% X.681 chapter 15
+InformationFromObjects
+ValueFromObject
+%ValueSetFromObjects
+TypeFromObject
+%ObjectFromObject
+%ObjectSetFromObjects
+ReferencedObjects
+FieldName
+PrimitiveFieldName
+
+ObjectAssignment
+ObjectSetAssignment
+ObjectSet
+ObjectSetElements
+Object
+ObjectDefn
+DefaultSyntax
+DefinedSyntax
+FieldSettings
+FieldSetting
+DefinedSyntaxTokens
+DefinedSyntaxToken
+Setting
+DefinedObject
+ObjectFromObject
+ObjectSetFromObjects
+ParameterizedObject
+ExternalObjectReference
+DefinedObjectSet
+DefinedObjectClass
+ExternalObjectClassReference
+
+% X.682
+TableConstraint
+ComponentRelationConstraint
+ComponentIdList
+
+% X.683
+ActualParameter
+.
+
+%UsefulType.
+
+Terminals
+'ABSENT' 'ABSTRACT-SYNTAX' 'ALL' 'ANY'
+'APPLICATION' 'AUTOMATIC' 'BEGIN' 'BIT'
+'BOOLEAN' 'BY' 'CHARACTER' 'CHOICE' 'CLASS' 'COMPONENT'
+'COMPONENTS' 'CONSTRAINED' 'DEFAULT' 'DEFINED' 'DEFINITIONS'
+'EMBEDDED' 'END' 'ENUMERATED' 'EXCEPT' 'EXPLICIT'
+'EXPORTS' 'EXTENSIBILITY' 'EXTERNAL' 'FALSE' 'FROM' 'GeneralizedTime'
+'TYPE-IDENTIFIER'
+'IDENTIFIER' 'IMPLICIT' 'IMPLIED' 'IMPORTS'
+'INCLUDES' 'INSTANCE' 'INTEGER' 'INTERSECTION'
+'MAX' 'MIN' 'MINUS-INFINITY' 'NULL'
+'OBJECT' 'ObjectDescriptor' 'OCTET' 'OF' 'OPTIONAL' 'PDV' 'PLUS-INFINITY'
+'PRESENT' 'PRIVATE' 'REAL' 'SEQUENCE' 'SET' 'SIZE'
+'STRING' 'SYNTAX' 'TAGS' 'TRUE' 'UNION'
+'UNIQUE' 'UNIVERSAL' 'UTCTime' 'WITH'
+'{' '}' '(' ')' '.' '::=' ';' ',' '@' '*' '-' '[' ']'
+'!' '..' '...' '|' '<' ':' '^'
+number identifier typereference restrictedcharacterstringtype
+bstring hstring cstring typefieldreference valuefieldreference
+objectclassreference word.
+
+Rootsymbol ModuleDefinition.
+Endsymbol '$end'.
+
+Left 300 'EXCEPT'.
+Left 200 '^'.
+Left 200 'INTERSECTION'.
+Left 100 '|'.
+Left 100 'UNION'.
+
+
+ModuleDefinition -> ModuleIdentifier
+ 'DEFINITIONS'
+ TagDefault
+ ExtensionDefault
+ '::='
+ 'BEGIN'
+ ModuleBody
+ 'END' :
+ {'ModuleBody',Ex,Im,Types} = '$7',
+ {{typereference,Pos,Name},Defid} = '$1',
+ #module{
+ pos= Pos,
+ name= Name,
+ defid= Defid,
+ tagdefault='$3',
+ extensiondefault='$4',
+ exports=Ex,
+ imports=Im,
+ typeorval=Types}.
+% {module, '$1','$3','$6'}.
+% Results always in a record of type module defined in asn_records.hlr
+
+ModuleIdentifier -> typereference DefinitiveIdentifier :
+ put(asn1_module,'$1'#typereference.val),
+ {'$1','$2'}.
+
+DefinitiveIdentifier -> '{' DefinitiveObjIdComponentList '}' : '$2' .
+DefinitiveIdentifier -> '$empty': [].
+
+DefinitiveObjIdComponentList -> DefinitiveObjIdComponent : ['$1'].
+DefinitiveObjIdComponentList -> DefinitiveObjIdComponent DefinitiveObjIdComponentList : ['$1'|'$2'].
+
+DefinitiveObjIdComponent -> identifier : '$1' . %expanded->
+% DefinitiveObjIdComponent -> NameForm : '$1' .
+DefinitiveObjIdComponent -> number : '$1' . %expanded->
+% DefinitiveObjIdComponent -> DefinitiveNumberForm : 'fix' .
+DefinitiveObjIdComponent -> identifier '(' number ')' : {'$1','$3'} . %expanded->
+% DefinitiveObjIdComponent -> DefinitiveNameAndNumberForm : {'$1','$3'} .
+
+% DefinitiveNumberForm -> number : 'fix' .
+
+% DefinitiveNameAndNumberForm -> identifier '(' DefinitiveNumberForm ')' : 'fix' .
+
+TagDefault -> 'EXPLICIT' 'TAGS' : put(tagdefault,'EXPLICIT'),'EXPLICIT' .
+TagDefault -> 'IMPLICIT' 'TAGS' : put(tagdefault,'IMPLICIT'),'IMPLICIT' .
+TagDefault -> 'AUTOMATIC' 'TAGS' : put(tagdefault,'AUTOMATIC'),'AUTOMATIC' .
+TagDefault -> '$empty': put(tagdefault,'EXPLICIT'),'EXPLICIT'. % because this is the default
+
+ExtensionDefault -> 'EXTENSIBILITY' 'IMPLIED' : 'IMPLIED'.
+ExtensionDefault -> '$empty' : 'false'. % because this is the default
+
+ModuleBody -> Exports Imports AssignmentList : {'ModuleBody','$1','$2','$3'}.
+ModuleBody -> '$empty' : {'ModuleBody',nil,nil,[]}.
+
+Exports -> 'EXPORTS' SymbolList ';' : {exports,'$2'}.
+Exports -> 'EXPORTS' ';' : {exports,[]}.
+Exports -> '$empty' : {exports,all} .
+
+% inlined above SymbolsExported -> SymbolList : '$1'.
+% inlined above SymbolsExported -> '$empty' : [].
+
+Imports -> 'IMPORTS' SymbolsFromModuleList ';' : {imports,'$2'}.
+Imports -> 'IMPORTS' ';' : {imports,[]}.
+Imports -> '$empty' : {imports,[]} .
+
+% inlined above SymbolsImported -> SymbolsFromModuleList : '$1'.
+% inlined above SymbolsImported -> '$empty' : [].
+
+SymbolsFromModuleList -> SymbolsFromModule :['$1'].
+% SymbolsFromModuleList -> SymbolsFromModuleList SymbolsFromModule :$1.%changed
+SymbolsFromModuleList -> SymbolsFromModule SymbolsFromModuleList :['$1'|'$2'].
+
+% expanded SymbolsFromModule -> SymbolList 'FROM' GlobalModuleReference : #'SymbolsFromModule'{symbols = '$1',module='$3'}.
+SymbolsFromModule -> SymbolList 'FROM' typereference : #'SymbolsFromModule'{symbols = '$1',module='$3'}.
+SymbolsFromModule -> SymbolList 'FROM' typereference '{' ValueList '}': #'SymbolsFromModule'{symbols = '$1',module='$3'}.
+%SymbolsFromModule -> SymbolList 'FROM' typereference identifier: #'SymbolsFromModule'{symbols = '$1',module='$3'}.
+%SymbolsFromModule -> SymbolList 'FROM' typereference Externalvaluereference: #'SymbolsFromModule'{symbols = '$1',module='$3'}.
+%SymbolsFromModule -> SymbolList 'FROM' typereference DefinedValue: #'SymbolsFromModule'{symbols = '$1',module='$3'}.
+
+% inlined GlobalModuleReference -> typereference AssignedIdentifier : {'$1','$2'} .
+
+% inlined above AssignedIdentifier -> '{' ValueList '}' : '$2'.
+% replaced AssignedIdentifier -> '{' DefinedValue ObjIdComponentList '}' :{'$2','$3'}.
+% not necessary , replaced by SAndSOfValue AssignedIdentifier -> ObjectIdentifierValue :'$1'.
+% AssignedIdentifier -> DefinedValue : '$1'.
+% inlined AssignedIdentifier -> '$empty' : undefined.
+
+SymbolList -> Symbol : ['$1'].
+SymbolList -> Symbol ',' SymbolList :['$1'|'$3'].
+
+Symbol -> Reference :'$1'.
+% later Symbol -> ParameterizedReference :'$1'.
+
+Reference -> typereference :'$1'.
+Reference -> identifier:'$1'.
+Reference -> typereference '{' '}':'$1'.
+Reference -> Externaltypereference '{' '}':'$1'.
+
+% later Reference -> objectclassreference :'$1'.
+% later Reference -> objectreference :'$1'.
+% later Reference -> objectsetreference :'$1'.
+
+AssignmentList -> Assignment : ['$1'].
+% modified AssignmentList -> AssignmentList Assignment : '$1'.
+AssignmentList -> Assignment AssignmentList : ['$1'|'$2'].
+
+Assignment -> TypeAssignment : '$1'.
+Assignment -> ValueAssignment : '$1'.
+% later Assignment -> ValueSetTypeAssignment : '$1'.
+Assignment -> ObjectClassAssignment : '$1'.
+% later Assignment -> ObjectAssignment : '$1'.
+% combined with ValueAssignment Assignment -> ObjectAssignment : '$1'.
+Assignment -> ObjectSetAssignment : '$1'.
+Assignment -> ParameterizedTypeAssignment : '$1'.
+%Assignment -> ParameterizedValueAssignment : '$1'.
+%Assignment -> ParameterizedValueSetTypeAssignment : '$1'.
+%Assignment -> ParameterizedObjectClassAssignment : '$1'.
+
+ObjectClassAssignment -> typereference '::=' 'CLASS' '{' FieldSpecs '}' :
+%ObjectClassAssignment -> objectclassreference '::=' 'CLASS' '{' FieldSpecs '}' :
+ #typedef{pos=element(2,'$1'),name=element(3,'$1'),typespec={'CLASS','$5',[]}}.
+ObjectClassAssignment -> typereference '::=' 'CLASS' '{' FieldSpecs '}' WithSyntaxSpec :
+%ObjectClassAssignment -> objectclassreference '::=' 'CLASS' '{' FieldSpecs '}' WithSyntaxSpec :
+ #typedef{pos=element(2,'$1'),name=element(3,'$1'),typespec={'CLASS','$5','$7'}}.
+
+FieldSpecs -> FieldSpec : ['$1'].
+FieldSpecs -> FieldSpec ',' FieldSpecs : ['$1'|'$3'].
+
+FieldSpec -> typefieldreference TypeOptionalitySpec : {typefield,'$1','$2'}.
+
+FieldSpec -> valuefieldreference Type 'UNIQUE' ValueOrObjectOptSpec :
+ {fixedtypevaluefield,'$1','$2','UNIQUE','$4'}.
+FieldSpec -> valuefieldreference Type ValueOrObjectOptSpec :
+ {fixedtypevaluefield,'$1','$2',undefined,'$3'}.
+
+FieldSpec -> valuefieldreference typefieldreference ValueOrObjectOptSpec :
+ {variabletypevaluefield, '$1','$2','$3'}.
+
+FieldSpec -> typefieldreference typefieldreference VSetOrOSetOptSpec :
+ {variabletypevaluesetfield, '$1','$2','$3'}.
+
+FieldSpec -> typefieldreference Type VSetOrOSetOptSpec :
+ {fixedtypevaluesetfield, '$1','$2','$3'}.
+
+TypeOptionalitySpec -> 'DEFAULT' Type : {'DEFAULT','$2'}.
+TypeOptionalitySpec -> 'OPTIONAL' : 'OPTIONAL'.
+TypeOptionalitySpec -> '$empty' : 'MANDATORY'.
+
+ValueOrObjectOptSpec -> ValueOptionalitySpec : '$1'.
+ValueOrObjectOptSpec -> ObjectOptionalitySpec : '$1'.
+ValueOrObjectOptSpec -> 'OPTIONAL' : 'OPTIONAL'.
+ValueOrObjectOptSpec -> '$empty' : 'MANDATORY'.
+
+ValueOptionalitySpec -> 'DEFAULT' Value :
+ case '$2' of
+ {identifier,_,Id} -> {'DEFAULT',Id};
+ _ -> {'DEFAULT','$2'}
+ end.
+
+%ObjectOptionalitySpec -> 'DEFAULT' Object :{'DEFAULT','$1'}.
+ObjectOptionalitySpec -> 'DEFAULT' '{' FieldSetting ',' FieldSettings '}' :
+ {'DEFAULT',{object,['$2'|'$4']}}.
+ObjectOptionalitySpec -> 'DEFAULT' '{' FieldSetting '}' :
+ {'DEFAULT',{object, ['$2']}}.
+%ObjectOptionalitySpec -> 'DEFAULT' '{' DefinedSyntaxTokens '}' :
+% {'DEFAULT',{object, '$2'}}.
+ObjectOptionalitySpec -> 'DEFAULT' ObjectFromObject :
+ {'DEFAULT',{object, '$2'}}.
+
+
+VSetOrOSetOptSpec -> ValueSetOptionalitySpec : '$1'.
+%VSetOrOSetOptSpec -> ObjectSetOptionalitySpec : '$1'.
+VSetOrOSetOptSpec -> 'OPTIONAL' : 'OPTIONAL'.
+VSetOrOSetOptSpec -> '$empty' : 'MANDATORY'.
+
+ValueSetOptionalitySpec -> 'DEFAULT' ValueSet : {'DEFAULT','$1'}.
+
+%ObjectSetOptionalitySpec -> 'DEFAULT' ObjectSet : {'DEFAULT','$1'}.
+
+OptionalitySpec -> 'DEFAULT' Type : {'DEFAULT','$2'}.
+OptionalitySpec -> 'DEFAULT' ValueNotNull :
+ case '$2' of
+ {identifier,_,Id} -> {'DEFAULT',Id};
+ _ -> {'DEFAULT','$2'}
+ end.
+OptionalitySpec -> 'OPTIONAL' : 'OPTIONAL'.
+OptionalitySpec -> '$empty' : 'MANDATORY'.
+
+WithSyntaxSpec -> 'WITH' 'SYNTAX' SyntaxList : {'WITH SYNTAX','$3'}.
+
+SyntaxList -> '{' TokenOrGroupSpecs '}' : '$2'.
+SyntaxList -> '{' '}' : [].
+
+TokenOrGroupSpecs -> TokenOrGroupSpec : ['$1'].
+TokenOrGroupSpecs -> TokenOrGroupSpec TokenOrGroupSpecs : ['$1'|'$2'].
+
+TokenOrGroupSpec -> RequiredToken : '$1'.
+TokenOrGroupSpec -> OptionalGroup : '$1'.
+
+OptionalGroup -> '[' TokenOrGroupSpecs ']' : '$2'.
+
+RequiredToken -> typereference : '$1'.
+RequiredToken -> Word : '$1'.
+RequiredToken -> ',' : '$1'.
+RequiredToken -> PrimitiveFieldName : '$1'.
+
+Word -> 'BY' : 'BY'.
+
+ParameterizedTypeAssignment -> typereference ParameterList '::=' Type :
+ #ptypedef{pos=element(2,'$1'),name=element(3,'$1'),
+ args='$2', typespec='$4'}.
+
+ParameterList -> '{' Parameters '}':'$2'.
+
+Parameters -> Parameter: ['$1'].
+Parameters -> Parameter ',' Parameters: ['$1'|'$3'].
+
+Parameter -> typereference: '$1'.
+Parameter -> Value: '$1'.
+Parameter -> Type ':' typereference: {'$1','$3'}.
+Parameter -> Type ':' Value: {'$1','$3'}.
+Parameter -> '{' typereference '}': {objectset,'$2'}.
+
+
+% Externaltypereference -> modulereference '.' typereference : {'$1','$3'} .
+Externaltypereference -> typereference '.' typereference : #'Externaltypereference'{pos=element(2,'$1'),module=element(3,'$1'),type=element(3,'$3')}.
+
+% Externalvaluereference -> modulereference '.' valuereference : {'$1','$3'} .
+% inlined Externalvaluereference -> typereference '.' identifier : #'Externalvaluereference'{pos=element(2,'$1'),module=element(3,'$1'),value=element(3,'$3')}.
+
+
+DefinedType -> Externaltypereference : '$1' .
+DefinedType -> typereference :
+ #'Externaltypereference'{pos='$1'#typereference.pos,
+ module= get(asn1_module),
+ type= '$1'#typereference.val} .
+DefinedType -> typereference ParameterList : {pt,'$1','$2'}.
+DefinedType -> Externaltypereference ParameterList : {pt,'$1','$2'}.
+
+% ActualParameterList -> '{' ActualParameters '}' : '$1'.
+
+% ActualParameters -> ActualParameter : ['$1'].
+% ActualParameters -> ActualParameter ',' ActualParameters : ['$1'|'$3'].
+
+ActualParameter -> Type : '$1'.
+ActualParameter -> ValueNotNull : '$1'.
+ActualParameter -> ValueSet : '$1'.
+% later DefinedType -> ParameterizedType : '$1' .
+% later DefinedType -> ParameterizedValueSetType : '$1' .
+
+% inlined DefinedValue -> Externalvaluereference :'$1'.
+% inlined DefinedValue -> identifier :'$1'.
+% later DefinedValue -> ParameterizedValue :'$1'.
+
+% not referenced yet AbsoluteReference -> '@' GlobalModuleReference '.' ItemSpec :{'$2','$4'}.
+
+% not referenced yet ItemSpec -> typereference :'$1'.
+% not referenced yet ItemSpec -> ItemId '.' ComponentId : {'$1','$3'}.
+
+% not referenced yet ItemId -> ItemSpec : '$1'.
+
+% not referenced yet ComponentId -> identifier :'$1'.
+% not referenced yet ComponentId -> number :'$1'.
+% not referenced yet ComponentId -> '*' :'$1'.
+
+TypeAssignment -> typereference '::=' Type :
+ #typedef{pos=element(2,'$1'),name=element(3,'$1'),typespec='$3'}.
+
+ValueAssignment -> identifier Type '::=' Value :
+ #valuedef{pos=element(2,'$1'),name=element(3,'$1'),type='$2',value='$4'}.
+
+% later ValueSetTypeAssignment -> typereference Type '::=' ValueSet :{'ValueSetTypeAssignment','$1','$2','$4'}.
+
+
+ValueSet -> '{' ElementSetSpec '}' : {valueset,'$2'}.
+
+% record(type,{tag,def,constraint}).
+Type -> BuiltinType :#type{def='$1'}.
+Type -> 'NULL' :#type{def='NULL'}.
+Type -> TaggedType:'$1'.
+Type -> ReferencedType:#type{def='$1'}. % change notag later
+Type -> ConstrainedType:'$1'.
+
+%ANY is here for compatibility with the old ASN.1 standard from 1988
+BuiltinType -> 'ANY' AnyDefBy:
+ case '$2' of
+ [] -> 'ANY';
+ _ -> {'ANY DEFINED BY','$2'}
+ end.
+BuiltinType -> BitStringType :'$1'.
+BuiltinType -> 'BOOLEAN' :element(1,'$1').
+BuiltinType -> CharacterStringType :'$1'.
+BuiltinType -> ChoiceType :'$1'.
+BuiltinType -> 'EMBEDDED' 'PDV' :'EMBEDDED PDV'.
+BuiltinType -> EnumeratedType :'$1'.
+BuiltinType -> 'EXTERNAL' :element(1,'$1').
+% later BuiltinType -> InstanceOfType :'$1'.
+BuiltinType -> IntegerType :'$1'.
+% BuiltinType -> 'NULL' :element(1,'$1').
+% later BuiltinType -> ObjectClassFieldType :'$1'.
+BuiltinType -> 'OBJECT' 'IDENTIFIER' :'OBJECT IDENTIFIER'.
+BuiltinType -> 'OCTET' 'STRING' :'OCTET STRING'.
+BuiltinType -> 'REAL' :element(1,'$1').
+BuiltinType -> SequenceType :'$1'.
+BuiltinType -> SequenceOfType :'$1'.
+BuiltinType -> SetType :'$1'.
+BuiltinType -> SetOfType :'$1'.
+% The so called Useful types
+BuiltinType -> 'GeneralizedTime': 'GeneralizedTime'.
+BuiltinType -> 'UTCTime' :'UTCTime'.
+BuiltinType -> 'ObjectDescriptor' : 'ObjectDescriptor'.
+
+% moved BuiltinType -> TaggedType :'$1'.
+
+
+AnyDefBy -> 'DEFINED' 'BY' identifier: '$3'.
+AnyDefBy -> '$empty': [].
+
+NamedType -> identifier Type :
+%{_,Pos,Val} = '$1',
+%{'NamedType',Pos,{Val,'$2'}}.
+V1 = '$1',
+{'NamedType',V1#identifier.pos,{V1#identifier.val,'$2'}}.
+NamedType -> SelectionType :'$1'.
+
+ReferencedType -> DefinedType : '$1'.
+% redundant ReferencedType -> UsefulType : 'fix'.
+ReferencedType -> SelectionType : '$1'.
+ReferencedType -> TypeFromObject : '$1'.
+% later ReferencedType -> ValueSetFromObjects : 'fix'.
+
+% to much conflicts Value -> AnyValue :'$1'.
+Value -> ValueNotNull : '$1'.
+Value -> 'NULL' :element(1,'$1').
+
+ValueNotNull -> BuiltinValue :'$1'.
+% inlined Value -> DefinedValue :'$1'. % DefinedValue , identifier
+% inlined Externalvaluereference -> Externalvaluereference :'$1'.
+ValueNotNull -> typereference '.' identifier :
+ #'Externalvaluereference'{pos=element(2,'$1'),module=element(3,'$1'),
+ value=element(3,'$3')}.
+ValueNotNull -> identifier :'$1'.
+
+
+%tmp Value -> NamedNumber: '$1'. % not a value but part of ObjIdC
+% redundant BuiltinValue -> BitStringValue :'$1'.
+BuiltinValue -> BooleanValue :'$1'.
+BuiltinValue -> CharacterStringValue :'$1'.
+BuiltinValue -> ChoiceValue :'$1'.
+% BuiltinValue -> EmbeddedPDVValue :'$1'. ==SequenceValue
+% BuiltinValue -> EnumeratedValue :'$1'. identifier
+% BuiltinValue -> ExternalValue :'$1'. ==SequenceValue
+% later BuiltinValue -> InstanceOfValue :'$1'.
+BuiltinValue -> SignedNumber :'$1'.
+% BuiltinValue -> 'NULL' :'$1'.
+% later BuiltinValue -> ObjectClassFieldValue :'$1'.
+% replaced by SAndSOfValue BuiltinValue -> ObjectIdentifierValue :'$1'.
+BuiltinValue -> bstring :element(3,'$1').
+BuiltinValue -> hstring :element(3,'$1').
+% conflict BuiltinValue -> RealValue :'$1'.
+BuiltinValue -> SAndSOfValue :'$1'.
+% replaced BuiltinValue -> SequenceOfValue :'$1'.
+% replaced BuiltinValue -> SequenceValue :'$1'.
+% replaced BuiltinValue -> SetValue :'$1'.
+% replaced BuiltinValue -> SetOfValue :'$1'.
+% conflict redundant BuiltinValue -> TaggedValue :'$1'.
+
+% inlined ReferencedValue -> DefinedValue:'$1'.
+% ReferencedValue -> Externalvaluereference:'$1'.
+% ReferencedValue -> identifier :'$1'.
+% later ReferencedValue -> ValueFromObject:'$1'.
+
+% inlined BooleanType -> BOOLEAN :'BOOLEAN'.
+
+% to much conflicts AnyValue -> Type ':' Value : {'ANYVALUE',{'$1','$3'}}.
+
+BooleanValue -> TRUE :true.
+BooleanValue -> FALSE :false.
+
+IntegerType -> 'INTEGER' : 'INTEGER'.
+IntegerType -> 'INTEGER' '{' NamedNumberList '}' : {'INTEGER','$3'}.
+
+NamedNumberList -> NamedNumber :['$1'].
+% modified NamedNumberList -> NamedNumberList ',' NamedNumber :'fix'.
+NamedNumberList -> NamedNumber ',' NamedNumberList :['$1'|'$3'].
+
+NamedNumber -> identifier '(' SignedNumber ')' : {'NamedNumber',element(3,'$1'),'$3'}.
+NamedNumber -> identifier '(' typereference '.' identifier ')' : {'NamedNumber',element(3,'$1'),{'ExternalValue',element(3,'$3'),element(3,'$5')}}.
+NamedNumber -> identifier '(' identifier ')' : {'NamedNumber',element(3,'$1'),element(3,'$3')}.
+
+%NamedValue -> identifier Value :
+% {'NamedValue',element(2,'$1'),element(3,'$1'),'$2'}.
+
+
+SignedNumber -> number : element(3,'$1').
+SignedNumber -> '-' number : - element(3,'$1').
+
+% inlined IntegerValue -> SignedNumber :'$1'.
+% conflict moved to Value IntegerValue -> identifier:'$1'.
+
+EnumeratedType -> ENUMERATED '{' Enumeration '}' :{'ENUMERATED','$3'}.
+
+% inlined Enumerations -> Enumeration :{'$1','false',[]}.
+% inlined Enumerations -> Enumeration ',' '...' : {'$1','true',[]}.
+% inlined Enumerations -> Enumeration ',' '...' ',' Enumeration : {'$1','true','$5'}.
+
+Enumeration -> EnumerationItem :['$1'].
+% modified Enumeration -> EnumerationItem ',' Enumeration :'fix'.
+Enumeration -> EnumerationItem ',' Enumeration :['$1'|'$3'].
+
+EnumerationItem -> identifier:element(3,'$1').
+EnumerationItem -> NamedNumber :'$1'.
+EnumerationItem -> '...' :'EXTENSIONMARK'.
+
+% conflict moved to Value EnumeratedValue -> identifier:'$1'.
+
+% inlined RealType -> REAL:'REAL'.
+
+RealValue -> NumericRealValue :'$1'.
+RealValue -> SpecialRealValue:'$1'.
+
+% ?? NumericRealValue -> number:'$1'. % number MUST BE '0'
+NumericRealValue -> SAndSOfValue : '$1'. % Value of the associated sequence type
+
+SpecialRealValue -> 'PLUS-INFINITY' :'$1'.
+SpecialRealValue -> 'MINUS-INFINITY' :'$1'.
+
+BitStringType -> 'BIT' 'STRING' :{'BIT STRING',[]}.
+BitStringType -> 'BIT' 'STRING' '{' NamedNumberList '}' :{'BIT STRING','$4'}.
+% NamedBitList replaced by NamedNumberList to reduce the grammar
+% Must check later that all "numbers" are positive
+
+% inlined BitStringValue -> bstring:'$1'.
+% inlined BitStringValue -> hstring:'$1'.
+% redundant use SequenceValue BitStringValue -> '{' IdentifierList '}' :$2.
+% redundant use SequenceValue BitStringValue -> '{' '}' :'fix'.
+
+IdentifierList -> identifier :[element(3,'$1')].
+% modified IdentifierList -> IdentifierList ',' identifier :'$1'.
+IdentifierList -> identifier ',' IdentifierList :[element(3,'$1')|'$3'].
+
+% inlined OctetStringType -> 'OCTET' 'STRING' :'OCTET STRING'.
+
+% inlined OctetStringValue -> bstring:'$1'.
+% inlined OctetStringValue -> hstring:'$1'.
+
+% inlined NullType -> 'NULL':'NULL'.
+
+% inlined NullValue -> NULL:'NULL'.
+
+% result is {'SEQUENCE',Optionals,Extensionmark,Componenttypelist}.
+SequenceType -> SEQUENCE '{' ComponentTypeList '}' :{'SEQUENCE','$3'}.
+% SequenceType -> SEQUENCE '{' ComponentTypeLists '}' :{'SEQUENCE','$3'}.
+% SequenceType -> SEQUENCE '{' ExtensionAndException '}' :{'SEQUENCE','$3'}.
+SequenceType -> SEQUENCE '{' '}' :{'SEQUENCE',[]}.
+
+% result is {RootComponentList,ExtensionAndException,AdditionalComponentTypeList}.
+%ComponentTypeLists -> ComponentTypeList ',' ExtensionAndException :{'$1','$3',[]}.
+%ComponentTypeLists -> ComponentTypeList :{'$1','false',[]}.
+%ComponentTypeLists -> ComponentTypeList ',' ExtensionAndException
+% ',' ComponentTypeList :{'$1','$3', '$5'}.
+%ComponentTypeLists -> ExtensionAndException ',' ComponentTypeList :{[],'$1','$3'}.
+
+ComponentTypeList -> ComponentType :['$1'].
+% modified below ComponentTypeList -> ComponentTypeList ',' ComponentType :'$1'.
+ComponentTypeList -> ComponentType ',' ComponentTypeList :['$1'|'$3'].
+
+% -record('ComponentType',{pos,name,type,attrib}).
+ComponentType -> '...' ExceptionSpec :{'EXTENSIONMARK',element(2,'$1'),'$2'}.
+ComponentType -> NamedType :
+ {'NamedType',Pos,{Name,Type}} = '$1',
+ #'ComponentType'{pos=Pos,name=Name,typespec=Type,prop=mandatory}.
+ComponentType -> NamedType 'OPTIONAL' :
+ {'NamedType',Pos,{Name,Type}} = '$1',
+ #'ComponentType'{pos=Pos,name=Name,typespec=Type,prop='OPTIONAL'}.
+ComponentType -> NamedType 'DEFAULT' Value:
+ {'NamedType',Pos,{Name,Type}} = '$1',
+ #'ComponentType'{pos=Pos,name=Name,typespec=Type,prop={'DEFAULT','$3'}}.
+ComponentType -> 'COMPONENTS' 'OF' Type :{'COMPONENTS OF','$3'}.
+
+% redundant ExtensionAndException -> '...' : extensionmark.
+% ExtensionAndException -> '...' ExceptionSpec : {extensionmark,'$2'}.
+
+% replaced SequenceValue -> '{' ComponentValueList '}':'$2'.
+% replaced SequenceValue -> '{' '}':[].
+
+ValueList -> Value :['$1'].
+ValueList -> NamedNumber :['$1'].
+% modified ValueList -> ValueList ',' Value :'$1'.
+ValueList -> Value ',' ValueList :['$1'|'$3'].
+ValueList -> Value ',' '...' :['$1' |[]].
+ValueList -> Value ValueList : ['$1',space|'$2'].
+ValueList -> NamedNumber ValueList: ['$1',space|'$2'].
+
+%ComponentValueList -> identifier ObjIdComponent:[{'NamedValue','$1','$2'}].
+%ComponentValueList -> NamedValue :['$1'].
+%ComponentValueList -> NamedValue ',' ComponentValueList:['$1'|'$3'].
+%ComponentValueList -> identifier ObjIdComponent ',' ComponentValueList :[{'NamedValue', '$1','$2'}|'$4'].
+
+SequenceOfType -> SEQUENCE OF Type : {'SEQUENCE OF','$3'}.
+
+% replaced SequenceOfValue with SAndSOfValue
+
+SAndSOfValue -> '{' ValueList '}' :'$2'.
+%SAndSOfValue -> '{' ComponentValueList '}' :'$2'.
+SAndSOfValue -> '{' '}' :[].
+
+% save for later SetType ->
+% result is {'SET',Optionals,Extensionmark,Componenttypelist}.
+SetType -> SET '{' ComponentTypeList '}' :{'SET','$3'}.
+% SetType -> SET '{' ExtensionAndException '}' :{'SET','$3'}.
+SetType -> SET '{' '}' :{'SET',[]}.
+
+% replaced SetValue with SAndSOfValue
+
+SetOfType -> SET OF Type : {'SET OF','$3'}.
+
+% replaced SetOfValue with SAndSOfValue
+
+ChoiceType -> 'CHOICE' '{' ComponentTypeList '}' :{'CHOICE','$3'}.
+% AlternativeTypeList is replaced by ComponentTypeList
+ChoiceValue -> identifier ':' Value : {'ChoiceValue',element(3,'$1'),'$3'}.
+% save for later SelectionType ->
+
+TaggedType -> Tag Type : '$2'#type{tag=['$1'#tag{type={default,get(tagdefault)}}]}.
+TaggedType -> Tag IMPLICIT Type :'$3'#type{tag=['$1'#tag{type='IMPLICIT'}]}.
+TaggedType -> Tag EXPLICIT Type :'$3'#type{tag=['$1'#tag{type='EXPLICIT'}]}.
+
+Tag -> '[' Class ClassNumber ']': #tag{class='$2',number='$3'}.
+Tag -> '[' Class typereference '.' identifier ']':
+ #tag{class='$2',number=#'Externalvaluereference'{pos=element(2,'$3'),module=element(3,'$3'),
+ value=element(3,'$5')}}.
+Tag -> '[' Class number ']': #tag{class='$2',number=element(3,'$3')}.
+Tag -> '[' Class identifier ']': #tag{class='$2',number=element(3,'$3')}.
+
+ClassNumber -> number :element(3,'$1').
+% inlined above ClassNumber -> typereference '.' identifier :{'Externalvaluereference',element(3,'$1'),element(3,'$3')}.
+ClassNumber -> identifier :element(3,'$1').
+
+Class -> 'UNIVERSAL' :element(1,'$1').
+Class -> 'APPLICATION' :element(1,'$1').
+Class -> 'PRIVATE' :element(1,'$1').
+Class -> '$empty' :'CONTEXT'.
+
+% conflict redundant TaggedValue -> Value:'$1'.
+
+% inlined EmbeddedPDVType -> 'EMBEDDED' 'PDV' :'EMBEDDED PDV'.
+
+% inlined EmbeddedPDVValue -> SequenceValue:'$1'.
+
+% inlined ExternalType -> 'EXTERNAL' :'EXTERNAL'.
+
+% inlined ExternalValue -> SequenceValue :'$1'.
+
+% inlined ObjectIdentifierType -> 'OBJECT' 'IDENTIFIER' :'OBJECT IDENTIFIER'.
+
+ObjectIdentifierValue -> '{' ObjIdComponentList '}' :'$2'.
+% inlined ObjectIdentifierValue -> SequenceAndSequenceOfValue :'$1'.
+% ObjectIdentifierValue -> '{' identifier ObjIdComponentList '}' :{'ObjectIdentifierValue','$2','$3'}.
+% ObjectIdentifierValue -> '{' typereference '.' identifier ObjIdComponentList '}' :{'ObjectIdentifierValue',{'$2','$4'},'$5'}.
+
+ObjIdComponentList -> Value:'$1'.
+ObjIdComponentList -> Value ObjIdComponentList :['$1'|'$2'].
+%ObjIdComponentList -> DefinedValue:'$1'.
+%ObjIdComponentList -> number:'$1'.
+%ObjIdComponentList -> DefinedValue ObjIdComponentList :['$1'|'$2'].
+%ObjIdComponentList -> number ObjIdComponentList :['$1'|'$2'].
+%ObjIdComponentList -> ObjIdComponent ObjIdComponentList :['$1'|'$2'].
+%ObjIdComponentList -> ObjIdComponent ObjIdComponentList :['$1'|'$2'].
+
+% redundant ObjIdComponent -> NameForm :'$1'. % expanded
+% replaced by 2 ObjIdComponent -> NumberForm :'$1'.
+% ObjIdComponent -> number :'$1'.
+% ObjIdComponent -> DefinedValue :'$1'. % means DefinedValue
+% ObjIdComponent -> NameAndNumberForm :'$1'.
+% ObjIdComponent -> NamedNumber :'$1'.
+% NamedBit replaced by NamedNumber to reduce grammar
+% must check later that "number" is positive
+
+% NameForm -> identifier:'$1'.
+
+% inlined NumberForm -> number :'$1'.
+% inlined NumberForm -> DefinedValue :'$1'.
+
+% replaced by NamedBit NameAndNumberForm -> identifier '(' NumberForm ')'.
+% NameAndNumberForm -> NamedBit:'$1'.
+
+
+CharacterStringType -> restrictedcharacterstringtype :element(3,'$1').
+CharacterStringType -> 'CHARACTER' 'STRING' :'CHARACTER STRING'.
+
+RestrictedCharacterStringValue -> cstring :element(3, '$1').
+% modified below RestrictedCharacterStringValue -> CharacterStringList :'$1'.
+% conflict vs BuiltinValue RestrictedCharacterStringValue -> SequenceAndSequenceOfValue :'$1'.
+RestrictedCharacterStringValue -> Quadruple :'$1'.
+RestrictedCharacterStringValue -> Tuple :'$1'.
+
+% redundant CharacterStringList -> '{' ValueList '}' :'$2'. % modified
+
+% redundant CharSyms -> CharsDefn :'$1'.
+% redundant CharSyms -> CharSyms ',' CharsDefn :['$1'|'$3'].
+
+% redundant CharsDefn -> cstring :'$1'.
+% temporary replaced see below CharsDefn -> DefinedValue :'$1'.
+% redundant CharsDefn -> Value :'$1'.
+
+Quadruple -> '{' number ',' number ',' number ',' number '}' :{'Quadruple','$2','$4','$6','$8'}.
+% {Group,Plane,Row,Cell}
+
+Tuple -> '{' number ',' number '}' :{'Tuple', '$2','$4'}.
+% {TableColumn,TableRow}
+
+% inlined UnrestrictedCharacterString -> 'CHARACTER' 'STRING' :'CHARACTER STRING'.
+
+CharacterStringValue -> RestrictedCharacterStringValue :'$1'.
+% conflict vs BuiltinValue CharacterStringValue -> SequenceValue :'$1'. % UnrestrictedCharacterStringValue
+
+% inlined UsefulType -> typereference :'$1'.
+
+SelectionType -> identifier '<' Type : {'SelectionType',element(3,'$1'),'$3'}.
+
+ConstrainedType -> Type Constraint :
+ '$1'#type{constraint=merge_constraints(['$2'])}.
+ConstrainedType -> Type Constraint Constraint :
+ '$1'#type{constraint=merge_constraints(['$2','$3'])}.
+ConstrainedType -> Type Constraint Constraint Constraint:
+ '$1'#type{constraint=merge_constraints(['$2','$3','$4'])}.
+ConstrainedType -> Type Constraint Constraint Constraint Constraint:
+ '$1'#type{constraint=merge_constraints(['$2','$3','$4','$5'])}.
+%ConstrainedType -> Type Constraint :'$1'#type{constraint='$2'}.
+%ConstrainedType -> Type Constraint :'$1'#type{constraint='$2'}.
+ConstrainedType -> TypeWithConstraint :'$1'.
+
+TypeWithConstraint -> 'SET' Constraint 'OF' Type :
+ #type{def = {'SET OF','$4'},constraint=merge_constraints(['$2'])}.
+TypeWithConstraint -> 'SET' 'SIZE' Constraint 'OF' Type :
+ #type{def = {'SET OF','$5'},constraint = merge_constraints([#constraint{c={'SizeConstraint','$3'#constraint.c}}])}.
+TypeWithConstraint -> 'SEQUENCE' Constraint 'OF' Type :
+ #type{def = {'SEQUENCE OF','$4'},constraint =
+ merge_constraints(['$2'])}.
+TypeWithConstraint -> 'SEQUENCE' 'SIZE' Constraint 'OF' Type :
+ #type{def = {'SEQUENCE OF','$5'},constraint = merge_constraints([#constraint{c={'SizeConstraint','$3'#constraint.c}}])}.
+
+
+Constraint -> '(' ConstraintSpec ExceptionSpec ')' :
+ #constraint{c='$2',e='$3'}.
+
+% inlined Constraint -> SubTypeConstraint :'$1'.
+ConstraintSpec -> ElementSetSpecs :'$1'.
+ConstraintSpec -> UserDefinedConstraint :'$1'.
+ConstraintSpec -> TableConstraint :'$1'.
+
+TableConstraint -> ComponentRelationConstraint : '$1'.
+TableConstraint -> ObjectSet : '$1'.
+%TableConstraint -> '{' typereference '}' :tableconstraint.
+
+ComponentRelationConstraint -> '{' typereference '}' '{' '@' ComponentIdList '}' : componentrelation.
+ComponentRelationConstraint -> '{' typereference '}' '{' '@' '.' ComponentIdList '}' : componentrelation.
+
+ComponentIdList -> identifier: ['$1'].
+ComponentIdList -> identifier '.' ComponentIdList: ['$1'| '$3'].
+
+
+% later ConstraintSpec -> GeneralConstraint :'$1'.
+
+% from X.682
+UserDefinedConstraint -> 'CONSTRAINED' 'BY' '{' '}' : {constrained_by,[]}.
+UserDefinedConstraint -> 'CONSTRAINED' 'BY'
+ '{' UserDefinedConstraintParameters '}' : {constrained_by,'$4'}.
+
+UserDefinedConstraintParameters -> UserDefinedConstraintParameter : ['$1'].
+UserDefinedConstraintParameters ->
+ UserDefinedConstraintParameter ','
+ UserDefinedConstraintParameters: ['$1'|'$3'].
+
+UserDefinedConstraintParameter -> Type '.' ActualParameter : {'$1','$3'}.
+UserDefinedConstraintParameter -> ActualParameter : '$1'.
+
+
+
+ExceptionSpec -> '!' ExceptionIdentification : '$1'.
+ExceptionSpec -> '$empty' : undefined.
+
+ExceptionIdentification -> SignedNumber : '$1'.
+% inlined ExceptionIdentification -> DefinedValue : '$1'.
+ExceptionIdentification -> typereference '.' identifier :
+ #'Externalvaluereference'{pos=element(2,'$1'),module=element(3,'$1'),
+ value=element(3,'$1')}.
+ExceptionIdentification -> identifier :'$1'.
+ExceptionIdentification -> Type ':' Value : {'$1','$3'}.
+
+% inlined SubTypeConstraint -> ElementSetSpec
+
+ElementSetSpecs -> ElementSetSpec : '$1'.
+ElementSetSpecs -> ElementSetSpec ',' '...': {'$1',[]}.
+ElementSetSpecs -> '...' ',' ElementSetSpec : {[],'$3'}.
+ElementSetSpecs -> ElementSetSpec ',' '...' ',' ElementSetSpec : {'$1','$5'}.
+
+ElementSetSpec -> Unions : '$1'.
+ElementSetSpec -> 'ALL' Exclusions : {'ALL','$2'}.
+
+Unions -> Intersections : '$1'.
+Unions -> UElems UnionMark IntersectionElements :
+ case {'$1','$3'} of
+ {{'SingleValue',V1},{'SingleValue',V2}} ->
+ {'SingleValue',ordsets:union(to_set(V1),to_set(V2))}
+ end.
+
+UElems -> Unions :'$1'.
+
+Intersections -> IntersectionElements :'$1'.
+Intersections -> IElems IntersectionMark IntersectionElements :
+ case {'$1','$3'} of
+ {{'SingleValue',V1},{'SingleValue',V2}} ->
+ {'SingleValue',ordsets:intersection(to_set(V1),to_set(V2))};
+ {V1,V2} when list(V1) ->
+ V1 ++ [V2];
+ {V1,V2} ->
+ [V1,V2]
+ end.
+%Intersections -> IElems '^' IntersectionElements :{'INTERSECTION','$1','$3'}.
+%Intersections -> IElems 'INTERSECTION' IntersectionElements :{'INTERSECTION','$1','$3'}.
+
+IElems -> Intersections :'$1'.
+
+IntersectionElements -> Elements :'$1'.
+IntersectionElements -> Elems Exclusions :{'$1','$2'}.
+
+Elems -> Elements :'$1'.
+
+Exclusions -> 'EXCEPT' Elements :{'EXCEPT','$2'}.
+
+IntersectionMark -> 'INTERSECTION':'$1'.
+IntersectionMark -> '^':'$1'.
+UnionMark -> 'UNION':'$1'.
+UnionMark -> '|':'$1'.
+
+
+Elements -> SubTypeElements : '$1'.
+%Elements -> ObjectSetElements : '$1'.
+Elements -> '(' ElementSetSpec ')' : '$2'.
+Elements -> ReferencedType : '$1'.
+
+SubTypeElements -> ValueList : {'SingleValue','$1'}. % NOTE it must be a Value
+% The rule above modifyed only because of conflicts
+SubTypeElements -> 'INCLUDES' Type : {'ContainedSubType','$2'}.
+%not lalr1 if this is activated SubTypeElements -> Type : {'TypeConstraint','$1'}.
+SubTypeElements -> LowerEndpoint '..' UpperEndpoint : {'ValueRange',{'$1','$3'}}.
+SubTypeElements -> 'FROM' Constraint : {'PermittedAlphabet','$2'#constraint.c}.
+SubTypeElements -> 'SIZE' Constraint: {'SizeConstraint','$2'#constraint.c}.
+% later will introduce conflicts related to NULL SubTypeElements -> Type : {'TypeConstraint','$1'}.
+SubTypeElements -> 'WITH' 'COMPONENT' Constraint:{'WITH COMPONENT','$3'}.
+SubTypeElements -> 'WITH' 'COMPONENTS' '{' TypeConstraints '}':{'WITH COMPONENTS',{'FullSpecification','$4'}}.
+SubTypeElements -> 'WITH' 'COMPONENTS' '{' '...' ',' TypeConstraints '}' :{'WITH COMPONENTS',{'PartialSpecification','$3'}}.
+
+% inlined above InnerTypeConstraints ::=
+% inlined above SingleTypeConstraint::= Constraint
+% inlined above MultipleTypeConstraints ::= FullSpecification | PartialSpecification
+% inlined above FullSpecification ::= "{" TypeConstraints "}"
+% inlined above PartialSpecification ::= "{" "..." "," TypeConstraints "}"
+% TypeConstraints -> identifier : [{'NamedConstraint',element(3,'$1'),undefined,undefined}]. % is this really meaningful or allowed
+TypeConstraints -> NamedConstraint : ['$1'].
+TypeConstraints -> NamedConstraint ',' TypeConstraints : ['$1'|'$3'].
+TypeConstraints -> identifier : ['$1'].
+TypeConstraints -> identifier ',' TypeConstraints : ['$1'|'$3'].
+
+NamedConstraint -> identifier Constraint PresenceConstraint :{'NamedConstraint',element(3,'$1'),'$2','$3'}.
+NamedConstraint -> identifier Constraint :{'NamedConstraint',element(3,'$1'),'$2',undefined}.
+NamedConstraint -> identifier PresenceConstraint :{'NamedConstraint',element(3,'$1'),undefined,'$2'}.
+
+PresenceConstraint -> 'PRESENT' : 'PRESENT'.
+PresenceConstraint -> 'ABSENT' : 'ABSENT'.
+PresenceConstraint -> 'OPTIONAL' : 'OPTIONAL'.
+
+
+
+LowerEndpoint -> LowerEndValue :'$1'.
+%LowerEndpoint -> LowerEndValue '<':{gt,'$1'}.
+LowerEndpoint -> LowerEndValue '<':('$1'+1).
+
+UpperEndpoint -> UpperEndValue :'$1'.
+%UpperEndpoint -> '<' UpperEndValue :{lt,'$2'}.
+UpperEndpoint -> '<' UpperEndValue :('$2'-1).
+
+LowerEndValue -> Value :'$1'.
+LowerEndValue -> 'MIN' :'MIN'.
+
+UpperEndValue -> Value :'$1'.
+UpperEndValue -> 'MAX' :'MAX'.
+
+
+% X.681
+
+
+% X.681 chap 15
+
+%TypeFromObject -> ReferencedObjects '.' FieldName : {'$1','$3'}.
+TypeFromObject -> typereference '.' FieldName : {'$1','$3'}.
+
+ReferencedObjects -> typereference : '$1'.
+%ReferencedObjects -> ParameterizedObject
+%ReferencedObjects -> DefinedObjectSet
+%ReferencedObjects -> ParameterizedObjectSet
+
+FieldName -> typefieldreference : ['$1'].
+FieldName -> valuefieldreference : ['$1'].
+FieldName -> FieldName '.' FieldName : ['$1' | '$3'].
+
+PrimitiveFieldName -> typefieldreference : '$1'.
+PrimitiveFieldName -> valuefieldreference : '$1'.
+
+%ObjectSetAssignment -> typereference DefinedObjectClass '::=' ObjectSet: null.
+ObjectSetAssignment -> typereference typereference '::=' ObjectSet :
+ #typedef{pos=element(2,'$1'),name=element(3,'$1'),typespec={'ObjectSet',element(3,'$2'), '$4'}}.
+ObjectSetAssignment -> typereference typereference '.' typereference '::=' ObjectSet.
+
+ObjectSet -> '{' ElementSetSpecs '}' : '$2'.
+ObjectSet -> '{' '...' '}' : ['EXTENSIONMARK'].
+
+%ObjectSetElements -> Object.
+% ObjectSetElements -> identifier : '$1'.
+%ObjectSetElements -> DefinedObjectSet.
+%ObjectSetElements -> ObjectSetFromObjects.
+%ObjectSetElements -> ParameterizedObjectSet.
+
+%ObjectAssignment -> identifier DefinedObjectClass '::=' Object.
+ObjectAssignment -> ValueAssignment.
+%ObjectAssignment -> identifier typereference '::=' Object.
+%ObjectAssignment -> identifier typereference '.' typereference '::=' Object.
+
+%Object -> DefinedObject: '$1'.
+%Object -> ExternalObjectReference: '$1'.%Object -> DefinedObject: '$1'.
+Object -> typereference '.' identifier: '$1'.%Object -> DefinedObject: '$1'.
+Object -> identifier: '$1'.%Object -> DefinedObject: '$1'.
+
+%Object -> ObjectDefn -> DefaultSyntax: '$1'.
+Object -> '{' FieldSetting ',' FieldSettings '}' : ['$2'|'$4'].
+Object -> '{' FieldSetting '}' :['$2'].
+
+%% For User-friendly notation
+%% Object -> ObjectDefn -> DefinedSyntax
+Object -> '{' '}'.
+Object -> '{' DefinedSyntaxTokens '}'.
+
+% later Object -> ParameterizedObject: '$1'. look in x.683
+
+%DefinedObject -> ExternalObjectReference: '$1'.
+%DefinedObject -> identifier: '$1'.
+
+DefinedObjectClass -> typereference.
+%DefinedObjectClass -> objectclassreference.
+DefinedObjectClass -> ExternalObjectClassReference.
+%DefinedObjectClass -> typereference '.' objectclassreference.
+%%DefinedObjectClass -> UsefulObjectClassReference.
+
+ExternalObjectReference -> typereference '.' identifier.
+ExternalObjectClassReference -> typereference '.' typereference.
+%%ExternalObjectClassReference -> typereference '.' objectclassreference.
+
+ObjectDefn -> DefaultSyntax: '$1'.
+%ObjectDefn -> DefinedSyntax: '$1'.
+
+ObjectFromObject -> ReferencedObjects '.' FieldName : {'ObjectFromObject','$1','$3'}.
+
+% later look in x.683 ParameterizedObject ->
+
+%DefaultSyntax -> '{' '}'.
+%DefaultSyntax -> '{' FieldSettings '}': '$2'.
+DefaultSyntax -> '{' FieldSetting ',' FieldSettings '}': '$2'.
+DefaultSyntax -> '{' FieldSetting '}': '$2'.
+
+FieldSetting -> PrimitiveFieldName Setting: {'$1','$2'}.
+
+FieldSettings -> FieldSetting ',' FieldSettings: ['$1'|'$3'].
+FieldSettings -> FieldSetting ',' FieldSettings: ['$1'|'$3'].
+FieldSettings -> FieldSetting: '$1'.
+
+%DefinedSyntax -> '{' '}'.
+DefinedSyntax -> '{' DefinedSyntaxTokens '}': '$2'.
+
+DefinedSyntaxTokens -> DefinedSyntaxToken: '$1'.
+DefinedSyntaxTokens -> DefinedSyntaxToken DefinedSyntaxTokens: ['$1'|'$2'].
+
+% expanded DefinedSyntaxToken -> Literal: '$1'.
+%DefinedSyntaxToken -> typereference: '$1'.
+DefinedSyntaxToken -> word: '$1'.
+DefinedSyntaxToken -> ',': '$1'.
+DefinedSyntaxToken -> Setting: '$1'.
+%DefinedSyntaxToken -> '$empty': nil .
+
+% Setting ::= Type|Value|ValueSet|Object|ObjectSet
+Setting -> Type: '$1'.
+%Setting -> Value: '$1'.
+%Setting -> ValueNotNull: '$1'.
+Setting -> BuiltinValue: '$1'.
+Setting -> ValueSet: '$1'.
+%Setting -> Object: '$1'.
+%Setting -> ExternalObjectReference.
+Setting -> typereference '.' identifier.
+Setting -> identifier.
+Setting -> ObjectDefn.
+
+Setting -> ObjectSet: '$1'.
+
+
+Erlang code.
+%%-author('kenneth@erix.ericsson.se').
+-copyright('Copyright (c) 1991-99 Ericsson Telecom AB').
+-vsn('$Revision: 1.1 $').
+-include("asn1_records.hrl").
+
+to_set(V) when list(V) ->
+ ordsets:list_to_set(V);
+to_set(V) ->
+ ordsets:list_to_set([V]).
+
+merge_constraints({Rlist,ExtList}) -> % extensionmarker in constraint
+ {merge_constraints(Rlist,[],[]),
+ merge_constraints(ExtList,[],[])};
+
+merge_constraints(Clist) ->
+ merge_constraints(Clist, [], []).
+
+merge_constraints([Ch|Ct],Cacc, Eacc) ->
+ NewEacc = case Ch#constraint.e of
+ undefined -> Eacc;
+ E -> [E|Eacc]
+ end,
+ merge_constraints(Ct,[fixup_constraint(Ch#constraint.c)|Cacc],NewEacc);
+
+merge_constraints([],Cacc,[]) ->
+ lists:flatten(Cacc);
+merge_constraints([],Cacc,Eacc) ->
+ lists:flatten(Cacc) ++ [{'Errors',Eacc}].
+
+fixup_constraint(C) ->
+ case C of
+ {'SingleValue',V} when list(V) ->
+ [C,
+ {'ValueRange',{lists:min(V),lists:max(V)}}];
+ {'PermittedAlphabet',{'SingleValue',V}} when list(V) ->
+ V2 = {'SingleValue',
+ ordsets:list_to_set(lists:flatten(V))},
+ {'PermittedAlphabet',V2};
+ {'PermittedAlphabet',{'SingleValue',V}} ->
+ V2 = {'SingleValue',[V]},
+ {'PermittedAlphabet',V2};
+ {'SizeConstraint',Sc} ->
+ {'SizeConstraint',fixup_size_constraint(Sc)};
+
+ List when list(List) ->
+ [fixup_constraint(Xc)||Xc <- List];
+ Other ->
+ Other
+ end.
+
+fixup_size_constraint({'ValueRange',{Lb,Ub}}) ->
+ {Lb,Ub};
+fixup_size_constraint({{'ValueRange',R},[]}) ->
+ {R,[]};
+fixup_size_constraint({[],{'ValueRange',R}}) ->
+ {[],R};
+fixup_size_constraint({{'ValueRange',R1},{'ValueRange',R2}}) ->
+ {R1,R2};
+fixup_size_constraint({'SingleValue',[Sv]}) ->
+ fixup_size_constraint({'SingleValue',Sv});
+fixup_size_constraint({'SingleValue',L}) when list(L) ->
+ ordsets:list_to_set(L);
+fixup_size_constraint({'SingleValue',L}) ->
+ {L,L};
+fixup_size_constraint({C1,C2}) ->
+ {fixup_size_constraint(C1), fixup_size_constraint(C2)}.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_parser2.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_parser2.erl
new file mode 100644
index 0000000000..07dacb73c8
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_parser2.erl
@@ -0,0 +1,2763 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 2000, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_parser2.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1ct_parser2).
+
+-export([parse/1]).
+-include("asn1_records.hrl").
+
+%% parse all types in module
+parse(Tokens) ->
+ case catch parse_ModuleDefinition(Tokens) of
+ {'EXIT',Reason} ->
+ {error,{{undefined,get(asn1_module),
+ [internal,error,'when',parsing,module,definition,Reason]},
+ hd(Tokens)}};
+ {asn1_error,Reason} ->
+ {error,{Reason,hd(Tokens)}};
+ {ModuleDefinition,Rest1} ->
+ {Types,Rest2} = parse_AssignmentList(Rest1),
+ case Rest2 of
+ [{'END',_}|_Rest3] ->
+ {ok,ModuleDefinition#module{typeorval = Types}};
+ _ ->
+ {error,{{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'END']},
+ hd(Rest2)}}
+ end
+ end.
+
+parse_ModuleDefinition([{typereference,L1,ModuleIdentifier}|Rest0]) ->
+ put(asn1_module,ModuleIdentifier),
+ {_DefinitiveIdentifier,Rest02} =
+ case Rest0 of
+ [{'{',_}|_Rest01] ->
+ parse_ObjectIdentifierValue(Rest0);
+ _ ->
+ {[],Rest0}
+ end,
+ Rest = case Rest02 of
+ [{'DEFINITIONS',_}|Rest03] ->
+ Rest03;
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest02)),get(asn1_module),
+ [got,get_token(hd(Rest02)),
+ expected,'DEFINITIONS']}})
+ end,
+ {TagDefault,Rest2} =
+ case Rest of
+ [{'EXPLICIT',_L3},{'TAGS',_L4}|Rest1] ->
+ put(tagdefault,'EXPLICIT'), {'EXPLICIT',Rest1};
+ [{'IMPLICIT',_L3},{'TAGS',_L4}|Rest1] ->
+ put(tagdefault,'IMPLICIT'), {'IMPLICIT',Rest1};
+ [{'AUTOMATIC',_L3},{'TAGS',_L4}|Rest1] ->
+ put(tagdefault,'AUTOMATIC'), {'AUTOMATIC',Rest1};
+ Rest1 ->
+ put(tagdefault,'EXPLICIT'), {'EXPLICIT',Rest1} % The default
+ end,
+ {ExtensionDefault,Rest3} =
+ case Rest2 of
+ [{'EXTENSIBILITY',_L5}, {'IMPLIED',_L6}|Rest21] ->
+ {'IMPLIED',Rest21};
+ _ -> {false,Rest2}
+ end,
+ case Rest3 of
+ [{'::=',_L7}, {'BEGIN',_L8}|Rest4] ->
+ {Exports, Rest5} = parse_Exports(Rest4),
+ {Imports, Rest6} = parse_Imports(Rest5),
+ {#module{ pos = L1,
+ name = ModuleIdentifier,
+ defid = [], % fix this
+ tagdefault = TagDefault,
+ extensiondefault = ExtensionDefault,
+ exports = Exports,
+ imports = Imports},Rest6};
+ _ -> throw({asn1_error,{get_line(hd(Rest3)),get(asn1_module),
+ [got,get_token(hd(Rest3)),expected,"::= BEGIN"]}})
+ end;
+parse_ModuleDefinition(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,typereference]}}).
+
+parse_Exports([{'EXPORTS',_L1},{';',_L2}|Rest]) ->
+ {{exports,[]},Rest};
+parse_Exports([{'EXPORTS',_L1}|Rest]) ->
+ {SymbolList,Rest2} = parse_SymbolList(Rest),
+ case Rest2 of
+ [{';',_}|Rest3] ->
+ {{exports,SymbolList},Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,';']}})
+ end;
+parse_Exports(Rest) ->
+ {{exports,all},Rest}.
+
+parse_SymbolList(Tokens) ->
+ parse_SymbolList(Tokens,[]).
+
+parse_SymbolList(Tokens,Acc) ->
+ {Symbol,Rest} = parse_Symbol(Tokens),
+ case Rest of
+ [{',',_L1}|Rest2] ->
+ parse_SymbolList(Rest2,[Symbol|Acc]);
+ Rest2 ->
+ {lists:reverse([Symbol|Acc]),Rest2}
+ end.
+
+parse_Symbol(Tokens) ->
+ parse_Reference(Tokens).
+
+parse_Reference([{typereference,L1,TrefName},{'{',_L2},{'}',_L3}|Rest]) ->
+% {Tref,Rest};
+ {tref2Exttref(L1,TrefName),Rest};
+parse_Reference([Tref1 = {typereference,_,_},{'.',_},Tref2 = {typereference,_,_},
+ {'{',_L2},{'}',_L3}|Rest]) ->
+% {{Tref1,Tref2},Rest};
+ {{tref2Exttref(Tref1),tref2Exttref(Tref2)},Rest};
+parse_Reference([Tref = {typereference,_L1,_TrefName}|Rest]) ->
+ {tref2Exttref(Tref),Rest};
+parse_Reference([Vref = {identifier,_L1,_VName}|Rest]) ->
+ {identifier2Extvalueref(Vref),Rest};
+parse_Reference(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ [typereference,identifier]]}}).
+
+parse_Imports([{'IMPORTS',_L1},{';',_L2}|Rest]) ->
+ {{imports,[]},Rest};
+parse_Imports([{'IMPORTS',_L1}|Rest]) ->
+ {SymbolsFromModuleList,Rest2} = parse_SymbolsFromModuleList(Rest),
+ case Rest2 of
+ [{';',_L2}|Rest3] ->
+ {{imports,SymbolsFromModuleList},Rest3};
+ Rest3 ->
+ throw({asn1_error,{get_line(hd(Rest3)),get(asn1_module),
+ [got,get_token(hd(Rest3)),expected,';']}})
+ end;
+parse_Imports(Tokens) ->
+ {{imports,[]},Tokens}.
+
+parse_SymbolsFromModuleList(Tokens) ->
+ parse_SymbolsFromModuleList(Tokens,[]).
+
+parse_SymbolsFromModuleList(Tokens,Acc) ->
+ {SymbolsFromModule,Rest} = parse_SymbolsFromModule(Tokens),
+ case (catch parse_SymbolsFromModule(Rest)) of
+ {Sl,_Rest2} when record(Sl,'SymbolsFromModule') ->
+ parse_SymbolsFromModuleList(Rest,[SymbolsFromModule|Acc]);
+ _ ->
+ {lists:reverse([SymbolsFromModule|Acc]),Rest}
+ end.
+
+parse_SymbolsFromModule(Tokens) ->
+ SetRefModuleName =
+ fun(N) ->
+ fun(X) when record(X,'Externaltypereference')->
+ X#'Externaltypereference'{module=N};
+ (X) when record(X,'Externalvaluereference')->
+ X#'Externalvaluereference'{module=N}
+ end
+ end,
+ {SymbolList,Rest} = parse_SymbolList(Tokens),
+ case Rest of
+ %%How does this case correspond to x.680 ?
+ [{'FROM',_L1},Tref = {typereference,_,_},Ref={identifier,_L2,_Id},C={',',_}|Rest2] ->
+ {#'SymbolsFromModule'{symbols=SymbolList,
+ module=tref2Exttref(Tref)},[Ref,C|Rest2]};
+ %%How does this case correspond to x.680 ?
+ [{'FROM',_L1},Tref = {typereference,_,_},{identifier,_L2,_Id}|Rest2] ->
+ {#'SymbolsFromModule'{symbols=SymbolList,
+ module=tref2Exttref(Tref)},Rest2};
+ [{'FROM',_L1},Tref = {typereference,_,Name},Brace = {'{',_}|Rest2] ->
+ {_ObjIdVal,Rest3} = parse_ObjectIdentifierValue([Brace|Rest2]), % value not used yet, fix me
+ NewSymbolList = lists:map(SetRefModuleName(Name),SymbolList),
+ {#'SymbolsFromModule'{symbols=NewSymbolList,
+ module=tref2Exttref(Tref)},Rest3};
+ [{'FROM',_L1},Tref = {typereference,_,Name}|Rest2] ->
+ NewSymbolList = lists:map(SetRefModuleName(Name),SymbolList),
+ {#'SymbolsFromModule'{symbols=NewSymbolList,
+ module=tref2Exttref(Tref)},Rest2};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest)),get(asn1_module),
+ [got,get_token(hd(Rest)),expected,
+ ['FROM typerefernece identifier ,',
+ 'FROM typereference identifier',
+ 'FROM typereference {',
+ 'FROM typereference']]}})
+ end.
+
+parse_ObjectIdentifierValue([{'{',_}|Rest]) ->
+ parse_ObjectIdentifierValue(Rest,[]).
+
+parse_ObjectIdentifierValue([{number,_,Num}|Rest],Acc) ->
+ parse_ObjectIdentifierValue(Rest,[Num|Acc]);
+parse_ObjectIdentifierValue([{identifier,_,Id},{'(',_}, {number,_,Num}, {')',_}|Rest],Acc) ->
+ parse_ObjectIdentifierValue(Rest,[{'NamedNumber',Id,Num}|Acc]);
+parse_ObjectIdentifierValue([{identifier,_,Id},{'(',_}, {identifier,_,Id2}, {')',_}|Rest],Acc) ->
+ parse_ObjectIdentifierValue(Rest,[{'NamedNumber',Id,Id2}|Acc]);
+parse_ObjectIdentifierValue([{identifier,_,Id},{'(',_}, {typereference,_,Tref},{'.',_},{identifier,_,Id2}, {')',_}|Rest],Acc) ->
+ parse_ObjectIdentifierValue(Rest,[{'NamedNumber',Id,{'ExternalValue',Tref,Id2}}|Acc]);
+parse_ObjectIdentifierValue([Id = {identifier,_,_}|Rest],Acc) ->
+ parse_ObjectIdentifierValue(Rest,[identifier2Extvalueref(Id)|Acc]);
+parse_ObjectIdentifierValue([{'}',_}|Rest],Acc) ->
+ {lists:reverse(Acc),Rest};
+parse_ObjectIdentifierValue([H|_T],_Acc) ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,
+ ['{ some of the following }',number,'identifier ( number )',
+ 'identifier ( identifier )',
+ 'identifier ( typereference.identifier)',identifier]]}}).
+
+parse_AssignmentList(Tokens = [{'END',_}|_Rest]) ->
+ {[],Tokens};
+parse_AssignmentList(Tokens = [{'$end',_}|_Rest]) ->
+ {[],Tokens};
+parse_AssignmentList(Tokens) ->
+ parse_AssignmentList(Tokens,[]).
+
+parse_AssignmentList(Tokens= [{'END',_}|_Rest],Acc) ->
+ {lists:reverse(Acc),Tokens};
+parse_AssignmentList(Tokens= [{'$end',_}|_Rest],Acc) ->
+ {lists:reverse(Acc),Tokens};
+parse_AssignmentList(Tokens,Acc) ->
+ case (catch parse_Assignment(Tokens)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ {asn1_error,R} ->
+% [H|T] = Tokens,
+ throw({error,{R,hd(Tokens)}});
+ {Assignment,Rest} ->
+ parse_AssignmentList(Rest,[Assignment|Acc])
+ end.
+
+parse_Assignment(Tokens) ->
+ Flist = [fun parse_TypeAssignment/1,
+ fun parse_ValueAssignment/1,
+ fun parse_ObjectClassAssignment/1,
+ fun parse_ObjectAssignment/1,
+ fun parse_ObjectSetAssignment/1,
+ fun parse_ParameterizedAssignment/1,
+ fun parse_ValueSetTypeAssignment/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ {asn1_assignment_error,Reason} ->
+ throw({asn1_error,Reason});
+ Result ->
+ Result
+ end.
+
+
+parse_or(Tokens,Flist) ->
+ parse_or(Tokens,Flist,[]).
+
+parse_or(_Tokens,[],ErrList) ->
+ case ErrList of
+ [] ->
+ throw({asn1_error,{parse_or,ErrList}});
+ L when list(L) ->
+%%% throw({asn1_error,{parse_or,hd(lists:reverse(ErrList))}});
+ %% chose to throw 1) the error with the highest line no,
+ %% 2) the last error which is not a asn1_assignment_error or
+ %% 3) the last error.
+ throw(prioritize_error(ErrList));
+ Other ->
+ throw({asn1_error,{parse_or,Other}})
+ end;
+parse_or(Tokens,[Fun|Frest],ErrList) ->
+ case (catch Fun(Tokens)) of
+ Exit = {'EXIT',_Reason} ->
+ parse_or(Tokens,Frest,[Exit|ErrList]);
+ AsnErr = {asn1_error,_} ->
+ parse_or(Tokens,Frest,[AsnErr|ErrList]);
+ AsnAssErr = {asn1_assignment_error,_} ->
+ parse_or(Tokens,Frest,[AsnAssErr|ErrList]);
+ Result = {_,L} when list(L) ->
+ Result;
+% Result ->
+% Result
+ Error ->
+ parse_or(Tokens,Frest,[Error|ErrList])
+ end.
+
+parse_TypeAssignment([{typereference,L1,Tref},{'::=',_}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ {#typedef{pos=L1,name=Tref,typespec=Type},Rest2};
+parse_TypeAssignment([H1,H2|_Rest]) ->
+ throw({asn1_assignment_error,{get_line(H1),get(asn1_module),
+ [got,[get_token(H1),get_token(H2)], expected,
+ typereference,'::=']}});
+parse_TypeAssignment([H|_T]) ->
+ throw({asn1_assignment_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,
+ typereference]}}).
+
+parse_Type(Tokens) ->
+ {Tag,Rest3} = case Tokens of
+ [Lbr= {'[',_}|Rest] ->
+ parse_Tag([Lbr|Rest]);
+ Rest-> {[],Rest}
+ end,
+ {Tag2,Rest4} = case Rest3 of
+ [{'IMPLICIT',_}|Rest31] when record(Tag,tag)->
+ {[Tag#tag{type='IMPLICIT'}],Rest31};
+ [{'EXPLICIT',_}|Rest31] when record(Tag,tag)->
+ {[Tag#tag{type='EXPLICIT'}],Rest31};
+ Rest31 when record(Tag,tag) ->
+ {[Tag#tag{type={default,get(tagdefault)}}],Rest31};
+ Rest31 ->
+ {Tag,Rest31}
+ end,
+ Flist = [fun parse_BuiltinType/1,fun parse_ReferencedType/1,fun parse_TypeWithConstraint/1],
+ {Type,Rest5} = case (catch parse_or(Rest4,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_Reason} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end,
+ case hd(Rest5) of
+ {'(',_} ->
+ {Constraints,Rest6} = parse_Constraints(Rest5),
+ if record(Type,type) ->
+ {Type#type{constraint=merge_constraints(Constraints),
+ tag=Tag2},Rest6};
+ true ->
+ {#type{def=Type,constraint=merge_constraints(Constraints),
+ tag=Tag2},Rest6}
+ end;
+ _ ->
+ if record(Type,type) ->
+ {Type#type{tag=Tag2},Rest5};
+ true ->
+ {#type{def=Type,tag=Tag2},Rest5}
+ end
+ end.
+
+parse_BuiltinType([{'BIT',_},{'STRING',_}|Rest]) ->
+ case Rest of
+ [{'{',_}|Rest2] ->
+ {NamedNumberList,Rest3} = parse_NamedNumberList(Rest2),
+ case Rest3 of
+ [{'}',_}|Rest4] ->
+ {#type{def={'BIT STRING',NamedNumberList}},Rest4};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest3)),get(asn1_module),
+ [got,get_token(hd(Rest3)),expected,'}']}})
+ end;
+ _ ->
+ {{'BIT STRING',[]},Rest}
+ end;
+parse_BuiltinType([{'BOOLEAN',_}|Rest]) ->
+ {#type{def='BOOLEAN'},Rest};
+%% CharacterStringType ::= RestrictedCharacterStringType |
+%% UnrestrictedCharacterStringType
+parse_BuiltinType([{restrictedcharacterstringtype,_,StringName}|Rest]) ->
+ {#type{def=StringName},Rest};
+parse_BuiltinType([{'CHARACTER',_},{'STRING',_}|Rest]) ->
+ {#type{def='CHARACTER STRING'},Rest};
+
+parse_BuiltinType([{'CHOICE',_},{'{',_}|Rest]) ->
+ {AlternativeTypeLists,Rest2} = parse_AlternativeTypeLists(Rest),
+ case Rest2 of
+ [{'}',_}|Rest3] ->
+ {#type{def={'CHOICE',AlternativeTypeLists}},Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'}']}})
+ end;
+parse_BuiltinType([{'EMBEDDED',_},{'PDV',_}|Rest]) ->
+ {#type{def='EMBEDDED PDV'},Rest};
+parse_BuiltinType([{'ENUMERATED',_},{'{',_}|Rest]) ->
+ {Enumerations,Rest2} = parse_Enumerations(Rest),
+ case Rest2 of
+ [{'}',_}|Rest3] ->
+ {#type{def={'ENUMERATED',Enumerations}},Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'}']}})
+ end;
+parse_BuiltinType([{'EXTERNAL',_}|Rest]) ->
+ {#type{def='EXTERNAL'},Rest};
+
+% InstanceOfType
+parse_BuiltinType([{'INSTANCE',_},{'OF',_}|Rest]) ->
+ {DefinedObjectClass,Rest2} = parse_DefinedObjectClass(Rest),
+ case Rest2 of
+ [{'(',_}|_] ->
+ {Constraint,Rest3} = parse_Constraint(Rest2),
+ {#type{def={'INSTANCE OF',DefinedObjectClass,Constraint}},Rest3};
+ _ ->
+ {#type{def={'INSTANCE OF',DefinedObjectClass,[]}},Rest2}
+ end;
+
+% parse_BuiltinType(Tokens) ->
+
+parse_BuiltinType([{'INTEGER',_}|Rest]) ->
+ case Rest of
+ [{'{',_}|Rest2] ->
+ {NamedNumberList,Rest3} = parse_NamedNumberList(Rest2),
+ case Rest3 of
+ [{'}',_}|Rest4] ->
+ {#type{def={'INTEGER',NamedNumberList}},Rest4};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest3)),get(asn1_module),
+ [got,get_token(hd(Rest3)),expected,'}']}})
+ end;
+ _ ->
+ {#type{def='INTEGER'},Rest}
+ end;
+parse_BuiltinType([{'NULL',_}|Rest]) ->
+ {#type{def='NULL'},Rest};
+
+% ObjectClassFieldType fix me later
+
+parse_BuiltinType([{'OBJECT',_},{'IDENTIFIER',_}|Rest]) ->
+ {#type{def='OBJECT IDENTIFIER'},Rest};
+parse_BuiltinType([{'OCTET',_},{'STRING',_}|Rest]) ->
+ {#type{def='OCTET STRING'},Rest};
+parse_BuiltinType([{'REAL',_}|Rest]) ->
+ {#type{def='REAL'},Rest};
+parse_BuiltinType([{'SEQUENCE',_},{'{',_},{'...',Line},{'}',_}|Rest]) ->
+ {#type{def=#'SEQUENCE'{components=[{'EXTENSIONMARK',Line,undefined}]}},
+ Rest};
+parse_BuiltinType([{'SEQUENCE',_},{'{',_},{'...',Line},{'!',_}|Rest]) ->
+ {ExceptionIdentification,Rest2} = parse_ExceptionIdentification(Rest),
+ case Rest2 of
+ [{'}',_}|Rest3] ->
+ {#type{def=#'SEQUENCE'{components=[{'EXTENSIONMARK',
+ Line,
+ ExceptionIdentification}]}},
+ Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'}']}})
+ end;
+parse_BuiltinType([{'SEQUENCE',_},{'{',_}|Rest]) ->
+ {ComponentTypeLists,Rest2} = parse_ComponentTypeLists(Rest),
+ case Rest2 of
+ [{'}',_}|Rest3] ->
+ {#type{def=#'SEQUENCE'{components=ComponentTypeLists}},Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'}']}})
+ end;
+parse_BuiltinType([{'SEQUENCE',_},{'OF',_}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ {#type{def={'SEQUENCE OF',Type}},Rest2};
+
+
+parse_BuiltinType([{'SET',_},{'{',_},{'...',Line},{'}',_}|Rest]) ->
+ {#type{def=#'SET'{components=[{'EXTENSIONMARK',Line,undefined}]}},Rest};
+parse_BuiltinType([{'SET',_},{'{',_},{'...',Line},{'!',_}|Rest]) ->
+ {ExceptionIdentification,Rest2} = parse_ExceptionIdentification(Rest),
+ case Rest2 of
+ [{'}',_}|Rest3] ->
+ {#type{def=#'SET'{components=
+ [{'EXTENSIONMARK',Line,ExceptionIdentification}]}},
+ Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'}']}})
+ end;
+parse_BuiltinType([{'SET',_},{'{',_}|Rest]) ->
+ {ComponentTypeLists,Rest2} = parse_ComponentTypeLists(Rest),
+ case Rest2 of
+ [{'}',_}|Rest3] ->
+ {#type{def=#'SET'{components=ComponentTypeLists}},Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'}']}})
+ end;
+parse_BuiltinType([{'SET',_},{'OF',_}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ {#type{def={'SET OF',Type}},Rest2};
+
+%% The so called Useful types
+parse_BuiltinType([{'GeneralizedTime',_}|Rest]) ->
+ {#type{def='GeneralizedTime'},Rest};
+parse_BuiltinType([{'UTCTime',_}|Rest]) ->
+ {#type{def='UTCTime'},Rest};
+parse_BuiltinType([{'ObjectDescriptor',_}|Rest]) ->
+ {#type{def='ObjectDescriptor'},Rest};
+
+%% For compatibility with old standard
+parse_BuiltinType([{'ANY',_},{'DEFINED',_},{'BY',_},{identifier,_,Id}|Rest]) ->
+ {#type{def={'ANY_DEFINED_BY',Id}},Rest};
+parse_BuiltinType([{'ANY',_}|Rest]) ->
+ {#type{def='ANY'},Rest};
+
+parse_BuiltinType(Tokens) ->
+ parse_ObjectClassFieldType(Tokens).
+% throw({asn1_error,unhandled_type}).
+
+
+parse_TypeWithConstraint([{'SEQUENCE',_},Lpar = {'(',_}|Rest]) ->
+ {Constraint,Rest2} = parse_Constraint([Lpar|Rest]),
+ case Rest2 of
+ [{'OF',_}|Rest3] ->
+ {Type,Rest4} = parse_Type(Rest3),
+ {#type{def = {'SEQUENCE OF',Type}, constraint = merge_constraints([Constraint])},Rest4};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'OF']}})
+ end;
+parse_TypeWithConstraint([{'SEQUENCE',_},{'SIZE',_},Lpar = {'(',_}|Rest]) ->
+ {Constraint,Rest2} = parse_Constraint([Lpar|Rest]),
+ Constraint2 =
+ case Constraint of
+ #constraint{c=C} ->
+ Constraint#constraint{c={'SizeConstraint',C}};
+ _ -> Constraint
+ end,
+ case Rest2 of
+ [{'OF',_}|Rest3] ->
+ {Type,Rest4} = parse_Type(Rest3),
+ {#type{def = {'SEQUENCE OF',Type}, constraint = merge_constraints([Constraint2])},Rest4};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'OF']}})
+ end;
+parse_TypeWithConstraint([{'SET',_},Lpar = {'(',_}|Rest]) ->
+ {Constraint,Rest2} = parse_Constraint([Lpar|Rest]),
+ case Rest2 of
+ [{'OF',_}|Rest3] ->
+ {Type,Rest4} = parse_Type(Rest3),
+ {#type{def = {'SET OF',Type}, constraint = merge_constraints([Constraint])},Rest4};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'OF']}})
+ end;
+parse_TypeWithConstraint([{'SET',_},{'SIZE',_},Lpar = {'(',_}|Rest]) ->
+ {Constraint,Rest2} = parse_Constraint([Lpar|Rest]),
+ Constraint2 =
+ case Constraint of
+ #constraint{c=C} ->
+ Constraint#constraint{c={'SizeConstraint',C}};
+ _ -> Constraint
+ end,
+ case Rest2 of
+ [{'OF',_}|Rest3] ->
+ {Type,Rest4} = parse_Type(Rest3),
+ {#type{def = {'SET OF',Type}, constraint = merge_constraints([Constraint2])},Rest4};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'OF']}})
+ end;
+parse_TypeWithConstraint(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ ['SEQUENCE','SEQUENCE SIZE','SET','SET SIZE'],
+ followed,by,a,constraint]}}).
+
+
+%% --------------------------
+
+parse_ReferencedType(Tokens) ->
+ Flist = [fun parse_DefinedType/1,
+ fun parse_SelectionType/1,
+ fun parse_TypeFromObject/1,
+ fun parse_ValueSetFromObjects/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_DefinedType(Tokens=[{typereference,_,_},{'{',_}|_Rest]) ->
+ parse_ParameterizedType(Tokens);
+parse_DefinedType(Tokens=[{typereference,L1,TypeName},
+ T2={typereference,_,_},T3={'{',_}|Rest]) ->
+ case (catch parse_ParameterizedType(Tokens)) of
+ {'EXIT',_Reason} ->
+ Rest2 = [T2,T3|Rest],
+ {#type{def = #'Externaltypereference'{pos=L1,
+ module=get(asn1_module),
+ type=TypeName}},Rest2};
+ {asn1_error,_} ->
+ Rest2 = [T2,T3|Rest],
+ {#type{def = #'Externaltypereference'{pos=L1,
+ module=get(asn1_module),
+ type=TypeName}},Rest2};
+ Result ->
+ Result
+ end;
+parse_DefinedType([{typereference,L1,Module},{'.',_},{typereference,_,TypeName}|Rest]) ->
+ {#type{def = #'Externaltypereference'{pos=L1,module=Module,type=TypeName}},Rest};
+parse_DefinedType([{typereference,L1,TypeName}|Rest]) ->
+ {#type{def = #'Externaltypereference'{pos=L1,module=get(asn1_module),
+ type=TypeName}},Rest};
+parse_DefinedType(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ [typereference,'typereference.typereference',
+ 'typereference typereference']]}}).
+
+parse_SelectionType([{identifier,_,Name},{'<',_}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ {{'SelectionType',Name,Type},Rest2};
+parse_SelectionType(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'identifier <']}}).
+
+
+%% --------------------------
+
+
+%% This should probably be removed very soon
+% parse_ConstrainedType(Tokens) ->
+% case (catch parse_TypeWithConstraint(Tokens)) of
+% {'EXIT',Reason} ->
+% {Type,Rest} = parse_Type(Tokens),
+% {Constraint,Rest2} = parse_Constraint(Rest),
+% {Type#type{constraint=Constraint},Rest2};
+% {asn1_error,Reason2} ->
+% {Type,Rest} = parse_Type(Tokens),
+% {Constraint,Rest2} = parse_Constraint(Rest),
+% {Type#type{constraint=Constraint},Rest2};
+% Result ->
+% Result
+% end.
+
+parse_Constraints(Tokens) ->
+ parse_Constraints(Tokens,[]).
+
+parse_Constraints(Tokens,Acc) ->
+ {Constraint,Rest} = parse_Constraint(Tokens),
+ case Rest of
+ [{'(',_}|_Rest2] ->
+ parse_Constraints(Rest,[Constraint|Acc]);
+ _ ->
+ {lists:reverse([Constraint|Acc]),Rest}
+ end.
+
+parse_Constraint([{'(',_}|Rest]) ->
+ {Constraint,Rest2} = parse_ConstraintSpec(Rest),
+ {Exception,Rest3} = parse_ExceptionSpec(Rest2),
+ case Rest3 of
+ [{')',_}|Rest4] ->
+ {#constraint{c=Constraint,e=Exception},Rest4};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,')']}})
+ end;
+parse_Constraint(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'(']}}).
+
+parse_ConstraintSpec(Tokens) ->
+ Flist = [fun parse_GeneralConstraint/1,
+ fun parse_SubtypeConstraint/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ {asn1_error,Reason2} ->
+ throw({asn1_error,Reason2});
+ Result ->
+ Result
+ end.
+
+parse_ExceptionSpec([LPar={')',_}|Rest]) ->
+ {undefined,[LPar|Rest]};
+parse_ExceptionSpec([{'!',_}|Rest]) ->
+ parse_ExceptionIdentification(Rest);
+parse_ExceptionSpec(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,[')','!']]}}).
+
+parse_ExceptionIdentification(Tokens) ->
+ Flist = [fun parse_SignedNumber/1,
+ fun parse_DefinedValue/1,
+ fun parse_TypeColonValue/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ {asn1_error,Reason2} ->
+ throw({asn1_error,Reason2});
+ Result ->
+ Result
+ end.
+
+parse_TypeColonValue(Tokens) ->
+ {Type,Rest} = parse_Type(Tokens),
+ case Rest of
+ [{':',_}|Rest2] ->
+ {Value,Rest3} = parse_Value(Rest2),
+ {{Type,Value},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,':']}})
+ end.
+
+parse_SubtypeConstraint(Tokens) ->
+ parse_ElementSetSpecs(Tokens).
+
+parse_ElementSetSpecs([{'...',_}|Rest]) ->
+ {Elements,Rest2} = parse_ElementSetSpec(Rest),
+ {{[],Elements},Rest2};
+parse_ElementSetSpecs(Tokens) ->
+ {RootElems,Rest} = parse_ElementSetSpec(Tokens),
+ case Rest of
+ [{',',_},{'...',_},{',',_}|Rest2] ->
+ {AdditionalElems,Rest3} = parse_ElementSetSpec(Rest2),
+ {{RootElems,AdditionalElems},Rest3};
+ [{',',_},{'...',_}|Rest2] ->
+ {{RootElems,[]},Rest2};
+ _ ->
+ {RootElems,Rest}
+ end.
+
+parse_ElementSetSpec([{'ALL',_},{'EXCEPT',_}|Rest]) ->
+ {Exclusions,Rest2} = parse_Elements(Rest),
+ {{'ALL',{'EXCEPT',Exclusions}},Rest2};
+parse_ElementSetSpec(Tokens) ->
+ parse_Unions(Tokens).
+
+
+parse_Unions(Tokens) ->
+ {InterSec,Rest} = parse_Intersections(Tokens),
+ {Unions,Rest2} = parse_UnionsRec(Rest),
+ case {InterSec,Unions} of
+ {InterSec,[]} ->
+ {InterSec,Rest2};
+ {{'SingleValue',V1},{'SingleValue',V2}} ->
+ {{'SingleValue',ordsets:union(to_set(V1),to_set(V2))},Rest2};
+ {V1,V2} when list(V2) ->
+ {[V1] ++ [union|V2],Rest2};
+ {V1,V2} ->
+ {[V1,union,V2],Rest2}
+% Other ->
+% throw(Other)
+ end.
+
+parse_UnionsRec([{'|',_}|Rest]) ->
+ {InterSec,Rest2} = parse_Intersections(Rest),
+ {URec,Rest3} = parse_UnionsRec(Rest2),
+ case {InterSec,URec} of
+ {V1,[]} ->
+ {V1,Rest3};
+ {{'SingleValue',V1},{'SingleValue',V2}} ->
+ {{'SingleValue',ordsets:union(to_set(V1),to_set(V2))},Rest3};
+ {V1,V2} when list(V2) ->
+ {[V1] ++ V2,Rest3};
+ {V1,V2} ->
+ {[V1,V2],Rest3}
+ end;
+parse_UnionsRec([{'UNION',_}|Rest]) ->
+ {InterSec,Rest2} = parse_Intersections(Rest),
+ {URec,Rest3} = parse_UnionsRec(Rest2),
+ case {InterSec,URec} of
+ {V1,[]} ->
+ {V1,Rest3};
+ {{'SingleValue',V1},{'SingleValue',V2}} ->
+ {{'SingleValue',ordsets:union(to_set(V1),to_set(V2))},Rest3};
+ {V1,V2} when list(V2) ->
+ {[V1] ++ V2,Rest3};
+ {V1,V2} ->
+ {[V1,V2],Rest3}
+ end;
+parse_UnionsRec(Tokens) ->
+ {[],Tokens}.
+
+parse_Intersections(Tokens) ->
+ {InterSec,Rest} = parse_IntersectionElements(Tokens),
+ {IRec,Rest2} = parse_IElemsRec(Rest),
+ case {InterSec,IRec} of
+ {V1,[]} ->
+ {V1,Rest2};
+ {{'SingleValue',V1},{'SingleValue',V2}} ->
+ {{'SingleValue',
+ ordsets:intersection(to_set(V1),to_set(V2))},Rest2};
+ {V1,V2} when list(V2) ->
+ {[V1] ++ [intersection|V2],Rest2};
+ {V1,V2} ->
+ {[V1,intersection,V2],Rest2};
+ _ ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'a Union']}})
+ end.
+
+parse_IElemsRec([{'^',_}|Rest]) ->
+ {InterSec,Rest2} = parse_IntersectionElements(Rest),
+ {IRec,Rest3} = parse_IElemsRec(Rest2),
+ case {InterSec,IRec} of
+ {{'SingleValue',V1},{'SingleValue',V2}} ->
+ {{'SingleValue',
+ ordsets:intersection(to_set(V1),to_set(V2))},Rest3};
+ {V1,[]} ->
+ {V1,Rest3};
+ {V1,V2} when list(V2) ->
+ {[V1] ++ V2,Rest3};
+ {V1,V2} ->
+ {[V1,V2],Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest)),get(asn1_module),
+ [got,get_token(hd(Rest)),expected,'an Intersection']}})
+ end;
+parse_IElemsRec([{'INTERSECTION',_}|Rest]) ->
+ {InterSec,Rest2} = parse_IntersectionElements(Rest),
+ {IRec,Rest3} = parse_IElemsRec(Rest2),
+ case {InterSec,IRec} of
+ {{'SingleValue',V1},{'SingleValue',V2}} ->
+ {{'SingleValue',
+ ordsets:intersection(to_set(V1),to_set(V2))},Rest3};
+ {V1,[]} ->
+ {V1,Rest3};
+ {V1,V2} when list(V2) ->
+ {[V1] ++ V2,Rest3};
+ {V1,V2} ->
+ {[V1,V2],Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest)),get(asn1_module),
+ [got,get_token(hd(Rest)),expected,'an Intersection']}})
+ end;
+parse_IElemsRec(Tokens) ->
+ {[],Tokens}.
+
+parse_IntersectionElements(Tokens) ->
+ {InterSec,Rest} = parse_Elements(Tokens),
+ case Rest of
+ [{'EXCEPT',_}|Rest2] ->
+ {Exclusion,Rest3} = parse_Elements(Rest2),
+ {{InterSec,{'EXCEPT',Exclusion}},Rest3};
+ Rest ->
+ {InterSec,Rest}
+ end.
+
+parse_Elements([{'(',_}|Rest]) ->
+ {Elems,Rest2} = parse_ElementSetSpec(Rest),
+ case Rest2 of
+ [{')',_}|Rest3] ->
+ {Elems,Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,')']}})
+ end;
+parse_Elements(Tokens) ->
+ Flist = [fun parse_SubtypeElements/1,
+ fun parse_ObjectSetElements/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ Err = {asn1_error,_} ->
+ throw(Err);
+ Result ->
+ Result
+ end.
+
+
+
+
+%% --------------------------
+
+parse_DefinedObjectClass([{typereference,_,_ModName},{'.',_},Tr={typereference,_,_ObjClName}|Rest]) ->
+%% {{objectclassname,ModName,ObjClName},Rest};
+% {{objectclassname,tref2Exttref(Tr)},Rest};
+ {tref2Exttref(Tr),Rest};
+parse_DefinedObjectClass([Tr={typereference,_,_ObjClName}|Rest]) ->
+% {{objectclassname,tref2Exttref(Tr)},Rest};
+ {tref2Exttref(Tr),Rest};
+parse_DefinedObjectClass([{'TYPE-IDENTIFIER',_}|Rest]) ->
+ {'TYPE-IDENTIFIER',Rest};
+parse_DefinedObjectClass([{'ABSTRACT-SYNTAX',_}|Rest]) ->
+ {'ABSTRACT-SYNTAX',Rest};
+parse_DefinedObjectClass(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ ['typereference . typereference',
+ typereference,
+ 'TYPE-IDENTIFIER',
+ 'ABSTRACT-SYNTAX']]}}).
+
+parse_ObjectClassAssignment([{typereference,L1,ObjClName},{'::=',_}|Rest]) ->
+ {Type,Rest2} = parse_ObjectClass(Rest),
+ {#classdef{pos=L1,name=ObjClName,typespec=Type},Rest2};
+parse_ObjectClassAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ 'typereference ::=']}}).
+
+parse_ObjectClass(Tokens) ->
+ Flist = [fun parse_DefinedObjectClass/1,
+ fun parse_ObjectClassDefn/1,
+ fun parse_ParameterizedObjectClass/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ {asn1_error,Reason2} ->
+ throw({asn1_error,Reason2});
+ Result ->
+ Result
+ end.
+
+parse_ObjectClassDefn([{'CLASS',_},{'{',_}|Rest]) ->
+ {Type,Rest2} = parse_FieldSpec(Rest),
+ {WithSyntaxSpec,Rest3} = parse_WithSyntaxSpec(Rest2),
+ {#objectclass{fields=Type,syntax=WithSyntaxSpec},Rest3};
+parse_ObjectClassDefn(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'CLASS {']}}).
+
+parse_FieldSpec(Tokens) ->
+ parse_FieldSpec(Tokens,[]).
+
+parse_FieldSpec(Tokens,Acc) ->
+ Flist = [fun parse_FixedTypeValueFieldSpec/1,
+ fun parse_VariableTypeValueFieldSpec/1,
+ fun parse_ObjectFieldSpec/1,
+ fun parse_FixedTypeValueSetFieldSpec/1,
+ fun parse_VariableTypeValueSetFieldSpec/1,
+ fun parse_TypeFieldSpec/1,
+ fun parse_ObjectSetFieldSpec/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ {Type,[{'}',_}|Rest]} ->
+ {lists:reverse([Type|Acc]),Rest};
+ {Type,[{',',_}|Rest2]} ->
+ parse_FieldSpec(Rest2,[Type|Acc]);
+ {_,[H|_T]} ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'}']}})
+ end.
+
+parse_PrimitiveFieldName([{typefieldreference,_,FieldName}|Rest]) ->
+ {{typefieldreference,FieldName},Rest};
+parse_PrimitiveFieldName([{valuefieldreference,_,FieldName}|Rest]) ->
+ {{valuefieldreference,FieldName},Rest};
+parse_PrimitiveFieldName(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ [typefieldreference,valuefieldreference]]}}).
+
+parse_FieldName(Tokens) ->
+ {Field,Rest} = parse_PrimitiveFieldName(Tokens),
+ parse_FieldName(Rest,[Field]).
+
+parse_FieldName([{'.',_}|Rest],Acc) ->
+ case (catch parse_PrimitiveFieldName(Rest)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ {FieldName,Rest2} ->
+ parse_FieldName(Rest2,[FieldName|Acc])
+ end;
+parse_FieldName(Tokens,Acc) ->
+ {lists:reverse(Acc),Tokens}.
+
+parse_FixedTypeValueFieldSpec([{valuefieldreference,L1,VFieldName}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ {Unique,Rest3} =
+ case Rest2 of
+ [{'UNIQUE',_}|Rest4] ->
+ {'UNIQUE',Rest4};
+ _ ->
+ {undefined,Rest2}
+ end,
+ {OptionalitySpec,Rest5} = parse_ValueOptionalitySpec(Rest3),
+ case Unique of
+ 'UNIQUE' ->
+ case OptionalitySpec of
+ {'DEFAULT',_} ->
+ throw({asn1_error,
+ {L1,get(asn1_module),
+ ['UNIQUE and DEFAULT in same field',VFieldName]}});
+ _ ->
+ {{fixedtypevaluefield,VFieldName,Type,Unique,OptionalitySpec},Rest5}
+ end;
+ _ ->
+ {{object_or_fixedtypevalue_field,VFieldName,Type,Unique,OptionalitySpec},Rest5}
+ end;
+parse_FixedTypeValueFieldSpec(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,valuefieldreference]}}).
+
+parse_VariableTypeValueFieldSpec([{valuefieldreference,_,VFieldName}|Rest]) ->
+ {FieldRef,Rest2} = parse_FieldName(Rest),
+ {OptionalitySpec,Rest3} = parse_ValueOptionalitySpec(Rest2),
+ {{variabletypevaluefield,VFieldName,FieldRef,OptionalitySpec},Rest3};
+parse_VariableTypeValueFieldSpec(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,valuefieldreference]}}).
+
+parse_ObjectFieldSpec([{valuefieldreference,_,VFieldName}|Rest]) ->
+ {Class,Rest2} = parse_DefinedObjectClass(Rest),
+ {OptionalitySpec,Rest3} = parse_ObjectOptionalitySpec(Rest2),
+ {{objectfield,VFieldName,Class,OptionalitySpec},Rest3};
+parse_ObjectFieldSpec(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,valuefieldreference]}}).
+
+parse_TypeFieldSpec([{typefieldreference,_,TFieldName}|Rest]) ->
+ {OptionalitySpec,Rest2} = parse_TypeOptionalitySpec(Rest),
+ {{typefield,TFieldName,OptionalitySpec},Rest2};
+parse_TypeFieldSpec(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,typefieldreference]}}).
+
+parse_FixedTypeValueSetFieldSpec([{typefieldreference,_,TFieldName}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ {OptionalitySpec,Rest3} = parse_ValueSetOptionalitySpec(Rest2),
+ {{objectset_or_fixedtypevalueset_field,TFieldName,Type,
+ OptionalitySpec},Rest3};
+parse_FixedTypeValueSetFieldSpec(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,typefieldreference]}}).
+
+parse_VariableTypeValueSetFieldSpec([{typefieldreference,_,TFieldName}|Rest]) ->
+ {FieldRef,Rest2} = parse_FieldName(Rest),
+ {OptionalitySpec,Rest3} = parse_ValueSetOptionalitySpec(Rest2),
+ {{variabletypevaluesetfield,TFieldName,FieldRef,OptionalitySpec},Rest3};
+parse_VariableTypeValueSetFieldSpec(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,typefieldreference]}}).
+
+parse_ObjectSetFieldSpec([{typefieldreference,_,TFieldName}|Rest]) ->
+ {Class,Rest2} = parse_DefinedObjectClass(Rest),
+ {OptionalitySpec,Rest3} = parse_ObjectSetOptionalitySpec(Rest2),
+ {{objectsetfield,TFieldName,Class,OptionalitySpec},Rest3};
+parse_ObjectSetFieldSpec(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,typefieldreference]}}).
+
+parse_ValueOptionalitySpec(Tokens)->
+ case Tokens of
+ [{'OPTIONAL',_}|Rest] -> {'OPTIONAL',Rest};
+ [{'DEFAULT',_}|Rest] ->
+ {Value,Rest2} = parse_Value(Rest),
+ {{'DEFAULT',Value},Rest2};
+ _ -> {'MANDATORY',Tokens}
+ end.
+
+parse_ObjectOptionalitySpec(Tokens) ->
+ case Tokens of
+ [{'OPTIONAL',_}|Rest] -> {'OPTIONAL',Rest};
+ [{'DEFAULT',_}|Rest] ->
+ {Object,Rest2} = parse_Object(Rest),
+ {{'DEFAULT',Object},Rest2};
+ _ -> {'MANDATORY',Tokens}
+ end.
+
+parse_TypeOptionalitySpec(Tokens) ->
+ case Tokens of
+ [{'OPTIONAL',_}|Rest] -> {'OPTIONAL',Rest};
+ [{'DEFAULT',_}|Rest] ->
+ {Type,Rest2} = parse_Type(Rest),
+ {{'DEFAULT',Type},Rest2};
+ _ -> {'MANDATORY',Tokens}
+ end.
+
+parse_ValueSetOptionalitySpec(Tokens) ->
+ case Tokens of
+ [{'OPTIONAL',_}|Rest] -> {'OPTIONAL',Rest};
+ [{'DEFAULT',_}|Rest] ->
+ {ValueSet,Rest2} = parse_ValueSet(Rest),
+ {{'DEFAULT',ValueSet},Rest2};
+ _ -> {'MANDATORY',Tokens}
+ end.
+
+parse_ObjectSetOptionalitySpec(Tokens) ->
+ case Tokens of
+ [{'OPTIONAL',_}|Rest] -> {'OPTIONAL',Rest};
+ [{'DEFAULT',_}|Rest] ->
+ {ObjectSet,Rest2} = parse_ObjectSet(Rest),
+ {{'DEFAULT',ObjectSet},Rest2};
+ _ -> {'MANDATORY',Tokens}
+ end.
+
+parse_WithSyntaxSpec([{'WITH',_},{'SYNTAX',_}|Rest]) ->
+ {SyntaxList,Rest2} = parse_SyntaxList(Rest),
+ {{'WITH SYNTAX',SyntaxList},Rest2};
+parse_WithSyntaxSpec(Tokens) ->
+ {[],Tokens}.
+
+parse_SyntaxList([{'{',_},{'}',_}|Rest]) ->
+ {[],Rest};
+parse_SyntaxList([{'{',_}|Rest]) ->
+ parse_SyntaxList(Rest,[]);
+parse_SyntaxList(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,['{}','{']]}}).
+
+parse_SyntaxList(Tokens,Acc) ->
+ {SyntaxList,Rest} = parse_TokenOrGroupSpec(Tokens),
+ case Rest of
+ [{'}',_}|Rest2] ->
+ {lists:reverse([SyntaxList|Acc]),Rest2};
+ _ ->
+ parse_SyntaxList(Rest,[SyntaxList|Acc])
+ end.
+
+parse_TokenOrGroupSpec(Tokens) ->
+ Flist = [fun parse_RequiredToken/1,
+ fun parse_OptionalGroup/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_RequiredToken([{typereference,L1,WordName}|Rest]) ->
+ case is_word(WordName) of
+ false ->
+ throw({asn1_error,{L1,get(asn1_module),
+ [got,WordName,expected,a,'Word']}});
+ true ->
+ {WordName,Rest}
+ end;
+parse_RequiredToken([{',',L1}|Rest]) ->
+ {{',',L1},Rest};
+parse_RequiredToken([{WordName,L1}|Rest]) ->
+ case is_word(WordName) of
+ false ->
+ throw({asn1_error,{L1,get(asn1_module),
+ [got,WordName,expected,a,'Word']}});
+ true ->
+ {WordName,Rest}
+ end;
+parse_RequiredToken(Tokens) ->
+ parse_PrimitiveFieldName(Tokens).
+
+parse_OptionalGroup([{'[',_}|Rest]) ->
+ {Spec,Rest2} = parse_TokenOrGroupSpec(Rest),
+ {SpecList,Rest3} = parse_OptionalGroup(Rest2,[Spec]),
+ {SpecList,Rest3}.
+
+parse_OptionalGroup([{']',_}|Rest],Acc) ->
+ {lists:reverse(Acc),Rest};
+parse_OptionalGroup(Tokens,Acc) ->
+ {Spec,Rest} = parse_TokenOrGroupSpec(Tokens),
+ parse_OptionalGroup(Rest,[Spec|Acc]).
+
+parse_DefinedObject([Id={identifier,_,_ObjName}|Rest]) ->
+ {{object,identifier2Extvalueref(Id)},Rest};
+parse_DefinedObject([{typereference,L1,ModName},{'.',_},{identifier,_,ObjName}|Rest]) ->
+ {{object, #'Externaltypereference'{pos=L1,module=ModName,type=ObjName}},Rest};
+parse_DefinedObject(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ [identifier,'typereference.identifier']]}}).
+
+parse_ObjectAssignment([{identifier,L1,ObjName}|Rest]) ->
+ {Class,Rest2} = parse_DefinedObjectClass(Rest),
+ case Rest2 of
+ [{'::=',_}|Rest3] ->
+ {Object,Rest4} = parse_Object(Rest3),
+ {#typedef{pos=L1,name=ObjName,
+ typespec=#'Object'{classname=Class,def=Object}},Rest4};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'::=']}});
+ Other ->
+ throw({asn1_error,{L1,get(asn1_module),
+ [got,Other,expected,'::=']}})
+ end;
+parse_ObjectAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,identifier]}}).
+
+parse_Object(Tokens) ->
+ Flist=[fun parse_ObjectDefn/1,
+ fun parse_ObjectFromObject/1,
+ fun parse_ParameterizedObject/1,
+ fun parse_DefinedObject/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_ObjectDefn(Tokens) ->
+ Flist=[fun parse_DefaultSyntax/1,
+ fun parse_DefinedSyntax/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_DefaultSyntax([{'{',_},{'}',_}|Rest]) ->
+ {{object,defaultsyntax,[]},Rest};
+parse_DefaultSyntax([{'{',_}|Rest]) ->
+ parse_DefaultSyntax(Rest,[]);
+parse_DefaultSyntax(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,['{}','{']]}}).
+
+parse_DefaultSyntax(Tokens,Acc) ->
+ {Setting,Rest} = parse_FieldSetting(Tokens),
+ case Rest of
+ [{',',_}|Rest2] ->
+ parse_DefaultSyntax(Rest2,[Setting|Acc]);
+ [{'}',_}|Rest3] ->
+ {{object,defaultsyntax,lists:reverse([Setting|Acc])},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,[',','}']]}})
+ end.
+
+parse_FieldSetting(Tokens) ->
+ {{_,PrimFieldName},Rest} = parse_PrimitiveFieldName(Tokens),
+ {Setting,Rest2} = parse_Setting(Rest),
+ {{PrimFieldName,Setting},Rest2}.
+
+parse_DefinedSyntax([{'{',_}|Rest]) ->
+ parse_DefinedSyntax(Rest,[]).
+
+parse_DefinedSyntax(Tokens,Acc) ->
+ case Tokens of
+ [{'}',_}|Rest2] ->
+ {{object,definedsyntax,lists:reverse(Acc)},Rest2};
+ _ ->
+ {DefSynTok,Rest3} = parse_DefinedSyntaxToken(Tokens),
+ parse_DefinedSyntax(Rest3,[DefSynTok|Acc])
+ end.
+
+parse_DefinedSyntaxToken([{',',L1}|Rest]) ->
+ {{',',L1},Rest};
+parse_DefinedSyntaxToken([{typereference,L1,Name}|Rest]) ->
+ case is_word(Name) of
+ false ->
+ {{setting,L1,Name},Rest};
+ true ->
+ {{word_or_setting,L1,Name},Rest}
+ end;
+parse_DefinedSyntaxToken(Tokens) ->
+ case catch parse_Setting(Tokens) of
+ {asn1_error,_} ->
+ parse_Word(Tokens);
+ {'EXIT',Reason} ->
+ exit(Reason);
+ Result ->
+ Result
+ end.
+
+parse_Word([{Name,Pos}|Rest]) ->
+ case is_word(Name) of
+ false ->
+ throw({asn1_error,{Pos,get(asn1_module),
+ [got,Name, expected,a,'Word']}});
+ true ->
+ {{word_or_setting,Pos,Name},Rest}
+ end.
+
+parse_Setting(Tokens) ->
+ Flist = [fun parse_Type/1,
+ fun parse_Value/1,
+ fun parse_Object/1,
+ fun parse_ObjectSet/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_DefinedObjectSet([{typereference,L1,ModuleName},{'.',_},
+ {typereference,L2,ObjSetName}|Rest]) ->
+ {{objectset,L1,#'Externaltypereference'{pos=L2,module=ModuleName,
+ type=ObjSetName}},Rest};
+parse_DefinedObjectSet([{typereference,L1,ObjSetName}|Rest]) ->
+ {{objectset,L1,#'Externaltypereference'{pos=L1,module=get(asn1_module),
+ type=ObjSetName}},Rest};
+parse_DefinedObjectSet(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ [typereference,'typereference.typereference']]}}).
+
+parse_ObjectSetAssignment([{typereference,L1,ObjSetName}|Rest]) ->
+ {Class,Rest2} = parse_DefinedObjectClass(Rest),
+ case Rest2 of
+ [{'::=',_}|Rest3] ->
+ {ObjectSet,Rest4} = parse_ObjectSet(Rest3),
+ {#typedef{pos=L1,name=ObjSetName,
+ typespec=#'ObjectSet'{class=Class,
+ set=ObjectSet}},Rest4};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'::=']}})
+%%% Other ->
+%%% throw(Other)
+ end;
+parse_ObjectSetAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ typereference]}}).
+
+parse_ObjectSet([{'{',_}|Rest]) ->
+ {ObjSetSpec,Rest2} = parse_ObjectSetSpec(Rest),
+ case Rest2 of
+ [{'}',_}|Rest3] ->
+ {ObjSetSpec,Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'}']}})
+ end;
+parse_ObjectSet(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'{']}}).
+
+parse_ObjectSetSpec([{'...',_}|Rest]) ->
+ {['EXTENSIONMARK'],Rest};
+parse_ObjectSetSpec(Tokens) ->
+ parse_ElementSetSpecs(Tokens).
+
+parse_ObjectSetElements(Tokens) ->
+ Flist = [fun parse_Object/1,
+ fun parse_DefinedObjectSet/1,
+ fun parse_ObjectSetFromObjects/1,
+ fun parse_ParameterizedObjectSet/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_ObjectClassFieldType(Tokens) ->
+ {Class,Rest} = parse_DefinedObjectClass(Tokens),
+ case Rest of
+ [{'.',_}|Rest2] ->
+ {FieldName,Rest3} = parse_FieldName(Rest2),
+ OCFT = #'ObjectClassFieldType'{
+ classname=Class,
+ class=Class,fieldname=FieldName},
+ {#type{def=OCFT},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'.']}})
+%%% Other ->
+%%% throw(Other)
+ end.
+
+%parse_ObjectClassFieldValue(Tokens) ->
+% Flist = [fun parse_OpenTypeFieldVal/1,
+% fun parse_FixedTypeFieldVal/1],
+% case (catch parse_or(Tokens,Flist)) of
+% {'EXIT',Reason} ->
+% throw(Reason);
+% AsnErr = {asn1_error,_} ->
+% throw(AsnErr);
+% Result ->
+% Result
+% end.
+
+parse_ObjectClassFieldValue(Tokens) ->
+ parse_OpenTypeFieldVal(Tokens).
+
+parse_OpenTypeFieldVal(Tokens) ->
+ {Type,Rest} = parse_Type(Tokens),
+ case Rest of
+ [{':',_}|Rest2] ->
+ {Value,Rest3} = parse_Value(Rest2),
+ {{opentypefieldvalue,Type,Value},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,':']}})
+ end.
+
+% parse_FixedTypeFieldVal(Tokens) ->
+% parse_Value(Tokens).
+
+% parse_InformationFromObjects(Tokens) ->
+% Flist = [fun parse_ValueFromObject/1,
+% fun parse_ValueSetFromObjects/1,
+% fun parse_TypeFromObject/1,
+% fun parse_ObjectFromObject/1],
+% case (catch parse_or(Tokens,Flist)) of
+% {'EXIT',Reason} ->
+% throw(Reason);
+% AsnErr = {asn1_error,_} ->
+% throw(AsnErr);
+% Result ->
+% Result
+% end.
+
+parse_ReferencedObjects(Tokens) ->
+ Flist = [fun parse_DefinedObject/1,
+ fun parse_DefinedObjectSet/1,
+ fun parse_ParameterizedObject/1,
+ fun parse_ParameterizedObjectSet/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_ValueFromObject(Tokens) ->
+ {Objects,Rest} = parse_ReferencedObjects(Tokens),
+ case Rest of
+ [{'.',_}|Rest2] ->
+ {Name,Rest3} = parse_FieldName(Rest2),
+ case lists:last(Name) of
+ {valuefieldreference,_} ->
+ {{'ValueFromObject',Objects,Name},Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,typefieldreference,expected,
+ valuefieldreference]}})
+ end;
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'.']}})
+%%% Other ->
+%%% throw({asn1_error,{got,Other,expected,'.'}})
+ end.
+
+parse_ValueSetFromObjects(Tokens) ->
+ {Objects,Rest} = parse_ReferencedObjects(Tokens),
+ case Rest of
+ [{'.',_}|Rest2] ->
+ {Name,Rest3} = parse_FieldName(Rest2),
+ case lists:last(Name) of
+ {typefieldreference,_FieldName} ->
+ {{'ValueSetFromObjects',Objects,Name},Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,
+ typefieldreference]}})
+ end;
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'.']}})
+%%% Other ->
+%%% throw({asn1_error,{got,Other,expected,'.'}})
+ end.
+
+parse_TypeFromObject(Tokens) ->
+ {Objects,Rest} = parse_ReferencedObjects(Tokens),
+ case Rest of
+ [{'.',_}|Rest2] ->
+ {Name,Rest3} = parse_FieldName(Rest2),
+ case lists:last(Name) of
+ {typefieldreference,_FieldName} ->
+ {{'TypeFromObject',Objects,Name},Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,
+ typefieldreference]}})
+ end;
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'.']}})
+%%% Other ->
+%%% throw({asn1_error,{got,Other,expected,'.'}})
+ end.
+
+parse_ObjectFromObject(Tokens) ->
+ {Objects,Rest} = parse_ReferencedObjects(Tokens),
+ case Rest of
+ [{'.',_}|Rest2] ->
+ {Name,Rest3} = parse_FieldName(Rest2),
+ {{'ObjectFromObject',Objects,Name},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'.']}})
+%%% Other ->
+%%% throw({asn1_error,{got,Other,expected,'.'}})
+ end.
+
+parse_ObjectSetFromObjects(Tokens) ->
+ {Objects,Rest} = parse_ReferencedObjects(Tokens),
+ case Rest of
+ [{'.',_}|Rest2] ->
+ {Name,Rest3} = parse_FieldName(Rest2),
+ {{'ObjectSetFromObjects',Objects,Name},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'.']}})
+%%% Other ->
+%%% throw({asn1_error,{got,Other,expected,'.'}})
+ end.
+
+% parse_InstanceOfType([{'INSTANCE',_},{'OF',_}|Rest]) ->
+% {Class,Rest2} = parse_DefinedObjectClass(Rest),
+% {{'InstanceOfType',Class},Rest2}.
+
+% parse_InstanceOfValue(Tokens) ->
+% parse_Value(Tokens).
+
+
+
+%% X.682 constraint specification
+
+parse_GeneralConstraint(Tokens) ->
+ Flist = [fun parse_UserDefinedConstraint/1,
+ fun parse_TableConstraint/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_UserDefinedConstraint([{'CONSTRAINED',_},{'BY',_},{'{',_},{'}',_}|Rest])->
+ {{constrained_by,[]},Rest};
+parse_UserDefinedConstraint([{'CONSTRAINED',_},
+ {'BY',_},
+ {'{',_}|Rest]) ->
+ {Param,Rest2} = parse_UserDefinedConstraintParameter(Rest),
+ case Rest2 of
+ [{'}',_}|Rest3] ->
+ {{constrained_by,Param},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'}']}})
+ end;
+parse_UserDefinedConstraint(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ ['CONSTRAINED BY {}','CONSTRAINED BY {']]}}).
+
+parse_UserDefinedConstraintParameter(Tokens) ->
+ parse_UserDefinedConstraintParameter(Tokens,[]).
+parse_UserDefinedConstraintParameter(Tokens,Acc) ->
+ Flist = [fun parse_GovernorAndActualParameter/1,
+ fun parse_ActualParameter/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ {Result,Rest} ->
+ case Rest of
+ [{',',_}|_Rest2] ->
+ parse_UserDefinedConstraintParameter(Tokens,[Result|Acc]);
+ _ ->
+ {lists:reverse([Result|Acc]),Rest}
+ end
+ end.
+
+parse_GovernorAndActualParameter(Tokens) ->
+ {Governor,Rest} = parse_Governor(Tokens),
+ case Rest of
+ [{':',_}|Rest2] ->
+ {Params,Rest3} = parse_ActualParameter(Rest2),
+ {{'Governor_Params',Governor,Params},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,':']}})
+ end.
+
+parse_TableConstraint(Tokens) ->
+ Flist = [fun parse_ComponentRelationConstraint/1,
+ fun parse_SimpleTableConstraint/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_SimpleTableConstraint(Tokens) ->
+ {ObjectSet,Rest} = parse_ObjectSet(Tokens),
+ {{simpletable,ObjectSet},Rest}.
+
+parse_ComponentRelationConstraint([{'{',_}|Rest]) ->
+ {ObjectSet,Rest2} = parse_DefinedObjectSet(Rest),
+ case Rest2 of
+ [{'}',_},{'{',_}|Rest3] ->
+ {AtNot,Rest4} = parse_AtNotationList(Rest3,[]),
+ case Rest4 of
+ [{'}',_}|Rest5] ->
+ {{componentrelation,ObjectSet,AtNot},Rest5};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'}']}})
+ end;
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,
+ 'ComponentRelationConstraint',ended,with,'}']}})
+%%% Other ->
+%%% throw(Other)
+ end;
+parse_ComponentRelationConstraint(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'{']}}).
+
+parse_AtNotationList(Tokens,Acc) ->
+ {AtNot,Rest} = parse_AtNotation(Tokens),
+ case Rest of
+ [{',',_}|Rest2] ->
+ parse_AtNotationList(Rest2,[AtNot|Acc]);
+ _ ->
+ {lists:reverse([AtNot|Acc]),Rest}
+ end.
+
+parse_AtNotation([{'@',_},{'.',_}|Rest]) ->
+ {CIdList,Rest2} = parse_ComponentIdList(Rest),
+ {{innermost,CIdList},Rest2};
+parse_AtNotation([{'@',_}|Rest]) ->
+ {CIdList,Rest2} = parse_ComponentIdList(Rest),
+ {{outermost,CIdList},Rest2};
+parse_AtNotation(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,['@','@.']]}}).
+
+parse_ComponentIdList(Tokens) ->
+ parse_ComponentIdList(Tokens,[]).
+
+parse_ComponentIdList([Id = {identifier,_,_},{'.',_}|Rest],Acc) ->
+ parse_ComponentIdList(Rest,[identifier2Extvalueref(Id)|Acc]);
+parse_ComponentIdList([Id = {identifier,_,_}|Rest],Acc) ->
+ {lists:reverse([identifier2Extvalueref(Id)|Acc]),Rest};
+parse_ComponentIdList(Tokens,_) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ [identifier,'identifier.']]}}).
+
+
+
+
+
+% X.683 Parameterization of ASN.1 specifications
+
+parse_Governor(Tokens) ->
+ Flist = [fun parse_Type/1,
+ fun parse_DefinedObjectClass/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_ActualParameter(Tokens) ->
+ Flist = [fun parse_Type/1,
+ fun parse_Value/1,
+ fun parse_ValueSet/1,
+ fun parse_DefinedObjectClass/1,
+ fun parse_Object/1,
+ fun parse_ObjectSet/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_ParameterizedAssignment(Tokens) ->
+ Flist = [fun parse_ParameterizedTypeAssignment/1,
+ fun parse_ParameterizedValueAssignment/1,
+ fun parse_ParameterizedValueSetTypeAssignment/1,
+ fun parse_ParameterizedObjectClassAssignment/1,
+ fun parse_ParameterizedObjectAssignment/1,
+ fun parse_ParameterizedObjectSetAssignment/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ AsnAssErr = {asn1_assignment_error,_} ->
+ throw(AsnAssErr);
+ Result ->
+ Result
+ end.
+
+parse_ParameterizedTypeAssignment([{typereference,L1,Name}|Rest]) ->
+ {ParameterList,Rest2} = parse_ParameterList(Rest),
+ case Rest2 of
+ [{'::=',_}|Rest3] ->
+ {Type,Rest4} = parse_Type(Rest3),
+ {#ptypedef{pos=L1,name=Name,args=ParameterList,typespec=Type},
+ Rest4};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'::=']}})
+ end;
+parse_ParameterizedTypeAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ typereference]}}).
+
+parse_ParameterizedValueAssignment([{identifier,L1,Name}|Rest]) ->
+ {ParameterList,Rest2} = parse_ParameterList(Rest),
+ {Type,Rest3} = parse_Type(Rest2),
+ case Rest3 of
+ [{'::=',_}|Rest4] ->
+ {Value,Rest5} = parse_Value(Rest4),
+ {#pvaluedef{pos=L1,name=Name,args=ParameterList,type=Type,
+ value=Value},Rest5};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'::=']}})
+ end;
+parse_ParameterizedValueAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,identifier]}}).
+
+parse_ParameterizedValueSetTypeAssignment([{typereference,L1,Name}|Rest]) ->
+ {ParameterList,Rest2} = parse_ParameterList(Rest),
+ {Type,Rest3} = parse_Type(Rest2),
+ case Rest3 of
+ [{'::=',_}|Rest4] ->
+ {ValueSet,Rest5} = parse_ValueSet(Rest4),
+ {#pvaluesetdef{pos=L1,name=Name,args=ParameterList,
+ type=Type,valueset=ValueSet},Rest5};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'::=']}})
+ end;
+parse_ParameterizedValueSetTypeAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ typereference]}}).
+
+parse_ParameterizedObjectClassAssignment([{typereference,L1,Name}|Rest]) ->
+ {ParameterList,Rest2} = parse_ParameterList(Rest),
+ case Rest2 of
+ [{'::=',_}|Rest3] ->
+ {Class,Rest4} = parse_ObjectClass(Rest3),
+ {#ptypedef{pos=L1,name=Name,args=ParameterList,typespec=Class},
+ Rest4};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'::=']}})
+ end;
+parse_ParameterizedObjectClassAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ typereference]}}).
+
+parse_ParameterizedObjectAssignment([{identifier,L1,Name}|Rest]) ->
+ {ParameterList,Rest2} = parse_ParameterList(Rest),
+ {Class,Rest3} = parse_DefinedObjectClass(Rest2),
+ case Rest3 of
+ [{'::=',_}|Rest4] ->
+ {Object,Rest5} = parse_Object(Rest4),
+ {#pobjectdef{pos=L1,name=Name,args=ParameterList,
+ class=Class,def=Object},Rest5};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'::=']}})
+%%% Other ->
+%%% throw(Other)
+ end;
+parse_ParameterizedObjectAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,identifier]}}).
+
+parse_ParameterizedObjectSetAssignment([{typereference,L1,Name}|Rest]) ->
+ {ParameterList,Rest2} = parse_ParameterList(Rest),
+ {Class,Rest3} = parse_DefinedObjectClass(Rest2),
+ case Rest3 of
+ [{'::=',_}|Rest4] ->
+ {ObjectSet,Rest5} = parse_ObjectSet(Rest4),
+ {#pobjectsetdef{pos=L1,name=Name,args=ParameterList,
+ class=Class,def=ObjectSet},Rest5};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'::=']}})
+%%% Other ->
+%%% throw(Other)
+ end;
+parse_ParameterizedObjectSetAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ typereference]}}).
+
+parse_ParameterList([{'{',_}|Rest]) ->
+ parse_ParameterList(Rest,[]);
+parse_ParameterList(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'{']}}).
+
+parse_ParameterList(Tokens,Acc) ->
+ {Parameter,Rest} = parse_Parameter(Tokens),
+ case Rest of
+ [{',',_}|Rest2] ->
+ parse_ParameterList(Rest2,[Parameter|Acc]);
+ [{'}',_}|Rest3] ->
+ {lists:reverse([Parameter|Acc]),Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,[',','}']]}})
+ end.
+
+parse_Parameter(Tokens) ->
+ Flist = [fun parse_ParamGovAndRef/1,
+ fun parse_Reference/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_ParamGovAndRef(Tokens) ->
+ {ParamGov,Rest} = parse_ParamGovernor(Tokens),
+ case Rest of
+ [{':',_}|Rest2] ->
+ {Ref,Rest3} = parse_Reference(Rest2),
+ {{ParamGov,Ref},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,':']}})
+ end.
+
+parse_ParamGovernor(Tokens) ->
+ Flist = [fun parse_Governor/1,
+ fun parse_Reference/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+% parse_ParameterizedReference(Tokens) ->
+% {Ref,Rest} = parse_Reference(Tokens),
+% case Rest of
+% [{'{',_},{'}',_}|Rest2] ->
+% {{ptref,Ref},Rest2};
+% _ ->
+% {{ptref,Ref},Rest}
+% end.
+
+parse_SimpleDefinedType([{typereference,L1,ModuleName},{'.',_},
+ {typereference,_,TypeName}|Rest]) ->
+ {#'Externaltypereference'{pos=L1,module=ModuleName,
+ type=TypeName},Rest};
+parse_SimpleDefinedType([Tref={typereference,_,_}|Rest]) ->
+% {#'Externaltypereference'{pos=L2,module=get(asn1_module),
+% type=TypeName},Rest};
+ {tref2Exttref(Tref),Rest};
+parse_SimpleDefinedType(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ [typereference,'typereference.typereference']]}}).
+
+parse_SimpleDefinedValue([{typereference,L1,ModuleName},{'.',_},
+ {identifier,_,Value}|Rest]) ->
+ {{simpledefinedvalue,#'Externalvaluereference'{pos=L1,module=ModuleName,
+ value=Value}},Rest};
+parse_SimpleDefinedValue([{identifier,L2,Value}|Rest]) ->
+ {{simpledefinedvalue,L2,Value},Rest};
+parse_SimpleDefinedValue(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ ['typereference.identifier',identifier]]}}).
+
+parse_ParameterizedType(Tokens) ->
+ {Type,Rest} = parse_SimpleDefinedType(Tokens),
+ {Params,Rest2} = parse_ActualParameterList(Rest),
+ {{pt,Type,Params},Rest2}.
+
+parse_ParameterizedValue(Tokens) ->
+ {Value,Rest} = parse_SimpleDefinedValue(Tokens),
+ {Params,Rest2} = parse_ActualParameterList(Rest),
+ {{pv,Value,Params},Rest2}.
+
+parse_ParameterizedObjectClass(Tokens) ->
+ {Type,Rest} = parse_DefinedObjectClass(Tokens),
+ {Params,Rest2} = parse_ActualParameterList(Rest),
+ {{poc,Type,Params},Rest2}.
+
+parse_ParameterizedObjectSet(Tokens) ->
+ {ObjectSet,Rest} = parse_DefinedObjectSet(Tokens),
+ {Params,Rest2} = parse_ActualParameterList(Rest),
+ {{pos,ObjectSet,Params},Rest2}.
+
+parse_ParameterizedObject(Tokens) ->
+ {Object,Rest} = parse_DefinedObject(Tokens),
+ {Params,Rest2} = parse_ActualParameterList(Rest),
+ {{po,Object,Params},Rest2}.
+
+parse_ActualParameterList([{'{',_}|Rest]) ->
+ parse_ActualParameterList(Rest,[]);
+parse_ActualParameterList(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'{']}}).
+
+parse_ActualParameterList(Tokens,Acc) ->
+ {Parameter,Rest} = parse_ActualParameter(Tokens),
+ case Rest of
+ [{',',_}|Rest2] ->
+ parse_ActualParameterList(Rest2,[Parameter|Acc]);
+ [{'}',_}|Rest3] ->
+ {lists:reverse([Parameter|Acc]),Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,[',','}']]}})
+%%% Other ->
+%%% throw(Other)
+ end.
+
+
+
+
+
+
+
+%-------------------------
+
+is_word(Token) ->
+ case not_allowed_word(Token) of
+ true -> false;
+ _ ->
+ if
+ atom(Token) ->
+ Item = atom_to_list(Token),
+ is_word(Item);
+ list(Token), length(Token) == 1 ->
+ check_one_char_word(Token);
+ list(Token) ->
+ [A|Rest] = Token,
+ case check_first(A) of
+ true ->
+ check_rest(Rest);
+ _ ->
+ false
+ end
+ end
+ end.
+
+not_allowed_word(Name) ->
+ lists:member(Name,["BIT",
+ "BOOLEAN",
+ "CHARACTER",
+ "CHOICE",
+ "EMBEDDED",
+ "END",
+ "ENUMERATED",
+ "EXTERNAL",
+ "FALSE",
+ "INSTANCE",
+ "INTEGER",
+ "INTERSECTION",
+ "MINUS-INFINITY",
+ "NULL",
+ "OBJECT",
+ "OCTET",
+ "PLUS-INFINITY",
+ "REAL",
+ "SEQUENCE",
+ "SET",
+ "TRUE",
+ "UNION"]).
+
+check_one_char_word([A]) when $A =< A, $Z >= A ->
+ true;
+check_one_char_word([_]) ->
+ false. %% unknown item in SyntaxList
+
+check_first(A) when $A =< A, $Z >= A ->
+ true;
+check_first(_) ->
+ false. %% unknown item in SyntaxList
+
+check_rest([R,R|_Rs]) when $- == R ->
+ false; %% two consecutive hyphens are not allowed in a word
+check_rest([R]) when $- == R ->
+ false; %% word cannot end with hyphen
+check_rest([R|Rs]) when $A==R; $-==R ->
+ check_rest(Rs);
+check_rest([]) ->
+ true;
+check_rest(_) ->
+ false.
+
+
+to_set(V) when list(V) ->
+ ordsets:list_to_set(V);
+to_set(V) ->
+ ordsets:list_to_set([V]).
+
+
+parse_AlternativeTypeLists(Tokens) ->
+ {AlternativeTypeList,Rest1} = parse_AlternativeTypeList(Tokens),
+ {ExtensionAndException,Rest2} =
+ case Rest1 of
+ [{',',_},{'...',L1},{'!',_}|Rest12] ->
+ {_,Rest13} = parse_ExceptionIdentification(Rest12),
+ %% Exception info is currently thrown away
+ {[#'EXTENSIONMARK'{pos=L1}],Rest13};
+ [{',',_},{'...',L1}|Rest12] ->
+ {[#'EXTENSIONMARK'{pos=L1}],Rest12};
+ _ ->
+ {[],Rest1}
+ end,
+ case ExtensionAndException of
+ [] ->
+ {AlternativeTypeList,Rest2};
+ _ ->
+ {ExtensionAddition,Rest3} =
+ case Rest2 of
+ [{',',_}|Rest23] ->
+ parse_ExtensionAdditionAlternativeList(Rest23);
+ _ ->
+ {[],Rest2}
+ end,
+ {OptionalExtensionMarker,Rest4} =
+ case Rest3 of
+ [{',',_},{'...',L3}|Rest31] ->
+ {[#'EXTENSIONMARK'{pos=L3}],Rest31};
+ _ ->
+ {[],Rest3}
+ end,
+ {AlternativeTypeList ++ ExtensionAndException ++ ExtensionAddition ++ OptionalExtensionMarker, Rest4}
+ end.
+
+
+parse_AlternativeTypeList(Tokens) ->
+ parse_AlternativeTypeList(Tokens,[]).
+
+parse_AlternativeTypeList(Tokens,Acc) ->
+ {NamedType,Rest} = parse_NamedType(Tokens),
+ case Rest of
+ [{',',_},Id = {identifier,_,_}|Rest2] ->
+ parse_AlternativeTypeList([Id|Rest2],[NamedType|Acc]);
+ _ ->
+ {lists:reverse([NamedType|Acc]),Rest}
+ end.
+
+
+
+parse_ExtensionAdditionAlternativeList(Tokens) ->
+ parse_ExtensionAdditionAlternativeList(Tokens,[]).
+
+parse_ExtensionAdditionAlternativeList(Tokens,Acc) ->
+ {Element,Rest0} =
+ case Tokens of
+ [{identifier,_,_}|_Rest] ->
+ parse_NamedType(Tokens);
+ [{'[[',_}|_] ->
+ parse_ExtensionAdditionAlternatives(Tokens)
+ end,
+ case Rest0 of
+ [{',',_}|Rest01] ->
+ parse_ExtensionAdditionAlternativeList(Rest01,[Element|Acc]);
+ _ ->
+ {lists:reverse([Element|Acc]),Rest0}
+ end.
+
+parse_ExtensionAdditionAlternatives([{'[[',_}|Rest]) ->
+ parse_ExtensionAdditionAlternatives(Rest,[]);
+parse_ExtensionAdditionAlternatives(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'[[']}}).
+
+parse_ExtensionAdditionAlternatives([Id = {identifier,_,_}|Rest],Acc) ->
+ {NamedType, Rest2} = parse_NamedType([Id|Rest]),
+ case Rest2 of
+ [{',',_}|Rest21] ->
+ parse_ExtensionAdditionAlternatives(Rest21,[NamedType|Acc]);
+ [{']]',_}|Rest21] ->
+ {lists:reverse(Acc),Rest21};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,[',',']]']]}})
+ end.
+
+parse_NamedType([{identifier,L1,Idname}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ {#'ComponentType'{pos=L1,name=Idname,typespec=Type,prop=mandatory},Rest2};
+parse_NamedType(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,identifier]}}).
+
+
+parse_ComponentTypeLists(Tokens) ->
+% Resulting tuple {ComponentTypeList,Rest1} is returned
+ case Tokens of
+ [{identifier,_,_}|_Rest0] ->
+ {Clist,Rest01} = parse_ComponentTypeList(Tokens),
+ case Rest01 of
+ [{',',_}|Rest02] ->
+ parse_ComponentTypeLists(Rest02,Clist);
+ _ ->
+ {Clist,Rest01}
+ end;
+ [{'COMPONENTS',_},{'OF',_}|_Rest] ->
+ {Clist,Rest01} = parse_ComponentTypeList(Tokens),
+ case Rest01 of
+ [{',',_}|Rest02] ->
+ parse_ComponentTypeLists(Rest02,Clist);
+ _ ->
+ {Clist,Rest01}
+ end;
+ _ ->
+ parse_ComponentTypeLists(Tokens,[])
+ end.
+
+parse_ComponentTypeLists([{'...',L1},{'!',_}|Rest],Clist1) ->
+ {_,Rest2} = parse_ExceptionIdentification(Rest),
+ %% Exception info is currently thrown away
+ parse_ComponentTypeLists2(Rest2,Clist1++[#'EXTENSIONMARK'{pos=L1}]);
+parse_ComponentTypeLists([{'...',L1}|Rest],Clist1) ->
+ parse_ComponentTypeLists2(Rest,Clist1++[#'EXTENSIONMARK'{pos=L1}]);
+parse_ComponentTypeLists(Tokens,Clist1) ->
+ {Clist1,Tokens}.
+
+
+parse_ComponentTypeLists2(Tokens,Clist1) ->
+ {ExtensionAddition,Rest2} =
+ case Tokens of
+ [{',',_}|Rest1] ->
+ parse_ExtensionAdditionList(Rest1);
+ _ ->
+ {[],Tokens}
+ end,
+ {OptionalExtensionMarker,Rest3} =
+ case Rest2 of
+ [{',',_},{'...',L2}|Rest21] ->
+ {[#'EXTENSIONMARK'{pos=L2}],Rest21};
+ _ ->
+ {[],Rest2}
+ end,
+ {RootComponentTypeList,Rest4} =
+ case Rest3 of
+ [{',',_}|Rest31] ->
+ parse_ComponentTypeList(Rest31);
+ _ ->
+ {[],Rest3}
+ end,
+ {Clist1 ++ ExtensionAddition ++ OptionalExtensionMarker ++ RootComponentTypeList, Rest4}.
+
+
+parse_ComponentTypeList(Tokens) ->
+ parse_ComponentTypeList(Tokens,[]).
+
+parse_ComponentTypeList(Tokens,Acc) ->
+ {ComponentType,Rest} = parse_ComponentType(Tokens),
+ case Rest of
+ [{',',_},Id = {identifier,_,_}|Rest2] ->
+ parse_ComponentTypeList([Id|Rest2],[ComponentType|Acc]);
+ [{',',_},C1={'COMPONENTS',_},C2={'OF',_}|Rest2] ->
+ parse_ComponentTypeList([C1,C2|Rest2],[ComponentType|Acc]);
+% _ ->
+% {lists:reverse([ComponentType|Acc]),Rest}
+ [{'}',_}|_] ->
+ {lists:reverse([ComponentType|Acc]),Rest};
+ [{',',_},{'...',_}|_] ->
+ {lists:reverse([ComponentType|Acc]),Rest};
+ _ ->
+ throw({asn1_error,
+ {get_line(hd(Tokens)),get(asn1_module),
+ [got,[get_token(hd(Rest)),get_token(hd(tl(Rest)))],
+ expected,['}',', identifier']]}})
+ end.
+
+
+parse_ExtensionAdditionList(Tokens) ->
+ parse_ExtensionAdditionList(Tokens,[]).
+
+parse_ExtensionAdditionList(Tokens,Acc) ->
+ {Element,Rest0} =
+ case Tokens of
+ [{identifier,_,_}|_Rest] ->
+ parse_ComponentType(Tokens);
+ [{'[[',_}|_] ->
+ parse_ExtensionAdditions(Tokens);
+ _ ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ [identifier,'[[']]}})
+ end,
+ case Rest0 of
+ [{',',_}|Rest01] ->
+ parse_ExtensionAdditionList(Rest01,[Element|Acc]);
+ _ ->
+ {lists:reverse([Element|Acc]),Rest0}
+ end.
+
+parse_ExtensionAdditions([{'[[',_}|Rest]) ->
+ parse_ExtensionAdditions(Rest,[]);
+parse_ExtensionAdditions(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'[[']}}).
+
+parse_ExtensionAdditions([Id = {identifier,_,_}|Rest],Acc) ->
+ {ComponentType, Rest2} = parse_ComponentType([Id|Rest]),
+ case Rest2 of
+ [{',',_}|Rest21] ->
+ parse_ExtensionAdditions(Rest21,[ComponentType|Acc]);
+ [{']]',_}|Rest21] ->
+ {lists:reverse(Acc),Rest21};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,[',',']]']]}})
+ end;
+parse_ExtensionAdditions(Tokens,_) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,identifier]}}).
+
+parse_ComponentType([{'COMPONENTS',_},{'OF',_}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ {{'COMPONENTS OF',Type},Rest2};
+parse_ComponentType(Tokens) ->
+ {NamedType,Rest} = parse_NamedType(Tokens),
+ case Rest of
+ [{'OPTIONAL',_}|Rest2] ->
+ {NamedType#'ComponentType'{prop='OPTIONAL'},Rest2};
+ [{'DEFAULT',_}|Rest2] ->
+ {Value,Rest21} = parse_Value(Rest2),
+ {NamedType#'ComponentType'{prop={'DEFAULT',Value}},Rest21};
+ _ ->
+ {NamedType,Rest}
+ end.
+
+
+
+parse_SignedNumber([{number,_,Value}|Rest]) ->
+ {Value,Rest};
+parse_SignedNumber([{'-',_},{number,_,Value}|Rest]) ->
+ {-Value,Rest};
+parse_SignedNumber(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ [number,'-number']]}}).
+
+parse_Enumerations(Tokens=[{identifier,_,_}|_Rest]) ->
+ parse_Enumerations(Tokens,[]);
+parse_Enumerations([H|_T]) ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,identifier]}}).
+
+parse_Enumerations(Tokens = [{identifier,_,_},{'(',_}|_Rest], Acc) ->
+ {NamedNumber,Rest2} = parse_NamedNumber(Tokens),
+ case Rest2 of
+ [{',',_}|Rest3] ->
+ parse_Enumerations(Rest3,[NamedNumber|Acc]);
+ _ ->
+ {lists:reverse([NamedNumber|Acc]),Rest2}
+ end;
+parse_Enumerations([{identifier,_,Id}|Rest], Acc) ->
+ case Rest of
+ [{',',_}|Rest2] ->
+ parse_Enumerations(Rest2,[Id|Acc]);
+ _ ->
+ {lists:reverse([Id|Acc]),Rest}
+ end;
+parse_Enumerations([{'...',_}|Rest], Acc) ->
+ case Rest of
+ [{',',_}|Rest2] ->
+ parse_Enumerations(Rest2,['EXTENSIONMARK'|Acc]);
+ _ ->
+ {lists:reverse(['EXTENSIONMARK'|Acc]),Rest}
+ end;
+parse_Enumerations([H|_T],_) ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,identifier]}}).
+
+parse_NamedNumberList(Tokens) ->
+ parse_NamedNumberList(Tokens,[]).
+
+parse_NamedNumberList(Tokens,Acc) ->
+ {NamedNum,Rest} = parse_NamedNumber(Tokens),
+ case Rest of
+ [{',',_}|Rest2] ->
+ parse_NamedNumberList(Rest2,[NamedNum|Acc]);
+ _ ->
+ {lists:reverse([NamedNum|Acc]),Rest}
+ end.
+
+parse_NamedNumber([{identifier,_,Name},{'(',_}|Rest]) ->
+ Flist = [fun parse_SignedNumber/1,
+ fun parse_DefinedValue/1],
+ case (catch parse_or(Rest,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ {NamedNum,[{')',_}|Rest2]} ->
+ {{'NamedNumber',Name,NamedNum},Rest2};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest)),get(asn1_module),
+ [got,get_token(hd(Rest)),expected,'NamedNumberList']}})
+ end;
+parse_NamedNumber(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,identifier]}}).
+
+
+parse_Tag([{'[',_}|Rest]) ->
+ {Class,Rest2} = parse_Class(Rest),
+ {ClassNumber,Rest3} =
+ case Rest2 of
+ [{number,_,Num}|Rest21] ->
+ {Num,Rest21};
+ _ ->
+ parse_DefinedValue(Rest2)
+ end,
+ case Rest3 of
+ [{']',_}|Rest4] ->
+ {#tag{class=Class,number=ClassNumber},Rest4};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest3)),get(asn1_module),
+ [got,get_token(hd(Rest3)),expected,']']}})
+ end;
+parse_Tag(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'[']}}).
+
+parse_Class([{'UNIVERSAL',_}|Rest]) ->
+ {'UNIVERSAL',Rest};
+parse_Class([{'APPLICATION',_}|Rest]) ->
+ {'APPLICATION',Rest};
+parse_Class([{'PRIVATE',_}|Rest]) ->
+ {'PRIVATE',Rest};
+parse_Class(Tokens) ->
+ {'CONTEXT',Tokens}.
+
+parse_Value(Tokens) ->
+ Flist = [fun parse_BuiltinValue/1,
+ fun parse_ValueFromObject/1,
+ fun parse_DefinedValue/1],
+
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_BuiltinValue([{bstring,_,Bstr}|Rest]) ->
+ {{bstring,Bstr},Rest};
+parse_BuiltinValue([{hstring,_,Hstr}|Rest]) ->
+ {{hstring,Hstr},Rest};
+parse_BuiltinValue([{'{',_},{'}',_}|Rest]) ->
+ {[],Rest};
+parse_BuiltinValue(Tokens = [{'{',_}|_Rest]) ->
+ Flist = [
+ fun parse_SequenceOfValue/1,
+ fun parse_SequenceValue/1,
+ fun parse_ObjectIdentifierValue/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end;
+parse_BuiltinValue([{identifier,_,IdName},{':',_}|Rest]) ->
+ {Value,Rest2} = parse_Value(Rest),
+ {{'CHOICE',{IdName,Value}},Rest2};
+parse_BuiltinValue([{'NULL',_}|Rest]) ->
+ {'NULL',Rest};
+parse_BuiltinValue([{'TRUE',_}|Rest]) ->
+ {true,Rest};
+parse_BuiltinValue([{'FALSE',_}|Rest]) ->
+ {false,Rest};
+parse_BuiltinValue([{'PLUS-INFINITY',_}|Rest]) ->
+ {'PLUS-INFINITY',Rest};
+parse_BuiltinValue([{'MINUS-INFINITY',_}|Rest]) ->
+ {'MINUS-INFINITY',Rest};
+parse_BuiltinValue([{cstring,_,Cstr}|Rest]) ->
+ {Cstr,Rest};
+parse_BuiltinValue([{number,_,Num}|Rest]) ->
+ {Num,Rest};
+parse_BuiltinValue([{'-',_},{number,_,Num}|Rest]) ->
+ {- Num,Rest};
+parse_BuiltinValue(Tokens) ->
+ parse_ObjectClassFieldValue(Tokens).
+
+%% Externalvaluereference
+parse_DefinedValue([{typereference,L1,Tname},{'.',_},{identifier,_,Idname}|Rest]) ->
+ {#'Externalvaluereference'{pos=L1,module=Tname,value=Idname},Rest};
+%% valuereference
+parse_DefinedValue([Id = {identifier,_,_}|Rest]) ->
+ {identifier2Extvalueref(Id),Rest};
+%% ParameterizedValue
+parse_DefinedValue(Tokens) ->
+ parse_ParameterizedValue(Tokens).
+
+
+parse_SequenceValue([{'{',_}|Tokens]) ->
+ parse_SequenceValue(Tokens,[]);
+parse_SequenceValue(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'{']}}).
+
+parse_SequenceValue([{identifier,_,IdName}|Rest],Acc) ->
+ {Value,Rest2} = parse_Value(Rest),
+ case Rest2 of
+ [{',',_}|Rest3] ->
+ parse_SequenceValue(Rest3,[{IdName,Value}|Acc]);
+ [{'}',_}|Rest3] ->
+ {lists:reverse([{IdName,Value}|Acc]),Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'}']}})
+ end;
+parse_SequenceValue(Tokens,_Acc) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,identifier]}}).
+
+parse_SequenceOfValue([{'{',_}|Tokens]) ->
+ parse_SequenceOfValue(Tokens,[]);
+parse_SequenceOfValue(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'{']}}).
+
+parse_SequenceOfValue(Tokens,Acc) ->
+ {Value,Rest2} = parse_Value(Tokens),
+ case Rest2 of
+ [{',',_}|Rest3] ->
+ parse_SequenceOfValue(Rest3,[Value|Acc]);
+ [{'}',_}|Rest3] ->
+ {lists:reverse([Value|Acc]),Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'}']}})
+ end.
+
+parse_ValueSetTypeAssignment([{typereference,L1,Name}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ case Rest2 of
+ [{'::=',_}|Rest3] ->
+ {ValueSet,Rest4} = parse_ValueSet(Rest3),
+ {#valuedef{pos=L1,name=Name,type=Type,value=ValueSet},Rest4};
+ [H|_T] ->
+ throw({asn1_error,{get_line(L1),get(asn1_module),
+ [got,get_token(H),expected,'::=']}})
+ end;
+parse_ValueSetTypeAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ typereference]}}).
+
+parse_ValueSet([{'{',_}|Rest]) ->
+ {Elems,Rest2} = parse_ElementSetSpecs(Rest),
+ case Rest2 of
+ [{'}',_}|Rest3] ->
+ {{valueset,Elems},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'}']}})
+ end;
+parse_ValueSet(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'{']}}).
+
+parse_ValueAssignment([{identifier,L1,IdName}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ case Rest2 of
+ [{'::=',_}|Rest3] ->
+ {Value,Rest4} = parse_Value(Rest3),
+ case lookahead_assignment(Rest4) of
+ ok ->
+ {#valuedef{pos=L1,name=IdName,type=Type,value=Value},Rest4};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'::=']}})
+ end;
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'::=']}})
+ end;
+parse_ValueAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,identifier]}}).
+
+%% SizeConstraint
+parse_SubtypeElements([{'SIZE',_}|Tokens]) ->
+ {Constraint,Rest} = parse_Constraint(Tokens),
+ {{'SizeConstraint',Constraint#constraint.c},Rest};
+%% PermittedAlphabet
+parse_SubtypeElements([{'FROM',_}|Tokens]) ->
+ {Constraint,Rest} = parse_Constraint(Tokens),
+ {{'PermittedAlphabet',Constraint#constraint.c},Rest};
+%% InnerTypeConstraints
+parse_SubtypeElements([{'WITH',_},{'COMPONENT',_}|Tokens]) ->
+ {Constraint,Rest} = parse_Constraint(Tokens),
+ {{'WITH COMPONENT',Constraint},Rest};
+parse_SubtypeElements([{'WITH',_},{'COMPONENTS',_},{'{',_},{'...',_},{',',_}|Tokens]) ->
+ {Constraint,Rest} = parse_TypeConstraints(Tokens),
+ case Rest of
+ [{'}',_}|Rest2] ->
+ {{'WITH COMPONENTS',{'PartialSpecification',Constraint}},Rest2};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest)),get(asn1_module),
+ [got,get_token(hd(Rest)),expected,'}']}})
+ end;
+parse_SubtypeElements([{'WITH',_},{'COMPONENTS',_},{'{',_}|Tokens]) ->
+ {Constraint,Rest} = parse_TypeConstraints(Tokens),
+ case Rest of
+ [{'}',_}|Rest2] ->
+ {{'WITH COMPONENTS',{'FullSpecification',Constraint}},Rest2};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest)),get(asn1_module),
+ [got,get_token(hd(Rest)),expected,'}']}})
+ end;
+%% SingleValue
+%% ContainedSubtype
+%% ValueRange
+%% TypeConstraint
+parse_SubtypeElements(Tokens) ->
+ Flist = [fun parse_ContainedSubtype/1,
+ fun parse_Value/1,
+ fun([{'MIN',_}|T]) -> {'MIN',T} end,
+ fun parse_Type/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ {asn1_error,Reason} ->
+ throw(Reason);
+ Result = {Val,_} when record(Val,type) ->
+ Result;
+ {Lower,[{'..',_}|Rest]} ->
+ {Upper,Rest2} = parse_UpperEndpoint(Rest),
+ {{'ValueRange',{Lower,Upper}},Rest2};
+ {Lower,[{'<',_},{'..',_}|Rest]} ->
+ {Upper,Rest2} = parse_UpperEndpoint(Rest),
+ {{'ValueRange',{{gt,Lower},Upper}},Rest2};
+ {Res={'ContainedSubtype',_Type},Rest} ->
+ {Res,Rest};
+ {Value,Rest} ->
+ {{'SingleValue',Value},Rest}
+ end.
+
+parse_ContainedSubtype([{'INCLUDES',_}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ {{'ContainedSubtype',Type},Rest2};
+parse_ContainedSubtype(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'INCLUDES']}}).
+%%parse_ContainedSubtype(Tokens) -> %this option is moved to parse_SubtypeElements
+%% parse_Type(Tokens).
+
+parse_UpperEndpoint([{'<',_}|Rest]) ->
+ parse_UpperEndpoint(lt,Rest);
+parse_UpperEndpoint(Tokens) ->
+ parse_UpperEndpoint(false,Tokens).
+
+parse_UpperEndpoint(Lt,Tokens) ->
+ Flist = [ fun([{'MAX',_}|T]) -> {'MAX',T} end,
+ fun parse_Value/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ {Value,Rest2} when Lt == lt ->
+ {{lt,Value},Rest2};
+ {Value,Rest2} ->
+ {Value,Rest2}
+ end.
+
+parse_TypeConstraints(Tokens) ->
+ parse_TypeConstraints(Tokens,[]).
+
+parse_TypeConstraints([{identifier,_,_}|Rest],Acc) ->
+ {ComponentConstraint,Rest2} = parse_ComponentConstraint(Rest),
+ case Rest2 of
+ [{',',_}|Rest3] ->
+ parse_TypeConstraints(Rest3,[ComponentConstraint|Acc]);
+ _ ->
+ {lists:reverse([ComponentConstraint|Acc]),Rest2}
+ end;
+parse_TypeConstraints([H|_T],_) ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,identifier]}}).
+
+parse_ComponentConstraint(Tokens = [{'(',_}|_Rest]) ->
+ {ValueConstraint,Rest2} = parse_Constraint(Tokens),
+ {PresenceConstraint,Rest3} = parse_PresenceConstraint(Rest2),
+ {{ValueConstraint,PresenceConstraint},Rest3};
+parse_ComponentConstraint(Tokens) ->
+ {PresenceConstraint,Rest} = parse_PresenceConstraint(Tokens),
+ {{asn1_empty,PresenceConstraint},Rest}.
+
+parse_PresenceConstraint([{'PRESENT',_}|Rest]) ->
+ {'PRESENT',Rest};
+parse_PresenceConstraint([{'ABSENT',_}|Rest]) ->
+ {'ABSENT',Rest};
+parse_PresenceConstraint([{'OPTIONAL',_}|Rest]) ->
+ {'OPTIONAL',Rest};
+parse_PresenceConstraint(Tokens) ->
+ {asn1_empty,Tokens}.
+
+
+merge_constraints({Rlist,ExtList}) -> % extensionmarker in constraint
+ {merge_constraints(Rlist,[],[]),
+ merge_constraints(ExtList,[],[])};
+
+merge_constraints(Clist) ->
+ merge_constraints(Clist, [], []).
+
+merge_constraints([Ch|Ct],Cacc, Eacc) ->
+ NewEacc = case Ch#constraint.e of
+ undefined -> Eacc;
+ E -> [E|Eacc]
+ end,
+ merge_constraints(Ct,[fixup_constraint(Ch#constraint.c)|Cacc],NewEacc);
+
+merge_constraints([],Cacc,[]) ->
+%% lists:flatten(Cacc);
+ lists:reverse(Cacc);
+merge_constraints([],Cacc,Eacc) ->
+%% lists:flatten(Cacc) ++ [{'Errors',Eacc}].
+ lists:reverse(Cacc) ++ [{'Errors',Eacc}].
+
+fixup_constraint(C) ->
+ case C of
+ {'SingleValue',SubType} when element(1,SubType) == 'ContainedSubtype' ->
+ SubType;
+ {'SingleValue',V} when list(V) ->
+ C;
+ %% [C,{'ValueRange',{lists:min(V),lists:max(V)}}];
+ %% bug, turns wrong when an element in V is a reference to a defined value
+ {'PermittedAlphabet',{'SingleValue',V}} when list(V) ->
+ %%sort and remove duplicates
+ V2 = {'SingleValue',
+ ordsets:list_to_set(lists:flatten(V))},
+ {'PermittedAlphabet',V2};
+ {'PermittedAlphabet',{'SingleValue',V}} ->
+ V2 = {'SingleValue',[V]},
+ {'PermittedAlphabet',V2};
+ {'SizeConstraint',Sc} ->
+ {'SizeConstraint',fixup_size_constraint(Sc)};
+
+ List when list(List) -> %% In This case maybe a union or intersection
+ [fixup_constraint(Xc)||Xc <- List];
+ Other ->
+ Other
+ end.
+
+fixup_size_constraint({'ValueRange',{Lb,Ub}}) ->
+ {Lb,Ub};
+fixup_size_constraint({{'ValueRange',R},[]}) ->
+ {R,[]};
+fixup_size_constraint({[],{'ValueRange',R}}) ->
+ {[],R};
+fixup_size_constraint({{'ValueRange',R1},{'ValueRange',R2}}) ->
+ {R1,R2};
+fixup_size_constraint({'SingleValue',[Sv]}) ->
+ fixup_size_constraint({'SingleValue',Sv});
+fixup_size_constraint({'SingleValue',L}) when list(L) ->
+ ordsets:list_to_set(L);
+fixup_size_constraint({'SingleValue',L}) ->
+ {L,L};
+fixup_size_constraint({C1,C2}) ->
+ {fixup_size_constraint(C1), fixup_size_constraint(C2)}.
+
+get_line({_,Pos,Token}) when integer(Pos),atom(Token) ->
+ Pos;
+get_line({Token,Pos}) when integer(Pos),atom(Token) ->
+ Pos;
+get_line(_) ->
+ undefined.
+
+get_token({_,Pos,Token}) when integer(Pos),atom(Token) ->
+ Token;
+get_token({'$end',Pos}) when integer(Pos) ->
+ undefined;
+get_token({Token,Pos}) when integer(Pos),atom(Token) ->
+ Token;
+get_token(_) ->
+ undefined.
+
+prioritize_error(ErrList) ->
+ case lists:keymember(asn1_error,1,ErrList) of
+ false -> % only asn1_assignment_error -> take the last
+ lists:last(ErrList);
+ true -> % contains errors from deeper in a Type
+ NewErrList = [_Err={_,_}|_RestErr] =
+ lists:filter(fun({asn1_error,_})->true;(_)->false end,
+ ErrList),
+ SplitErrs =
+ lists:splitwith(fun({_,X})->
+ case element(1,X) of
+ Int when integer(Int) -> true;
+ _ -> false
+ end
+ end,
+ NewErrList),
+ case SplitErrs of
+ {[],UndefPosErrs} -> % if no error with Positon exists
+ lists:last(UndefPosErrs);
+ {IntPosErrs,_} ->
+ IntPosReasons = lists:map(fun(X)->element(2,X) end,IntPosErrs),
+ SortedReasons = lists:keysort(1,IntPosReasons),
+ {asn1_error,lists:last(SortedReasons)}
+ end
+ end.
+
+%% most_prio_error([H={_,Reason}|T],Atom,Err) when atom(Atom) ->
+%% most_prio_error(T,element(1,Reason),H);
+%% most_prio_error([H={_,Reason}|T],Greatest,Err) ->
+%% case element(1,Reason) of
+%% Pos when integer(Pos),Pos>Greatest ->
+%% most_prio_error(
+
+
+tref2Exttref(#typereference{pos=Pos,val=Name}) ->
+ #'Externaltypereference'{pos=Pos,
+ module=get(asn1_module),
+ type=Name}.
+
+tref2Exttref(Pos,Name) ->
+ #'Externaltypereference'{pos=Pos,
+ module=get(asn1_module),
+ type=Name}.
+
+identifier2Extvalueref(#identifier{pos=Pos,val=Name}) ->
+ #'Externalvaluereference'{pos=Pos,
+ module=get(asn1_module),
+ value=Name}.
+
+%% lookahead_assignment/1 checks that the next sequence of tokens
+%% in Token contain a valid assignment or the
+%% 'END' token. Otherwise an exception is thrown.
+lookahead_assignment([{'END',_}|_Rest]) ->
+ ok;
+lookahead_assignment(Tokens) ->
+ parse_Assignment(Tokens),
+ ok.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_pretty_format.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_pretty_format.erl
new file mode 100644
index 0000000000..99dd246d5c
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_pretty_format.erl
@@ -0,0 +1,197 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_pretty_format.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+
+%% usage: pretty_format:term(Term) -> PNF list of characters
+%%
+%% Note: this is usually used in expressions like:
+%% io:format('~s\n',[pretty_format:term(Term)]).
+%%
+%% Uses the following simple heuristics
+%%
+%% 1) Simple tuples are printed across the page
+%% (Simple means *all* the elements are "flat")
+%% 2) The Complex tuple {Arg1, Arg2, Arg3,....} is printed thus:
+%% {Arg1,
+%% Arg2,
+%% Arg3,
+%% ...}
+%% 3) Lists are treated as for tuples
+%% 4) Lists of printable characters are treated as strings
+%%
+%% This method seems to work reasonable well for {Tag, ...} type
+%% data structures
+
+-module(asn1ct_pretty_format).
+
+-export([term/1]).
+
+-import(io_lib, [write/1, write_string/1]).
+
+term(Term) ->
+ element(2, term(Term, 0)).
+
+%%______________________________________________________________________
+%% pretty_format:term(Term, Indent} -> {Indent', Chars}
+%% Format -- use to indent the *next* line
+%% Note: Indent' is a new indentaion level (sometimes printing
+%% the next line to need an "extra" indent!).
+
+term([], Indent) ->
+ {Indent, [$[,$]]};
+term(L, Indent) when is_list(L) ->
+ case is_string(L) of
+ true ->
+ {Indent, write_string(L)};
+ false ->
+ case complex_list(L) of
+ true ->
+ write_complex_list(L, Indent);
+ false ->
+ write_simple_list(L, Indent)
+ end
+ end;
+term(T, Indent) when is_tuple(T) ->
+ case complex_tuple(T) of
+ true ->
+ write_complex_tuple(T, Indent);
+ false ->
+ write_simple_tuple(T, Indent)
+ end;
+term(A, Indent) ->
+ {Indent, write(A)}.
+
+%%______________________________________________________________________
+%% write_simple_list([H|T], Indent) -> {Indent', Chars}
+
+write_simple_list([H|T], Indent) ->
+ {_, S1} = term(H, Indent),
+ {_, S2} = write_simple_list_tail(T, Indent),
+ {Indent, [$[,S1|S2]}.
+
+write_simple_list_tail([H|T], Indent) ->
+ {_, S1} = term(H, Indent),
+ {_, S2} = write_simple_list_tail(T, Indent),
+ {Indent, [$,,S1| S2]};
+write_simple_list_tail([], Indent) ->
+ {Indent, "]"};
+write_simple_list_tail(Other, Indent) ->
+ {_, S} = term(Other, Indent),
+ {Indent, [$|,S,$]]}.
+
+%%______________________________________________________________________
+%% write_complex_list([H|T], Indent) -> {Indent', Chars}
+
+write_complex_list([H|T], Indent) ->
+ {I1, S1} = term(H, Indent+1),
+ {_, S2} = write_complex_list_tail(T, I1),
+ {Indent, [$[,S1|S2]}.
+
+write_complex_list_tail([H|T], Indent) ->
+ {I1, S1} = term(H, Indent),
+ {_, S2} = write_complex_list_tail(T, I1),
+ {Indent, [$,,nl_indent(Indent),S1,S2]};
+write_complex_list_tail([], Indent) ->
+ {Indent, "]"};
+write_complex_list_tail(Other, Indent) ->$,,
+ {_, S} = term(Other, Indent),
+ {Indent, [$|,S,$]]}.
+
+%%______________________________________________________________________
+%% complex_list(List) -> true | false
+%% returns true if the list is complex otherwise false
+
+complex_list([]) ->
+ false;
+complex_list([H|T]) when is_number(H); is_atom(H) ->
+ complex_list(T);
+complex_list([H|T]) ->
+ case is_string(H) of
+ true ->
+ complex_list(T);
+ false ->
+ true
+ end;
+complex_list(_) -> true.
+
+%%______________________________________________________________________
+%% complex_tuple(Tuple) -> true | false
+%% returns true if the tuple is complex otherwise false
+
+complex_tuple(T) ->
+ complex_list(tuple_to_list(T)).
+
+%%______________________________________________________________________
+%% write_simple_tuple(Tuple, Indent} -> {Indent', Chars}
+
+write_simple_tuple({}, Indent) ->
+ {Indent, "{}"};
+write_simple_tuple(Tuple, Indent) ->
+ {_, S} = write_simple_tuple_args(tuple_to_list(Tuple), Indent),
+ {Indent, [${, S, $}]}.
+
+write_simple_tuple_args([X], Indent) ->
+ term(X, Indent);
+write_simple_tuple_args([H|T], Indent) ->
+ {_, SH} = term(H, Indent),
+ {_, ST} = write_simple_tuple_args(T, Indent),
+ {Indent, [SH, $,, ST]}.
+
+%%______________________________________________________________________
+%% write_complex_tuple(Tuple, Indent} -> {Indent', Chars}
+
+write_complex_tuple(Tuple, Indent) ->
+ [H|T] = tuple_to_list(Tuple),
+ {I1, SH} = term(H, Indent+2),
+ {_, ST} = write_complex_tuple_args(T, I1),
+ {Indent, [${, SH, ST, $}]}.
+
+write_complex_tuple_args([X], Indent) ->
+ {_, S} = term(X, Indent),
+ {Indent, [$,, nl_indent(Indent), S]};
+write_complex_tuple_args([H|T], Indent) ->
+ {I1, SH} = term(H, Indent),
+ {_, ST} = write_complex_tuple_args(T, I1),
+ {Indent, [$,, nl_indent(Indent) , SH, ST]};
+write_complex_tuple_args([], Indent) ->
+ {Indent, []}.
+
+%%______________________________________________________________________
+%% utilities
+
+nl_indent(I) when I >= 0 ->
+ ["\n"|indent(I)];
+nl_indent(_) ->
+ [$\s].
+
+indent(I) when I >= 8 ->
+ [$\t|indent(I-8)];
+indent(I) when I > 0 ->
+ [$\s|indent(I-1)];
+indent(_) ->
+ [].
+
+is_string([9|T]) ->
+ is_string(T);
+is_string([10|T]) ->
+ is_string(T);
+is_string([H|T]) when H >31, H < 127 ->
+ is_string(T);
+is_string([]) ->
+ true;
+is_string(_) ->
+ false.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_tok.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_tok.erl
new file mode 100644
index 0000000000..b5ccc4a5d2
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_tok.erl
@@ -0,0 +1,351 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_tok.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1ct_tok).
+
+%% Tokenize ASN.1 code (input to parser generated with yecc)
+
+-export([get_name/2,tokenise/2, file/1]).
+
+
+file(File) ->
+ case file:open(File, [read]) of
+ {error, Reason} ->
+ {error,{File,file:format_error(Reason)}};
+ {ok,Stream} ->
+ process0(Stream)
+ end.
+
+process0(Stream) ->
+ process(Stream,0,[]).
+
+process(Stream,Lno,R) ->
+ process(io:get_line(Stream, ''), Stream,Lno+1,R).
+
+process(eof, Stream,Lno,R) ->
+ file:close(Stream),
+ lists:flatten(lists:reverse([{'$end',Lno}|R]));
+
+
+process(L, Stream,Lno,R) when list(L) ->
+ %%io:format('read:~s',[L]),
+ case catch tokenise(L,Lno) of
+ {'ERR',Reason} ->
+ io:format("Tokeniser error on line: ~w ~w~n",[Lno,Reason]),
+ exit(0);
+ T ->
+ %%io:format('toks:~w~n',[T]),
+ process(Stream,Lno,[T|R])
+ end.
+
+
+tokenise([H|T],Lno) when $a =< H , H =< $z ->
+ {X, T1} = get_name(T, [H]),
+ [{identifier,Lno, list_to_atom(X)}|tokenise(T1,Lno)];
+
+tokenise([$&,H|T],Lno) when $A =< H , H =< $Z ->
+ {Y, T1} = get_name(T, [H]),
+ X = list_to_atom(Y),
+ [{typefieldreference, Lno, X} | tokenise(T1, Lno)];
+
+tokenise([$&,H|T],Lno) when $a =< H , H =< $z ->
+ {Y, T1} = get_name(T, [H]),
+ X = list_to_atom(Y),
+ [{valuefieldreference, Lno, X} | tokenise(T1, Lno)];
+
+tokenise([H|T],Lno) when $A =< H , H =< $Z ->
+ {Y, T1} = get_name(T, [H]),
+ X = list_to_atom(Y),
+ case reserved_word(X) of
+ true ->
+ [{X,Lno}|tokenise(T1,Lno)];
+ false ->
+ [{typereference,Lno,X}|tokenise(T1,Lno)];
+ rstrtype ->
+ [{restrictedcharacterstringtype,Lno,X}|tokenise(T1,Lno)]
+ end;
+
+tokenise([$-,H|T],Lno) when $0 =< H , H =< $9 ->
+ {X, T1} = get_number(T, [H]),
+ [{number,Lno,-1 * list_to_integer(X)}|tokenise(T1,Lno)];
+
+tokenise([H|T],Lno) when $0 =< H , H =< $9 ->
+ {X, T1} = get_number(T, [H]),
+ [{number,Lno,list_to_integer(X)}|tokenise(T1,Lno)];
+
+tokenise([$-,$-|T],Lno) ->
+ tokenise(skip_comment(T),Lno);
+tokenise([$:,$:,$=|T],Lno) ->
+ [{'::=',Lno}|tokenise(T,Lno)];
+
+tokenise([$'|T],Lno) ->
+ case catch collect_quoted(T,Lno,[]) of
+ {'ERR',_} ->
+ throw({'ERR','bad_quote'});
+ {Thing, T1} ->
+ [Thing|tokenise(T1,Lno)]
+ end;
+
+tokenise([$"|T],Lno) ->
+ collect_string(T,Lno);
+
+tokenise([${|T],Lno) ->
+ [{'{',Lno}|tokenise(T,Lno)];
+
+tokenise([$}|T],Lno) ->
+ [{'}',Lno}|tokenise(T,Lno)];
+
+tokenise([$]|T],Lno) ->
+ [{']',Lno}|tokenise(T,Lno)];
+
+tokenise([$[|T],Lno) ->
+ [{'[',Lno}|tokenise(T,Lno)];
+
+tokenise([$,|T],Lno) ->
+ [{',',Lno}|tokenise(T,Lno)];
+
+tokenise([$(|T],Lno) ->
+ [{'(',Lno}|tokenise(T,Lno)];
+tokenise([$)|T],Lno) ->
+ [{')',Lno}|tokenise(T,Lno)];
+
+tokenise([$.,$.,$.|T],Lno) ->
+ [{'...',Lno}|tokenise(T,Lno)];
+
+tokenise([$.,$.|T],Lno) ->
+ [{'..',Lno}|tokenise(T,Lno)];
+
+tokenise([$.|T],Lno) ->
+ [{'.',Lno}|tokenise(T,Lno)];
+tokenise([$^|T],Lno) ->
+ [{'^',Lno}|tokenise(T,Lno)];
+tokenise([$!|T],Lno) ->
+ [{'!',Lno}|tokenise(T,Lno)];
+tokenise([$||T],Lno) ->
+ [{'|',Lno}|tokenise(T,Lno)];
+
+
+tokenise([H|T],Lno) ->
+ case white_space(H) of
+ true ->
+ tokenise(T,Lno);
+ false ->
+ [{list_to_atom([H]),Lno}|tokenise(T,Lno)]
+ end;
+tokenise([],_) ->
+ [].
+
+
+collect_string(L,Lno) ->
+ collect_string(L,Lno,[]).
+
+collect_string([],_,_) ->
+ throw({'ERR','bad_quote found eof'});
+
+collect_string([H|T],Lno,Str) ->
+ case H of
+ $" ->
+ [{cstring,1,lists:reverse(Str)}|tokenise(T,Lno)];
+ Ch ->
+ collect_string(T,Lno,[Ch|Str])
+ end.
+
+
+
+% is letters digits hyphens
+% hypen is not the last character. Hypen hyphen is NOT allowed
+%
+% ::=
+
+get_name([$-,Char|T], L) ->
+ case isalnum(Char) of
+ true ->
+ get_name(T,[Char,$-|L]);
+ false ->
+ {lists:reverse(L),[$-,Char|T]}
+ end;
+get_name([$-|T], L) ->
+ {lists:reverse(L),[$-|T]};
+get_name([Char|T], L) ->
+ case isalnum(Char) of
+ true ->
+ get_name(T,[Char|L]);
+ false ->
+ {lists:reverse(L),[Char|T]}
+ end;
+get_name([], L) ->
+ {lists:reverse(L), []}.
+
+
+isalnum(H) when $A =< H , H =< $Z ->
+ true;
+isalnum(H) when $a =< H , H =< $z ->
+ true;
+isalnum(H) when $0 =< H , H =< $9 ->
+ true;
+isalnum(_) ->
+ false.
+
+isdigit(H) when $0 =< H , H =< $9 ->
+ true;
+isdigit(_) ->
+ false.
+
+white_space(9) -> true;
+white_space(10) -> true;
+white_space(13) -> true;
+white_space(32) -> true;
+white_space(_) -> false.
+
+
+get_number([H|T], L) ->
+ case isdigit(H) of
+ true ->
+ get_number(T, [H|L]);
+ false ->
+ {lists:reverse(L), [H|T]}
+ end;
+get_number([], L) ->
+ {lists:reverse(L), []}.
+
+skip_comment([]) ->
+ [];
+skip_comment([$-,$-|T]) ->
+ T;
+skip_comment([_|T]) ->
+ skip_comment(T).
+
+collect_quoted([$',$B|T],Lno, L) ->
+ case check_bin(L) of
+ true ->
+ {{bstring,Lno, lists:reverse(L)}, T};
+ false ->
+ throw({'ERR',{invalid_binary_number, lists:reverse(L)}})
+ end;
+collect_quoted([$',$H|T],Lno, L) ->
+ case check_hex(L) of
+ true ->
+ {{hstring,Lno, lists:reverse(L)}, T};
+ false ->
+ throw({'ERR',{invalid_binary_number, lists:reverse(L)}})
+ end;
+collect_quoted([H|T], Lno, L) ->
+ collect_quoted(T, Lno,[H|L]);
+collect_quoted([], _, _) -> % This should be allowed FIX later
+ throw({'ERR',{eol_in_token}}).
+
+check_bin([$0|T]) ->
+ check_bin(T);
+check_bin([$1|T]) ->
+ check_bin(T);
+check_bin([]) ->
+ true;
+check_bin(_) ->
+ false.
+
+check_hex([H|T]) when $0 =< H , H =< $9 ->
+ check_hex(T);
+check_hex([H|T]) when $A =< H , H =< $F ->
+ check_hex(T);
+check_hex([]) ->
+ true;
+check_hex(_) ->
+ false.
+
+
+%% reserved_word(A) -> true|false|rstrtype
+%% A = atom()
+%% returns true if A is a reserved ASN.1 word
+%% returns false if A is not a reserved word
+%% returns rstrtype if A is a reserved word in the group
+%% RestrictedCharacterStringType
+reserved_word('ABSENT') -> true;
+%reserved_word('ABSTRACT-SYNTAX') -> true; % impl as predef item
+reserved_word('ALL') -> true;
+reserved_word('ANY') -> true;
+reserved_word('APPLICATION') -> true;
+reserved_word('AUTOMATIC') -> true;
+reserved_word('BEGIN') -> true;
+reserved_word('BIT') -> true;
+reserved_word('BMPString') -> rstrtype;
+reserved_word('BOOLEAN') -> true;
+reserved_word('BY') -> true;
+reserved_word('CHARACTER') -> true;
+reserved_word('CHOICE') -> true;
+reserved_word('CLASS') -> true;
+reserved_word('COMPONENT') -> true;
+reserved_word('COMPONENTS') -> true;
+reserved_word('CONSTRAINED') -> true;
+reserved_word('DEFAULT') -> true;
+reserved_word('DEFINED') -> true;
+reserved_word('DEFINITIONS') -> true;
+reserved_word('EMBEDDED') -> true;
+reserved_word('END') -> true;
+reserved_word('ENUMERATED') -> true;
+reserved_word('EXCEPT') -> true;
+reserved_word('EXPLICIT') -> true;
+reserved_word('EXPORTS') -> true;
+reserved_word('EXTERNAL') -> true;
+reserved_word('FALSE') -> true;
+reserved_word('FROM') -> true;
+reserved_word('GeneralizedTime') -> true;
+reserved_word('GeneralString') -> rstrtype;
+reserved_word('GraphicString') -> rstrtype;
+reserved_word('IA5String') -> rstrtype;
+% reserved_word('TYPE-IDENTIFIER') -> true; % impl as predef item
+reserved_word('IDENTIFIER') -> true;
+reserved_word('IMPLICIT') -> true;
+reserved_word('IMPORTS') -> true;
+reserved_word('INCLUDES') -> true;
+reserved_word('INSTANCE') -> true;
+reserved_word('INTEGER') -> true;
+reserved_word('INTERSECTION') -> true;
+reserved_word('ISO646String') -> rstrtype;
+reserved_word('MAX') -> true;
+reserved_word('MIN') -> true;
+reserved_word('MINUS-INFINITY') -> true;
+reserved_word('NULL') -> true;
+reserved_word('NumericString') -> rstrtype;
+reserved_word('OBJECT') -> true;
+reserved_word('ObjectDescriptor') -> true;
+reserved_word('OCTET') -> true;
+reserved_word('OF') -> true;
+reserved_word('OPTIONAL') -> true;
+reserved_word('PDV') -> true;
+reserved_word('PLUS-INFINITY') -> true;
+reserved_word('PRESENT') -> true;
+reserved_word('PrintableString') -> rstrtype;
+reserved_word('PRIVATE') -> true;
+reserved_word('REAL') -> true;
+reserved_word('SEQUENCE') -> true;
+reserved_word('SET') -> true;
+reserved_word('SIZE') -> true;
+reserved_word('STRING') -> true;
+reserved_word('SYNTAX') -> true;
+reserved_word('T61String') -> rstrtype;
+reserved_word('TAGS') -> true;
+reserved_word('TeletexString') -> rstrtype;
+reserved_word('TRUE') -> true;
+reserved_word('UNION') -> true;
+reserved_word('UNIQUE') -> true;
+reserved_word('UNIVERSAL') -> true;
+reserved_word('UniversalString') -> rstrtype;
+reserved_word('UTCTime') -> true;
+reserved_word('VideotexString') -> rstrtype;
+reserved_word('VisibleString') -> rstrtype;
+reserved_word('WITH') -> true;
+reserved_word(_) -> false.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_value.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_value.erl
new file mode 100644
index 0000000000..3d366a1a27
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_value.erl
@@ -0,0 +1,330 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_value.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1ct_value).
+
+%% Generate Erlang values for ASN.1 types.
+%% The value is randomized within it's constraints
+
+-include("asn1_records.hrl").
+%-compile(export_all).
+
+-export([get_type/3]).
+
+
+
+%% Generate examples of values ******************************
+%%****************************************x
+
+
+get_type(M,Typename,Tellname) ->
+ case asn1_db:dbget(M,Typename) of
+ undefined ->
+ {asn1_error,{not_found,{M,Typename}}};
+ Tdef when record(Tdef,typedef) ->
+ Type = Tdef#typedef.typespec,
+ get_type(M,[Typename],Type,Tellname);
+ Err ->
+ {asn1_error,{other,Err}}
+ end.
+
+get_type(M,Typename,Type,Tellname) when record(Type,type) ->
+ InnerType = get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ get_type(Emod,Etype,Tellname);
+ {_,user} ->
+ case Tellname of
+ yes -> {Typename,get_type(M,InnerType,no)};
+ no -> get_type(M,InnerType,no)
+ end;
+ {notype,_} ->
+ true;
+ {primitive,bif} ->
+ get_type_prim(Type);
+ 'ASN1_OPEN_TYPE' ->
+ case Type#type.constraint of
+ [#'Externaltypereference'{type=TrefConstraint}] ->
+ get_type(M,TrefConstraint,no);
+ _ ->
+ "open_type"
+ end;
+ {constructed,bif} ->
+ get_type_constructed(M,Typename,InnerType,Type)
+ end;
+get_type(M,Typename,#'ComponentType'{name = Name,typespec = Type},_) ->
+ get_type(M,[Name|Typename],Type,no);
+get_type(_,_,_,_) -> % 'EXTENSIONMARK'
+ undefined.
+
+get_inner(A) when atom(A) -> A;
+get_inner(Ext) when record(Ext,'Externaltypereference') -> Ext;
+get_inner({typereference,_Pos,Name}) -> Name;
+get_inner(T) when tuple(T) ->
+ case asn1ct_gen:get_inner(T) of
+ {fixedtypevaluefield,_,Type} ->
+ Type#type.def;
+ {typefield,_FieldName} ->
+ 'ASN1_OPEN_TYPE';
+ Other ->
+ Other
+ end.
+%%get_inner(T) when tuple(T) -> element(1,T).
+
+
+
+get_type_constructed(M,Typename,InnerType,D) when record(D,type) ->
+ case InnerType of
+ 'SET' ->
+ get_sequence(M,Typename,D);
+ 'SEQUENCE' ->
+ get_sequence(M,Typename,D);
+ 'CHOICE' ->
+ get_choice(M,Typename,D);
+ 'SEQUENCE OF' ->
+ {_,Type} = D#type.def,
+ NameSuffix = asn1ct_gen:constructed_suffix(InnerType,Type#type.def),
+ get_sequence_of(M,Typename,D,NameSuffix);
+ 'SET OF' ->
+ {_,Type} = D#type.def,
+ NameSuffix = asn1ct_gen:constructed_suffix(InnerType,Type#type.def),
+ get_sequence_of(M,Typename,D,NameSuffix);
+ _ ->
+ exit({nyi,InnerType})
+ end.
+
+get_sequence(M,Typename,Type) ->
+ {_SEQorSET,CompList} =
+ case Type#type.def of
+ #'SEQUENCE'{components=Cl} -> {'SEQUENCE',Cl};
+ #'SET'{components=Cl} -> {'SET',Cl}
+ end,
+ case get_components(M,Typename,CompList) of
+ [] ->
+ {list_to_atom(asn1ct_gen:list2rname(Typename))};
+ C ->
+ list_to_tuple([list_to_atom(asn1ct_gen:list2rname(Typename))|C])
+ end.
+
+get_components(M,Typename,{Root,Ext}) ->
+ get_components(M,Typename,Root++Ext);
+
+%% Should enhance this *** HERE *** with proper handling of extensions
+
+get_components(M,Typename,[H|T]) ->
+ [get_type(M,Typename,H,no)|
+ get_components(M,Typename,T)];
+get_components(_,_,[]) ->
+ [].
+
+get_choice(M,Typename,Type) ->
+ {'CHOICE',TCompList} = Type#type.def,
+ case TCompList of
+ [] ->
+ {asn1_EMPTY,asn1_EMPTY};
+ {CompList,ExtList} -> % Should be enhanced to handle extensions too
+ CList = CompList ++ ExtList,
+ C = lists:nth(random(length(CList)),CList),
+ {C#'ComponentType'.name,get_type(M,Typename,C,no)};
+ CompList when list(CompList) ->
+ C = lists:nth(random(length(CompList)),CompList),
+ {C#'ComponentType'.name,get_type(M,Typename,C,no)}
+ end.
+
+get_sequence_of(M,Typename,Type,TypeSuffix) ->
+ %% should generate length according to constraints later
+ {_,Oftype} = Type#type.def,
+ C = Type#type.constraint,
+ S = size_random(C),
+ NewTypeName = [TypeSuffix|Typename],
+ gen_list(M,NewTypeName,Oftype,no,S).
+
+gen_list(_,_,_,_,0) ->
+ [];
+gen_list(M,Typename,Oftype,Tellname,N) ->
+ [get_type(M,Typename,Oftype,no)|gen_list(M,Typename,Oftype,Tellname,N-1)].
+
+get_type_prim(D) ->
+ C = D#type.constraint,
+ case D#type.def of
+ 'INTEGER' ->
+ i_random(C);
+ {'INTEGER',NamedNumberList} ->
+ NN = [X||{X,_} <- NamedNumberList],
+ case NN of
+ [] ->
+ i_random(C);
+ _ ->
+ lists:nth(random(length(NN)),NN)
+ end;
+ Enum when tuple(Enum),element(1,Enum)=='ENUMERATED' ->
+ NamedNumberList =
+ case Enum of
+ {_,_,NNL} -> NNL;
+ {_,NNL} -> NNL
+ end,
+ NNew=
+ case NamedNumberList of
+ {N1,N2} ->
+ N1 ++ N2;
+ _->
+ NamedNumberList
+ end,
+ NN = [X||{X,_} <- NNew],
+ case NN of
+ [] ->
+ asn1_EMPTY;
+ _ ->
+ lists:nth(random(length(NN)),NN)
+ end;
+ {'BIT STRING',NamedNumberList} ->
+%% io:format("get_type_prim 1: ~w~n",[NamedNumberList]),
+ NN = [X||{X,_} <- NamedNumberList],
+ case NN of
+ [] ->
+ Bl1 =lists:reverse(adjust_list(size_random(C),[1,0,1,1])),
+ lists:reverse(lists:dropwhile(fun(0)->true;(1)->false end,Bl1));
+ _ ->
+%% io:format("get_type_prim 2: ~w~n",[NN]),
+ [lists:nth(random(length(NN)),NN)]
+ end;
+ 'ANY' ->
+ exit({asn1_error,nyi,'ANY'});
+ 'NULL' ->
+ 'NULL';
+ 'OBJECT IDENTIFIER' ->
+ Len = random(3),
+ Olist = [(random(1000)-1)||_X <-lists:seq(1,Len)],
+ list_to_tuple([random(3)-1,random(40)-1|Olist]);
+ 'ObjectDescriptor' ->
+ object_descriptor_nyi;
+ 'BOOLEAN' ->
+ true;
+ 'OCTET STRING' ->
+ adjust_list(size_random(C),c_string(C,"OCTET STRING"));
+ 'NumericString' ->
+ adjust_list(size_random(C),c_string(C,"0123456789"));
+ 'TeletexString' ->
+ adjust_list(size_random(C),c_string(C,"TeletexString"));
+ 'VideotexString' ->
+ adjust_list(size_random(C),c_string(C,"VideotexString"));
+ 'UTCTime' ->
+ "97100211-0500";
+ 'GeneralizedTime' ->
+ "19971002103130.5";
+ 'GraphicString' ->
+ adjust_list(size_random(C),c_string(C,"GraphicString"));
+ 'VisibleString' ->
+ adjust_list(size_random(C),c_string(C,"VisibleString"));
+ 'GeneralString' ->
+ adjust_list(size_random(C),c_string(C,"GeneralString"));
+ 'PrintableString' ->
+ adjust_list(size_random(C),c_string(C,"PrintableString"));
+ 'IA5String' ->
+ adjust_list(size_random(C),c_string(C,"IA5String"));
+ 'BMPString' ->
+ adjust_list(size_random(C),c_string(C,"BMPString"));
+ 'UniversalString' ->
+ adjust_list(size_random(C),c_string(C,"UniversalString"));
+ XX ->
+ exit({asn1_error,nyi,XX})
+ end.
+
+c_string(undefined,Default) ->
+ Default;
+c_string(C,Default) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ {'SingleValue',Sv} when list(Sv) ->
+ Sv;
+ {'SingleValue',V} when integer(V) ->
+ [V];
+ no ->
+ Default
+ end.
+
+random(Upper) ->
+ {A1,A2,A3} = erlang:now(),
+ random:seed(A1,A2,A3),
+ random:uniform(Upper).
+
+size_random(C) ->
+ case get_constraint(C,'SizeConstraint') of
+ no ->
+ c_random({0,5},no);
+ {Lb,Ub} when Ub-Lb =< 4 ->
+ c_random({Lb,Ub},no);
+ {Lb,_} ->
+ c_random({Lb,Lb+4},no);
+ Sv ->
+ c_random(no,Sv)
+ end.
+
+i_random(C) ->
+ c_random(get_constraint(C,'ValueRange'),get_constraint(C,'SingleValue')).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% c_random(Range,SingleValue)
+%% only called from other X_random functions
+
+c_random(VRange,Single) ->
+ case {VRange,Single} of
+ {no,no} ->
+ random(16#fffffff) - (16#fffffff bsr 1);
+ {R,no} ->
+ case R of
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ Range = Ub - Lb +1,
+ Lb + (random(Range)-1);
+ {Lb,'MAX'} ->
+ Lb + random(16#fffffff)-1;
+ {'MIN',Ub} ->
+ Ub - random(16#fffffff)-1;
+ {A,{'ASN1_OK',B}} ->
+ Range = B - A +1,
+ A + (random(Range)-1)
+ end;
+ {_,S} when integer(S) ->
+ S;
+ {_,S} when list(S) ->
+ lists:nth(random(length(S)),S)
+%% {S1,S2} ->
+%% io:format("asn1ct_value: hejsan hoppsan~n");
+%% _ ->
+%% io:format("asn1ct_value: hejsan hoppsan 2~n")
+%% io:format("asn1ct_value: c_random/2: S1 = ~w~n"
+%% "S2 = ~w,~n",[S1,S2])
+%% exit(self(),goodbye)
+ end.
+
+adjust_list(Len,Orig) ->
+ adjust_list1(Len,Orig,Orig,[]).
+
+adjust_list1(0,_Orig,[_Oh|_Ot],Acc) ->
+ lists:reverse(Acc);
+adjust_list1(Len,Orig,[],Acc) ->
+ adjust_list1(Len,Orig,Orig,Acc);
+adjust_list1(Len,Orig,[Oh|Ot],Acc) ->
+ adjust_list1(Len-1,Orig,Ot,[Oh|Acc]).
+
+
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V
+ end.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt.erl
new file mode 100644
index 0000000000..efac8daf6b
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt.erl
@@ -0,0 +1,69 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1rt.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1rt).
+
+%% Runtime functions for ASN.1 (i.e encode, decode)
+
+-export([encode/2,encode/3,decode/3,load_driver/0,unload_driver/0,info/1]).
+
+encode(Module,{Type,Term}) ->
+ encode(Module,Type,Term).
+
+encode(Module,Type,Term) ->
+ case catch apply(Module,encode,[Type,Term]) of
+ {'EXIT',undef} ->
+ {error,{asn1,{undef,Module,Type}}};
+ Result ->
+ Result
+ end.
+
+decode(Module,Type,Bytes) ->
+ case catch apply(Module,decode,[Type,Bytes]) of
+ {'EXIT',undef} ->
+ {error,{asn1,{undef,Module,Type}}};
+ Result ->
+ Result
+ end.
+
+load_driver() ->
+ asn1rt_driver_handler:load_driver(),
+ receive
+ driver_ready ->
+ ok;
+ Err={error,_Reason} ->
+ Err;
+ Error ->
+ {error,Error}
+ end.
+
+unload_driver() ->
+ case catch asn1rt_driver_handler:unload_driver() of
+ ok ->
+ ok;
+ Error ->
+ {error,Error}
+ end.
+
+
+info(Module) ->
+ case catch apply(Module,info,[]) of
+ {'EXIT',{undef,_Reason}} ->
+ {error,{asn1,{undef,Module,info}}};
+ Result ->
+ {ok,Result}
+ end.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin.erl
new file mode 100644
index 0000000000..6064515a7e
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin.erl
@@ -0,0 +1,2310 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1rt_ber_bin.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1rt_ber_bin).
+
+%% encoding / decoding of BER
+
+-export([decode/1]).
+-export([fixoptionals/2,split_list/2,cindex/3,restbytes2/3,
+ list_to_record/2,
+ encode_tag_val/1,decode_tag/1,peek_tag/1,
+ check_tags/3, encode_tags/3]).
+-export([encode_boolean/2,decode_boolean/3,
+ encode_integer/3,encode_integer/4,
+ decode_integer/4,decode_integer/5,encode_enumerated/2,
+ encode_enumerated/4,decode_enumerated/5,
+ encode_real/2,decode_real/4,
+ encode_bit_string/4,decode_bit_string/6,
+ decode_compact_bit_string/6,
+ encode_octet_string/3,decode_octet_string/5,
+ encode_null/2,decode_null/3,
+ encode_object_identifier/2,decode_object_identifier/3,
+ encode_restricted_string/4,decode_restricted_string/6,
+ encode_universal_string/3,decode_universal_string/5,
+ encode_BMP_string/3,decode_BMP_string/5,
+ encode_generalized_time/3,decode_generalized_time/5,
+ encode_utc_time/3,decode_utc_time/5,
+ encode_length/1,decode_length/1,
+ check_if_valid_tag/3,
+ decode_tag_and_length/1, decode_components/6,
+ decode_components/7, decode_set/6]).
+
+-export([encode_open_type/1,encode_open_type/2,decode_open_type/1,decode_open_type/2,decode_open_type/3]).
+-export([skipvalue/1, skipvalue/2]).
+
+-include("asn1_records.hrl").
+
+% the encoding of class of tag bits 8 and 7
+-define(UNIVERSAL, 0).
+-define(APPLICATION, 16#40).
+-define(CONTEXT, 16#80).
+-define(PRIVATE, 16#C0).
+
+%%% primitive or constructed encoding % bit 6
+-define(PRIMITIVE, 0).
+-define(CONSTRUCTED, 2#00100000).
+
+%%% The tag-number for universal types
+-define(N_BOOLEAN, 1).
+-define(N_INTEGER, 2).
+-define(N_BIT_STRING, 3).
+-define(N_OCTET_STRING, 4).
+-define(N_NULL, 5).
+-define(N_OBJECT_IDENTIFIER, 6).
+-define(N_OBJECT_DESCRIPTOR, 7).
+-define(N_EXTERNAL, 8).
+-define(N_REAL, 9).
+-define(N_ENUMERATED, 10).
+-define(N_EMBEDDED_PDV, 11).
+-define(N_SEQUENCE, 16).
+-define(N_SET, 17).
+-define(N_NumericString, 18).
+-define(N_PrintableString, 19).
+-define(N_TeletexString, 20).
+-define(N_VideotexString, 21).
+-define(N_IA5String, 22).
+-define(N_UTCTime, 23).
+-define(N_GeneralizedTime, 24).
+-define(N_GraphicString, 25).
+-define(N_VisibleString, 26).
+-define(N_GeneralString, 27).
+-define(N_UniversalString, 28).
+-define(N_BMPString, 30).
+
+
+% the complete tag-word of built-in types
+-define(T_BOOLEAN, ?UNIVERSAL bor ?PRIMITIVE bor 1).
+-define(T_INTEGER, ?UNIVERSAL bor ?PRIMITIVE bor 2).
+-define(T_BIT_STRING, ?UNIVERSAL bor ?PRIMITIVE bor 3). % can be CONSTRUCTED
+-define(T_OCTET_STRING, ?UNIVERSAL bor ?PRIMITIVE bor 4). % can be CONSTRUCTED
+-define(T_NULL, ?UNIVERSAL bor ?PRIMITIVE bor 5).
+-define(T_OBJECT_IDENTIFIER,?UNIVERSAL bor ?PRIMITIVE bor 6).
+-define(T_OBJECT_DESCRIPTOR,?UNIVERSAL bor ?PRIMITIVE bor 7).
+-define(T_EXTERNAL, ?UNIVERSAL bor ?PRIMITIVE bor 8).
+-define(T_REAL, ?UNIVERSAL bor ?PRIMITIVE bor 9).
+-define(T_ENUMERATED, ?UNIVERSAL bor ?PRIMITIVE bor 10).
+-define(T_EMBEDDED_PDV, ?UNIVERSAL bor ?PRIMITIVE bor 11).
+-define(T_SEQUENCE, ?UNIVERSAL bor ?CONSTRUCTED bor 16).
+-define(T_SET, ?UNIVERSAL bor ?CONSTRUCTED bor 17).
+-define(T_NumericString, ?UNIVERSAL bor ?PRIMITIVE bor 18). %can be constructed
+-define(T_PrintableString, ?UNIVERSAL bor ?PRIMITIVE bor 19). %can be constructed
+-define(T_TeletexString, ?UNIVERSAL bor ?PRIMITIVE bor 20). %can be constructed
+-define(T_VideotexString, ?UNIVERSAL bor ?PRIMITIVE bor 21). %can be constructed
+-define(T_IA5String, ?UNIVERSAL bor ?PRIMITIVE bor 22). %can be constructed
+-define(T_UTCTime, ?UNIVERSAL bor ?PRIMITIVE bor 23).
+-define(T_GeneralizedTime, ?UNIVERSAL bor ?PRIMITIVE bor 24).
+-define(T_GraphicString, ?UNIVERSAL bor ?PRIMITIVE bor 25). %can be constructed
+-define(T_VisibleString, ?UNIVERSAL bor ?PRIMITIVE bor 26). %can be constructed
+-define(T_GeneralString, ?UNIVERSAL bor ?PRIMITIVE bor 27). %can be constructed
+-define(T_UniversalString, ?UNIVERSAL bor ?PRIMITIVE bor 28). %can be constructed
+-define(T_BMPString, ?UNIVERSAL bor ?PRIMITIVE bor 30). %can be constructed
+
+
+decode(Bin) ->
+ decode_primitive(Bin).
+
+decode_primitive(Bin) ->
+ {Tlv = {Tag,Len,V},<<>>} = decode_tlv(Bin),
+ case element(2,Tag) of
+ ?CONSTRUCTED ->
+ {Tag,Len,decode_constructed(V)};
+ _ ->
+ Tlv
+ end.
+
+decode_constructed(<<>>) ->
+ [];
+decode_constructed(Bin) ->
+ {Tlv = {Tag,Len,V},Rest} = decode_tlv(Bin),
+ NewTlv =
+ case element(2,Tag) of
+ ?CONSTRUCTED ->
+ {Tag,Len,decode_constructed(V)};
+ _ ->
+ Tlv
+ end,
+ [NewTlv|decode_constructed(Rest)].
+
+decode_tlv(Bin) ->
+ {Tag,Bin1,_Rb1} = decode_tag(Bin),
+ {{Len,Bin2},_Rb2} = decode_length(Bin1),
+ <> = Bin2,
+ {{Tag,Len,V},Bin3}.
+
+
+
+%%%%%%%%%%%%%
+% split_list(List,HeadLen) -> {HeadList,TailList}
+%
+% splits List into HeadList (Length=HeadLen) and TailList
+% if HeadLen == indefinite -> return {List,indefinite}
+split_list(List,indefinite) ->
+ {List, indefinite};
+split_list(Bin, Len) when binary(Bin) ->
+ split_binary(Bin,Len);
+split_list(List,Len) ->
+ {lists:sublist(List,Len),lists:nthtail(Len,List)}.
+
+
+%%% new function which fixes a bug regarding indefinite length decoding
+restbytes2(indefinite,<<0,0,RemBytes/binary>>,_) ->
+ {RemBytes,2};
+restbytes2(indefinite,RemBytes,ext) ->
+ skipvalue(indefinite,RemBytes);
+restbytes2(RemBytes,<<>>,_) ->
+ {RemBytes,0};
+restbytes2(_RemBytes,Bytes,noext) ->
+ exit({error,{asn1, {unexpected,Bytes}}});
+restbytes2(RemBytes,_Bytes,ext) ->
+ {RemBytes,0}.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% skipvalue(Length, Bytes) -> {RemainingBytes, RemovedNumberOfBytes}
+%%
+%% skips the one complete (could be nested) TLV from Bytes
+%% handles both definite and indefinite length encodings
+%%
+
+skipvalue(L, Bytes) ->
+ skipvalue(L, Bytes, 0).
+
+skipvalue(indefinite, Bytes, Rb) ->
+ {_T,Bytes2,R2} = decode_tag(Bytes),
+ {{L,Bytes3},R3} = decode_length(Bytes2),
+ {Bytes4,Rb4} = case L of
+ indefinite ->
+ skipvalue(indefinite,Bytes3,R2+R3);
+ _ ->
+ <<_:L/binary, RestBytes/binary>> = Bytes3,
+ {RestBytes, R2+R3+L}
+ end,
+ case Bytes4 of
+ <<0,0,Bytes5/binary>> ->
+ {Bytes5,Rb+Rb4+2};
+ _ -> skipvalue(indefinite,Bytes4,Rb+Rb4)
+ end;
+skipvalue(L, Bytes, Rb) ->
+% <> = Bytes,
+ <<_:L/binary, RestBytes/binary>> = Bytes,
+ {RestBytes,Rb+L}.
+
+%%skipvalue(indefinite, Bytes, Rb) ->
+%% {T,Bytes2,R2} = decode_tag(Bytes),
+%% {L,Bytes3,R3} = decode_length(Bytes2),
+%% {Bytes4,Rb4} = case L of
+%% indefinite ->
+%% skipvalue(indefinite,Bytes3,R2+R3);
+%% _ ->
+%% lists:nthtail(L,Bytes3) %% konstigt !?
+%% end,
+%% case Bytes4 of
+%% [0,0|Bytes5] ->
+%% {Bytes5,Rb4+2};
+%% _ -> skipvalue(indefinite,Bytes4,Rb4)
+%% end;
+%%skipvalue(L, Bytes, Rb) ->
+%% {lists:nthtail(L,Bytes),Rb+L}.
+
+skipvalue(Bytes) ->
+ {_T,Bytes2,R2} = decode_tag(Bytes),
+ {{L,Bytes3},R3} = decode_length(Bytes2),
+ skipvalue(L,Bytes3,R2+R3).
+
+
+cindex(Ix,Val,Cname) ->
+ case element(Ix,Val) of
+ {Cname,Val2} -> Val2;
+ X -> X
+ end.
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Optionals, preset not filled optionals with asn1_NOVALUE
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+% converts a list to a record if necessary
+list_to_record(Name,List) when list(List) ->
+ list_to_tuple([Name|List]);
+list_to_record(_Name,Tuple) when tuple(Tuple) ->
+ Tuple.
+
+
+fixoptionals(OptList,Val) when list(Val) ->
+ fixoptionals(OptList,Val,1,[],[]).
+
+fixoptionals([{Name,Pos}|Ot],[{Name,Val}|Vt],_Opt,Acc1,Acc2) ->
+ fixoptionals(Ot,Vt,Pos+1,[1|Acc1],[{Name,Val}|Acc2]);
+fixoptionals([{_Name,Pos}|Ot],V,Pos,Acc1,Acc2) ->
+ fixoptionals(Ot,V,Pos+1,[0|Acc1],[asn1_NOVALUE|Acc2]);
+fixoptionals(O,[Vh|Vt],Pos,Acc1,Acc2) ->
+ fixoptionals(O,Vt,Pos+1,Acc1,[Vh|Acc2]);
+fixoptionals([],[Vh|Vt],Pos,Acc1,Acc2) ->
+ fixoptionals([],Vt,Pos+1,Acc1,[Vh|Acc2]);
+fixoptionals([],[],_,_Acc1,Acc2) ->
+ % return Val as a record
+ list_to_tuple([asn1_RECORDNAME|lists:reverse(Acc2)]).
+
+
+%%encode_tag(TagClass(?UNI, APP etc), Form (?PRIM etx), TagInteger) ->
+%% 8bit Int | binary
+encode_tag_val({Class, Form, TagNo}) when (TagNo =< 30) ->
+ <<(Class bsr 6):2,(Form bsr 5):1,TagNo:5>>;
+
+encode_tag_val({Class, Form, TagNo}) ->
+ {Octets,_Len} = mk_object_val(TagNo),
+ BinOct = list_to_binary(Octets),
+ <<(Class bsr 6):2, (Form bsr 5):1, 31:5,BinOct/binary>>;
+
+%% asumes whole correct tag bitpattern, multiple of 8
+encode_tag_val(Tag) when (Tag =< 255) -> Tag; %% används denna funktion??!!
+%% asumes correct bitpattern of 0-5
+encode_tag_val(Tag) -> encode_tag_val2(Tag,[]).
+
+encode_tag_val2(Tag, OctAck) when (Tag =< 255) ->
+ [Tag | OctAck];
+encode_tag_val2(Tag, OctAck) ->
+ encode_tag_val2(Tag bsr 8, [255 band Tag | OctAck]).
+
+
+%%%encode_tag(TagClass(?UNI, APP etc), Form (?PRIM etx), TagInteger) ->
+%%% 8bit Int | [list of octets]
+%encode_tag_val({Class, Form, TagNo}) when (TagNo =< 30) ->
+%%% <>;
+% [Class bor Form bor TagNo];
+%encode_tag_val({Class, Form, TagNo}) ->
+% {Octets,L} = mk_object_val(TagNo),
+% [Class bor Form bor 31 | Octets];
+
+
+%%============================================================================\%% Peek on the initial tag
+%% peek_tag(Bytes) -> TagBytes
+%% interprets the first byte and possible second, third and fourth byte as
+%% a tag and returns all the bytes comprising the tag, the constructed/primitive bit (6:th bit of first byte) is normalised to 0
+%%
+
+peek_tag(<>) ->
+ Bin = peek_tag(Buffer, <<>>),
+ <>;
+%% single tag (tagno < 31)
+peek_tag(<>) ->
+ <>.
+
+peek_tag(<<0:1,PartialTag:7,_Buffer/binary>>, TagAck) ->
+ <>;
+peek_tag(<>, TagAck) ->
+ peek_tag(Buffer,<>);
+peek_tag(_,TagAck) ->
+ exit({error,{asn1, {invalid_tag,TagAck}}}).
+%%peek_tag([Tag|Buffer]) when (Tag band 31) == 31 ->
+%% [Tag band 2#11011111 | peek_tag(Buffer,[])];
+%%%% single tag (tagno < 31)
+%%peek_tag([Tag|Buffer]) ->
+%% [Tag band 2#11011111].
+
+%%peek_tag([PartialTag|Buffer], TagAck) when (PartialTag < 128 ) ->
+%% lists:reverse([PartialTag|TagAck]);
+%%peek_tag([PartialTag|Buffer], TagAck) ->
+%% peek_tag(Buffer,[PartialTag|TagAck]);
+%%peek_tag(Buffer,TagAck) ->
+%% exit({error,{asn1, {invalid_tag,lists:reverse(TagAck)}}}).
+
+
+%%===============================================================================
+%% Decode a tag
+%%
+%% decode_tag(OctetListBuffer) -> {{Class, Form, TagNo}, RestOfBuffer, RemovedBytes}
+%%===============================================================================
+
+%% multiple octet tag
+decode_tag(<>) ->
+ {TagNo, Buffer1, RemovedBytes} = decode_tag(Buffer, 0, 1),
+ {{(Class bsl 6), (Form bsl 5), TagNo}, Buffer1, RemovedBytes};
+
+%% single tag (< 31 tags)
+decode_tag(<>) ->
+ {{(Class bsl 6), (Form bsl 5), TagNo}, Buffer, 1}.
+
+%% last partial tag
+decode_tag(<<0:1,PartialTag:7, Buffer/binary>>, TagAck, RemovedBytes) ->
+ TagNo = (TagAck bsl 7) bor PartialTag,
+ %%<> = <>,
+ {TagNo, Buffer, RemovedBytes+1};
+% more tags
+decode_tag(<<_:1,PartialTag:7, Buffer/binary>>, TagAck, RemovedBytes) ->
+ TagAck1 = (TagAck bsl 7) bor PartialTag,
+ %%<> = <>,
+ decode_tag(Buffer, TagAck1, RemovedBytes+1).
+
+%%------------------------------------------------------------------
+%% check_tags_i is the same as check_tags except that it stops and
+%% returns the remaining tags not checked when it encounters an
+%% indefinite length field
+%% only called internally within this module
+
+check_tags_i([Tag], Buffer, OptOrMand) -> % optimized very usual case
+ {[],check_one_tag(Tag, Buffer, OptOrMand)};
+check_tags_i(Tags, Buffer, OptOrMand) ->
+ check_tags_i(Tags, Buffer, 0, OptOrMand).
+
+check_tags_i([Tag1,Tag2|TagRest], Buffer, Rb, OptOrMand)
+ when Tag1#tag.type == 'IMPLICIT' ->
+ check_tags_i([Tag1#tag{type=Tag2#tag.type}|TagRest], Buffer, Rb, OptOrMand);
+
+check_tags_i([Tag1|TagRest], Buffer, Rb, OptOrMand) ->
+ {Form_Length,Buffer2,Rb1} = check_one_tag(Tag1, Buffer, OptOrMand),
+ case TagRest of
+ [] -> {TagRest, {Form_Length, Buffer2, Rb + Rb1}};
+ _ ->
+ case Form_Length of
+ {?CONSTRUCTED,_} ->
+ {TagRest, {Form_Length, Buffer2, Rb + Rb1}};
+ _ ->
+ check_tags_i(TagRest, Buffer2, Rb + Rb1, mandatory)
+ end
+ end;
+
+check_tags_i([], Buffer, Rb, _) ->
+ {[],{{0,0},Buffer,Rb}}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This function is called from generated code
+
+check_tags([Tag], Buffer, OptOrMand) -> % optimized very usual case
+ check_one_tag(Tag, Buffer, OptOrMand);
+check_tags(Tags, Buffer, OptOrMand) ->
+ check_tags(Tags, Buffer, 0, OptOrMand).
+
+check_tags([Tag1,Tag2|TagRest], Buffer, Rb, OptOrMand)
+ when Tag1#tag.type == 'IMPLICIT' ->
+ check_tags([Tag1#tag{type=Tag2#tag.type}|TagRest], Buffer, Rb, OptOrMand);
+
+check_tags([Tag1|TagRest], Buffer, Rb, OptOrMand) ->
+ {Form_Length,Buffer2,Rb1} = check_one_tag(Tag1, Buffer, OptOrMand),
+ case TagRest of
+ [] -> {Form_Length, Buffer2, Rb + Rb1};
+ _ -> check_tags(TagRest, Buffer2, Rb + Rb1, mandatory)
+ end;
+
+check_tags([], Buffer, Rb, _) ->
+ {{0,0},Buffer,Rb}.
+
+check_one_tag(Tag=#tag{class=ExpectedClass,number=ExpectedNumber}, Buffer, OptOrMand) ->
+ case catch decode_tag(Buffer) of
+ {'EXIT',_Reason} ->
+ tag_error(no_data,Tag,Buffer,OptOrMand);
+ {{ExpectedClass,Form,ExpectedNumber},Buffer2,Rb} ->
+ {{L,Buffer3},RemBytes2} = decode_length(Buffer2),
+ {{Form,L}, Buffer3, RemBytes2+Rb};
+ {ErrorTag,_,_} ->
+ tag_error(ErrorTag, Tag, Buffer, OptOrMand)
+ end.
+
+tag_error(ErrorTag, Tag, Buffer, OptOrMand) ->
+ case OptOrMand of
+ mandatory ->
+ exit({error,{asn1, {invalid_tag,
+ {ErrorTag, Tag, Buffer}}}});
+ _ ->
+ exit({error,{asn1, {no_optional_tag,
+ {ErrorTag, Tag, Buffer}}}})
+ end.
+%%=======================================================================
+%%
+%% Encode all tags in the list Tags and return a possibly deep list of
+%% bytes with tag and length encoded
+%%
+%% prepend_tags(Tags, BytesSoFar, LenSoFar) -> {Bytes, Len}
+encode_tags(Tags, BytesSoFar, LenSoFar) ->
+ NewTags = encode_tags1(Tags, []),
+ %% NewTags contains the resulting tags in reverse order
+ encode_tags2(NewTags, BytesSoFar, LenSoFar).
+
+%encode_tags2([#tag{class=?UNIVERSAL,number=No}|Trest], BytesSoFar, LenSoFar) ->
+% {Bytes2,L2} = encode_length(LenSoFar),
+% encode_tags2(Trest,[[No|Bytes2],BytesSoFar], LenSoFar + 1 + L2);
+encode_tags2([Tag|Trest], BytesSoFar, LenSoFar) ->
+ {Bytes1,L1} = encode_one_tag(Tag),
+ {Bytes2,L2} = encode_length(LenSoFar),
+ encode_tags2(Trest, [Bytes1,Bytes2|BytesSoFar],
+ LenSoFar + L1 + L2);
+encode_tags2([], BytesSoFar, LenSoFar) ->
+ {BytesSoFar,LenSoFar}.
+
+encode_tags1([Tag1, Tag2| Trest], Acc)
+ when Tag1#tag.type == 'IMPLICIT' ->
+ encode_tags1([Tag1#tag{type=Tag2#tag.type,form=Tag2#tag.form}|Trest],Acc);
+encode_tags1([Tag1 | Trest], Acc) ->
+ encode_tags1(Trest, [Tag1|Acc]);
+encode_tags1([], Acc) ->
+ Acc. % the resulting tags are returned in reverse order
+
+encode_one_tag(Bin) when binary(Bin) ->
+ {Bin,size(Bin)};
+encode_one_tag(#tag{class=Class,number=No,type=Type, form = Form}) ->
+ NewForm = case Type of
+ 'EXPLICIT' ->
+ ?CONSTRUCTED;
+ _ ->
+ Form
+ end,
+ Bytes = encode_tag_val({Class,NewForm,No}),
+ {Bytes,size(Bytes)}.
+
+%%===============================================================================
+%% Change the tag (used when an implicit tagged type has a reference to something else)
+%% The constructed bit in the tag is taken from the tag to be replaced.
+%%
+%% change_tag(NewTag,[Tag,Buffer]) -> [NewTag,Buffer]
+%%===============================================================================
+
+%change_tag({NewClass,NewTagNr}, Buffer) ->
+% {{OldClass, OldForm, OldTagNo}, Buffer1, RemovedBytes} = decode_tag(lists:flatten(Buffer)),
+% [encode_tag_val({NewClass, OldForm, NewTagNr}) | Buffer1].
+
+
+
+
+
+
+
+%%===============================================================================
+%%
+%% This comment is valid for all the encode/decode functions
+%%
+%% C = Constraint -> typically {'ValueRange',LowerBound,UpperBound}
+%% used for PER-coding but not for BER-coding.
+%%
+%% Val = Value. If Val is an atom then it is a symbolic integer value
+%% (i.e the atom must be one of the names in the NamedNumberList).
+%% The NamedNumberList is used to translate the atom to an integer value
+%% before encoding.
+%%
+%%===============================================================================
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_open_type(Value) -> CompleteList
+%% Value = list of bytes of an already encoded value (the list must be flat)
+%% | binary
+
+%% This version does not consider Explicit tagging of the open type. It
+%% is only left because of backward compatibility.
+encode_open_type(Val) when list(Val) ->
+ {Val,size(list_to_binary(Val))};
+encode_open_type(Val) ->
+ {Val, size(Val)}.
+
+%%
+encode_open_type(Val, []) when list(Val) ->
+ {Val,size(list_to_binary(Val))};
+encode_open_type(Val,[]) ->
+ {Val, size(Val)};
+encode_open_type(Val, Tag) when list(Val) ->
+ encode_tags(Tag,Val,size(list_to_binary(Val)));
+encode_open_type(Val,Tag) ->
+ encode_tags(Tag,Val, size(Val)).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_open_type(Buffer) -> Value
+%% Bytes = [byte] with BER encoded data
+%% Value = [byte] with decoded data (which must be decoded again as some type)
+%%
+decode_open_type(Bytes) ->
+ {_Tag, Len, _RemainingBuffer, RemovedBytes} = decode_tag_and_length(Bytes),
+ N = Len + RemovedBytes,
+ <> = Bytes,
+ {Val, RemainingBytes, Len + RemovedBytes}.
+
+decode_open_type(Bytes,ExplTag) ->
+ {Tag, Len, RemainingBuffer, RemovedBytes} = decode_tag_and_length(Bytes),
+ case {Tag,ExplTag} of
+ {{Class,Form,No},[#tag{class=Class,number=No,form=Form}]} ->
+ {_Tag2, Len2, _RemainingBuffer2, RemovedBytes2} = decode_tag_and_length(RemainingBuffer),
+ N = Len2 + RemovedBytes2,
+ <<_:RemovedBytes/unit:8,Val:N/binary,RemainingBytes/binary>> = Bytes,
+ {Val, RemainingBytes, N + RemovedBytes};
+ _ ->
+ N = Len + RemovedBytes,
+ <> = Bytes,
+ {Val, RemainingBytes, Len + RemovedBytes}
+ end.
+
+decode_open_type(ber_bin,Bytes,ExplTag) ->
+ decode_open_type(Bytes,ExplTag);
+decode_open_type(ber,Bytes,ExplTag) ->
+ {Val,RemBytes,Len}=decode_open_type(Bytes,ExplTag),
+ {binary_to_list(Val),RemBytes,Len}.
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Boolean, ITU_T X.690 Chapter 8.2
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% encode_boolean(Integer, tag | notag) -> [octet list]
+%%===============================================================================
+
+encode_boolean({Name, Val}, DoTag) when atom(Name) ->
+ dotag(DoTag, ?N_BOOLEAN, encode_boolean(Val));
+encode_boolean(true,[]) ->
+ {[1,1,16#FF],3};
+encode_boolean(false,[]) ->
+ {[1,1,0],3};
+encode_boolean(Val, DoTag) ->
+ dotag(DoTag, ?N_BOOLEAN, encode_boolean(Val)).
+
+%% encode_boolean(Boolean) -> [Len, Boolean] = [1, $FF | 0]
+encode_boolean(true) -> {[16#FF],1};
+encode_boolean(false) -> {[0],1};
+encode_boolean(X) -> exit({error,{asn1, {encode_boolean, X}}}).
+
+
+%%===============================================================================
+%% decode_boolean(BuffList, HasTag, TotalLen) -> {true, Remain, RemovedBytes} |
+%% {false, Remain, RemovedBytes}
+%%===============================================================================
+
+decode_boolean(Buffer, Tags, OptOrMand) ->
+ NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,number=?N_BOOLEAN}),
+ decode_boolean_notag(Buffer, NewTags, OptOrMand).
+
+decode_boolean_notag(Buffer, Tags, OptOrMand) ->
+ {RestTags, {FormLen,Buffer0,Rb0}} =
+ check_tags_i(Tags, Buffer, OptOrMand),
+ case FormLen of
+ {?CONSTRUCTED,Len} ->
+ {Buffer00,RestBytes} = split_list(Buffer0,Len),
+ {Val,Buffer1,Rb1} = decode_boolean_notag(Buffer00, RestTags, OptOrMand),
+ {Buffer2, Rb2} = restbytes2(RestBytes,Buffer1,noext),
+ {Val, Buffer2, Rb0+Rb1+Rb2};
+ {_,_} ->
+ decode_boolean2(Buffer0, Rb0)
+ end.
+
+decode_boolean2(<<0:8, Buffer/binary>>, RemovedBytes) ->
+ {false, Buffer, RemovedBytes + 1};
+decode_boolean2(<<_:8, Buffer/binary>>, RemovedBytes) ->
+ {true, Buffer, RemovedBytes + 1};
+decode_boolean2(Buffer, _) ->
+ exit({error,{asn1, {decode_boolean, Buffer}}}).
+
+
+
+
+%%===========================================================================
+%% Integer, ITU_T X.690 Chapter 8.3
+
+%% encode_integer(Constraint, Value, Tag) -> [octet list]
+%% encode_integer(Constraint, Name, NamedNumberList, Tag) -> [octet list]
+%% Value = INTEGER | {Name,INTEGER}
+%% Tag = tag | notag
+%%===========================================================================
+
+encode_integer(C, Val, []) when integer(Val) ->
+ {EncVal,Len}=encode_integer(C, Val),
+ dotag_universal(?N_INTEGER,EncVal,Len);
+encode_integer(C, Val, Tag) when integer(Val) ->
+ dotag(Tag, ?N_INTEGER, encode_integer(C, Val));
+encode_integer(C,{Name,Val},Tag) when atom(Name) ->
+ encode_integer(C,Val,Tag);
+encode_integer(_, Val, _) ->
+ exit({error,{asn1, {encode_integer, Val}}}).
+
+
+
+encode_integer(C, Val, NamedNumberList, Tag) when atom(Val) ->
+ case lists:keysearch(Val, 1, NamedNumberList) of
+ {value,{_, NewVal}} ->
+ dotag(Tag, ?N_INTEGER, encode_integer(C, NewVal));
+ _ ->
+ exit({error,{asn1, {encode_integer_namednumber, Val}}})
+ end;
+encode_integer(C,{_,Val},NamedNumberList,Tag) ->
+ encode_integer(C,Val,NamedNumberList,Tag);
+encode_integer(C, Val, _NamedNumberList, Tag) ->
+ dotag(Tag, ?N_INTEGER, encode_integer(C, Val)).
+
+
+
+
+encode_integer(_C, Val) ->
+ Bytes =
+ if
+ Val >= 0 ->
+ encode_integer_pos(Val, []);
+ true ->
+ encode_integer_neg(Val, [])
+ end,
+ {Bytes,length(Bytes)}.
+
+encode_integer_pos(0, L=[B|_Acc]) when B < 128 ->
+ L;
+encode_integer_pos(N, Acc) ->
+ encode_integer_pos((N bsr 8), [N band 16#ff| Acc]).
+
+encode_integer_neg(-1, L=[B1|_T]) when B1 > 127 ->
+ L;
+encode_integer_neg(N, Acc) ->
+ encode_integer_neg(N bsr 8, [N band 16#ff|Acc]).
+
+%%===============================================================================
+%% decode integer
+%% (Buffer, Range, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes}
+%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes}
+%%===============================================================================
+
+
+decode_integer(Buffer, Range, Tags, OptOrMand) ->
+ NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,number=?N_INTEGER}),
+ decode_integer_notag(Buffer, Range, [], NewTags, OptOrMand).
+
+decode_integer(Buffer, Range, NamedNumberList, Tags, OptOrMand) ->
+ NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,number=?N_INTEGER}),
+ decode_integer_notag(Buffer, Range, NamedNumberList, NewTags, OptOrMand).
+
+decode_integer_notag(Buffer, Range, NamedNumberList, NewTags, OptOrMand) ->
+ {RestTags, {FormLen, Buffer0, Rb0}} =
+ check_tags_i(NewTags, Buffer, OptOrMand),
+% Result = {Val, Buffer2, RemovedBytes} =
+ case FormLen of
+ {?CONSTRUCTED,Len} ->
+ {Buffer00, RestBytes} = split_list(Buffer0,Len),
+ {Val01, Buffer01, Rb01} =
+ decode_integer_notag(Buffer00, Range, NamedNumberList,
+ RestTags, OptOrMand),
+ {Buffer02, Rb02} = restbytes2(RestBytes,Buffer01,noext),
+ {Val01, Buffer02, Rb0+Rb01+Rb02};
+ {_, Len} ->
+ Result =
+ decode_integer2(Len,Buffer0,Rb0+Len),
+ Result2 = check_integer_constraint(Result,Range),
+ resolve_named_value(Result2,NamedNumberList)
+ end.
+
+resolve_named_value(Result={Val,Buffer,RemBytes},NamedNumberList) ->
+ case NamedNumberList of
+ [] -> Result;
+ _ ->
+ NewVal = case lists:keysearch(Val, 2, NamedNumberList) of
+ {value,{NamedVal, _}} ->
+ NamedVal;
+ _ ->
+ Val
+ end,
+ {NewVal, Buffer, RemBytes}
+ end.
+
+check_integer_constraint(Result={Val, _Buffer,_},Range) ->
+ case Range of
+ [] -> % No length constraint
+ Result;
+ {Lb,Ub} when Val >= Lb, Ub >= Val -> % variable length constraint
+ Result;
+ Val -> % fixed value constraint
+ Result;
+ {_,_} ->
+ exit({error,{asn1,{integer_range,Range,Val}}});
+ SingleValue when integer(SingleValue) ->
+ exit({error,{asn1,{integer_range,Range,Val}}});
+ _ -> % some strange constraint that we don't support yet
+ Result
+ end.
+
+%%============================================================================
+%% Enumerated value, ITU_T X.690 Chapter 8.4
+
+%% encode enumerated value
+%%============================================================================
+encode_enumerated(Val, []) when integer(Val)->
+ {EncVal,Len} = encode_integer(false,Val),
+ dotag_universal(?N_ENUMERATED,EncVal,Len);
+encode_enumerated(Val, DoTag) when integer(Val)->
+ dotag(DoTag, ?N_ENUMERATED, encode_integer(false,Val));
+encode_enumerated({Name,Val}, DoTag) when atom(Name) ->
+ encode_enumerated(Val, DoTag).
+
+%% The encode_enumerated functions below this line can be removed when the
+%% new code generation is stable. (the functions might have to be kept here
+%% a while longer for compatibility reasons)
+
+encode_enumerated(C, Val, {NamedNumberList,ExtList}, DoTag) when atom(Val) ->
+ case catch encode_enumerated(C, Val, NamedNumberList, DoTag) of
+ {'EXIT',_} -> encode_enumerated(C, Val, ExtList, DoTag);
+ Result -> Result
+ end;
+
+encode_enumerated(C, Val, NamedNumberList, DoTag) when atom(Val) ->
+ case lists:keysearch(Val, 1, NamedNumberList) of
+ {value, {_, NewVal}} when DoTag == []->
+ {EncVal,Len} = encode_integer(C,NewVal),
+ dotag_universal(?N_ENUMERATED,EncVal,Len);
+ {value, {_, NewVal}} ->
+ dotag(DoTag, ?N_ENUMERATED, encode_integer(C, NewVal));
+ _ ->
+ exit({error,{asn1, {enumerated_not_in_range, Val}}})
+ end;
+
+encode_enumerated(C, {asn1_enum, Val}, {_,_}, DoTag) when integer(Val) ->
+ dotag(DoTag, ?N_ENUMERATED, encode_integer(C,Val));
+
+encode_enumerated(C, {Name,Val}, NamedNumberList, DoTag) when atom(Name) ->
+ encode_enumerated(C, Val, NamedNumberList, DoTag);
+
+encode_enumerated(_, Val, _, _) ->
+ exit({error,{asn1, {enumerated_not_namednumber, Val}}}).
+
+
+
+%%============================================================================
+%% decode enumerated value
+%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) ->
+%% {Value, RemainingBuffer, RemovedBytes}
+%%===========================================================================
+decode_enumerated(Buffer, Range, NamedNumberList, Tags, OptOrMand) ->
+ NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,number=?N_ENUMERATED}),
+ decode_enumerated_notag(Buffer, Range, NamedNumberList,
+ NewTags, OptOrMand).
+
+decode_enumerated_notag(Buffer, Range, NNList = {NamedNumberList,ExtList}, Tags, OptOrMand) ->
+ {RestTags, {FormLen, Buffer0, Rb0}} =
+ check_tags_i(Tags, Buffer, OptOrMand),
+
+ case FormLen of
+ {?CONSTRUCTED,Len} ->
+ {Buffer00,RestBytes} = split_list(Buffer0,Len),
+ {Val01, Buffer01, Rb01} =
+ decode_enumerated_notag(Buffer00, Range, NNList, RestTags, OptOrMand),
+ {Buffer02, Rb02} = restbytes2(RestBytes,Buffer01,noext),
+ {Val01, Buffer02, Rb0+Rb01+Rb02};
+ {_,Len} ->
+ {Val01, Buffer01, Rb01} =
+ decode_integer2(Len, Buffer0, Rb0+Len),
+ case decode_enumerated1(Val01, NamedNumberList) of
+ {asn1_enum,Val01} ->
+ {decode_enumerated1(Val01,ExtList), Buffer01, Rb01};
+ Result01 ->
+ {Result01, Buffer01, Rb01}
+ end
+ end;
+
+decode_enumerated_notag(Buffer, Range, NNList, Tags, OptOrMand) ->
+ {RestTags, {FormLen, Buffer0, Rb0}} =
+ check_tags_i(Tags, Buffer, OptOrMand),
+
+ case FormLen of
+ {?CONSTRUCTED,Len} ->
+ {Buffer00,RestBytes} = split_list(Buffer0,Len),
+ {Val01, Buffer01, Rb01} =
+ decode_enumerated_notag(Buffer00, Range, NNList, RestTags, OptOrMand),
+ {Buffer02, Rb02} = restbytes2(RestBytes,Buffer01,noext),
+ {Val01, Buffer02, Rb0+Rb01+Rb02};
+ {_,Len} ->
+ {Val01, Buffer02, Rb02} =
+ decode_integer2(Len, Buffer0, Rb0+Len),
+ case decode_enumerated1(Val01, NNList) of
+ {asn1_enum,_} ->
+ exit({error,{asn1, {illegal_enumerated, Val01}}});
+ Result01 ->
+ {Result01, Buffer02, Rb02}
+ end
+ end.
+
+decode_enumerated1(Val, NamedNumberList) ->
+ %% it must be a named integer
+ case lists:keysearch(Val, 2, NamedNumberList) of
+ {value,{NamedVal, _}} ->
+ NamedVal;
+ _ ->
+ {asn1_enum,Val}
+ end.
+
+
+%%============================================================================
+%%
+%% Real value, ITU_T X.690 Chapter 8.5
+%%============================================================================
+%%
+%% encode real value
+%%============================================================================
+
+%% only base 2 internally so far!!
+encode_real(0, DoTag) ->
+ dotag(DoTag, ?N_REAL, {[],0});
+encode_real('PLUS-INFINITY', DoTag) ->
+ dotag(DoTag, ?N_REAL, {[64],1});
+encode_real('MINUS-INFINITY', DoTag) ->
+ dotag(DoTag, ?N_REAL, {[65],1});
+encode_real(Val, DoTag) when tuple(Val)->
+ dotag(DoTag, ?N_REAL, encode_real(Val)).
+
+%%%%%%%%%%%%%%
+%% not optimal efficient..
+%% only base 2 of Mantissa encoding!
+%% only base 2 of ExpBase encoding!
+encode_real({Man, Base, Exp}) ->
+%% io:format("Mantissa: ~w Base: ~w, Exp: ~w~n",[Man, Base, Exp]),
+
+ OctExp = if Exp >= 0 -> list_to_binary(encode_integer_pos(Exp, []));
+ true -> list_to_binary(encode_integer_neg(Exp, []))
+ end,
+%% ok = io:format("OctExp: ~w~n",[OctExp]),
+ SignBit = if Man > 0 -> 0; % bit 7 is pos or neg, no Zeroval
+ true -> 1
+ end,
+%% ok = io:format("SignBitMask: ~w~n",[SignBitMask]),
+ InBase = if Base =:= 2 -> 0; % bit 6,5: only base 2 this far!
+ true ->
+ exit({error,{asn1, {encode_real_non_supported_encodeing, Base}}})
+ end,
+ SFactor = 0, % bit 4,3: no scaling since only base 2
+ OctExpLen = size(OctExp),
+ if OctExpLen > 255 ->
+ exit({error,{asn1, {to_big_exp_in_encode_real, OctExpLen}}});
+ true -> true %% make real assert later..
+ end,
+ {LenCode, EOctets} = case OctExpLen of % bit 2,1
+ 1 -> {0, OctExp};
+ 2 -> {1, OctExp};
+ 3 -> {2, OctExp};
+ _ -> {3, <>}
+ end,
+ FirstOctet = <<1:1,SignBit:1,InBase:2,SFactor:2,LenCode:2>>,
+ OctMantissa = if Man > 0 -> list_to_binary(minimum_octets(Man));
+ true -> list_to_binary(minimum_octets(-(Man))) % signbit keeps track of sign
+ end,
+ %% ok = io:format("LenMask: ~w EOctets: ~w~nFirstOctet: ~w OctMantissa: ~w OctExpLen: ~w~n", [LenMask, EOctets, FirstOctet, OctMantissa, OctExpLen]),
+ Bin = <>,
+ {Bin, size(Bin)}.
+
+
+%encode_real({Man, Base, Exp}) ->
+%% io:format("Mantissa: ~w Base: ~w, Exp: ~w~n",[Man, Base, Exp]),
+
+% OctExp = if Exp >= 0 -> encode_integer_pos(Exp, []);
+% true -> encode_integer_neg(Exp, [])
+% end,
+%% ok = io:format("OctExp: ~w~n",[OctExp]),
+% SignBitMask = if Man > 0 -> 2#00000000; % bit 7 is pos or neg, no Zeroval
+% true -> 2#01000000
+% end,
+%% ok = io:format("SignBitMask: ~w~n",[SignBitMask]),
+% InternalBaseMask = if Base =:= 2 -> 2#00000000; % bit 6,5: only base 2 this far!
+% true ->
+% exit({error,{asn1, {encode_real_non_supported_encodeing, Base}}})
+% end,
+% ScalingFactorMask =2#00000000, % bit 4,3: no scaling since only base 2
+% OctExpLen = length(OctExp),
+% if OctExpLen > 255 ->
+% exit({error,{asn1, {to_big_exp_in_encode_real, OctExpLen}}});
+% true -> true %% make real assert later..
+% end,
+% {LenMask, EOctets} = case OctExpLen of % bit 2,1
+% 1 -> {0, OctExp};
+% 2 -> {1, OctExp};
+% 3 -> {2, OctExp};
+% _ -> {3, [OctExpLen, OctExp]}
+% end,
+% FirstOctet = (SignBitMask bor InternalBaseMask bor
+% ScalingFactorMask bor LenMask bor
+% 2#10000000), % bit set for binary mantissa encoding!
+% OctMantissa = if Man > 0 -> minimum_octets(Man);
+% true -> minimum_octets(-(Man)) % signbit keeps track of sign
+% end,
+%% ok = io:format("LenMask: ~w EOctets: ~w~nFirstOctet: ~w OctMantissa: ~w OctExpLen: ~w~n", [LenMask, EOctets, FirstOctet, OctMantissa, OctExpLen]),
+% {[FirstOctet, EOctets, OctMantissa],
+% length(OctMantissa) +
+% (if OctExpLen > 3 ->
+% OctExpLen + 2;
+% true ->
+% OctExpLen + 1
+% end)
+% }.
+
+
+%%============================================================================
+%% decode real value
+%%
+%% decode_real([OctetBufferList], tuple|value, tag|notag) ->
+%% {{Mantissa, Base, Exp} | realval | PLUS-INFINITY | MINUS-INFINITY | 0,
+%% RestBuff}
+%%
+%% only for base 2 decoding sofar!!
+%%============================================================================
+
+decode_real(Buffer, Form, Tags, OptOrMand) ->
+ NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,number=?N_REAL}),
+ decode_real_notag(Buffer, Form, NewTags, OptOrMand).
+
+decode_real_notag(Buffer, Form, Tags, OptOrMand) ->
+ {RestTags, {FormLen, Buffer0, Rb0}} =
+ check_tags_i(Tags, Buffer, OptOrMand),
+
+ case FormLen of
+ {?CONSTRUCTED,Len} ->
+ {Buffer00,RestBytes} = split_list(Buffer0,Len),
+ {Val01, Buffer01, Rb01} =
+ decode_real_notag(Buffer00, Form, RestTags, OptOrMand),
+ {Buffer02, Rb02} = restbytes2(RestBytes,Buffer01,noext),
+ {Val01, Buffer02, Rb0+Rb01+Rb02};
+ {_,Len} ->
+ decode_real2(Buffer0, Form, Len, Rb0)
+ end.
+
+decode_real2(Buffer0, Form, Len, RemBytes1) ->
+ <> = Buffer0,
+ if
+ First =:= 2#01000000 -> {'PLUS-INFINITY', Buffer2};
+ First =:= 2#01000001 -> {'MINUS-INFINITY', Buffer2};
+ First =:= 2#00000000 -> {0, Buffer2};
+ true ->
+ %% have some check here to verify only supported bases (2)
+ <<_B7:1,B6:1,B5_4:2,B3_2:2,B1_0:2>> = <>,
+ Sign = B6,
+ Base =
+ case B5_4 of
+ 0 -> 2; % base 2, only one so far
+ _ -> exit({error,{asn1, {non_supported_base, First}}})
+ end,
+% ScalingFactor =
+ case B3_2 of
+ 0 -> 0; % no scaling so far
+ _ -> exit({error,{asn1, {non_supported_scaling, First}}})
+ end,
+ % ok = io:format("Buffer2: ~w~n",[Buffer2]),
+ {FirstLen, {Exp, Buffer3}, RemBytes2} =
+ case B1_0 of
+ 0 -> {2, decode_integer2(1, Buffer2, RemBytes1), RemBytes1+1};
+ 1 -> {3, decode_integer2(2, Buffer2, RemBytes1), RemBytes1+2};
+ 2 -> {4, decode_integer2(3, Buffer2, RemBytes1), RemBytes1+3};
+ 3 ->
+ <> = Buffer2,
+ { ExpLen1 + 2,
+ decode_integer2(ExpLen1, RestBuffer, RemBytes1),
+ RemBytes1+ExpLen1}
+ end,
+ % io:format("FirstLen: ~w, Exp: ~w, Buffer3: ~w ~n",
+ % [FirstLen, Exp, Buffer3]),
+ Length = Len - FirstLen,
+ <> = Buffer3,
+ {{Mantissa, Buffer4}, RemBytes3} =
+ if Sign =:= 0 ->
+ % io:format("sign plus~n"),
+ {{LongInt, RestBuff}, 1 + Length};
+ true ->
+ % io:format("sign minus~n"),
+ {{-LongInt, RestBuff}, 1 + Length}
+ end,
+ % io:format("Form: ~w~n",[Form]),
+ case Form of
+ tuple ->
+ {Val,Buf,_RemB} = Exp,
+ {{Mantissa, Base, {Val,Buf}}, Buffer4, RemBytes2+RemBytes3};
+ _value ->
+ comming
+ end
+ end.
+
+
+%%============================================================================
+%% Bitstring value, ITU_T X.690 Chapter 8.6
+%%
+%% encode bitstring value
+%%
+%% bitstring NamedBitList
+%% Val can be of:
+%% - [identifiers] where only named identifers are set to one,
+%% the Constraint must then have some information of the
+%% bitlength.
+%% - [list of ones and zeroes] all bits
+%% - integer value representing the bitlist
+%% C is constrint Len, only valid when identifiers
+%%============================================================================
+
+encode_bit_string(C,Bin={Unused,BinBits},NamedBitList,DoTag) when integer(Unused), binary(BinBits) ->
+ encode_bin_bit_string(C,Bin,NamedBitList,DoTag);
+encode_bit_string(C, [FirstVal | RestVal], NamedBitList, DoTag) when atom(FirstVal) ->
+ encode_bit_string_named(C, [FirstVal | RestVal], NamedBitList, DoTag);
+
+encode_bit_string(C, [{bit,X} | RestVal], NamedBitList, DoTag) ->
+ encode_bit_string_named(C, [{bit,X} | RestVal], NamedBitList, DoTag);
+
+encode_bit_string(C, [FirstVal| RestVal], NamedBitList, DoTag) when integer(FirstVal) ->
+ encode_bit_string_bits(C, [FirstVal | RestVal], NamedBitList, DoTag);
+
+encode_bit_string(_, 0, _, []) ->
+ {[?N_BIT_STRING,1,0],3};
+
+encode_bit_string(_, 0, _, DoTag) ->
+ dotag(DoTag, ?N_BIT_STRING, {<<0>>,1});
+
+encode_bit_string(_, [], _, []) ->
+ {[?N_BIT_STRING,1,0],3};
+
+encode_bit_string(_, [], _, DoTag) ->
+ dotag(DoTag, ?N_BIT_STRING, {<<0>>,1});
+
+encode_bit_string(C, IntegerVal, NamedBitList, DoTag) when integer(IntegerVal) ->
+ BitListVal = int_to_bitlist(IntegerVal),
+ encode_bit_string_bits(C, BitListVal, NamedBitList, DoTag);
+
+encode_bit_string(C, {Name,BitList}, NamedBitList, DoTag) when atom(Name) ->
+ encode_bit_string(C, BitList, NamedBitList, DoTag).
+
+
+
+int_to_bitlist(0) ->
+ [];
+int_to_bitlist(Int) when integer(Int), Int >= 0 ->
+ [Int band 1 | int_to_bitlist(Int bsr 1)].
+
+
+%%=================================================================
+%% Encode BIT STRING of the form {Unused,BinBits}.
+%% Unused is the number of unused bits in the last byte in BinBits
+%% and BinBits is a binary representing the BIT STRING.
+%%=================================================================
+encode_bin_bit_string(C,{Unused,BinBits},_NamedBitList,DoTag)->
+ case get_constraint(C,'SizeConstraint') of
+ no ->
+ remove_unused_then_dotag(DoTag,?N_BIT_STRING,Unused,BinBits);
+ {_Min,Max} ->
+ BBLen = (size(BinBits)*8)-Unused,
+ if
+ BBLen > Max ->
+ exit({error,{asn1,
+ {bitstring_length,
+ {{was,BBLen},{maximum,Max}}}}});
+ true ->
+ remove_unused_then_dotag(DoTag,?N_BIT_STRING,
+ Unused,BinBits)
+ end;
+ Size ->
+ case ((size(BinBits)*8)-Unused) of
+ BBSize when BBSize =< Size ->
+ remove_unused_then_dotag(DoTag,?N_BIT_STRING,
+ Unused,BinBits);
+ BBSize ->
+ exit({error,{asn1,
+ {bitstring_length,
+ {{was,BBSize},{should_be,Size}}}}})
+ end
+ end.
+
+remove_unused_then_dotag(DoTag,StringType,Unused,BinBits) ->
+ case Unused of
+ 0 when (size(BinBits) == 0),DoTag==[] ->
+ %% time optimization of next case
+ {[StringType,1,0],3};
+ 0 when (size(BinBits) == 0) ->
+ dotag(DoTag,StringType,{<<0>>,1});
+ 0 when DoTag==[]-> % time optimization of next case
+ dotag_universal(StringType,[Unused|BinBits],size(BinBits)+1);
+% {LenEnc,Len} = encode_legth(size(BinBits)+1),
+% {[StringType,LenEnc,[Unused|BinBits]],size(BinBits)+1+Len+1};
+ 0 ->
+ dotag(DoTag,StringType,<>);
+ Num when DoTag == [] -> % time optimization of next case
+ N = (size(BinBits)-1),
+ <> = BinBits,
+ dotag_universal(StringType,
+ [Unused,BBits,(LastByte bsr Num) bsl Num],
+ size(BinBits)+1);
+% {LenEnc,Len} = encode_legth(size(BinBits)+1),
+% {[StringType,LenEnc,[Unused,BBits,(LastByte bsr Num) bsl Num],
+% 1+Len+size(BinBits)+1};
+ Num ->
+ N = (size(BinBits)-1),
+ <> = BinBits,
+ dotag(DoTag,StringType,{[Unused,binary_to_list(BBits) ++
+ [(LastByte bsr Num) bsl Num]],
+ 1+size(BinBits)})
+ end.
+
+
+%%=================================================================
+%% Encode named bits
+%%=================================================================
+
+encode_bit_string_named(C, [FirstVal | RestVal], NamedBitList, DoTag) ->
+ {Len,Unused,OctetList} =
+ case get_constraint(C,'SizeConstraint') of
+ no ->
+ ToSetPos = get_all_bitposes([FirstVal | RestVal],
+ NamedBitList, []),
+ BitList = make_and_set_list(lists:max(ToSetPos)+1,
+ ToSetPos, 0),
+ encode_bitstring(BitList);
+ {_Min,Max} ->
+ ToSetPos = get_all_bitposes([FirstVal | RestVal],
+ NamedBitList, []),
+ BitList = make_and_set_list(Max, ToSetPos, 0),
+ encode_bitstring(BitList);
+ Size ->
+ ToSetPos = get_all_bitposes([FirstVal | RestVal],
+ NamedBitList, []),
+ BitList = make_and_set_list(Size, ToSetPos, 0),
+ encode_bitstring(BitList)
+ end,
+ case DoTag of
+ [] ->
+ dotag_universal(?N_BIT_STRING,[Unused|OctetList],Len+1);
+% {EncLen,LenLen} = encode_length(Len+1),
+% {[?N_BIT_STRING,EncLen,Unused,OctetList],1+LenLen+Len+1};
+ _ ->
+ dotag(DoTag, ?N_BIT_STRING, {[Unused|OctetList],Len+1})
+ end.
+
+
+%%----------------------------------------
+%% get_all_bitposes([list of named bits to set], named_bit_db, []) ->
+%% [sorted_list_of_bitpositions_to_set]
+%%----------------------------------------
+
+get_all_bitposes([{bit,ValPos}|Rest], NamedBitList, Ack) ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack ]);
+get_all_bitposes([Val | Rest], NamedBitList, Ack) when atom(Val) ->
+ case lists:keysearch(Val, 1, NamedBitList) of
+ {value, {_ValName, ValPos}} ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack]);
+ _ ->
+ exit({error,{asn1, {bitstring_namedbit, Val}}})
+ end;
+get_all_bitposes([], _NamedBitList, Ack) ->
+ lists:sort(Ack).
+
+
+%%----------------------------------------
+%% make_and_set_list(Len of list to return, [list of positions to set to 1])->
+%% returns list of Len length, with all in SetPos set.
+%% in positioning in list the first element is 0, the second 1 etc.., but
+%% Len will make a list of length Len, not Len + 1.
+%% BitList = make_and_set_list(C, ToSetPos, 0),
+%%----------------------------------------
+
+make_and_set_list(0, [], _) -> [];
+make_and_set_list(0, _, _) ->
+ exit({error,{asn1,bitstring_sizeconstraint}});
+make_and_set_list(Len, [XPos|SetPos], XPos) ->
+ [1 | make_and_set_list(Len - 1, SetPos, XPos + 1)];
+make_and_set_list(Len, [Pos|SetPos], XPos) ->
+ [0 | make_and_set_list(Len - 1, [Pos | SetPos], XPos + 1)];
+make_and_set_list(Len, [], XPos) ->
+ [0 | make_and_set_list(Len - 1, [], XPos + 1)].
+
+
+
+
+
+
+%%=================================================================
+%% Encode bit string for lists of ones and zeroes
+%%=================================================================
+encode_bit_string_bits(C, BitListVal, _NamedBitList, DoTag) when list(BitListVal) ->
+ {Len,Unused,OctetList} =
+ case get_constraint(C,'SizeConstraint') of
+ no ->
+ encode_bitstring(BitListVal);
+ Constr={Min,Max} when integer(Min),integer(Max) ->
+ encode_constr_bit_str_bits(Constr,BitListVal,DoTag);
+ {Constr={_,_},[]} ->
+ %% constraint with extension mark
+ encode_constr_bit_str_bits(Constr,BitListVal,DoTag);
+ Constr={{_,_},{_,_}} ->%{{Min1,Max1},{Min2,Max2}}
+ %% constraint with extension mark
+ encode_constr_bit_str_bits(Constr,BitListVal,DoTag);
+ Size ->
+ case length(BitListVal) of
+ BitSize when BitSize == Size ->
+ encode_bitstring(BitListVal);
+ BitSize when BitSize < Size ->
+ PaddedList =
+ pad_bit_list(Size-BitSize,BitListVal),
+ encode_bitstring(PaddedList);
+ BitSize ->
+ exit({error,
+ {asn1,
+ {bitstring_length,
+ {{was,BitSize},
+ {should_be,Size}}}}})
+ end
+ end,
+ %%add unused byte to the Len
+ case DoTag of
+ [] ->
+ dotag_universal(?N_BIT_STRING,[Unused|OctetList],Len+1);
+% {EncLen,LenLen}=encode_length(Len+1),
+% {[?N_BIT_STRING,EncLen,Unused|OctetList],1+LenLen+Len+1};
+ _ ->
+ dotag(DoTag, ?N_BIT_STRING,
+ {[Unused | OctetList],Len+1})
+ end.
+
+
+encode_constr_bit_str_bits({_Min,Max},BitListVal,_DoTag) ->
+ BitLen = length(BitListVal),
+ if
+ BitLen > Max ->
+ exit({error,{asn1,{bitstring_length,{{was,BitLen},
+ {maximum,Max}}}}});
+ true ->
+ encode_bitstring(BitListVal)
+ end;
+encode_constr_bit_str_bits({{_Min1,Max1},{Min2,Max2}},BitListVal,_DoTag) ->
+ BitLen = length(BitListVal),
+ case BitLen of
+ Len when Len > Max2 ->
+ exit({error,{asn1,{bitstring_length,{{was,BitLen},
+ {maximum,Max2}}}}});
+ Len when Len > Max1, Len < Min2 ->
+ exit({error,{asn1,{bitstring_length,{{was,BitLen},
+ {not_allowed_interval,
+ Max1,Min2}}}}});
+ _ ->
+ encode_bitstring(BitListVal)
+ end.
+
+%% returns a list of length Size + length(BitListVal), with BitListVal
+%% as the most significant elements followed by padded zero elements
+pad_bit_list(Size,BitListVal) ->
+ Tail = lists:duplicate(Size,0),
+ lists:append(BitListVal,Tail).
+
+%%=================================================================
+%% Do the actual encoding
+%% ([bitlist]) -> {ListLen, UnusedBits, OctetList}
+%%=================================================================
+
+encode_bitstring([B8, B7, B6, B5, B4, B3, B2, B1 | Rest]) ->
+ Val = (B8 bsl 7) bor (B7 bsl 6) bor (B6 bsl 5) bor (B5 bsl 4) bor
+ (B4 bsl 3) bor (B3 bsl 2) bor (B2 bsl 1) bor B1,
+ encode_bitstring(Rest, [Val], 1);
+encode_bitstring(Val) ->
+ {Unused, Octet} = unused_bitlist(Val, 7, 0),
+ {1, Unused, [Octet]}.
+
+encode_bitstring([B8, B7, B6, B5, B4, B3, B2, B1 | Rest], Ack, Len) ->
+ Val = (B8 bsl 7) bor (B7 bsl 6) bor (B6 bsl 5) bor (B5 bsl 4) bor
+ (B4 bsl 3) bor (B3 bsl 2) bor (B2 bsl 1) bor B1,
+ encode_bitstring(Rest, [Ack | [Val]], Len + 1);
+%%even multiple of 8 bits..
+encode_bitstring([], Ack, Len) ->
+ {Len, 0, Ack};
+%% unused bits in last octet
+encode_bitstring(Rest, Ack, Len) ->
+% io:format("uneven ~w ~w ~w~n",[Rest, Ack, Len]),
+ {Unused, Val} = unused_bitlist(Rest, 7, 0),
+ {Len + 1, Unused, [Ack | [Val]]}.
+
+%%%%%%%%%%%%%%%%%%
+%% unused_bitlist([list of ones and zeros <= 7], 7, []) ->
+%% {Unused bits, Last octet with bits moved to right}
+unused_bitlist([], Trail, Ack) ->
+ {Trail + 1, Ack};
+unused_bitlist([Bit | Rest], Trail, Ack) ->
+%% io:format("trail Bit: ~w Rest: ~w Trail: ~w Ack:~w~n",[Bit, Rest, Trail, Ack]),
+ unused_bitlist(Rest, Trail - 1, (Bit bsl Trail) bor Ack).
+
+
+%%============================================================================
+%% decode bitstring value
+%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes}
+%%============================================================================
+
+decode_compact_bit_string(Buffer, Range, NamedNumberList, Tags, LenIn, OptOrMand) ->
+% NewTags = new_tags(HasTag,#tag{class=?UNIVERSAL,number=?N_BIT_STRING}),
+ decode_restricted_string(Buffer, Range, ?N_BIT_STRING, Tags, LenIn,
+ NamedNumberList, OptOrMand,bin).
+
+decode_bit_string(Buffer, Range, NamedNumberList, Tags, LenIn, OptOrMand) ->
+% NewTags = new_tags(HasTag,#tag{class=?UNIVERSAL,number=?N_BIT_STRING}),
+ decode_restricted_string(Buffer, Range, ?N_BIT_STRING, Tags, LenIn,
+ NamedNumberList, OptOrMand,old).
+
+
+decode_bit_string2(1,<<0 ,Buffer/binary>>,_NamedNumberList,RemovedBytes,BinOrOld) ->
+ case BinOrOld of
+ bin ->
+ {{0,<<>>},Buffer,RemovedBytes};
+ _ ->
+ {[], Buffer, RemovedBytes}
+ end;
+decode_bit_string2(Len,<>,NamedNumberList,
+ RemovedBytes,BinOrOld) ->
+ L = Len - 1,
+ <> = Buffer,
+ case NamedNumberList of
+ [] ->
+ case BinOrOld of
+ bin ->
+ {{Unused,Bits},BufferTail,RemovedBytes};
+ _ ->
+ BitString = decode_bitstring2(L, Unused, Buffer),
+ {BitString,BufferTail, RemovedBytes}
+ end;
+ _ ->
+ BitString = decode_bitstring2(L, Unused, Buffer),
+ {decode_bitstring_NNL(BitString,NamedNumberList),
+ BufferTail,
+ RemovedBytes}
+ end.
+
+%%----------------------------------------
+%% Decode the in buffer to bits
+%%----------------------------------------
+decode_bitstring2(1,Unused,<>) ->
+ lists:sublist([B7,B6,B5,B4,B3,B2,B1,B0],8-Unused);
+decode_bitstring2(Len, Unused,
+ <>) ->
+ [B7, B6, B5, B4, B3, B2, B1, B0 |
+ decode_bitstring2(Len - 1, Unused, Buffer)].
+
+%%decode_bitstring2(1, Unused, Buffer) ->
+%% make_bits_of_int(hd(Buffer), 128, 8-Unused);
+%%decode_bitstring2(Len, Unused, [BitVal | Buffer]) ->
+%% [B7, B6, B5, B4, B3, B2, B1, B0] = make_bits_of_int(BitVal, 128, 8),
+%% [B7, B6, B5, B4, B3, B2, B1, B0 |
+%% decode_bitstring2(Len - 1, Unused, Buffer)].
+
+
+%%make_bits_of_int(_, _, 0) ->
+%% [];
+%%make_bits_of_int(BitVal, MaskVal, Unused) when Unused > 0 ->
+%% X = case MaskVal band BitVal of
+%% 0 -> 0 ;
+%% _ -> 1
+%% end,
+%% [X | make_bits_of_int(BitVal, MaskVal bsr 1, Unused - 1)].
+
+
+
+%%----------------------------------------
+%% Decode the bitlist to names
+%%----------------------------------------
+
+
+decode_bitstring_NNL(BitList,NamedNumberList) ->
+ decode_bitstring_NNL(BitList,NamedNumberList,0,[]).
+
+
+decode_bitstring_NNL([],_,_No,Result) ->
+ lists:reverse(Result);
+
+decode_bitstring_NNL([B|BitList],[{Name,No}|NamedNumberList],No,Result) ->
+ if
+ B == 0 ->
+ decode_bitstring_NNL(BitList,NamedNumberList,No+1,Result);
+ true ->
+ decode_bitstring_NNL(BitList,NamedNumberList,No+1,[Name|Result])
+ end;
+decode_bitstring_NNL([1|BitList],NamedNumberList,No,Result) ->
+ decode_bitstring_NNL(BitList,NamedNumberList,No+1,[{bit,No}|Result]);
+decode_bitstring_NNL([0|BitList],NamedNumberList,No,Result) ->
+ decode_bitstring_NNL(BitList,NamedNumberList,No+1,Result).
+
+
+%%============================================================================
+%% Octet string, ITU_T X.690 Chapter 8.7
+%%
+%% encode octet string
+%% The OctetList must be a flat list of integers in the range 0..255
+%% the function does not check this because it takes to much time
+%%============================================================================
+encode_octet_string(_C, OctetList, []) when binary(OctetList) ->
+ dotag_universal(?N_OCTET_STRING,OctetList,size(OctetList));
+encode_octet_string(_C, OctetList, DoTag) when binary(OctetList) ->
+ dotag(DoTag, ?N_OCTET_STRING, {OctetList,size(OctetList)});
+encode_octet_string(_C, OctetList, DoTag) when list(OctetList) ->
+ case length(OctetList) of
+ Len when DoTag == [] ->
+ dotag_universal(?N_OCTET_STRING,OctetList,Len);
+ Len ->
+ dotag(DoTag, ?N_OCTET_STRING, {OctetList,Len})
+ end;
+% encode_octet_string(C, OctetList, DoTag) when list(OctetList) ->
+% dotag(DoTag, ?N_OCTET_STRING, {OctetList,length(OctetList)});
+encode_octet_string(C, {Name,OctetList}, DoTag) when atom(Name) ->
+ encode_octet_string(C, OctetList, DoTag).
+
+
+%%============================================================================
+%% decode octet string
+%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes}
+%%
+%% Octet string is decoded as a restricted string
+%%============================================================================
+decode_octet_string(Buffer, Range, Tags, TotalLen, OptOrMand) ->
+% NewTags = new_tags(HasTag,#tag{class=?UNIVERSAL,number=?N_OCTET_STRING}),
+ decode_restricted_string(Buffer, Range, ?N_OCTET_STRING,
+ Tags, TotalLen, [], OptOrMand,old).
+
+%%============================================================================
+%% Null value, ITU_T X.690 Chapter 8.8
+%%
+%% encode NULL value
+%%============================================================================
+
+encode_null(_, []) ->
+ {[?N_NULL,0],2};
+encode_null(_, DoTag) ->
+ dotag(DoTag, ?N_NULL, {[],0}).
+
+%%============================================================================
+%% decode NULL value
+%% (Buffer, HasTag, TotalLen) -> {NULL, Remain, RemovedBytes}
+%%============================================================================
+decode_null(Buffer, Tags, OptOrMand) ->
+ NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,number=?N_NULL}),
+ decode_null_notag(Buffer, NewTags, OptOrMand).
+
+decode_null_notag(Buffer, Tags, OptOrMand) ->
+ {RestTags, {FormLen, Buffer0, Rb0}} =
+ check_tags_i(Tags, Buffer, OptOrMand),
+
+ case FormLen of
+ {?CONSTRUCTED,Len} ->
+ {_Buffer00,RestBytes} = split_list(Buffer0,Len),
+ {Val01, Buffer01, Rb01} = decode_null_notag(Buffer0, RestTags,
+ OptOrMand),
+ {Buffer02, Rb02} = restbytes2(RestBytes,Buffer01,noext),
+ {Val01, Buffer02, Rb0+Rb01+Rb02};
+ {_,0} ->
+ {'NULL', Buffer0, Rb0};
+ {_,Len} ->
+ exit({error,{asn1,{invalid_length,'NULL',Len}}})
+ end.
+
+
+%%============================================================================
+%% Object identifier, ITU_T X.690 Chapter 8.19
+%%
+%% encode Object Identifier value
+%%============================================================================
+
+encode_object_identifier({Name,Val}, DoTag) when atom(Name) ->
+ encode_object_identifier(Val, DoTag);
+encode_object_identifier(Val, []) ->
+ {EncVal,Len} = e_object_identifier(Val),
+ dotag_universal(?N_OBJECT_IDENTIFIER,EncVal,Len);
+encode_object_identifier(Val, DoTag) ->
+ dotag(DoTag, ?N_OBJECT_IDENTIFIER, e_object_identifier(Val)).
+
+e_object_identifier({'OBJECT IDENTIFIER', V}) ->
+ e_object_identifier(V);
+e_object_identifier({Cname, V}) when atom(Cname), tuple(V) ->
+ e_object_identifier(tuple_to_list(V));
+e_object_identifier({Cname, V}) when atom(Cname), list(V) ->
+ e_object_identifier(V);
+e_object_identifier(V) when tuple(V) ->
+ e_object_identifier(tuple_to_list(V));
+
+%%%%%%%%%%%%%%%
+%% e_object_identifier([List of Obect Identifiers]) ->
+%% {[Encoded Octetlist of ObjIds], IntLength}
+%%
+e_object_identifier([E1, E2 | Tail]) ->
+ Head = 40*E1 + E2, % wow!
+ {H,Lh} = mk_object_val(Head),
+ {R,Lr} = enc_obj_id_tail(Tail, [], 0),
+ {[H|R], Lh+Lr}.
+
+enc_obj_id_tail([], Ack, Len) ->
+ {lists:reverse(Ack), Len};
+enc_obj_id_tail([H|T], Ack, Len) ->
+ {B, L} = mk_object_val(H),
+ enc_obj_id_tail(T, [B|Ack], Len+L).
+
+%% e_object_identifier([List of Obect Identifiers]) ->
+%% {[Encoded Octetlist of ObjIds], IntLength}
+%%
+%%e_object_identifier([E1, E2 | Tail]) ->
+%% Head = 40*E1 + E2, % wow!
+%% F = fun(Val, AckLen) ->
+%% {L, Ack} = mk_object_val(Val),
+%% {L, Ack + AckLen}
+%% end,
+%% {Octets, Len} = lists:mapfoldl(F, 0, [Head | Tail]).
+
+%%%%%%%%%%%
+%% mk_object_val(Value) -> {OctetList, Len}
+%% returns a Val as a list of octets, the 8 bit is allways set to one except
+%% for the last octet, where its 0
+%%
+
+
+mk_object_val(Val) when Val =< 127 ->
+ {[255 band Val], 1};
+mk_object_val(Val) ->
+ mk_object_val(Val bsr 7, [Val band 127], 1).
+mk_object_val(0, Ack, Len) ->
+ {Ack, Len};
+mk_object_val(Val, Ack, Len) ->
+ mk_object_val(Val bsr 7, [((Val band 127) bor 128) | Ack], Len + 1).
+
+
+
+%%============================================================================
+%% decode Object Identifier value
+%% (Buffer, HasTag, TotalLen) -> {{ObjId}, Remain, RemovedBytes}
+%%============================================================================
+
+decode_object_identifier(Buffer, Tags, OptOrMand) ->
+ NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,
+ number=?N_OBJECT_IDENTIFIER}),
+ decode_object_identifier_notag(Buffer, NewTags, OptOrMand).
+
+decode_object_identifier_notag(Buffer, Tags, OptOrMand) ->
+ {RestTags, {FormLen, Buffer0, Rb0}} =
+ check_tags_i(Tags, Buffer, OptOrMand),
+
+ case FormLen of
+ {?CONSTRUCTED,Len} ->
+ {Buffer00,RestBytes} = split_list(Buffer0,Len),
+ {Val01, Buffer01, Rb01} =
+ decode_object_identifier_notag(Buffer00,
+ RestTags, OptOrMand),
+ {Buffer02, Rb02} = restbytes2(RestBytes,Buffer01,noext),
+ {Val01, Buffer02, Rb0+Rb01+Rb02};
+ {_,Len} ->
+ {[AddedObjVal|ObjVals],Buffer01} =
+ dec_subidentifiers(Buffer0,0,[],Len),
+ {Val1, Val2} = if
+ AddedObjVal < 40 ->
+ {0, AddedObjVal};
+ AddedObjVal < 80 ->
+ {1, AddedObjVal - 40};
+ true ->
+ {2, AddedObjVal - 80}
+ end,
+ {list_to_tuple([Val1, Val2 | ObjVals]), Buffer01,
+ Rb0+Len}
+ end.
+
+dec_subidentifiers(Buffer,_Av,Al,0) ->
+ {lists:reverse(Al),Buffer};
+dec_subidentifiers(<<1:1,H:7,T/binary>>,Av,Al,Len) ->
+ dec_subidentifiers(T,(Av bsl 7) + H,Al,Len-1);
+dec_subidentifiers(<>,Av,Al,Len) ->
+ dec_subidentifiers(T,0,[((Av bsl 7) + H)|Al],Len-1).
+
+
+%%dec_subidentifiers(Buffer,Av,Al,0) ->
+%% {lists:reverse(Al),Buffer};
+%%dec_subidentifiers([H|T],Av,Al,Len) when H >=16#80 ->
+%% dec_subidentifiers(T,(Av bsl 7) + (H band 16#7F),Al,Len-1);
+%%dec_subidentifiers([H|T],Av,Al,Len) ->
+%% dec_subidentifiers(T,0,[(Av bsl 7) + H |Al],Len-1).
+
+
+%%============================================================================
+%% Restricted character string types, ITU_T X.690 Chapter 8.20
+%%
+%% encode Numeric Printable Teletex Videotex Visible IA5 Graphic General strings
+%%============================================================================
+encode_restricted_string(_C, OctetList, StringType, [])
+ when binary(OctetList) ->
+ dotag_universal(StringType,OctetList,size(OctetList));
+encode_restricted_string(_C, OctetList, StringType, DoTag)
+ when binary(OctetList) ->
+ dotag(DoTag, StringType, {OctetList, size(OctetList)});
+encode_restricted_string(_C, OctetList, StringType, [])
+ when list(OctetList) ->
+ dotag_universal(StringType,OctetList,length(OctetList));
+encode_restricted_string(_C, OctetList, StringType, DoTag)
+ when list(OctetList) ->
+ dotag(DoTag, StringType, {OctetList, length(OctetList)});
+encode_restricted_string(C,{Name,OctetL},StringType,DoTag) when atom(Name)->
+ encode_restricted_string(C, OctetL, StringType, DoTag).
+
+%%============================================================================
+%% decode Numeric Printable Teletex Videotex Visible IA5 Graphic General strings
+%% (Buffer, Range, StringType, HasTag, TotalLen) ->
+%% {String, Remain, RemovedBytes}
+%%============================================================================
+
+decode_restricted_string(Buffer, Range, StringType, Tags, LenIn, OptOrMand) ->
+ {Val,Buffer2,Rb} =
+ decode_restricted_string_tag(Buffer, Range, StringType, Tags,
+ LenIn, [], OptOrMand,old),
+ {check_and_convert_restricted_string(Val,StringType,Range,[],old),
+ Buffer2,Rb}.
+
+
+decode_restricted_string(Buffer, Range, StringType, Tags, LenIn, NNList, OptOrMand, BinOrOld ) ->
+ {Val,Buffer2,Rb} =
+ decode_restricted_string_tag(Buffer, Range, StringType, Tags,
+ LenIn, NNList, OptOrMand, BinOrOld),
+ {check_and_convert_restricted_string(Val,StringType,Range,NNList,BinOrOld),
+ Buffer2,Rb}.
+
+decode_restricted_string_tag(Buffer, Range, StringType, TagsIn, LenIn, NNList, OptOrMand, BinOrOld ) ->
+ NewTags = new_tags(TagsIn, #tag{class=?UNIVERSAL,number=StringType}),
+ decode_restricted_string_notag(Buffer, Range, StringType, NewTags,
+ LenIn, NNList, OptOrMand, BinOrOld).
+
+
+
+
+check_and_convert_restricted_string(Val,StringType,Range,NamedNumberList,_BinOrOld) ->
+ {StrLen,NewVal} = case StringType of
+ ?N_BIT_STRING when NamedNumberList /= [] ->
+ {no_check,Val};
+ ?N_BIT_STRING when list(Val) ->
+ {length(Val),Val};
+ ?N_BIT_STRING when tuple(Val) ->
+ {(size(element(2,Val))*8) - element(1,Val),Val};
+ _ when binary(Val) ->
+ {size(Val),binary_to_list(Val)};
+ _ when list(Val) ->
+ {length(Val), Val}
+ end,
+ case Range of
+ _ when StrLen == no_check ->
+ NewVal;
+ [] -> % No length constraint
+ NewVal;
+ {Lb,Ub} when StrLen >= Lb, Ub >= StrLen -> % variable length constraint
+ NewVal;
+ {{Lb,_Ub},[]} when StrLen >= Lb ->
+ NewVal;
+ {{Lb1,Ub1},{Lb2,Ub2}} when StrLen >= Lb1, StrLen =< Ub1;
+ StrLen =< Ub2, StrLen >= Lb2 ->
+ NewVal;
+ StrLen -> % fixed length constraint
+ NewVal;
+ {_,_} ->
+ exit({error,{asn1,{length,Range,Val}}});
+ _Len when integer(_Len) ->
+ exit({error,{asn1,{length,Range,Val}}});
+ _ -> % some strange constraint that we don't support yet
+ NewVal
+ end.
+
+
+%%=============================================================================
+%% Common routines for several string types including bit string
+%% handles indefinite length
+%%=============================================================================
+
+
+decode_restricted_string_notag(Buffer, _Range, StringType, TagsIn,
+ _, NamedNumberList, OptOrMand,BinOrOld) ->
+ %%-----------------------------------------------------------
+ %% Get inner (the implicit tag or no tag) and
+ %% outer (the explicit tag) lengths.
+ %%-----------------------------------------------------------
+ {RestTags, {FormLength={_,_Len01}, Buffer0, Rb0}} =
+ check_tags_i(TagsIn, Buffer, OptOrMand),
+
+ case FormLength of
+ {?CONSTRUCTED,Len} ->
+ {Buffer00, RestBytes} = split_list(Buffer0,Len),
+ {Val01, Buffer01, Rb01} =
+ decode_restricted_parts(Buffer00, RestBytes, [], StringType,
+ RestTags,
+ Len, NamedNumberList,
+ OptOrMand,
+ BinOrOld, 0, []),
+ {Val01, Buffer01, Rb0+Rb01};
+ {_, Len} ->
+ {Val01, Buffer01, Rb01} =
+ decode_restricted(Buffer0, Len, StringType,
+ NamedNumberList, BinOrOld),
+ {Val01, Buffer01, Rb0+Rb01}
+ end.
+
+
+decode_restricted_parts(Buffer, RestBytes, [], StringType, RestTags, Len, NNList,
+ OptOrMand, BinOrOld, AccRb, AccVal) ->
+ DecodeFun = case RestTags of
+ [] -> fun decode_restricted_string_tag/8;
+ _ -> fun decode_restricted_string_notag/8
+ end,
+ {Val, Buffer1, Rb} =
+ DecodeFun(Buffer, [], StringType, RestTags,
+ no_length, NNList,
+ OptOrMand, BinOrOld),
+ {Buffer2,More} =
+ case Buffer1 of
+ <<0,0,Buffer10/binary>> when Len == indefinite ->
+ {Buffer10,false};
+ <<>> ->
+ {RestBytes,false};
+ _ ->
+ {Buffer1,true}
+ end,
+ {NewVal, NewRb} =
+ case StringType of
+ ?N_BIT_STRING when BinOrOld == bin ->
+ {concat_bit_binaries(AccVal, Val), AccRb+Rb};
+ _ when binary(Val),binary(AccVal) ->
+ {<>,AccRb+Rb};
+ _ when binary(Val), AccVal==[] ->
+ {Val,AccRb+Rb};
+ _ ->
+ {AccVal++Val, AccRb+Rb}
+ end,
+ case More of
+ false ->
+ {NewVal, Buffer2, NewRb};
+ true ->
+ decode_restricted_parts(Buffer2, RestBytes, [], StringType, RestTags, Len, NNList,
+ OptOrMand, BinOrOld, NewRb, NewVal)
+ end.
+
+
+
+decode_restricted(Buffer, InnerLen, StringType, NamedNumberList,BinOrOld) ->
+
+ case StringType of
+ ?N_BIT_STRING ->
+ decode_bit_string2(InnerLen,Buffer,NamedNumberList,InnerLen,BinOrOld);
+
+ ?N_UniversalString ->
+ <> = Buffer,%%added for binary
+ UniString = mk_universal_string(binary_to_list(PreBuff)),
+ {UniString,RestBuff,InnerLen};
+ ?N_BMPString ->
+ <> = Buffer,%%added for binary
+ BMP = mk_BMP_string(binary_to_list(PreBuff)),
+ {BMP,RestBuff,InnerLen};
+ _ ->
+ <> = Buffer,%%added for binary
+ {PreBuff, RestBuff, InnerLen}
+ end.
+
+
+
+%%============================================================================
+%% encode Universal string
+%%============================================================================
+
+encode_universal_string(C, {Name, Universal}, DoTag) when atom(Name) ->
+ encode_universal_string(C, Universal, DoTag);
+encode_universal_string(_C, Universal, []) ->
+ OctetList = mk_uni_list(Universal),
+ dotag_universal(?N_UniversalString,OctetList,length(OctetList));
+encode_universal_string(_C, Universal, DoTag) ->
+ OctetList = mk_uni_list(Universal),
+ dotag(DoTag, ?N_UniversalString, {OctetList,length(OctetList)}).
+
+mk_uni_list(In) ->
+ mk_uni_list(In,[]).
+
+mk_uni_list([],List) ->
+ lists:reverse(List);
+mk_uni_list([{A,B,C,D}|T],List) ->
+ mk_uni_list(T,[D,C,B,A|List]);
+mk_uni_list([H|T],List) ->
+ mk_uni_list(T,[H,0,0,0|List]).
+
+%%===========================================================================
+%% decode Universal strings
+%% (Buffer, Range, StringType, HasTag, LenIn) ->
+%% {String, Remain, RemovedBytes}
+%%===========================================================================
+
+decode_universal_string(Buffer, Range, Tags, LenIn, OptOrMand) ->
+% NewTags = new_tags(HasTag, #tag{class=?UNIVERSAL,number=?N_UniversalString}),
+ decode_restricted_string(Buffer, Range, ?N_UniversalString,
+ Tags, LenIn, [], OptOrMand,old).
+
+
+mk_universal_string(In) ->
+ mk_universal_string(In,[]).
+
+mk_universal_string([],Acc) ->
+ lists:reverse(Acc);
+mk_universal_string([0,0,0,D|T],Acc) ->
+ mk_universal_string(T,[D|Acc]);
+mk_universal_string([A,B,C,D|T],Acc) ->
+ mk_universal_string(T,[{A,B,C,D}|Acc]).
+
+
+%%============================================================================
+%% encode BMP string
+%%============================================================================
+
+encode_BMP_string(C, {Name,BMPString}, DoTag) when atom(Name)->
+ encode_BMP_string(C, BMPString, DoTag);
+encode_BMP_string(_C, BMPString, []) ->
+ OctetList = mk_BMP_list(BMPString),
+ dotag_universal(?N_BMPString,OctetList,length(OctetList));
+encode_BMP_string(_C, BMPString, DoTag) ->
+ OctetList = mk_BMP_list(BMPString),
+ dotag(DoTag, ?N_BMPString, {OctetList,length(OctetList)}).
+
+mk_BMP_list(In) ->
+ mk_BMP_list(In,[]).
+
+mk_BMP_list([],List) ->
+ lists:reverse(List);
+mk_BMP_list([{0,0,C,D}|T],List) ->
+ mk_BMP_list(T,[D,C|List]);
+mk_BMP_list([H|T],List) ->
+ mk_BMP_list(T,[H,0|List]).
+
+%%============================================================================
+%% decode (OctetList, Range(ignored), tag|notag) -> {ValList, RestList}
+%% (Buffer, Range, StringType, HasTag, TotalLen) ->
+%% {String, Remain, RemovedBytes}
+%%============================================================================
+decode_BMP_string(Buffer, Range, Tags, LenIn, OptOrMand) ->
+% NewTags = new_tags(HasTag, #tag{class=?UNIVERSAL,number=?N_BMPString}),
+ decode_restricted_string(Buffer, Range, ?N_BMPString,
+ Tags, LenIn, [], OptOrMand,old).
+
+mk_BMP_string(In) ->
+ mk_BMP_string(In,[]).
+
+mk_BMP_string([],US) ->
+ lists:reverse(US);
+mk_BMP_string([0,B|T],US) ->
+ mk_BMP_string(T,[B|US]);
+mk_BMP_string([C,D|T],US) ->
+ mk_BMP_string(T,[{0,0,C,D}|US]).
+
+
+%%============================================================================
+%% Generalized time, ITU_T X.680 Chapter 39
+%%
+%% encode Generalized time
+%%============================================================================
+
+encode_generalized_time(C, {Name,OctetList}, DoTag) when atom(Name) ->
+ encode_generalized_time(C, OctetList, DoTag);
+encode_generalized_time(_C, OctetList, []) ->
+ dotag_universal(?N_GeneralizedTime,OctetList,length(OctetList));
+encode_generalized_time(_C, OctetList, DoTag) ->
+ dotag(DoTag, ?N_GeneralizedTime, {OctetList,length(OctetList)}).
+
+%%============================================================================
+%% decode Generalized time
+%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes}
+%%============================================================================
+
+decode_generalized_time(Buffer, Range, Tags, TotalLen, OptOrMand) ->
+ NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,
+ number=?N_GeneralizedTime}),
+ decode_generalized_time_notag(Buffer, Range, NewTags, TotalLen, OptOrMand).
+
+decode_generalized_time_notag(Buffer, Range, Tags, TotalLen, OptOrMand) ->
+ {RestTags, {FormLen, Buffer0, Rb0}} =
+ check_tags_i(Tags, Buffer, OptOrMand),
+
+ case FormLen of
+ {?CONSTRUCTED,Len} ->
+ {Buffer00,RestBytes} = split_list(Buffer0,Len),
+ {Val01, Buffer01, Rb01} =
+ decode_generalized_time_notag(Buffer00, Range,
+ RestTags, TotalLen,
+ OptOrMand),
+ {Buffer02, Rb02} = restbytes2(RestBytes,Buffer01,noext),
+ {Val01, Buffer02, Rb0+Rb01+Rb02};
+ {_,Len} ->
+ <> = Buffer0,
+ {binary_to_list(PreBuff), RestBuff, Rb0+Len}
+ end.
+
+%%============================================================================
+%% Universal time, ITU_T X.680 Chapter 40
+%%
+%% encode UTC time
+%%============================================================================
+
+encode_utc_time(C, {Name,OctetList}, DoTag) when atom(Name) ->
+ encode_utc_time(C, OctetList, DoTag);
+encode_utc_time(_C, OctetList, []) ->
+ dotag_universal(?N_UTCTime, OctetList,length(OctetList));
+encode_utc_time(_C, OctetList, DoTag) ->
+ dotag(DoTag, ?N_UTCTime, {OctetList,length(OctetList)}).
+
+%%============================================================================
+%% decode UTC time
+%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes}
+%%============================================================================
+
+decode_utc_time(Buffer, Range, Tags, TotalLen, OptOrMand) ->
+ NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,number=?N_UTCTime}),
+ decode_utc_time_notag(Buffer, Range, NewTags, TotalLen, OptOrMand).
+
+decode_utc_time_notag(Buffer, Range, Tags, TotalLen, OptOrMand) ->
+ {RestTags, {FormLen, Buffer0, Rb0}} =
+ check_tags_i(Tags, Buffer, OptOrMand),
+
+ case FormLen of
+ {?CONSTRUCTED,Len} ->
+ {Buffer00,RestBytes} = split_list(Buffer0,Len),
+ {Val01, Buffer01, Rb01} =
+ decode_utc_time_notag(Buffer00, Range,
+ RestTags, TotalLen,
+ OptOrMand),
+ {Buffer02, Rb02} = restbytes2(RestBytes,Buffer01,noext),
+ {Val01, Buffer02, Rb0+Rb01+Rb02};
+ {_,Len} ->
+ <> = Buffer0,
+ {binary_to_list(PreBuff), RestBuff, Rb0+Len}
+ end.
+
+
+%%============================================================================
+%% Length handling
+%%
+%% Encode length
+%%
+%% encode_length(Int | indefinite) ->
+%% [<127]| [128 + Int (<127),OctetList] | [16#80]
+%%============================================================================
+
+encode_length(indefinite) ->
+ {[16#80],1}; % 128
+encode_length(L) when L =< 16#7F ->
+ {[L],1};
+encode_length(L) ->
+ Oct = minimum_octets(L),
+ Len = length(Oct),
+ if
+ Len =< 126 ->
+ {[ (16#80+Len) | Oct ],Len+1};
+ true ->
+ exit({error,{asn1, to_long_length_oct, Len}})
+ end.
+
+
+%% Val must be >= 0
+minimum_octets(Val) ->
+ minimum_octets(Val,[]).
+
+minimum_octets(0,Acc) ->
+ Acc;
+minimum_octets(Val, Acc) ->
+ minimum_octets((Val bsr 8),[Val band 16#FF | Acc]).
+
+
+%%===========================================================================
+%% Decode length
+%%
+%% decode_length(OctetList) -> {{indefinite, RestOctetsL}, NoRemovedBytes} |
+%% {{Length, RestOctetsL}, NoRemovedBytes}
+%%===========================================================================
+
+decode_length(<<1:1,0:7,T/binary>>) ->
+ {{indefinite, T}, 1};
+decode_length(<<0:1,Length:7,T/binary>>) ->
+ {{Length,T},1};
+decode_length(<<1:1,LL:7,T/binary>>) ->
+ <> = T,
+ {{Length,Rest}, LL+1}.
+
+%decode_length([128 | T]) ->
+% {{indefinite, T},1};
+%decode_length([H | T]) when H =< 127 ->
+% {{H, T},1};
+%decode_length([H | T]) ->
+% dec_long_length(H band 16#7F, T, 0, 1).
+
+
+%%dec_long_length(0, Buffer, Acc, Len) ->
+%% {{Acc, Buffer},Len};
+%%dec_long_length(Bytes, [H | T], Acc, Len) ->
+%% dec_long_length(Bytes - 1, T, (Acc bsl 8) + H, Len+1).
+
+%%===========================================================================
+%% Decode tag and length
+%%
+%% decode_tag_and_length(Buffer) -> {Tag, Len, RemainingBuffer, RemovedBytes}
+%%
+%%===========================================================================
+
+decode_tag_and_length(Buffer) ->
+ {Tag, Buffer2, RemBytesTag} = decode_tag(Buffer),
+ {{Len, Buffer3}, RemBytesLen} = decode_length(Buffer2),
+ {Tag, Len, Buffer3, RemBytesTag+RemBytesLen}.
+
+
+%%============================================================================
+%% Check if valid tag
+%%
+%% check_if_valid_tag(Tag, List_of_valid_tags, OptOrMand) -> name of the tag
+%%===============================================================================
+
+check_if_valid_tag(<<0,0,_/binary>>,_,_) ->
+ asn1_EOC;
+check_if_valid_tag(<<>>, _, OptOrMand) ->
+ check_if_valid_tag2(false,[],[],OptOrMand);
+check_if_valid_tag(Bytes, ListOfTags, OptOrMand) when binary(Bytes) ->
+ {Tag, _, _} = decode_tag(Bytes),
+ check_if_valid_tag(Tag, ListOfTags, OptOrMand);
+
+%% This alternative should be removed in the near future
+%% Bytes as input should be the only necessary call
+check_if_valid_tag(Tag, ListOfTags, OptOrMand) ->
+ {Class, _Form, TagNo} = Tag,
+ C = code_class(Class),
+ T = case C of
+ 'UNIVERSAL' ->
+ code_type(TagNo);
+ _ ->
+ TagNo
+ end,
+ check_if_valid_tag2({C,T}, ListOfTags, Tag, OptOrMand).
+
+check_if_valid_tag2(_Class_TagNo, [], Tag, mandatory) ->
+ exit({error,{asn1,{invalid_tag,Tag}}});
+check_if_valid_tag2(_Class_TagNo, [], Tag, _) ->
+ exit({error,{asn1,{no_optional_tag,Tag}}});
+
+check_if_valid_tag2(Class_TagNo, [{TagName,TagList}|T], Tag, OptOrMand) ->
+ case check_if_valid_tag_loop(Class_TagNo, TagList) of
+ true ->
+ TagName;
+ false ->
+ check_if_valid_tag2(Class_TagNo, T, Tag, OptOrMand)
+ end.
+
+check_if_valid_tag_loop(_Class_TagNo,[]) ->
+ false;
+check_if_valid_tag_loop(Class_TagNo,[H|T]) ->
+ %% It is not possible to distinguish between SEQUENCE OF and SEQUENCE, and
+ %% between SET OF and SET because both are coded as 16 and 17, respectively.
+ H_without_OF = case H of
+ {C, 'SEQUENCE OF'} ->
+ {C, 'SEQUENCE'};
+ {C, 'SET OF'} ->
+ {C, 'SET'};
+ Else ->
+ Else
+ end,
+
+ case H_without_OF of
+ Class_TagNo ->
+ true;
+ {_,_} ->
+ check_if_valid_tag_loop(Class_TagNo,T);
+ _ ->
+ check_if_valid_tag_loop(Class_TagNo,H),
+ check_if_valid_tag_loop(Class_TagNo,T)
+ end.
+
+
+
+code_class(0) -> 'UNIVERSAL';
+code_class(16#40) -> 'APPLICATION';
+code_class(16#80) -> 'CONTEXT';
+code_class(16#C0) -> 'PRIVATE'.
+
+
+code_type(1) -> 'BOOLEAN';
+code_type(2) -> 'INTEGER';
+code_type(3) -> 'BIT STRING';
+code_type(4) -> 'OCTET STRING';
+code_type(5) -> 'NULL';
+code_type(6) -> 'OBJECT IDENTIFIER';
+code_type(7) -> 'OBJECT DESCRIPTOR';
+code_type(8) -> 'EXTERNAL';
+code_type(9) -> 'REAL';
+code_type(10) -> 'ENUMERATED';
+code_type(11) -> 'EMBEDDED_PDV';
+code_type(16) -> 'SEQUENCE';
+code_type(16) -> 'SEQUENCE OF';
+code_type(17) -> 'SET';
+code_type(17) -> 'SET OF';
+code_type(18) -> 'NumericString';
+code_type(19) -> 'PrintableString';
+code_type(20) -> 'TeletexString';
+code_type(21) -> 'VideotexString';
+code_type(22) -> 'IA5String';
+code_type(23) -> 'UTCTime';
+code_type(24) -> 'GeneralizedTime';
+code_type(25) -> 'GraphicString';
+code_type(26) -> 'VisibleString';
+code_type(27) -> 'GeneralString';
+code_type(28) -> 'UniversalString';
+code_type(30) -> 'BMPString';
+code_type(Else) -> exit({error,{asn1,{unrecognized_type,Else}}}).
+
+%%-------------------------------------------------------------------------
+%% decoding of the components of a SET
+%%-------------------------------------------------------------------------
+
+decode_set(Rb, indefinite, <<0,0,Bytes/binary>>, _OptOrMand, _Fun3, Acc) ->
+ {lists:reverse(Acc),Bytes,Rb+2};
+
+decode_set(Rb, indefinite, Bytes, OptOrMand, Fun3, Acc) ->
+ {Term, Remain, Rb1} = Fun3(Bytes, OptOrMand),
+ decode_set(Rb+Rb1, indefinite, Remain, OptOrMand, Fun3, [Term|Acc]);
+
+decode_set(Rb, Num, Bytes, _OptOrMand, _Fun3, Acc) when Num == 0 ->
+ {lists:reverse(Acc), Bytes, Rb};
+
+decode_set(_, Num, _, _, _, _) when Num < 0 ->
+ exit({error,{asn1,{length_error,'SET'}}});
+
+decode_set(Rb, Num, Bytes, OptOrMand, Fun3, Acc) ->
+ {Term, Remain, Rb1} = Fun3(Bytes, OptOrMand),
+ decode_set(Rb+Rb1, Num-Rb1, Remain, OptOrMand, Fun3, [Term|Acc]).
+
+
+%%-------------------------------------------------------------------------
+%% decoding of SEQUENCE OF and SET OF
+%%-------------------------------------------------------------------------
+
+decode_components(Rb, indefinite, <<0,0,Bytes/binary>>, _Fun3, _TagIn, Acc) ->
+ {lists:reverse(Acc),Bytes,Rb+2};
+
+decode_components(Rb, indefinite, Bytes, Fun3, TagIn, Acc) ->
+ {Term, Remain, Rb1} = Fun3(Bytes, mandatory, TagIn),
+ decode_components(Rb+Rb1, indefinite, Remain, Fun3, TagIn, [Term|Acc]);
+
+decode_components(Rb, Num, Bytes, _Fun3, _TagIn, Acc) when Num == 0 ->
+ {lists:reverse(Acc), Bytes, Rb};
+
+decode_components(_, Num, _, _, _, _) when Num < 0 ->
+ exit({error,{asn1,{length_error,'SET/SEQUENCE OF'}}});
+
+decode_components(Rb, Num, Bytes, Fun3, TagIn, Acc) ->
+ {Term, Remain, Rb1} = Fun3(Bytes, mandatory, TagIn),
+ decode_components(Rb+Rb1, Num-Rb1, Remain, Fun3, TagIn, [Term|Acc]).
+
+%%decode_components(Rb, indefinite, [0,0|Bytes], _Fun3, _TagIn, Acc) ->
+%% {lists:reverse(Acc),Bytes,Rb+2};
+
+decode_components(Rb, indefinite, <<0,0,Bytes/binary>>, _Fun4, _TagIn, _Fun, Acc) ->
+ {lists:reverse(Acc),Bytes,Rb+2};
+
+decode_components(Rb, indefinite, Bytes, _Fun4, TagIn, _Fun, Acc) ->
+ {Term, Remain, Rb1} = _Fun4(Bytes, mandatory, TagIn, _Fun),
+ decode_components(Rb+Rb1, indefinite, Remain, _Fun4, TagIn, _Fun, [Term|Acc]);
+
+decode_components(Rb, Num, Bytes, _Fun4, _TagIn, _Fun, Acc) when Num == 0 ->
+ {lists:reverse(Acc), Bytes, Rb};
+
+decode_components(_, Num, _, _, _, _, _) when Num < 0 ->
+ exit({error,{asn1,{length_error,'SET/SEQUENCE OF'}}});
+
+decode_components(Rb, Num, Bytes, _Fun4, TagIn, _Fun, Acc) ->
+ {Term, Remain, Rb1} = _Fun4(Bytes, mandatory, TagIn, _Fun),
+ decode_components(Rb+Rb1, Num-Rb1, Remain, _Fun4, TagIn, _Fun, [Term|Acc]).
+
+
+
+%%-------------------------------------------------------------------------
+%% INTERNAL HELPER FUNCTIONS (not exported)
+%%-------------------------------------------------------------------------
+
+
+%%==========================================================================
+%% Encode tag
+%%
+%% dotag(tag | notag, TagValpattern | TagValTuple, [Length, Value]) -> [Tag]
+%% TagValPattern is a correct bitpattern for a tag
+%% TagValTuple is a tuple of three bitpatterns, Class, Form and TagNo where
+%% Class = UNIVERSAL | APPLICATION | CONTEXT | PRIVATE
+%% Form = Primitive | Constructed
+%% TagNo = Number of tag
+%%==========================================================================
+
+
+dotag([], Tag, {Bytes,Len}) ->
+ dotag_universal(Tag,Bytes,Len);
+dotag(Tags, Tag, {Bytes,Len}) ->
+ encode_tags(Tags ++ [#tag{class=?UNIVERSAL,number=Tag,form=?PRIMITIVE}],
+ Bytes, Len);
+
+dotag(Tags, Tag, Bytes) ->
+ encode_tags(Tags ++ [#tag{class=?UNIVERSAL,number=Tag,form=?PRIMITIVE}],
+ Bytes, size(Bytes)).
+
+dotag_universal(UniversalTag,Bytes,Len) when Len =< 16#7F->
+ {[UniversalTag,Len,Bytes],2+Len};
+dotag_universal(UniversalTag,Bytes,Len) ->
+ {EncLen,LenLen}=encode_length(Len),
+ {[UniversalTag,EncLen,Bytes],1+LenLen+Len}.
+
+%% decoding postitive integer values.
+decode_integer2(Len,Bin = <<0:1,_:7,_Bs/binary>>,RemovedBytes) ->
+ <> = Bin,
+ {Int,Buffer2,RemovedBytes};
+%% decoding negative integer values.
+decode_integer2(Len,<<1:1,B2:7,Bs/binary>>,RemovedBytes) ->
+ <> = <>,
+ Int = N - (1 bsl (8 * Len - 1)),
+ {Int,Buffer2,RemovedBytes}.
+
+%%decode_integer2(Len,Buffer,Acc,RemovedBytes) when (hd(Buffer) band 16#FF) =< 16#7F ->
+%% {decode_integer_pos(Buffer, 8 * (Len - 1)),skip(Buffer,Len),RemovedBytes};
+%%decode_integer2(Len,Buffer,Acc,RemovedBytes) ->
+%% {decode_integer_neg(Buffer, 8 * (Len - 1)),skip(Buffer,Len),RemovedBytes}.
+
+%%decode_integer_pos([Byte|Tail], Shift) ->
+%% (Byte bsl Shift) bor decode_integer_pos(Tail, Shift-8);
+%%decode_integer_pos([], _) -> 0.
+
+
+%%decode_integer_neg([Byte|Tail], Shift) ->
+%% (-128 + (Byte band 127) bsl Shift) bor decode_integer_pos(Tail, Shift-8).
+
+
+concat_bit_binaries([],Bin={_,_}) ->
+ Bin;
+concat_bit_binaries({0,B1},{U2,B2}) ->
+ {U2,<>};
+concat_bit_binaries({U1,B1},{U2,B2}) ->
+ S1 = (size(B1) * 8) - U1,
+ S2 = (size(B2) * 8) - U2,
+ PadBits = 8 - ((S1+S2) rem 8),
+ {PadBits, <>};
+concat_bit_binaries(L1,L2) when list(L1),list(L2) ->
+ %% this case occur when decoding with NNL
+ L1 ++ L2.
+
+
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V
+ end.
+
+%%skip(Buffer, 0) ->
+%% Buffer;
+%%skip([H | T], Len) ->
+%% skip(T, Len-1).
+
+new_tags([],LastTag) ->
+ [LastTag];
+new_tags(Tags=[#tag{type='IMPLICIT'}],_LastTag) ->
+ Tags;
+new_tags([T1 = #tag{type='IMPLICIT'},#tag{type=T2Type}|Rest],LastTag) ->
+ new_tags([T1#tag{type=T2Type}|Rest],LastTag);
+new_tags(Tags,LastTag) ->
+ case lists:last(Tags) of
+ #tag{type='IMPLICIT'} ->
+ Tags;
+ _ ->
+ Tags ++ [LastTag]
+ end.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl
new file mode 100644
index 0000000000..50a91cf201
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl
@@ -0,0 +1,1849 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1rt_ber_bin_v2.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1rt_ber_bin_v2).
+
+%% encoding / decoding of BER
+
+-export([decode/1, decode/2, match_tags/2, encode/1]).
+-export([fixoptionals/2, cindex/3,
+ list_to_record/2,
+ encode_tag_val/1,
+ encode_tags/3]).
+-export([encode_boolean/2,decode_boolean/2,
+ encode_integer/3,encode_integer/4,
+ decode_integer/3, decode_integer/4,
+ encode_enumerated/2,
+ encode_enumerated/4,decode_enumerated/4,
+ encode_real/2,decode_real/3,
+ encode_bit_string/4,decode_bit_string/4,
+ decode_compact_bit_string/4,
+ encode_octet_string/3,decode_octet_string/3,
+ encode_null/2,decode_null/2,
+ encode_object_identifier/2,decode_object_identifier/2,
+ encode_restricted_string/4,decode_restricted_string/4,
+ encode_universal_string/3,decode_universal_string/3,
+ encode_BMP_string/3,decode_BMP_string/3,
+ encode_generalized_time/3,decode_generalized_time/3,
+ encode_utc_time/3,decode_utc_time/3,
+ encode_length/1,decode_length/1,
+ decode_tag_and_length/1]).
+
+-export([encode_open_type/1,encode_open_type/2,
+ decode_open_type/2,decode_open_type_as_binary/2]).
+
+-export([decode_primitive_incomplete/2]).
+
+-include("asn1_records.hrl").
+
+% the encoding of class of tag bits 8 and 7
+-define(UNIVERSAL, 0).
+-define(APPLICATION, 16#40).
+-define(CONTEXT, 16#80).
+-define(PRIVATE, 16#C0).
+
+%%% primitive or constructed encoding % bit 6
+-define(PRIMITIVE, 0).
+-define(CONSTRUCTED, 2#00100000).
+
+%%% The tag-number for universal types
+-define(N_BOOLEAN, 1).
+-define(N_INTEGER, 2).
+-define(N_BIT_STRING, 3).
+-define(N_OCTET_STRING, 4).
+-define(N_NULL, 5).
+-define(N_OBJECT_IDENTIFIER, 6).
+-define(N_OBJECT_DESCRIPTOR, 7).
+-define(N_EXTERNAL, 8).
+-define(N_REAL, 9).
+-define(N_ENUMERATED, 10).
+-define(N_EMBEDDED_PDV, 11).
+-define(N_SEQUENCE, 16).
+-define(N_SET, 17).
+-define(N_NumericString, 18).
+-define(N_PrintableString, 19).
+-define(N_TeletexString, 20).
+-define(N_VideotexString, 21).
+-define(N_IA5String, 22).
+-define(N_UTCTime, 23).
+-define(N_GeneralizedTime, 24).
+-define(N_GraphicString, 25).
+-define(N_VisibleString, 26).
+-define(N_GeneralString, 27).
+-define(N_UniversalString, 28).
+-define(N_BMPString, 30).
+
+
+% the complete tag-word of built-in types
+-define(T_BOOLEAN, ?UNIVERSAL bor ?PRIMITIVE bor 1).
+-define(T_INTEGER, ?UNIVERSAL bor ?PRIMITIVE bor 2).
+-define(T_BIT_STRING, ?UNIVERSAL bor ?PRIMITIVE bor 3). % can be CONSTRUCTED
+-define(T_OCTET_STRING, ?UNIVERSAL bor ?PRIMITIVE bor 4). % can be CONSTRUCTED
+-define(T_NULL, ?UNIVERSAL bor ?PRIMITIVE bor 5).
+-define(T_OBJECT_IDENTIFIER,?UNIVERSAL bor ?PRIMITIVE bor 6).
+-define(T_OBJECT_DESCRIPTOR,?UNIVERSAL bor ?PRIMITIVE bor 7).
+-define(T_EXTERNAL, ?UNIVERSAL bor ?PRIMITIVE bor 8).
+-define(T_REAL, ?UNIVERSAL bor ?PRIMITIVE bor 9).
+-define(T_ENUMERATED, ?UNIVERSAL bor ?PRIMITIVE bor 10).
+-define(T_EMBEDDED_PDV, ?UNIVERSAL bor ?PRIMITIVE bor 11).
+-define(T_SEQUENCE, ?UNIVERSAL bor ?CONSTRUCTED bor 16).
+-define(T_SET, ?UNIVERSAL bor ?CONSTRUCTED bor 17).
+-define(T_NumericString, ?UNIVERSAL bor ?PRIMITIVE bor 18). %can be constructed
+-define(T_PrintableString, ?UNIVERSAL bor ?PRIMITIVE bor 19). %can be constructed
+-define(T_TeletexString, ?UNIVERSAL bor ?PRIMITIVE bor 20). %can be constructed
+-define(T_VideotexString, ?UNIVERSAL bor ?PRIMITIVE bor 21). %can be constructed
+-define(T_IA5String, ?UNIVERSAL bor ?PRIMITIVE bor 22). %can be constructed
+-define(T_UTCTime, ?UNIVERSAL bor ?PRIMITIVE bor 23).
+-define(T_GeneralizedTime, ?UNIVERSAL bor ?PRIMITIVE bor 24).
+-define(T_GraphicString, ?UNIVERSAL bor ?PRIMITIVE bor 25). %can be constructed
+-define(T_VisibleString, ?UNIVERSAL bor ?PRIMITIVE bor 26). %can be constructed
+-define(T_GeneralString, ?UNIVERSAL bor ?PRIMITIVE bor 27). %can be constructed
+-define(T_UniversalString, ?UNIVERSAL bor ?PRIMITIVE bor 28). %can be constructed
+-define(T_BMPString, ?UNIVERSAL bor ?PRIMITIVE bor 30). %can be constructed
+
+% encode(Tlv={_Tag={?PRIMITIVE,_},_VList}) ->
+% encode_primitive(Tlv);
+% encode(Tlv) ->
+% encode_constructed(Tlv).
+
+encode([Tlv]) ->
+ encode(Tlv);
+encode({TlvTag,TlvVal}) when list(TlvVal) ->
+ %% constructed form of value
+ encode_tlv(TlvTag,TlvVal,?CONSTRUCTED);
+encode({TlvTag,TlvVal}) ->
+ encode_tlv(TlvTag,TlvVal,?PRIMITIVE);
+encode(Bin) when binary(Bin) ->
+ Bin.
+
+encode_tlv(TlvTag,TlvVal,Form) ->
+ Tag = encode_tlv_tag(TlvTag,Form),
+ {Val,VLen} = encode_tlv_val(TlvVal),
+ {Len,_LLen} = encode_length(VLen),
+ BinLen = list_to_binary(Len),
+ <>.
+
+encode_tlv_tag(ClassTagNo,Form) ->
+ Class = ClassTagNo bsr 16,
+ case encode_tag_val({Class bsl 6,Form,(ClassTagNo - (Class bsl 16))}) of
+ T when list(T) ->
+ list_to_binary(T);
+ T ->
+ T
+ end.
+
+encode_tlv_val(TlvL) when list(TlvL) ->
+ encode_tlv_list(TlvL,[]);
+encode_tlv_val(Bin) ->
+ {Bin,size(Bin)}.
+
+encode_tlv_list([Tlv|Tlvs],Acc) ->
+ EncTlv = encode(Tlv),
+ encode_tlv_list(Tlvs,[EncTlv|Acc]);
+encode_tlv_list([],Acc) ->
+ Bin=list_to_binary(lists:reverse(Acc)),
+ {Bin,size(Bin)}.
+
+% encode_primitive({{_,ClassTagNo},V}) ->
+% Len = size(V), % not sufficient as length encode
+% Class = ClassTagNo bsr 16,
+% {TagLen,Tag} =
+% case encode_tag_val({Class,?PRIMITIVE,ClassTagNo - Class}) of
+% T when list(T) ->
+% {length(T),list_to_binary(T)};
+% T ->
+% {1,T}
+% end,
+
+
+decode(B,driver) ->
+ case catch port_control(drv_complete,2,B) of
+ Bin when binary(Bin) ->
+ binary_to_term(Bin);
+ List when list(List) -> handle_error(List,B);
+ {'EXIT',{badarg,Reason}} ->
+ asn1rt_driver_handler:load_driver(),
+ receive
+ driver_ready ->
+ case catch port_control(drv_complete,2,B) of
+ Bin2 when binary(Bin2) -> binary_to_term(Bin2);
+ List when list(List) -> handle_error(List,B);
+ Error -> exit(Error)
+ end;
+ {error,Error} -> % error when loading driver
+ %% the driver could not be loaded
+ exit(Error);
+ Error={port_error,Reason} ->
+ exit(Error)
+ end;
+ {'EXIT',Reason} ->
+ exit(Reason)
+ end.
+
+handle_error([],_)->
+ exit({error,{"memory allocation problem"}});
+handle_error([$1|_],L) -> % error in driver
+ exit({error,{asn1_error,L}});
+handle_error([$2|_],L) -> % error in driver due to wrong tag
+ exit({error,{asn1_error,{"bad tag",L}}});
+handle_error([$3|_],L) -> % error in driver due to length error
+ exit({error,{asn1_error,{"bad length field",L}}});
+handle_error([$4|_],L) -> % error in driver due to indefinite length error
+ exit({error,{asn1_error,{"indefinite length without end bytes",L}}});
+handle_error(ErrL,L) ->
+ exit({error,{unknown_error,ErrL,L}}).
+
+
+decode(Bin) when binary(Bin) ->
+ decode_primitive(Bin);
+decode(Tlv) -> % assume it is a tlv
+ {Tlv,<<>>}.
+
+
+decode_primitive(Bin) ->
+ {{Form,TagNo,Len,V},Rest} = decode_tlv(Bin),
+ case Form of
+ 1 when Len == indefinite -> % constructed
+ {Vlist,Rest2} = decode_constructed_indefinite(V,[]),
+ {{TagNo,Vlist},Rest2};
+ 1 -> % constructed
+ {{TagNo,decode_constructed(V)},Rest};
+ 0 -> % primitive
+ {{TagNo,V},Rest}
+ end.
+
+decode_constructed(<<>>) ->
+ [];
+decode_constructed(Bin) ->
+ {Tlv,Rest} = decode_primitive(Bin),
+ [Tlv|decode_constructed(Rest)].
+
+decode_constructed_indefinite(<<0,0,Rest/binary>>,Acc) ->
+ {lists:reverse(Acc),Rest};
+decode_constructed_indefinite(Bin,Acc) ->
+ {Tlv,Rest} = decode_primitive(Bin),
+ decode_constructed_indefinite(Rest, [Tlv|Acc]).
+
+decode_tlv(Bin) ->
+ {Form,TagNo,Len,Bin2} = decode_tag_and_length(Bin),
+ case Len of
+ indefinite ->
+ {{Form,TagNo,Len,Bin2},[]};
+ _ ->
+ <> = Bin2,
+ {{Form,TagNo,Len,V},Bin3}
+ end.
+
+%% decode_primitive_incomplete/2 decodes an encoded message incomplete
+%% by help of the pattern attribute (first argument).
+decode_primitive_incomplete([[default,TagNo]],Bin) -> %default
+ case decode_tlv(Bin) of
+ {{Form,TagNo,Len,V},Rest} ->
+ decode_incomplete2(Form,TagNo,Len,V,[],Rest);
+ _ ->
+ %{asn1_DEFAULT,Bin}
+ asn1_NOVALUE
+ end;
+decode_primitive_incomplete([[default,TagNo,Directives]],Bin) -> %default, constructed type, Directives points into this type
+ case decode_tlv(Bin) of
+ {{Form,TagNo,Len,V},Rest} ->
+ decode_incomplete2(Form,TagNo,Len,V,Directives,Rest);
+ _ ->
+ %{asn1_DEFAULT,Bin}
+ asn1_NOVALUE
+ end;
+decode_primitive_incomplete([[opt,TagNo]],Bin) -> %optional
+ case decode_tlv(Bin) of
+ {{Form,TagNo,Len,V},Rest} ->
+ decode_incomplete2(Form,TagNo,Len,V,[],Rest);
+ _ ->
+ %{{TagNo,asn1_NOVALUE},Bin}
+ asn1_NOVALUE
+ end;
+decode_primitive_incomplete([[opt,TagNo,Directives]],Bin) -> %optional
+ case decode_tlv(Bin) of
+ {{Form,TagNo,Len,V},Rest} ->
+ decode_incomplete2(Form,TagNo,Len,V,Directives,Rest);
+ _ ->
+ %{{TagNo,asn1_NOVALUE},Bin}
+ asn1_NOVALUE
+ end;
+%% A choice alternative that shall be undecoded
+decode_primitive_incomplete([[alt_undec,TagNo]|RestAlts],Bin) ->
+% decode_incomplete_bin(Bin);
+ case decode_tlv(Bin) of
+ {{_Form,TagNo,_Len,_V},_R} ->
+ decode_incomplete_bin(Bin);
+ _ ->
+ decode_primitive_incomplete(RestAlts,Bin)
+ end;
+decode_primitive_incomplete([[alt,TagNo]|RestAlts],Bin) ->
+ case decode_tlv(Bin) of
+ {{_Form,TagNo,_Len,V},Rest} ->
+ {{TagNo,V},Rest};
+ _ ->
+ decode_primitive_incomplete(RestAlts,Bin)
+ end;
+decode_primitive_incomplete([[alt,TagNo,Directives]|RestAlts],Bin) ->
+ case decode_tlv(Bin) of
+ {{Form,TagNo,Len,V},Rest} ->
+ decode_incomplete2(Form,TagNo,Len,V,Directives,Rest);
+ _ ->
+ decode_primitive_incomplete(RestAlts,Bin)
+ end;
+decode_primitive_incomplete([[alt_parts,TagNo]|RestAlts],Bin) ->
+ case decode_tlv(Bin) of
+ {{_Form,TagNo,_Len,V},Rest} ->
+ {{TagNo,decode_parts_incomplete(V)},Rest};
+ _ ->
+ decode_primitive_incomplete(RestAlts,Bin)
+ end;
+decode_primitive_incomplete([[undec,_TagNo]|_RestTag],Bin) -> %incomlete decode
+ decode_incomplete_bin(Bin); %% use this if changing handling of
+decode_primitive_incomplete([[parts,TagNo]|_RestTag],Bin) ->
+ case decode_tlv(Bin) of
+ {{_Form,TagNo,_Len,V},Rest} ->
+ {{TagNo,decode_parts_incomplete(V)},Rest};
+ Err ->
+ {error,{asn1,"tag failure",TagNo,Err}}
+ end;
+decode_primitive_incomplete([mandatory|RestTag],Bin) ->
+ case decode_tlv(Bin) of
+ {{Form,TagNo,Len,V},Rest} ->
+ decode_incomplete2(Form,TagNo,Len,V,RestTag,Rest);
+ _ ->
+ {error,{asn1,"partial incomplete decode failure"}}
+ end;
+%% A choice that is a toptype or a mandatory component of a
+%% SEQUENCE or SET.
+decode_primitive_incomplete([[mandatory,Directives]],Bin) ->
+ case decode_tlv(Bin) of
+ {{Form,TagNo,Len,V},Rest} ->
+ decode_incomplete2(Form,TagNo,Len,V,Directives,Rest);
+ _ ->
+ {error,{asn1,"partial incomplete decode failure"}}
+ end;
+decode_primitive_incomplete([],Bin) ->
+ decode_primitive(Bin).
+
+%% decode_parts_incomplete/1 receives a number of values encoded in
+%% sequence and returns the parts as unencoded binaries
+decode_parts_incomplete(<<>>) ->
+ [];
+decode_parts_incomplete(Bin) ->
+ {ok,Rest} = skip_tag(Bin),
+ {ok,Rest2} = skip_length_and_value(Rest),
+ LenPart = size(Bin) - size(Rest2),
+ <> = Bin,
+ [Part|decode_parts_incomplete(RestBin)].
+
+
+%% decode_incomplete2 checks if V is a value of a constructed or
+%% primitive type, and continues the decode propeerly.
+decode_incomplete2(1,TagNo,indefinite,V,TagMatch,_) ->
+ %% constructed indefinite length
+ {Vlist,Rest2} = decode_constr_indef_incomplete(TagMatch,V,[]),
+ {{TagNo,Vlist},Rest2};
+decode_incomplete2(1,TagNo,_Len,V,TagMatch,Rest) ->
+ {{TagNo,decode_constructed_incomplete(TagMatch,V)},Rest};
+decode_incomplete2(0,TagNo,_Len,V,_TagMatch,Rest) ->
+ {{TagNo,V},Rest}.
+
+decode_constructed_incomplete(_TagMatch,<<>>) ->
+ [];
+decode_constructed_incomplete([mandatory|RestTag],Bin) ->
+ {Tlv,Rest} = decode_primitive(Bin),
+ [Tlv|decode_constructed_incomplete(RestTag,Rest)];
+decode_constructed_incomplete(Directives=[[Alt,_]|_],Bin)
+ when Alt == alt_undec; Alt == alt ->
+ case decode_tlv(Bin) of
+ {{_Form,TagNo,_Len,V},Rest} ->
+ case incomplete_choice_alt(TagNo,Directives) of
+ alt_undec ->
+ LenA = size(Bin)-size(Rest),
+ <> = Bin,
+ A;
+% {UndecBin,_}=decode_incomplete_bin(Bin),
+% UndecBin;
+% [{TagNo,V}];
+ alt ->
+ {Tlv,_} = decode_primitive(V),
+ [{TagNo,Tlv}];
+ alt_parts ->
+ %{{TagNo,decode_parts_incomplete(V)},Rest}; % maybe wrong
+ [{TagNo,decode_parts_incomplete(V)}];
+ Err ->
+ {error,{asn1,"partial incomplete decode failure",Err}}
+ end;
+ _ ->
+ {error,{asn1,"partial incomplete decode failure"}}
+ end;
+decode_constructed_incomplete([TagNo|RestTag],Bin) ->
+%% {Tlv,Rest} = decode_primitive_incomplete([TagNo],Bin),
+ case decode_primitive_incomplete([TagNo],Bin) of
+ {Tlv,Rest} ->
+ [Tlv|decode_constructed_incomplete(RestTag,Rest)];
+ asn1_NOVALUE ->
+ decode_constructed_incomplete(RestTag,Bin)
+ end;
+decode_constructed_incomplete([],Bin) ->
+ {Tlv,_Rest}=decode_primitive(Bin),
+ [Tlv].
+
+decode_constr_indef_incomplete(_TagMatch,<<0,0,Rest/binary>>,Acc) ->
+ {lists:reverse(Acc),Rest};
+decode_constr_indef_incomplete([Tag|RestTags],Bin,Acc) ->
+% {Tlv,Rest} = decode_primitive_incomplete([Tag],Bin),
+ case decode_primitive_incomplete([Tag],Bin) of
+ {Tlv,Rest} ->
+ decode_constr_indef_incomplete(RestTags,Rest,[Tlv|Acc]);
+ asn1_NOVALUE ->
+ decode_constr_indef_incomplete(RestTags,Bin,Acc)
+ end.
+
+
+decode_incomplete_bin(Bin) ->
+ {ok,Rest} = skip_tag(Bin),
+ {ok,Rest2} = skip_length_and_value(Rest),
+ IncLen = size(Bin) - size(Rest2),
+ <> = Bin,
+ {IncBin,Ret}.
+
+incomplete_choice_alt(TagNo,[[Alt,TagNo]|_Directives]) ->
+ Alt;
+incomplete_choice_alt(TagNo,[_H|Directives]) ->
+ incomplete_choice_alt(TagNo,Directives);
+incomplete_choice_alt(_,[]) ->
+ error.
+
+
+%% skip_tag and skip_length_and_value are rutines used both by
+%% decode_partial_incomplete and decode_partial (decode/2).
+
+skip_tag(<<_:3,31:5,Rest/binary>>)->
+ skip_long_tag(Rest);
+skip_tag(<<_:3,_Tag:5,Rest/binary>>) ->
+ {ok,Rest}.
+
+skip_long_tag(<<1:1,_:7,Rest/binary>>) ->
+ skip_long_tag(Rest);
+skip_long_tag(<<0:1,_:7,Rest/binary>>) ->
+ {ok,Rest}.
+
+skip_length_and_value(Binary) ->
+ case decode_length(Binary) of
+ {indefinite,RestBinary} ->
+ skip_indefinite_value(RestBinary);
+ {Length,RestBinary} ->
+ <<_:Length/unit:8,Rest/binary>> = RestBinary,
+ {ok,Rest}
+ end.
+
+skip_indefinite_value(<<0,0,Rest/binary>>) ->
+ {ok,Rest};
+skip_indefinite_value(Binary) ->
+ {ok,RestBinary}=skip_tag(Binary),
+ {ok,RestBinary2} = skip_length_and_value(RestBinary),
+ skip_indefinite_value(RestBinary2).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% match_tags takes a Tlv (Tag, Length, Value) structure and matches
+%% it with the tags in TagList. If the tags does not match the function
+%% crashes otherwise it returns the remaining Tlv after that the tags have
+%% been removed.
+%%
+%% match_tags(Tlv, TagList)
+%%
+
+
+match_tags({T,V}, [T|Tt]) ->
+ match_tags(V,Tt);
+match_tags([{T,V}],[T|Tt]) ->
+ match_tags(V, Tt);
+match_tags(Vlist = [{T,_V}|_], [T]) ->
+ Vlist;
+match_tags(Tlv, []) ->
+ Tlv;
+match_tags({Tag,_V},[T|_Tt]) ->
+ {error,{asn1,{wrong_tag,{Tag,T}}}}.
+
+
+cindex(Ix,Val,Cname) ->
+ case element(Ix,Val) of
+ {Cname,Val2} -> Val2;
+ X -> X
+ end.
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Optionals, preset not filled optionals with asn1_NOVALUE
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+% converts a list to a record if necessary
+list_to_record(Name,List) when list(List) ->
+ list_to_tuple([Name|List]);
+list_to_record(_Name,Tuple) when tuple(Tuple) ->
+ Tuple.
+
+
+fixoptionals(OptList,Val) when list(Val) ->
+ fixoptionals(OptList,Val,1,[],[]).
+
+fixoptionals([{Name,Pos}|Ot],[{Name,Val}|Vt],_Opt,Acc1,Acc2) ->
+ fixoptionals(Ot,Vt,Pos+1,[1|Acc1],[{Name,Val}|Acc2]);
+fixoptionals([{_Name,Pos}|Ot],V,Pos,Acc1,Acc2) ->
+ fixoptionals(Ot,V,Pos+1,[0|Acc1],[asn1_NOVALUE|Acc2]);
+fixoptionals(O,[Vh|Vt],Pos,Acc1,Acc2) ->
+ fixoptionals(O,Vt,Pos+1,Acc1,[Vh|Acc2]);
+fixoptionals([],[Vh|Vt],Pos,Acc1,Acc2) ->
+ fixoptionals([],Vt,Pos+1,Acc1,[Vh|Acc2]);
+fixoptionals([],[],_,_Acc1,Acc2) ->
+ % return Val as a record
+ list_to_tuple([asn1_RECORDNAME|lists:reverse(Acc2)]).
+
+
+%%encode_tag(TagClass(?UNI, APP etc), Form (?PRIM etx), TagInteger) ->
+%% 8bit Int | binary
+encode_tag_val({Class, Form, TagNo}) when (TagNo =< 30) ->
+ <<(Class bsr 6):2,(Form bsr 5):1,TagNo:5>>;
+
+encode_tag_val({Class, Form, TagNo}) ->
+ {Octets,_Len} = mk_object_val(TagNo),
+ BinOct = list_to_binary(Octets),
+ <<(Class bsr 6):2, (Form bsr 5):1, 31:5,BinOct/binary>>;
+
+%% asumes whole correct tag bitpattern, multiple of 8
+encode_tag_val(Tag) when (Tag =< 255) -> Tag; %% används denna funktion??!!
+%% asumes correct bitpattern of 0-5
+encode_tag_val(Tag) -> encode_tag_val2(Tag,[]).
+
+encode_tag_val2(Tag, OctAck) when (Tag =< 255) ->
+ [Tag | OctAck];
+encode_tag_val2(Tag, OctAck) ->
+ encode_tag_val2(Tag bsr 8, [255 band Tag | OctAck]).
+
+
+%%===============================================================================
+%% Decode a tag
+%%
+%% decode_tag(OctetListBuffer) -> {{Form, (Class bsl 16)+ TagNo}, RestOfBuffer, RemovedBytes}
+%%===============================================================================
+
+decode_tag_and_length(<>) when TagNo < 31 ->
+ {Form, (Class bsl 16) + TagNo, Length, RestBuffer};
+decode_tag_and_length(<>) when TagNo < 31 ->
+ {Form, (Class bsl 16) + TagNo, indefinite, T};
+decode_tag_and_length(<>) when TagNo < 31 ->
+ <> = T,
+ {Form, (Class bsl 16) + TagNo, Length, RestBuffer};
+decode_tag_and_length(<>) ->
+ {Form, (Class bsl 16) + TagNo, Length, RestBuffer};
+decode_tag_and_length(<>) ->
+ {Form, (Class bsl 16) + TagNo, indefinite, T};
+decode_tag_and_length(<>) ->
+ <> = T,
+ {Form, (Class bsl 16) + TagNo, Length, RestBuffer};
+decode_tag_and_length(<>) ->
+ {TagNo, Buffer1} = decode_tag(Buffer, 0),
+ {Length, RestBuffer} = decode_length(Buffer1),
+ {Form, (Class bsl 16) + TagNo, Length, RestBuffer}.
+
+
+
+%% last partial tag
+decode_tag(<<0:1,PartialTag:7, Buffer/binary>>, TagAck) ->
+ TagNo = (TagAck bsl 7) bor PartialTag,
+ %%<> = <>,
+ {TagNo, Buffer};
+% more tags
+decode_tag(<<_:1,PartialTag:7, Buffer/binary>>, TagAck) ->
+ TagAck1 = (TagAck bsl 7) bor PartialTag,
+ %%<> = <>,
+ decode_tag(Buffer, TagAck1).
+
+
+%%=======================================================================
+%%
+%% Encode all tags in the list Tags and return a possibly deep list of
+%% bytes with tag and length encoded
+%% The taglist must be in reverse order (fixed by the asn1 compiler)
+%% e.g [T1,T2] will result in
+%% {[EncodedT2,EncodedT1|BytesSoFar],LenSoFar+LenT2+LenT1}
+%%
+
+encode_tags([Tag|Trest], BytesSoFar, LenSoFar) ->
+% remove {Bytes1,L1} = encode_one_tag(Tag),
+ {Bytes2,L2} = encode_length(LenSoFar),
+ encode_tags(Trest, [Tag,Bytes2|BytesSoFar],
+ LenSoFar + size(Tag) + L2);
+encode_tags([], BytesSoFar, LenSoFar) ->
+ {BytesSoFar,LenSoFar}.
+
+encode_tags(TagIn, {BytesSoFar,LenSoFar}) ->
+ encode_tags(TagIn, BytesSoFar, LenSoFar).
+
+% encode_one_tag(#tag{class=Class,number=No,type=Type, form = Form}) ->
+% NewForm = case Type of
+% 'EXPLICIT' ->
+% ?CONSTRUCTED;
+% _ ->
+% Form
+% end,
+% Bytes = encode_tag_val({Class,NewForm,No}),
+% {Bytes,size(Bytes)}.
+
+
+%%===============================================================================
+%%
+%% This comment is valid for all the encode/decode functions
+%%
+%% C = Constraint -> typically {'ValueRange',LowerBound,UpperBound}
+%% used for PER-coding but not for BER-coding.
+%%
+%% Val = Value. If Val is an atom then it is a symbolic integer value
+%% (i.e the atom must be one of the names in the NamedNumberList).
+%% The NamedNumberList is used to translate the atom to an integer value
+%% before encoding.
+%%
+%%===============================================================================
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_open_type(Value) -> io_list (i.e nested list with integers, binaries)
+%% Value = list of bytes of an already encoded value (the list must be flat)
+%% | binary
+
+%%
+encode_open_type(Val) when list(Val) ->
+% {Val,length(Val)};
+ encode_open_type(list_to_binary(Val));
+encode_open_type(Val) ->
+ {Val, size(Val)}.
+
+%%
+encode_open_type(Val, T) when list(Val) ->
+ encode_open_type(list_to_binary(Val),T);
+encode_open_type(Val,[]) ->
+ {Val, size(Val)};
+encode_open_type(Val,Tag) ->
+ encode_tags(Tag,Val, size(Val)).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_open_type(Tlv, TagIn) -> Value
+%% Tlv = {Tag,V} | V where V -> binary()
+%% TagIn = [TagVal] where TagVal -> int()
+%% Value = binary with decoded data (which must be decoded again as some type)
+%%
+decode_open_type(Tlv, TagIn) ->
+ case match_tags(Tlv,TagIn) of
+ Bin when binary(Bin) ->
+ {InnerTlv,_} = decode(Bin),
+ InnerTlv;
+ TlvBytes -> TlvBytes
+ end.
+
+
+decode_open_type_as_binary(Tlv,TagIn)->
+ case match_tags(Tlv,TagIn) of
+ V when binary(V) ->
+ V;
+ [Tlv2] -> encode(Tlv2);
+ Tlv2 -> encode(Tlv2)
+ end.
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Boolean, ITU_T X.690 Chapter 8.2
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% encode_boolean(Integer, ReversedTagList) -> {[Octet],Len}
+%%===============================================================================
+
+encode_boolean({Name, Val}, TagIn) when atom(Name) ->
+ encode_boolean(Val, TagIn);
+encode_boolean(true, TagIn) ->
+ encode_tags(TagIn, [16#FF],1);
+encode_boolean(false, TagIn) ->
+ encode_tags(TagIn, [0],1);
+encode_boolean(X,_) ->
+ exit({error,{asn1, {encode_boolean, X}}}).
+
+
+%%===============================================================================
+%% decode_boolean(BuffList, HasTag, TotalLen) -> {true, Remain, RemovedBytes} |
+%% {false, Remain, RemovedBytes}
+%%===============================================================================
+decode_boolean(Tlv,TagIn) ->
+ Val = match_tags(Tlv, TagIn),
+ case Val of
+ <<0:8>> ->
+ false;
+ <<_:8>> ->
+ true;
+ _ ->
+ exit({error,{asn1, {decode_boolean, Val}}})
+ end.
+
+
+%%===========================================================================
+%% Integer, ITU_T X.690 Chapter 8.3
+
+%% encode_integer(Constraint, Value, Tag) -> [octet list]
+%% encode_integer(Constraint, Name, NamedNumberList, Tag) -> [octet list]
+%% Value = INTEGER | {Name,INTEGER}
+%% Tag = tag | notag
+%%===========================================================================
+
+encode_integer(C, Val, Tag) when integer(Val) ->
+ encode_tags(Tag, encode_integer(C, Val));
+encode_integer(C,{Name,Val},Tag) when atom(Name) ->
+ encode_integer(C,Val,Tag);
+encode_integer(_C, Val, _Tag) ->
+ exit({error,{asn1, {encode_integer, Val}}}).
+
+
+
+encode_integer(C, Val, NamedNumberList, Tag) when atom(Val) ->
+ case lists:keysearch(Val, 1, NamedNumberList) of
+ {value,{_, NewVal}} ->
+ encode_tags(Tag, encode_integer(C, NewVal));
+ _ ->
+ exit({error,{asn1, {encode_integer_namednumber, Val}}})
+ end;
+encode_integer(C,{_Name,Val},NamedNumberList,Tag) ->
+ encode_integer(C,Val,NamedNumberList,Tag);
+encode_integer(C, Val, _NamedNumberList, Tag) ->
+ encode_tags(Tag, encode_integer(C, Val)).
+
+
+encode_integer(_, Val) ->
+ Bytes =
+ if
+ Val >= 0 ->
+ encode_integer_pos(Val, []);
+ true ->
+ encode_integer_neg(Val, [])
+ end,
+ {Bytes,length(Bytes)}.
+
+encode_integer_pos(0, L=[B|_Acc]) when B < 128 ->
+ L;
+encode_integer_pos(N, Acc) ->
+ encode_integer_pos((N bsr 8), [N band 16#ff| Acc]).
+
+encode_integer_neg(-1, L=[B1|_T]) when B1 > 127 ->
+ L;
+encode_integer_neg(N, Acc) ->
+ encode_integer_neg(N bsr 8, [N band 16#ff|Acc]).
+
+%%===============================================================================
+%% decode integer
+%% (Buffer, Range, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes}
+%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes}
+%%===============================================================================
+
+decode_integer(Tlv,Range,NamedNumberList,TagIn) ->
+ V = match_tags(Tlv,TagIn),
+ Int = decode_integer(V),
+ range_check_integer(Int,Range),
+ number2name(Int,NamedNumberList).
+
+decode_integer(Tlv,Range,TagIn) ->
+ V = match_tags(Tlv, TagIn),
+ Int = decode_integer(V),
+ range_check_integer(Int,Range),
+ Int.
+
+%% decoding postitive integer values.
+decode_integer(Bin = <<0:1,_:7,_/binary>>) ->
+ Len = size(Bin),
+% <> = Bin,
+ <> = Bin,
+ Int;
+%% decoding negative integer values.
+decode_integer(Bin = <<1:1,B2:7,Bs/binary>>) ->
+ Len = size(Bin),
+% <> = <>,
+ <> = <>,
+ Int = N - (1 bsl (8 * Len - 1)),
+ Int.
+
+range_check_integer(Int,Range) ->
+ case Range of
+ [] -> % No length constraint
+ Int;
+ {Lb,Ub} when Int >= Lb, Ub >= Int -> % variable length constraint
+ Int;
+ Int -> % fixed value constraint
+ Int;
+ {_,_} ->
+ exit({error,{asn1,{integer_range,Range,Int}}});
+ SingleValue when integer(SingleValue) ->
+ exit({error,{asn1,{integer_range,Range,Int}}});
+ _ -> % some strange constraint that we don't support yet
+ Int
+ end.
+
+number2name(Int,[]) ->
+ Int;
+number2name(Int,NamedNumberList) ->
+ case lists:keysearch(Int, 2, NamedNumberList) of
+ {value,{NamedVal, _}} ->
+ NamedVal;
+ _ ->
+ Int
+ end.
+
+
+%%============================================================================
+%% Enumerated value, ITU_T X.690 Chapter 8.4
+
+%% encode enumerated value
+%%============================================================================
+encode_enumerated(Val, TagIn) when integer(Val)->
+ encode_tags(TagIn, encode_integer(false,Val));
+encode_enumerated({Name,Val}, TagIn) when atom(Name) ->
+ encode_enumerated(Val, TagIn).
+
+%% The encode_enumerated functions below this line can be removed when the
+%% new code generation is stable. (the functions might have to be kept here
+%% a while longer for compatibility reasons)
+
+encode_enumerated(C, Val, {NamedNumberList,ExtList}, TagIn) when atom(Val) ->
+ case catch encode_enumerated(C, Val, NamedNumberList, TagIn) of
+ {'EXIT',_} -> encode_enumerated(C, Val, ExtList, TagIn);
+ Result -> Result
+ end;
+
+encode_enumerated(C, Val, NamedNumberList, TagIn) when atom(Val) ->
+ case lists:keysearch(Val, 1, NamedNumberList) of
+ {value, {_, NewVal}} ->
+ encode_tags(TagIn, encode_integer(C, NewVal));
+ _ ->
+ exit({error,{asn1, {enumerated_not_in_range, Val}}})
+ end;
+
+encode_enumerated(C, {asn1_enum, Val}, {_,_}, TagIn) when integer(Val) ->
+ encode_tags(TagIn, encode_integer(C,Val));
+
+encode_enumerated(C, {Name,Val}, NamedNumberList, TagIn) when atom(Name) ->
+ encode_enumerated(C, Val, NamedNumberList, TagIn);
+
+encode_enumerated(_C, Val, _NamedNumberList, _TagIn) ->
+ exit({error,{asn1, {enumerated_not_namednumber, Val}}}).
+
+
+
+%%============================================================================
+%% decode enumerated value
+%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) -> Value
+%%===========================================================================
+decode_enumerated(Tlv, Range, NamedNumberList, Tags) ->
+ Buffer = match_tags(Tlv,Tags),
+ decode_enumerated_notag(Buffer, Range, NamedNumberList, Tags).
+
+decode_enumerated_notag(Buffer, _Range, {NamedNumberList,ExtList}, _Tags) ->
+
+ IVal = decode_integer2(size(Buffer), Buffer),
+ case decode_enumerated1(IVal, NamedNumberList) of
+ {asn1_enum,IVal} ->
+ decode_enumerated1(IVal,ExtList);
+ EVal ->
+ EVal
+ end;
+decode_enumerated_notag(Buffer, _Range, NNList, _Tags) ->
+ IVal = decode_integer2(size(Buffer), Buffer),
+ case decode_enumerated1(IVal, NNList) of
+ {asn1_enum,_} ->
+ exit({error,{asn1, {illegal_enumerated, IVal}}});
+ EVal ->
+ EVal
+ end.
+
+decode_enumerated1(Val, NamedNumberList) ->
+ %% it must be a named integer
+ case lists:keysearch(Val, 2, NamedNumberList) of
+ {value,{NamedVal, _}} ->
+ NamedVal;
+ _ ->
+ {asn1_enum,Val}
+ end.
+
+
+%%============================================================================
+%%
+%% Real value, ITU_T X.690 Chapter 8.5
+%%============================================================================
+%%
+%% encode real value
+%%============================================================================
+
+%% only base 2 internally so far!!
+encode_real(0, TagIn) ->
+ encode_tags(TagIn, {[],0});
+encode_real('PLUS-INFINITY', TagIn) ->
+ encode_tags(TagIn, {[64],1});
+encode_real('MINUS-INFINITY', TagIn) ->
+ encode_tags(TagIn, {[65],1});
+encode_real(Val, TagIn) when tuple(Val)->
+ encode_tags(TagIn, encode_real(Val)).
+
+%%%%%%%%%%%%%%
+%% not optimal efficient..
+%% only base 2 of Mantissa encoding!
+%% only base 2 of ExpBase encoding!
+encode_real({Man, Base, Exp}) ->
+%% io:format("Mantissa: ~w Base: ~w, Exp: ~w~n",[Man, Base, Exp]),
+
+ OctExp = if Exp >= 0 -> list_to_binary(encode_integer_pos(Exp, []));
+ true -> list_to_binary(encode_integer_neg(Exp, []))
+ end,
+%% ok = io:format("OctExp: ~w~n",[OctExp]),
+ SignBit = if Man > 0 -> 0; % bit 7 is pos or neg, no Zeroval
+ true -> 1
+ end,
+%% ok = io:format("SignBitMask: ~w~n",[SignBitMask]),
+ InBase = if Base =:= 2 -> 0; % bit 6,5: only base 2 this far!
+ true ->
+ exit({error,{asn1, {encode_real_non_supported_encodeing, Base}}})
+ end,
+ SFactor = 0, % bit 4,3: no scaling since only base 2
+ OctExpLen = size(OctExp),
+ if OctExpLen > 255 ->
+ exit({error,{asn1, {to_big_exp_in_encode_real, OctExpLen}}});
+ true -> true %% make real assert later..
+ end,
+ {LenCode, EOctets} = case OctExpLen of % bit 2,1
+ 1 -> {0, OctExp};
+ 2 -> {1, OctExp};
+ 3 -> {2, OctExp};
+ _ -> {3, <>}
+ end,
+ FirstOctet = <<1:1,SignBit:1,InBase:2,SFactor:2,LenCode:2>>,
+ OctMantissa = if Man > 0 -> list_to_binary(minimum_octets(Man));
+ true -> list_to_binary(minimum_octets(-(Man))) % signbit keeps track of sign
+ end,
+ %% ok = io:format("LenMask: ~w EOctets: ~w~nFirstOctet: ~w OctMantissa: ~w OctExpLen: ~w~n", [LenMask, EOctets, FirstOctet, OctMantissa, OctExpLen]),
+ Bin = <>,
+ {Bin, size(Bin)}.
+
+
+%%============================================================================
+%% decode real value
+%%
+%% decode_real([OctetBufferList], tuple|value, tag|notag) ->
+%% {{Mantissa, Base, Exp} | realval | PLUS-INFINITY | MINUS-INFINITY | 0,
+%% RestBuff}
+%%
+%% only for base 2 decoding sofar!!
+%%============================================================================
+
+decode_real(Tlv, Form, Tags) ->
+ Buffer = match_tags(Tlv,Tags),
+ decode_real_notag(Buffer, Form).
+
+decode_real_notag(_Buffer, _Form) ->
+ exit({error,{asn1, {unimplemented,real}}}).
+%% decode_real2(Buffer, Form, size(Buffer)).
+
+% decode_real2(Buffer, Form, Len) ->
+% <> = Buffer,
+% if
+% First =:= 2#01000000 -> {'PLUS-INFINITY', Buffer2};
+% First =:= 2#01000001 -> {'MINUS-INFINITY', Buffer2};
+% First =:= 2#00000000 -> {0, Buffer2};
+% true ->
+% %% have some check here to verify only supported bases (2)
+% <> = <>,
+% Sign = B6,
+% Base =
+% case B5_4 of
+% 0 -> 2; % base 2, only one so far
+% _ -> exit({error,{asn1, {non_supported_base, First}}})
+% end,
+% ScalingFactor =
+% case B3_2 of
+% 0 -> 0; % no scaling so far
+% _ -> exit({error,{asn1, {non_supported_scaling, First}}})
+% end,
+
+% {FirstLen,Exp,Buffer3} =
+% case B1_0 of
+% 0 ->
+% <<_:1/unit:8,Buffer21/binary>> = Buffer2,
+% {2, decode_integer2(1, Buffer2),Buffer21};
+% 1 ->
+% <<_:2/unit:8,Buffer21/binary>> = Buffer2,
+% {3, decode_integer2(2, Buffer2)};
+% 2 ->
+% <<_:3/unit:8,Buffer21/binary>> = Buffer2,
+% {4, decode_integer2(3, Buffer2)};
+% 3 ->
+% <> = Buffer2,
+% <<_:ExpLen1/unit:8,RestBuffer2/binary>> = RestBuffer,
+% { ExpLen1 + 2,
+% decode_integer2(ExpLen1, RestBuffer, RemBytes1),
+% RestBuffer2}
+% end,
+% Length = Len - FirstLen,
+% <> = Buffer3,
+% {Mantissa, Buffer4} =
+% if Sign =:= 0 ->
+
+% {LongInt, RestBuff};% sign plus,
+% true ->
+
+% {-LongInt, RestBuff}% sign minus
+% end,
+% case Form of
+% tuple ->
+% {Val,Buf,RemB} = Exp,
+% {{Mantissa, Base, {Val,Buf}}, Buffer4, RemBytes2+RemBytes3};
+% _value ->
+% comming
+% end
+% end.
+
+
+%%============================================================================
+%% Bitstring value, ITU_T X.690 Chapter 8.6
+%%
+%% encode bitstring value
+%%
+%% bitstring NamedBitList
+%% Val can be of:
+%% - [identifiers] where only named identifers are set to one,
+%% the Constraint must then have some information of the
+%% bitlength.
+%% - [list of ones and zeroes] all bits
+%% - integer value representing the bitlist
+%% C is constrint Len, only valid when identifiers
+%%============================================================================
+
+encode_bit_string(C,Bin={Unused,BinBits},NamedBitList,TagIn) when integer(Unused), binary(BinBits) ->
+ encode_bin_bit_string(C,Bin,NamedBitList,TagIn);
+encode_bit_string(C, [FirstVal | RestVal], NamedBitList, TagIn) when atom(FirstVal) ->
+ encode_bit_string_named(C, [FirstVal | RestVal], NamedBitList, TagIn);
+
+encode_bit_string(C, [{bit,X} | RestVal], NamedBitList, TagIn) ->
+ encode_bit_string_named(C, [{bit,X} | RestVal], NamedBitList, TagIn);
+
+encode_bit_string(C, [FirstVal| RestVal], NamedBitList, TagIn) when integer(FirstVal) ->
+ encode_bit_string_bits(C, [FirstVal | RestVal], NamedBitList, TagIn);
+
+encode_bit_string(_C, 0, _NamedBitList, TagIn) ->
+ encode_tags(TagIn, <<0>>,1);
+
+encode_bit_string(_C, [], _NamedBitList, TagIn) ->
+ encode_tags(TagIn, <<0>>,1);
+
+encode_bit_string(C, IntegerVal, NamedBitList, TagIn) when integer(IntegerVal) ->
+ BitListVal = int_to_bitlist(IntegerVal),
+ encode_bit_string_bits(C, BitListVal, NamedBitList, TagIn);
+
+encode_bit_string(C, {Name,BitList}, NamedBitList, TagIn) when atom(Name) ->
+ encode_bit_string(C, BitList, NamedBitList, TagIn).
+
+
+
+int_to_bitlist(0) ->
+ [];
+int_to_bitlist(Int) when integer(Int), Int >= 0 ->
+ [Int band 1 | int_to_bitlist(Int bsr 1)].
+
+
+%%=================================================================
+%% Encode BIT STRING of the form {Unused,BinBits}.
+%% Unused is the number of unused bits in the last byte in BinBits
+%% and BinBits is a binary representing the BIT STRING.
+%%=================================================================
+encode_bin_bit_string(C,{Unused,BinBits},_NamedBitList,TagIn)->
+ case get_constraint(C,'SizeConstraint') of
+ no ->
+ remove_unused_then_dotag(TagIn, Unused, BinBits);
+ {_Min,Max} ->
+ BBLen = (size(BinBits)*8)-Unused,
+ if
+ BBLen > Max ->
+ exit({error,{asn1,
+ {bitstring_length,
+ {{was,BBLen},{maximum,Max}}}}});
+ true ->
+ remove_unused_then_dotag(TagIn, Unused, BinBits)
+ end;
+ Size ->
+ case ((size(BinBits)*8)-Unused) of
+ BBSize when BBSize =< Size ->
+ remove_unused_then_dotag(TagIn, Unused, BinBits);
+ BBSize ->
+ exit({error,{asn1,
+ {bitstring_length,
+ {{was,BBSize},{should_be,Size}}}}})
+ end
+ end.
+
+remove_unused_then_dotag(TagIn,Unused,BinBits) ->
+ case Unused of
+ 0 when (size(BinBits) == 0) ->
+ encode_tags(TagIn,<<0>>,1);
+ 0 ->
+ Bin = <>,
+ encode_tags(TagIn,Bin,size(Bin));
+ Num ->
+ N = (size(BinBits)-1),
+ <> = BinBits,
+ encode_tags(TagIn,
+ [Unused,binary_to_list(BBits) ++[(LastByte bsr Num) bsl Num]],
+ 1+size(BinBits))
+ end.
+
+
+%%=================================================================
+%% Encode named bits
+%%=================================================================
+
+encode_bit_string_named(C, [FirstVal | RestVal], NamedBitList, TagIn) ->
+ ToSetPos = get_all_bitposes([FirstVal | RestVal], NamedBitList, []),
+ Size =
+ case get_constraint(C,'SizeConstraint') of
+ no ->
+ lists:max(ToSetPos)+1;
+ {_Min,Max} ->
+ Max;
+ TSize ->
+ TSize
+ end,
+ BitList = make_and_set_list(Size, ToSetPos, 0),
+ {Len, Unused, OctetList} = encode_bitstring(BitList),
+ encode_tags(TagIn, [Unused|OctetList],Len+1).
+
+
+%%----------------------------------------
+%% get_all_bitposes([list of named bits to set], named_bit_db, []) ->
+%% [sorted_list_of_bitpositions_to_set]
+%%----------------------------------------
+
+get_all_bitposes([{bit,ValPos}|Rest], NamedBitList, Ack) ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack ]);
+get_all_bitposes([Val | Rest], NamedBitList, Ack) when atom(Val) ->
+ case lists:keysearch(Val, 1, NamedBitList) of
+ {value, {_ValName, ValPos}} ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack]);
+ _ ->
+ exit({error,{asn1, {bitstring_namedbit, Val}}})
+ end;
+get_all_bitposes([], _NamedBitList, Ack) ->
+ lists:sort(Ack).
+
+
+%%----------------------------------------
+%% make_and_set_list(Len of list to return, [list of positions to set to 1])->
+%% returns list of Len length, with all in SetPos set.
+%% in positioning in list the first element is 0, the second 1 etc.., but
+%% Len will make a list of length Len, not Len + 1.
+%% BitList = make_and_set_list(C, ToSetPos, 0),
+%%----------------------------------------
+
+make_and_set_list(0, [], _) -> [];
+make_and_set_list(0, _, _) ->
+ exit({error,{asn1,bitstring_sizeconstraint}});
+make_and_set_list(Len, [XPos|SetPos], XPos) ->
+ [1 | make_and_set_list(Len - 1, SetPos, XPos + 1)];
+make_and_set_list(Len, [Pos|SetPos], XPos) ->
+ [0 | make_and_set_list(Len - 1, [Pos | SetPos], XPos + 1)];
+make_and_set_list(Len, [], XPos) ->
+ [0 | make_and_set_list(Len - 1, [], XPos + 1)].
+
+
+
+
+
+
+%%=================================================================
+%% Encode bit string for lists of ones and zeroes
+%%=================================================================
+encode_bit_string_bits(C, BitListVal, _NamedBitList, TagIn) when list(BitListVal) ->
+ case get_constraint(C,'SizeConstraint') of
+ no ->
+ {Len, Unused, OctetList} = encode_bitstring(BitListVal),
+ %%add unused byte to the Len
+ encode_tags(TagIn, [Unused | OctetList], Len+1);
+ Constr={Min,Max} when integer(Min),integer(Max) ->
+ encode_constr_bit_str_bits(Constr,BitListVal,TagIn);
+ {Constr={_,_},[]} ->%Constr={Min,Max}
+ %% constraint with extension mark
+ encode_constr_bit_str_bits(Constr,BitListVal,TagIn);
+ Constr={{_,_},{_,_}} ->%{{Min1,Max1},{Min2,Max2}}
+ %% constraint with extension mark
+ encode_constr_bit_str_bits(Constr,BitListVal,TagIn);
+ Size ->
+ case length(BitListVal) of
+ BitSize when BitSize == Size ->
+ {Len, Unused, OctetList} = encode_bitstring(BitListVal),
+ %%add unused byte to the Len
+ encode_tags(TagIn, [Unused | OctetList], Len+1);
+ BitSize when BitSize < Size ->
+ PaddedList = pad_bit_list(Size-BitSize,BitListVal),
+ {Len, Unused, OctetList} = encode_bitstring(PaddedList),
+ %%add unused byte to the Len
+ encode_tags(TagIn, [Unused | OctetList], Len+1);
+ BitSize ->
+ exit({error,{asn1,
+ {bitstring_length, {{was,BitSize},{should_be,Size}}}}})
+ end
+
+ end.
+
+encode_constr_bit_str_bits({_Min,Max},BitListVal,TagIn) ->
+ BitLen = length(BitListVal),
+ if
+ BitLen > Max ->
+ exit({error,{asn1,{bitstring_length,{{was,BitLen},
+ {maximum,Max}}}}});
+ true ->
+ {Len, Unused, OctetList} = encode_bitstring(BitListVal),
+ %%add unused byte to the Len
+ encode_tags(TagIn, [Unused, OctetList], Len+1)
+ end;
+encode_constr_bit_str_bits({{_Min1,Max1},{Min2,Max2}},BitListVal,TagIn) ->
+ BitLen = length(BitListVal),
+ case BitLen of
+ Len when Len > Max2 ->
+ exit({error,{asn1,{bitstring_length,{{was,BitLen},
+ {maximum,Max2}}}}});
+ Len when Len > Max1, Len < Min2 ->
+ exit({error,{asn1,{bitstring_length,{{was,BitLen},
+ {not_allowed_interval,
+ Max1,Min2}}}}});
+ _ ->
+ {Len, Unused, OctetList} = encode_bitstring(BitListVal),
+ %%add unused byte to the Len
+ encode_tags(TagIn, [Unused, OctetList], Len+1)
+ end.
+
+%% returns a list of length Size + length(BitListVal), with BitListVal
+%% as the most significant elements followed by padded zero elements
+pad_bit_list(Size,BitListVal) ->
+ Tail = lists:duplicate(Size,0),
+ lists:append(BitListVal,Tail).
+
+%%=================================================================
+%% Do the actual encoding
+%% ([bitlist]) -> {ListLen, UnusedBits, OctetList}
+%%=================================================================
+
+encode_bitstring([B8, B7, B6, B5, B4, B3, B2, B1 | Rest]) ->
+ Val = (B8 bsl 7) bor (B7 bsl 6) bor (B6 bsl 5) bor (B5 bsl 4) bor
+ (B4 bsl 3) bor (B3 bsl 2) bor (B2 bsl 1) bor B1,
+ encode_bitstring(Rest, [Val], 1);
+encode_bitstring(Val) ->
+ {Unused, Octet} = unused_bitlist(Val, 7, 0),
+ {1, Unused, [Octet]}.
+
+encode_bitstring([B8, B7, B6, B5, B4, B3, B2, B1 | Rest], Ack, Len) ->
+ Val = (B8 bsl 7) bor (B7 bsl 6) bor (B6 bsl 5) bor (B5 bsl 4) bor
+ (B4 bsl 3) bor (B3 bsl 2) bor (B2 bsl 1) bor B1,
+ encode_bitstring(Rest, [Ack | [Val]], Len + 1);
+%%even multiple of 8 bits..
+encode_bitstring([], Ack, Len) ->
+ {Len, 0, Ack};
+%% unused bits in last octet
+encode_bitstring(Rest, Ack, Len) ->
+% io:format("uneven ~w ~w ~w~n",[Rest, Ack, Len]),
+ {Unused, Val} = unused_bitlist(Rest, 7, 0),
+ {Len + 1, Unused, [Ack | [Val]]}.
+
+%%%%%%%%%%%%%%%%%%
+%% unused_bitlist([list of ones and zeros <= 7], 7, []) ->
+%% {Unused bits, Last octet with bits moved to right}
+unused_bitlist([], Trail, Ack) ->
+ {Trail + 1, Ack};
+unused_bitlist([Bit | Rest], Trail, Ack) ->
+%% io:format("trail Bit: ~w Rest: ~w Trail: ~w Ack:~w~n",[Bit, Rest, Trail, Ack]),
+ unused_bitlist(Rest, Trail - 1, (Bit bsl Trail) bor Ack).
+
+
+%%============================================================================
+%% decode bitstring value
+%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes}
+%%============================================================================
+
+decode_compact_bit_string(Buffer, Range, NamedNumberList, Tags) ->
+% NewTags = new_tags(HasTag,#tag{class=?UNIVERSAL,number=?N_BIT_STRING}),
+ decode_restricted_string(Buffer, Range, ?N_BIT_STRING, Tags,
+ NamedNumberList,bin).
+
+decode_bit_string(Buffer, Range, NamedNumberList, Tags) ->
+% NewTags = new_tags(HasTag,#tag{class=?UNIVERSAL,number=?N_BIT_STRING}),
+ decode_restricted_string(Buffer, Range, ?N_BIT_STRING, Tags,
+ NamedNumberList,old).
+
+
+decode_bit_string2(<<0>>,_NamedNumberList,BinOrOld) ->
+ case BinOrOld of
+ bin ->
+ {0,<<>>};
+ _ ->
+ []
+ end;
+decode_bit_string2(<>,NamedNumberList,BinOrOld) ->
+ case NamedNumberList of
+ [] ->
+ case BinOrOld of
+ bin ->
+ {Unused,Bits};
+ _ ->
+ decode_bitstring2(size(Bits), Unused, Bits)
+ end;
+ _ ->
+ BitString = decode_bitstring2(size(Bits), Unused, Bits),
+ decode_bitstring_NNL(BitString,NamedNumberList)
+ end.
+
+%%----------------------------------------
+%% Decode the in buffer to bits
+%%----------------------------------------
+decode_bitstring2(1,Unused,<>) ->
+ lists:sublist([B7,B6,B5,B4,B3,B2,B1,B0],8-Unused);
+decode_bitstring2(Len, Unused,
+ <>) ->
+ [B7, B6, B5, B4, B3, B2, B1, B0 |
+ decode_bitstring2(Len - 1, Unused, Buffer)].
+
+%%decode_bitstring2(1, Unused, Buffer) ->
+%% make_bits_of_int(hd(Buffer), 128, 8-Unused);
+%%decode_bitstring2(Len, Unused, [BitVal | Buffer]) ->
+%% [B7, B6, B5, B4, B3, B2, B1, B0] = make_bits_of_int(BitVal, 128, 8),
+%% [B7, B6, B5, B4, B3, B2, B1, B0 |
+%% decode_bitstring2(Len - 1, Unused, Buffer)].
+
+
+%%make_bits_of_int(_, _, 0) ->
+%% [];
+%%make_bits_of_int(BitVal, MaskVal, Unused) when Unused > 0 ->
+%% X = case MaskVal band BitVal of
+%% 0 -> 0 ;
+%% _ -> 1
+%% end,
+%% [X | make_bits_of_int(BitVal, MaskVal bsr 1, Unused - 1)].
+
+
+
+%%----------------------------------------
+%% Decode the bitlist to names
+%%----------------------------------------
+
+
+decode_bitstring_NNL(BitList,NamedNumberList) ->
+ decode_bitstring_NNL(BitList,NamedNumberList,0,[]).
+
+
+decode_bitstring_NNL([],_,_No,Result) ->
+ lists:reverse(Result);
+
+decode_bitstring_NNL([B|BitList],[{Name,No}|NamedNumberList],No,Result) ->
+ if
+ B == 0 ->
+ decode_bitstring_NNL(BitList,NamedNumberList,No+1,Result);
+ true ->
+ decode_bitstring_NNL(BitList,NamedNumberList,No+1,[Name|Result])
+ end;
+decode_bitstring_NNL([1|BitList],NamedNumberList,No,Result) ->
+ decode_bitstring_NNL(BitList,NamedNumberList,No+1,[{bit,No}|Result]);
+decode_bitstring_NNL([0|BitList],NamedNumberList,No,Result) ->
+ decode_bitstring_NNL(BitList,NamedNumberList,No+1,Result).
+
+
+%%============================================================================
+%% Octet string, ITU_T X.690 Chapter 8.7
+%%
+%% encode octet string
+%% The OctetList must be a flat list of integers in the range 0..255
+%% the function does not check this because it takes to much time
+%%============================================================================
+encode_octet_string(_C, OctetList, TagIn) when binary(OctetList) ->
+ encode_tags(TagIn, OctetList, size(OctetList));
+encode_octet_string(_C, OctetList, TagIn) when list(OctetList) ->
+ encode_tags(TagIn, OctetList, length(OctetList));
+encode_octet_string(C, {Name,OctetList}, TagIn) when atom(Name) ->
+ encode_octet_string(C, OctetList, TagIn).
+
+
+%%============================================================================
+%% decode octet string
+%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes}
+%%
+%% Octet string is decoded as a restricted string
+%%============================================================================
+decode_octet_string(Buffer, Range, Tags) ->
+% NewTags = new_tags(HasTag,#tag{class=?UNIVERSAL,number=?N_OCTET_STRING}),
+ decode_restricted_string(Buffer, Range, ?N_OCTET_STRING,
+ Tags, [], old).
+
+%%============================================================================
+%% Null value, ITU_T X.690 Chapter 8.8
+%%
+%% encode NULL value
+%%============================================================================
+
+encode_null({Name, _Val}, TagIn) when atom(Name) ->
+ encode_tags(TagIn, [], 0);
+encode_null(_Val, TagIn) ->
+ encode_tags(TagIn, [], 0).
+
+%%============================================================================
+%% decode NULL value
+%% (Buffer, HasTag, TotalLen) -> {NULL, Remain, RemovedBytes}
+%%============================================================================
+
+decode_null(Tlv, Tags) ->
+ Val = match_tags(Tlv, Tags),
+ case Val of
+ <<>> ->
+ 'NULL';
+ _ ->
+ exit({error,{asn1,{decode_null,Val}}})
+ end.
+
+%%============================================================================
+%% Object identifier, ITU_T X.690 Chapter 8.19
+%%
+%% encode Object Identifier value
+%%============================================================================
+
+encode_object_identifier({Name,Val}, TagIn) when atom(Name) ->
+ encode_object_identifier(Val, TagIn);
+encode_object_identifier(Val, TagIn) ->
+ encode_tags(TagIn, e_object_identifier(Val)).
+
+e_object_identifier({'OBJECT IDENTIFIER', V}) ->
+ e_object_identifier(V);
+e_object_identifier({Cname, V}) when atom(Cname), tuple(V) ->
+ e_object_identifier(tuple_to_list(V));
+e_object_identifier({Cname, V}) when atom(Cname), list(V) ->
+ e_object_identifier(V);
+e_object_identifier(V) when tuple(V) ->
+ e_object_identifier(tuple_to_list(V));
+
+%%%%%%%%%%%%%%%
+%% e_object_identifier([List of Obect Identifiers]) ->
+%% {[Encoded Octetlist of ObjIds], IntLength}
+%%
+e_object_identifier([E1, E2 | Tail]) ->
+ Head = 40*E1 + E2, % wow!
+ {H,Lh} = mk_object_val(Head),
+ {R,Lr} = enc_obj_id_tail(Tail, [], 0),
+ {[H|R], Lh+Lr}.
+
+enc_obj_id_tail([], Ack, Len) ->
+ {lists:reverse(Ack), Len};
+enc_obj_id_tail([H|T], Ack, Len) ->
+ {B, L} = mk_object_val(H),
+ enc_obj_id_tail(T, [B|Ack], Len+L).
+
+%% e_object_identifier([List of Obect Identifiers]) ->
+%% {[Encoded Octetlist of ObjIds], IntLength}
+%%
+%%e_object_identifier([E1, E2 | Tail]) ->
+%% Head = 40*E1 + E2, % wow!
+%% F = fun(Val, AckLen) ->
+%% {L, Ack} = mk_object_val(Val),
+%% {L, Ack + AckLen}
+%% end,
+%% {Octets, Len} = lists:mapfoldl(F, 0, [Head | Tail]).
+
+%%%%%%%%%%%
+%% mk_object_val(Value) -> {OctetList, Len}
+%% returns a Val as a list of octets, the 8 bit is allways set to one except
+%% for the last octet, where its 0
+%%
+
+
+mk_object_val(Val) when Val =< 127 ->
+ {[255 band Val], 1};
+mk_object_val(Val) ->
+ mk_object_val(Val bsr 7, [Val band 127], 1).
+mk_object_val(0, Ack, Len) ->
+ {Ack, Len};
+mk_object_val(Val, Ack, Len) ->
+ mk_object_val(Val bsr 7, [((Val band 127) bor 128) | Ack], Len + 1).
+
+
+
+%%============================================================================
+%% decode Object Identifier value
+%% (Buffer, HasTag, TotalLen) -> {{ObjId}, Remain, RemovedBytes}
+%%============================================================================
+
+decode_object_identifier(Tlv, Tags) ->
+ Val = match_tags(Tlv, Tags),
+ [AddedObjVal|ObjVals] = dec_subidentifiers(Val,0,[]),
+ {Val1, Val2} = if
+ AddedObjVal < 40 ->
+ {0, AddedObjVal};
+ AddedObjVal < 80 ->
+ {1, AddedObjVal - 40};
+ true ->
+ {2, AddedObjVal - 80}
+ end,
+ list_to_tuple([Val1, Val2 | ObjVals]).
+
+dec_subidentifiers(<<>>,_Av,Al) ->
+ lists:reverse(Al);
+dec_subidentifiers(<<1:1,H:7,T/binary>>,Av,Al) ->
+ dec_subidentifiers(T,(Av bsl 7) + H,Al);
+dec_subidentifiers(<>,Av,Al) ->
+ dec_subidentifiers(T,0,[((Av bsl 7) + H)|Al]).
+
+
+%%============================================================================
+%% Restricted character string types, ITU_T X.690 Chapter 8.20
+%%
+%% encode Numeric Printable Teletex Videotex Visible IA5 Graphic General strings
+%%============================================================================
+%% The StringType arg is kept for future use but might be removed
+encode_restricted_string(_C, OctetList, _StringType, TagIn)
+ when binary(OctetList) ->
+ encode_tags(TagIn, OctetList, size(OctetList));
+encode_restricted_string(_C, OctetList, _StringType, TagIn)
+ when list(OctetList) ->
+ encode_tags(TagIn, OctetList, length(OctetList));
+encode_restricted_string(C,{Name,OctetL}, StringType, TagIn) when atom(Name)->
+ encode_restricted_string(C, OctetL, StringType, TagIn).
+
+%%============================================================================
+%% decode Numeric Printable Teletex Videotex Visible IA5 Graphic General strings
+%% (Buffer, Range, StringType, HasTag, TotalLen) ->
+%% {String, Remain, RemovedBytes}
+%%============================================================================
+
+decode_restricted_string(Buffer, Range, StringType, Tags) ->
+ decode_restricted_string(Buffer, Range, StringType, Tags, [], old).
+
+
+decode_restricted_string(Tlv, Range, StringType, TagsIn,
+ NamedNumberList, BinOrOld) ->
+ Val = match_tags(Tlv, TagsIn),
+ Val2 =
+ case Val of
+ PartList = [_H|_T] -> % constructed val
+ Bin = collect_parts(PartList),
+ decode_restricted(Bin, StringType,
+ NamedNumberList, BinOrOld);
+ Bin ->
+ decode_restricted(Bin, StringType,
+ NamedNumberList, BinOrOld)
+ end,
+ check_and_convert_restricted_string(Val2,StringType,Range,NamedNumberList,BinOrOld).
+
+
+
+% case StringType of
+% ?N_BIT_STRING when BinOrOld == bin ->
+% {concat_bit_binaries(AccVal, Val), AccRb+Rb};
+% _ when binary(Val),binary(AccVal) ->
+% {<>,AccRb+Rb};
+% _ when binary(Val), AccVal==[] ->
+% {Val,AccRb+Rb};
+% _ ->
+% {AccVal++Val, AccRb+Rb}
+% end,
+
+
+
+decode_restricted(Bin, StringType, NamedNumberList,BinOrOld) ->
+ case StringType of
+ ?N_BIT_STRING ->
+ decode_bit_string2(Bin, NamedNumberList, BinOrOld);
+ ?N_UniversalString ->
+ mk_universal_string(binary_to_list(Bin));
+ ?N_BMPString ->
+ mk_BMP_string(binary_to_list(Bin));
+ _ ->
+ Bin
+ end.
+
+
+check_and_convert_restricted_string(Val,StringType,Range,NamedNumberList,_BinOrOld) ->
+ {StrLen,NewVal} = case StringType of
+ ?N_BIT_STRING when NamedNumberList /= [] ->
+ {no_check,Val};
+ ?N_BIT_STRING when list(Val) ->
+ {length(Val),Val};
+ ?N_BIT_STRING when tuple(Val) ->
+ {(size(element(2,Val))*8) - element(1,Val),Val};
+ _ when binary(Val) ->
+ {size(Val),binary_to_list(Val)};
+ _ when list(Val) ->
+ {length(Val), Val}
+ end,
+ case Range of
+ _ when StrLen == no_check ->
+ NewVal;
+ [] -> % No length constraint
+ NewVal;
+ {Lb,Ub} when StrLen >= Lb, Ub >= StrLen -> % variable length constraint
+ NewVal;
+ {{Lb,_Ub},[]} when StrLen >= Lb ->
+ NewVal;
+ {{Lb1,Ub1},{Lb2,Ub2}} when StrLen >= Lb1, StrLen =< Ub1;
+ StrLen =< Ub2, StrLen >= Lb2 ->
+ NewVal;
+ StrLen -> % fixed length constraint
+ NewVal;
+ {_,_} ->
+ exit({error,{asn1,{length,Range,Val}}});
+ _Len when integer(_Len) ->
+ exit({error,{asn1,{length,Range,Val}}});
+ _ -> % some strange constraint that we don't support yet
+ NewVal
+ end.
+
+
+%%============================================================================
+%% encode Universal string
+%%============================================================================
+
+encode_universal_string(C, {Name, Universal}, TagIn) when atom(Name) ->
+ encode_universal_string(C, Universal, TagIn);
+encode_universal_string(_C, Universal, TagIn) ->
+ OctetList = mk_uni_list(Universal),
+ encode_tags(TagIn, OctetList, length(OctetList)).
+
+mk_uni_list(In) ->
+ mk_uni_list(In,[]).
+
+mk_uni_list([],List) ->
+ lists:reverse(List);
+mk_uni_list([{A,B,C,D}|T],List) ->
+ mk_uni_list(T,[D,C,B,A|List]);
+mk_uni_list([H|T],List) ->
+ mk_uni_list(T,[H,0,0,0|List]).
+
+%%===========================================================================
+%% decode Universal strings
+%% (Buffer, Range, StringType, HasTag, LenIn) ->
+%% {String, Remain, RemovedBytes}
+%%===========================================================================
+
+decode_universal_string(Buffer, Range, Tags) ->
+ decode_restricted_string(Buffer, Range, ?N_UniversalString,
+ Tags, [], old).
+
+
+mk_universal_string(In) ->
+ mk_universal_string(In,[]).
+
+mk_universal_string([],Acc) ->
+ lists:reverse(Acc);
+mk_universal_string([0,0,0,D|T],Acc) ->
+ mk_universal_string(T,[D|Acc]);
+mk_universal_string([A,B,C,D|T],Acc) ->
+ mk_universal_string(T,[{A,B,C,D}|Acc]).
+
+
+%%============================================================================
+%% encode BMP string
+%%============================================================================
+
+encode_BMP_string(C, {Name,BMPString}, TagIn) when atom(Name)->
+ encode_BMP_string(C, BMPString, TagIn);
+encode_BMP_string(_C, BMPString, TagIn) ->
+ OctetList = mk_BMP_list(BMPString),
+ encode_tags(TagIn, OctetList, length(OctetList)).
+
+mk_BMP_list(In) ->
+ mk_BMP_list(In,[]).
+
+mk_BMP_list([],List) ->
+ lists:reverse(List);
+mk_BMP_list([{0,0,C,D}|T],List) ->
+ mk_BMP_list(T,[D,C|List]);
+mk_BMP_list([H|T],List) ->
+ mk_BMP_list(T,[H,0|List]).
+
+%%============================================================================
+%% decode (OctetList, Range(ignored), tag|notag) -> {ValList, RestList}
+%% (Buffer, Range, StringType, HasTag, TotalLen) ->
+%% {String, Remain, RemovedBytes}
+%%============================================================================
+decode_BMP_string(Buffer, Range, Tags) ->
+ decode_restricted_string(Buffer, Range, ?N_BMPString,
+ Tags, [], old).
+
+mk_BMP_string(In) ->
+ mk_BMP_string(In,[]).
+
+mk_BMP_string([],US) ->
+ lists:reverse(US);
+mk_BMP_string([0,B|T],US) ->
+ mk_BMP_string(T,[B|US]);
+mk_BMP_string([C,D|T],US) ->
+ mk_BMP_string(T,[{0,0,C,D}|US]).
+
+
+%%============================================================================
+%% Generalized time, ITU_T X.680 Chapter 39
+%%
+%% encode Generalized time
+%%============================================================================
+
+encode_generalized_time(C, {Name,OctetList}, TagIn) when atom(Name) ->
+ encode_generalized_time(C, OctetList, TagIn);
+encode_generalized_time(_C, OctetList, TagIn) ->
+ encode_tags(TagIn, OctetList, length(OctetList)).
+
+%%============================================================================
+%% decode Generalized time
+%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes}
+%%============================================================================
+
+decode_generalized_time(Tlv, _Range, Tags) ->
+ Val = match_tags(Tlv, Tags),
+ NewVal = case Val of
+ PartList = [_H|_T] -> % constructed
+ collect_parts(PartList);
+ Bin ->
+ Bin
+ end,
+ binary_to_list(NewVal).
+
+%%============================================================================
+%% Universal time, ITU_T X.680 Chapter 40
+%%
+%% encode UTC time
+%%============================================================================
+
+encode_utc_time(C, {Name,OctetList}, TagIn) when atom(Name) ->
+ encode_utc_time(C, OctetList, TagIn);
+encode_utc_time(_C, OctetList, TagIn) ->
+ encode_tags(TagIn, OctetList, length(OctetList)).
+
+%%============================================================================
+%% decode UTC time
+%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes}
+%%============================================================================
+
+decode_utc_time(Tlv, _Range, Tags) ->
+ Val = match_tags(Tlv, Tags),
+ NewVal = case Val of
+ PartList = [_H|_T] -> % constructed
+ collect_parts(PartList);
+ Bin ->
+ Bin
+ end,
+ binary_to_list(NewVal).
+
+
+%%============================================================================
+%% Length handling
+%%
+%% Encode length
+%%
+%% encode_length(Int | indefinite) ->
+%% [<127]| [128 + Int (<127),OctetList] | [16#80]
+%%============================================================================
+
+encode_length(indefinite) ->
+ {[16#80],1}; % 128
+encode_length(L) when L =< 16#7F ->
+ {[L],1};
+encode_length(L) ->
+ Oct = minimum_octets(L),
+ Len = length(Oct),
+ if
+ Len =< 126 ->
+ {[ (16#80+Len) | Oct ],Len+1};
+ true ->
+ exit({error,{asn1, to_long_length_oct, Len}})
+ end.
+
+
+%% Val must be >= 0
+minimum_octets(Val) ->
+ minimum_octets(Val,[]).
+
+minimum_octets(0,Acc) ->
+ Acc;
+minimum_octets(Val, Acc) ->
+ minimum_octets((Val bsr 8),[Val band 16#FF | Acc]).
+
+
+%%===========================================================================
+%% Decode length
+%%
+%% decode_length(OctetList) -> {{indefinite, RestOctetsL}, NoRemovedBytes} |
+%% {{Length, RestOctetsL}, NoRemovedBytes}
+%%===========================================================================
+
+decode_length(<<1:1,0:7,T/binary>>) ->
+ {indefinite, T};
+decode_length(<<0:1,Length:7,T/binary>>) ->
+ {Length,T};
+decode_length(<<1:1,LL:7,T/binary>>) ->
+ <> = T,
+ {Length,Rest}.
+
+
+
+%%-------------------------------------------------------------------------
+%% INTERNAL HELPER FUNCTIONS (not exported)
+%%-------------------------------------------------------------------------
+
+
+%% decoding postitive integer values.
+decode_integer2(Len,Bin = <<0:1,_:7,_Bs/binary>>) ->
+ <> = Bin,
+ Int;
+%% decoding negative integer values.
+decode_integer2(Len,<<1:1,B2:7,Bs/binary>>) ->
+ <> = <>,
+ Int = N - (1 bsl (8 * Len - 1)),
+ Int.
+
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V
+ end.
+
+collect_parts(TlvList) ->
+ collect_parts(TlvList,[]).
+
+collect_parts([{_,L}|Rest],Acc) when list(L) ->
+ collect_parts(Rest,[collect_parts(L)|Acc]);
+collect_parts([{?N_BIT_STRING,<>}|Rest],_Acc) ->
+ collect_parts_bit(Rest,[Bits],Unused);
+collect_parts([{_T,V}|Rest],Acc) ->
+ collect_parts(Rest,[V|Acc]);
+collect_parts([],Acc) ->
+ list_to_binary(lists:reverse(Acc)).
+
+collect_parts_bit([{?N_BIT_STRING,<>}|Rest],Acc,Uacc) ->
+ collect_parts_bit(Rest,[Bits|Acc],Unused+Uacc);
+collect_parts_bit([],Acc,Uacc) ->
+ list_to_binary([Uacc|lists:reverse(Acc)]).
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_check.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_check.erl
new file mode 100644
index 0000000000..cfda8a2a88
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_check.erl
@@ -0,0 +1,333 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1rt_check.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1rt_check).
+
+-include("asn1_records.hrl").
+
+-export([check_bool/2,
+ check_int/3,
+ check_bitstring/3,
+ check_octetstring/2,
+ check_null/2,
+ check_objectidentifier/2,
+ check_objectdescriptor/2,
+ check_real/2,
+ check_enum/3,
+ check_restrictedstring/2]).
+
+-export([transform_to_EXTERNAL1990/1,
+ transform_to_EXTERNAL1994/1]).
+
+
+check_bool(_Bool,asn1_DEFAULT) ->
+ true;
+check_bool(Bool,Bool) when Bool == true; Bool == false ->
+ true;
+check_bool(_Bool1,Bool2) ->
+ throw({error,Bool2}).
+
+check_int(_,asn1_DEFAULT,_) ->
+ true;
+check_int(Value,Value,_) when integer(Value) ->
+ true;
+check_int(DefValue,Value,NNL) when atom(Value) ->
+ case lists:keysearch(Value,1,NNL) of
+ {value,{_,DefValue}} ->
+ true;
+ _ ->
+ throw({error,DefValue})
+ end;
+check_int(DefaultValue,_Value,_) ->
+ throw({error,DefaultValue}).
+
+% check_bitstring([H|T],[H|T],_) when integer(H) ->
+% true;
+% check_bitstring(V,V,_) when integer(V) ->
+% true;
+%% Two equal lists or integers
+check_bitstring(_,asn1_DEFAULT,_) ->
+ true;
+check_bitstring(V,V,_) ->
+ true;
+%% Default value as a list of 1 and 0 and user value as an integer
+check_bitstring(L=[H|T],Int,_) when integer(Int),integer(H) ->
+ case bit_list_to_int(L,length(T)) of
+ Int -> true;
+ _ -> throw({error,L,Int})
+ end;
+%% Default value as an integer, val as list
+check_bitstring(Int,Val,NBL) when integer(Int),list(Val) ->
+ BL = int_to_bit_list(Int,[],length(Val)),
+ check_bitstring(BL,Val,NBL);
+%% Default value and user value as lists of ones and zeros
+check_bitstring(L1=[H1|_T1],L2=[H2|_T2],NBL=[_H|_T]) when integer(H1),integer(H2) ->
+ L2new = remove_trailing_zeros(L2),
+ check_bitstring(L1,L2new,NBL);
+%% Default value as a list of 1 and 0 and user value as a list of atoms
+check_bitstring(L1=[H1|_T1],L2=[H2|_T2],NBL) when integer(H1),atom(H2) ->
+ case bit_list_to_nbl(L1,NBL,0,[]) of
+ L3 -> check_bitstring(L3,L2,NBL);
+ _ -> throw({error,L2})
+ end;
+%% Both default value and user value as a list of atoms
+check_bitstring(L1=[H1|T1],L2=[H2|_T2],_) when atom(H1),atom(H2) ->
+ length(L1) == length(L2),
+ case lists:member(H1,L2) of
+ true ->
+ check_bitstring1(T1,L2);
+ false -> throw({error,L2})
+ end;
+%% Default value as a list of atoms and user value as a list of 1 and 0
+check_bitstring(L1=[H1|_T1],L2=[H2|_T2],NBL) when atom(H1),integer(H2) ->
+ case bit_list_to_nbl(L2,NBL,0,[]) of
+ L3 ->
+ check_bitstring(L1,L3,NBL);
+ _ -> throw({error,L2})
+ end;
+%% User value in compact format
+check_bitstring(DefVal,CBS={_,_},NBL) ->
+ NewVal = cbs_to_bit_list(CBS),
+ check_bitstring(DefVal,NewVal,NBL);
+check_bitstring(DV,V,_) ->
+ throw({error,DV,V}).
+
+
+bit_list_to_int([0|Bs],ShL)->
+ bit_list_to_int(Bs,ShL-1) + 0;
+bit_list_to_int([1|Bs],ShL) ->
+ bit_list_to_int(Bs,ShL-1) + (1 bsl ShL);
+bit_list_to_int([],_) ->
+ 0.
+
+int_to_bit_list(0,Acc,0) ->
+ Acc;
+int_to_bit_list(Int,Acc,Len) ->
+ int_to_bit_list(Int bsr 1,[Int band 1|Acc],Len - 1).
+
+bit_list_to_nbl([0|T],NBL,Pos,Acc) ->
+ bit_list_to_nbl(T,NBL,Pos+1,Acc);
+bit_list_to_nbl([1|T],NBL,Pos,Acc) ->
+ case lists:keysearch(Pos,2,NBL) of
+ {value,{N,_}} ->
+ bit_list_to_nbl(T,NBL,Pos+1,[N|Acc]);
+ _ ->
+ throw({error,{no,named,element,at,pos,Pos}})
+ end;
+bit_list_to_nbl([],_,_,Acc) ->
+ Acc.
+
+remove_trailing_zeros(L2) ->
+ remove_trailing_zeros1(lists:reverse(L2)).
+remove_trailing_zeros1(L) ->
+ lists:reverse(lists:dropwhile(fun(0)->true;
+ (_) ->false
+ end,
+ L)).
+
+check_bitstring1([H|T],NBL) ->
+ case lists:member(H,NBL) of
+ true ->
+ check_bitstring1(T,NBL);
+ V -> throw({error,V})
+ end;
+check_bitstring1([],_) ->
+ true.
+
+cbs_to_bit_list({Unused,<>}) when size(Rest) >= 1 ->
+ [B7,B6,B5,B4,B3,B2,B1,B0|cbs_to_bit_list({Unused,Rest})];
+cbs_to_bit_list({0,<>}) ->
+ [B7,B6,B5,B4,B3,B2,B1,B0];
+cbs_to_bit_list({Unused,Bin}) when size(Bin) == 1 ->
+ Used = 8-Unused,
+ <> = Bin,
+ int_to_bit_list(Int,[],Used).
+
+
+check_octetstring(_,asn1_DEFAULT) ->
+ true;
+check_octetstring(L,L) ->
+ true;
+check_octetstring(L,Int) when list(L),integer(Int) ->
+ case integer_to_octetlist(Int) of
+ L -> true;
+ V -> throw({error,V})
+ end;
+check_octetstring(_,V) ->
+ throw({error,V}).
+
+integer_to_octetlist(Int) ->
+ integer_to_octetlist(Int,[]).
+integer_to_octetlist(0,Acc) ->
+ Acc;
+integer_to_octetlist(Int,Acc) ->
+ integer_to_octetlist(Int bsr 8,[(Int band 255)|Acc]).
+
+check_null(_,asn1_DEFAULT) ->
+ true;
+check_null('NULL','NULL') ->
+ true;
+check_null(_,V) ->
+ throw({error,V}).
+
+check_objectidentifier(_,asn1_DEFAULT) ->
+ true;
+check_objectidentifier(OI,OI) ->
+ true;
+check_objectidentifier(DOI,OI) when tuple(DOI),tuple(OI) ->
+ check_objectidentifier1(tuple_to_list(DOI),tuple_to_list(OI));
+check_objectidentifier(_,OI) ->
+ throw({error,OI}).
+
+check_objectidentifier1([V|Rest1],[V|Rest2]) ->
+ check_objectidentifier1(Rest1,Rest2,V);
+check_objectidentifier1([V1|Rest1],[V2|Rest2]) ->
+ case reserved_objectid(V2,[]) of
+ V1 ->
+ check_objectidentifier1(Rest1,Rest2,[V1]);
+ V ->
+ throw({error,V})
+ end.
+check_objectidentifier1([V|Rest1],[V|Rest2],Above) ->
+ check_objectidentifier1(Rest1,Rest2,[V|Above]);
+check_objectidentifier1([V1|Rest1],[V2|Rest2],Above) ->
+ case reserved_objectid(V2,Above) of
+ V1 ->
+ check_objectidentifier1(Rest1,Rest2,[V1|Above]);
+ V ->
+ throw({error,V})
+ end;
+check_objectidentifier1([],[],_) ->
+ true;
+check_objectidentifier1(_,V,_) ->
+ throw({error,object,identifier,V}).
+
+%% ITU-T Rec. X.680 Annex B - D
+reserved_objectid('itu-t',[]) -> 0;
+reserved_objectid('ccitt',[]) -> 0;
+%% arcs below "itu-t"
+reserved_objectid('recommendation',[0]) -> 0;
+reserved_objectid('question',[0]) -> 1;
+reserved_objectid('administration',[0]) -> 2;
+reserved_objectid('network-operator',[0]) -> 3;
+reserved_objectid('identified-organization',[0]) -> 4;
+
+reserved_objectid(iso,[]) -> 1;
+%% arcs below "iso", note that number 1 is not used
+reserved_objectid('standard',[1]) -> 0;
+reserved_objectid('member-body',[1]) -> 2;
+reserved_objectid('identified-organization',[1]) -> 3;
+
+reserved_objectid('joint-iso-itu-t',[]) -> 2;
+reserved_objectid('joint-iso-ccitt',[]) -> 2;
+
+reserved_objectid(_,_) -> false.
+
+
+check_objectdescriptor(_,asn1_DEFAULT) ->
+ true;
+check_objectdescriptor(OD,OD) ->
+ true;
+check_objectdescriptor(OD,OD) ->
+ throw({error,{not_implemented_yet,check_objectdescriptor}}).
+
+check_real(_,asn1_DEFAULT) ->
+ true;
+check_real(R,R) ->
+ true;
+check_real(_,_) ->
+ throw({error,{not_implemented_yet,check_real}}).
+
+check_enum(_,asn1_DEFAULT,_) ->
+ true;
+check_enum(Val,Val,_) ->
+ true;
+check_enum(Int,Atom,Enumerations) when integer(Int),atom(Atom) ->
+ case lists:keysearch(Atom,1,Enumerations) of
+ {value,{_,Int}} -> true;
+ _ -> throw({error,{enumerated,Int,Atom}})
+ end;
+check_enum(DefVal,Val,_) ->
+ throw({error,{enumerated,DefVal,Val}}).
+
+
+check_restrictedstring(_,asn1_DEFAULT) ->
+ true;
+check_restrictedstring(Val,Val) ->
+ true;
+check_restrictedstring([V|Rest1],[V|Rest2]) ->
+ check_restrictedstring(Rest1,Rest2);
+check_restrictedstring([V1|Rest1],[V2|Rest2]) ->
+ check_restrictedstring(V1,V2),
+ check_restrictedstring(Rest1,Rest2);
+%% tuple format of value
+check_restrictedstring({V1,V2},[V1,V2]) ->
+ true;
+check_restrictedstring([V1,V2],{V1,V2}) ->
+ true;
+%% quadruple format of value
+check_restrictedstring({V1,V2,V3,V4},[V1,V2,V3,V4]) ->
+ true;
+check_restrictedstring([V1,V2,V3,V4],{V1,V2,V3,V4}) ->
+ true;
+%% character string list
+check_restrictedstring(V1,V2) when list(V1),tuple(V2) ->
+ check_restrictedstring(V1,tuple_to_list(V2));
+check_restrictedstring(V1,V2) ->
+ throw({error,{restricted,string,V1,V2}}).
+
+transform_to_EXTERNAL1990(Val) when tuple(Val),size(Val) == 4 ->
+ transform_to_EXTERNAL1990(tuple_to_list(Val),[]);
+transform_to_EXTERNAL1990(Val) when tuple(Val) ->
+ %% Data already in ASN1 1990 format
+ Val.
+
+transform_to_EXTERNAL1990(['EXTERNAL'|Rest],Acc) ->
+ transform_to_EXTERNAL1990(Rest,['EXTERNAL'|Acc]);
+transform_to_EXTERNAL1990([{syntax,Syntax}|Rest],Acc) ->
+ transform_to_EXTERNAL1990(Rest,[asn1_NOVALUE,Syntax|Acc]);
+transform_to_EXTERNAL1990([{'presentation-context-id',PCid}|Rest],Acc) ->
+ transform_to_EXTERNAL1990(Rest,[PCid,asn1_NOVALUE|Acc]);
+transform_to_EXTERNAL1990([{'context-negotiation',Context_negot}|Rest],Acc) ->
+ {_,Presentation_Cid,Transfer_syntax} = Context_negot,
+ transform_to_EXTERNAL1990(Rest,[Transfer_syntax,Presentation_Cid|Acc]);
+transform_to_EXTERNAL1990([asn1_NOVALUE|Rest],Acc) ->
+ transform_to_EXTERNAL1990(Rest,[asn1_NOVALUE|Acc]);
+transform_to_EXTERNAL1990([Data_val_desc,Data_value],Acc) when list(Data_value)->
+ list_to_tuple(lists:reverse([{'octet-aligned',Data_value},
+ Data_val_desc|Acc]));
+transform_to_EXTERNAL1990([Data_value],Acc) when list(Data_value)->
+ list_to_tuple(lists:reverse([{'octet-aligned',Data_value}|Acc])).
+
+
+transform_to_EXTERNAL1994(V={'EXTERNAL',DRef,IndRef,Data_v_desc,Encoding}) ->
+ Identification =
+ case {DRef,IndRef} of
+ {DRef,asn1_NOVALUE} ->
+ {syntax,DRef};
+ {asn1_NOVALUE,IndRef} ->
+ {'presentation-context-id',IndRef};
+ _ ->
+ {'context-negotiation',
+ {'EXTERNAL_identification_context-negotiation',IndRef,DRef}}
+ end,
+ case Encoding of
+ {_,Val} when list(Val) ->
+ {'EXTERNAL',Identification,Data_v_desc,Val};
+ _ ->
+ V
+ end.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_driver_handler.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_driver_handler.erl
new file mode 100644
index 0000000000..5200f9d2d9
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_driver_handler.erl
@@ -0,0 +1,108 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1rt_driver_handler.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+
+-module(asn1rt_driver_handler).
+
+-export([init/1,load_driver/0,unload_driver/0]).
+
+
+load_driver() ->
+ spawn(asn1rt_driver_handler, init, [self()]).
+
+init(From) ->
+ Port=
+ case load_driver("asn1_erl_drv") of
+ ok ->
+ open_named_port(From);
+ already_done ->
+ From ! driver_ready;
+ Error -> % if erl_ddll:load_driver fails
+ erl_ddll:unload_driver("asn1_erl_drv"),
+ From ! Error
+ end,
+ register_and_loop(Port).
+
+load_driver(DriverName) ->
+ case is_driver_loaded(DriverName) of
+ false ->
+ Dir = filename:join([code:priv_dir(asn1),"lib"]),
+ erl_ddll:load_driver(Dir,DriverName);
+ true ->
+ ok
+ end.
+
+
+is_driver_loaded(_Name) ->
+ case whereis(asn1_driver_owner) of
+ undefined ->
+ false;
+ _ ->
+ true
+ end.
+
+open_named_port(From) ->
+ case is_port_open(drv_complete) of
+ false ->
+ case catch open_port({spawn,"asn1_erl_drv"},[]) of
+ {'EXIT',Reason} ->
+ From ! {port_error,Reason};
+ Port ->
+ register(drv_complete,Port),
+ From ! driver_ready,
+ Port
+ end;
+ _ ->
+ From ! driver_ready,
+ ok
+ end.
+
+is_port_open(Name) ->
+ case whereis(Name) of
+ Port when port(Port) ->
+ true;
+ _ -> false
+ end.
+
+register_and_loop(Port) when port(Port) ->
+ register(asn1_driver_owner,self()),
+ loop();
+register_and_loop(_) ->
+ ok.
+
+loop() ->
+ receive
+ unload ->
+ case whereis(drv_complete) of
+ Port when port(Port) ->
+ port_close(Port);
+ _ -> ok
+ end,
+ erl_ddll:unload_driver("asn1_erl_drv"),
+ ok;
+ _ ->
+ loop()
+ end.
+
+unload_driver() ->
+ case whereis(asn1_driver_owner) of
+ Pid when pid(Pid) ->
+ Pid ! unload,
+ ok;
+ _ ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per.erl
new file mode 100644
index 0000000000..4999dde2cc
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per.erl
@@ -0,0 +1,1593 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1rt_per.erl,v 1.1 2008/12/17 09:53:31 mikpe Exp $
+%%
+-module(asn1rt_per).
+
+%% encoding / decoding of PER aligned
+
+-include("asn1_records.hrl").
+
+-export([dec_fixup/3, cindex/3, list_to_record/2]).
+-export([setchoiceext/1, setext/1, fixoptionals/2, fixextensions/2, setoptionals/1,
+ getext/1, getextension/2, skipextensions/3, getbit/1, getchoice/3 ]).
+-export([getoptionals/3, set_choice/3, encode_integer/2, encode_integer/3 ]).
+-export([decode_integer/2, decode_integer/3, encode_boolean/1,
+ decode_boolean/1, encode_length/2, decode_length/1, decode_length/2,
+ encode_small_length/1, decode_small_length/1]).
+-export([encode_enumerated/3, decode_enumerated/3,
+ encode_bit_string/3, decode_bit_string/3 ]).
+-export([encode_octet_string/2, decode_octet_string/2,
+ encode_restricted_string/4, encode_restricted_string/5,
+ decode_restricted_string/4, decode_restricted_string/5,
+ encode_null/1, decode_null/1,
+ encode_object_identifier/1, decode_object_identifier/1,
+ complete/1]).
+
+-export([encode_open_type/2, decode_open_type/2]).
+
+-export([encode_UniversalString/2, decode_UniversalString/2,
+ encode_PrintableString/2, decode_PrintableString/2,
+ encode_GeneralString/2, decode_GeneralString/2,
+ encode_GraphicString/2, decode_GraphicString/2,
+ encode_TeletexString/2, decode_TeletexString/2,
+ encode_VideotexString/2, decode_VideotexString/2,
+ encode_VisibleString/2, decode_VisibleString/2,
+ encode_BMPString/2, decode_BMPString/2,
+ encode_IA5String/2, decode_IA5String/2,
+ encode_NumericString/2, decode_NumericString/2
+ ]).
+
+
+dec_fixup(Terms,Cnames,RemBytes) ->
+ dec_fixup(Terms,Cnames,RemBytes,[]).
+
+dec_fixup([novalue|T],[_Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,Acc);
+dec_fixup([{_Name,novalue}|T],[_Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,Acc);
+dec_fixup([H|T],[Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,[{Hc,H}|Acc]);
+dec_fixup([],_Cnames,RemBytes,Acc) ->
+ {lists:reverse(Acc),RemBytes}.
+
+cindex(Ix,Val,Cname) ->
+ case element(Ix,Val) of
+ {Cname,Val2} -> Val2;
+ X -> X
+ end.
+
+% converts a list to a record if necessary
+list_to_record(Name,List) when list(List) ->
+ list_to_tuple([Name|List]);
+list_to_record(_Name,Tuple) when tuple(Tuple) ->
+ Tuple.
+
+%%--------------------------------------------------------
+%% setchoiceext(InRootSet) -> [{bit,X}]
+%% X is set to 1 when InRootSet==false
+%% X is set to 0 when InRootSet==true
+%%
+setchoiceext(true) ->
+ [{debug,choiceext},{bit,0}];
+setchoiceext(false) ->
+ [{debug,choiceext},{bit,1}].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% setext(true|false) -> CompleteList
+%%
+
+setext(true) ->
+ [{debug,ext},{bit,1}];
+setext(false) ->
+ [{debug,ext},{bit,0}].
+
+fixoptionals(OptList,Val) when tuple(Val) ->
+ fixoptionals(OptList,Val,[]);
+
+fixoptionals(OptList,Val) when list(Val) ->
+ fixoptionals(OptList,Val,1,[],[]).
+
+fixoptionals([],Val,Acc) ->
+ % return {Val,Opt}
+ {Val,lists:reverse(Acc)};
+fixoptionals([{_,Pos}|Ot],Val,Acc) ->
+ case element(Pos+1,Val) of
+ asn1_NOVALUE -> fixoptionals(Ot,Val,[0|Acc]);
+ asn1_DEFAULT -> fixoptionals(Ot,Val,[0|Acc]);
+ _ -> fixoptionals(Ot,Val,[1|Acc])
+ end.
+
+
+%setoptionals(OptList,Val) ->
+% Vlist = tuple_to_list(Val),
+% setoptionals(OptList,Vlist,1,[]).
+
+fixoptionals([{Name,Pos}|Ot],[{Name,Val}|Vt],_Opt,Acc1,Acc2) ->
+ fixoptionals(Ot,Vt,Pos+1,[1|Acc1],[{Name,Val}|Acc2]);
+fixoptionals([{_Name,Pos}|Ot],V,Pos,Acc1,Acc2) ->
+ fixoptionals(Ot,V,Pos+1,[0|Acc1],[asn1_NOVALUE|Acc2]);
+fixoptionals(O,[Vh|Vt],Pos,Acc1,Acc2) ->
+ fixoptionals(O,Vt,Pos+1,Acc1,[Vh|Acc2]);
+fixoptionals([],[Vh|Vt],Pos,Acc1,Acc2) ->
+ fixoptionals([],Vt,Pos+1,Acc1,[Vh|Acc2]);
+fixoptionals([],[],_,Acc1,Acc2) ->
+ % return {Val,Opt}
+ {list_to_tuple([asn1_RECORDNAME|lists:reverse(Acc2)]),lists:reverse(Acc1)}.
+
+setoptionals([H|T]) ->
+ [{bit,H}|setoptionals(T)];
+setoptionals([]) ->
+ [{debug,optionals}].
+
+getext(Bytes) when tuple(Bytes) ->
+ getbit(Bytes);
+getext(Bytes) when list(Bytes) ->
+ getbit({0,Bytes}).
+
+getextension(0, Bytes) ->
+ {{},Bytes};
+getextension(1, Bytes) ->
+ {Len,Bytes2} = decode_small_length(Bytes),
+ {Blist, Bytes3} = getbits_as_list(Len,Bytes2),
+ {list_to_tuple(Blist),Bytes3}.
+
+fixextensions({ext,ExtPos,ExtNum},Val) ->
+ case fixextensions(ExtPos,ExtNum+ExtPos,Val,0) of
+ 0 -> [];
+ ExtBits ->
+ [encode_small_length(ExtNum),{bits,ExtNum,ExtBits}]
+ end.
+
+fixextensions(Pos,MaxPos,_,Acc) when Pos >= MaxPos ->
+ Acc;
+fixextensions(Pos,ExtPos,Val,Acc) ->
+ Bit = case catch(element(Pos+1,Val)) of
+ asn1_NOVALUE ->
+ 0;
+ asn1_NOEXTVALUE ->
+ 0;
+ {'EXIT',_} ->
+ 0;
+ _ ->
+ 1
+ end,
+ fixextensions(Pos+1,ExtPos,Val,(Acc bsl 1)+Bit).
+
+skipextensions(Bytes,Nr,ExtensionBitPattern) ->
+ case (catch element(Nr,ExtensionBitPattern)) of
+ 1 ->
+ {_,Bytes2} = decode_open_type(Bytes,[]),
+ skipextensions(Bytes2, Nr+1, ExtensionBitPattern);
+ 0 ->
+ skipextensions(Bytes, Nr+1, ExtensionBitPattern);
+ {'EXIT',_} -> % badarg, no more extensions
+ Bytes
+ end.
+
+
+getchoice(Bytes,1,0) -> % only 1 alternative is not encoded
+ {0,Bytes};
+getchoice(Bytes,_NumChoices,1) ->
+ decode_small_number(Bytes);
+getchoice(Bytes,NumChoices,0) ->
+ decode_integer(Bytes,[{'ValueRange',{0,NumChoices-1}}]).
+
+getoptionals(Bytes,L,NumComp) when list(L) ->
+ {Blist,Bytes1} = getbits_as_list(length(L),Bytes),
+ {list_to_tuple(comptuple(Blist,L,NumComp,1)),Bytes1}.
+
+comptuple([Bh|Bt],[{_Name,Nr}|T],NumComp,Nr) ->
+ [Bh|comptuple(Bt,T,NumComp-1,Nr+1)];
+comptuple(Bl,[{Name,Tnr}|Tl],NumComp,Nr) ->
+ [0|comptuple(Bl,[{Name,Tnr}|Tl],NumComp-1,Nr+1)];
+comptuple(_B,_L,0,_Nr) ->
+ [];
+comptuple(B,O,N,Nr) ->
+ [0|comptuple(B,O,N-1,Nr+1)].
+
+getbits_as_list(Num,Bytes) ->
+ getbits_as_list(Num,Bytes,[]).
+
+getbits_as_list(0,Bytes,Acc) ->
+ {lists:reverse(Acc),Bytes};
+getbits_as_list(Num,Bytes,Acc) ->
+ {Bit,NewBytes} = getbit(Bytes),
+ getbits_as_list(Num-1,NewBytes,[Bit|Acc]).
+
+getbit(Bytes) ->
+% io:format("getbit:~p~n",[Bytes]),
+ getbit1(Bytes).
+
+getbit1({7,[H|T]}) ->
+ {H band 1,{0,T}};
+getbit1({Pos,[H|T]}) ->
+ {(H bsr (7-Pos)) band 1,{(Pos+1) rem 8,[H|T]}};
+getbit1(Bytes) when list(Bytes) ->
+ getbit1({0,Bytes}).
+
+%% This could be optimized
+getbits(Buffer,Num) ->
+% io:format("getbits:Buffer = ~p~nNum=~p~n",[Buffer,Num]),
+ getbits(Buffer,Num,0).
+
+getbits(Buffer,0,Acc) ->
+ {Acc,Buffer};
+getbits(Buffer,Num,Acc) ->
+ {B,NewBuffer} = getbit(Buffer),
+ getbits(NewBuffer,Num-1,B + (Acc bsl 1)).
+
+
+getoctet(Bytes) when list(Bytes) ->
+ getoctet({0,Bytes});
+getoctet(Bytes) ->
+% io:format("getoctet:Buffer = ~p~n",[Bytes]),
+ getoctet1(Bytes).
+
+getoctet1({0,[H|T]}) ->
+ {H,{0,T}};
+getoctet1({_Pos,[_,H|T]}) ->
+ {H,{0,T}}.
+
+align({0,L}) ->
+ {0,L};
+align({_Pos,[_H|T]}) ->
+ {0,T};
+align(Bytes) ->
+ {0,Bytes}.
+
+getoctets(Buffer,Num) ->
+% io:format("getoctets:Buffer = ~p~nNum = ~p~n",[Buffer,Num]),
+ getoctets(Buffer,Num,0).
+
+getoctets(Buffer,0,Acc) ->
+ {Acc,Buffer};
+getoctets(Buffer,Num,Acc) ->
+ {Oct,NewBuffer} = getoctet(Buffer),
+ getoctets(NewBuffer,Num-1,(Acc bsl 8)+Oct).
+
+getoctets_as_list(Buffer,Num) ->
+ getoctets_as_list(Buffer,Num,[]).
+
+getoctets_as_list(Buffer,0,Acc) ->
+ {lists:reverse(Acc),Buffer};
+getoctets_as_list(Buffer,Num,Acc) ->
+ {Oct,NewBuffer} = getoctet(Buffer),
+ getoctets_as_list(NewBuffer,Num-1,[Oct|Acc]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% set_choice(Alt,Choices,Altnum) -> ListofBitSettings
+%% Alt = atom()
+%% Altnum = integer() | {integer(),integer()}% number of alternatives
+%% Choices = [atom()] | {[atom()],[atom()]}
+%% When Choices is a tuple the first list is the Rootset and the
+%% second is the Extensions and then Altnum must also be a tuple with the
+%% lengths of the 2 lists
+%%
+set_choice(Alt,{L1,L2},{Len1,_Len2}) ->
+ case set_choice_tag(Alt,L1) of
+ N when integer(N), Len1 > 1 ->
+ [{bit,0}, % the value is in the root set
+ encode_integer([{'ValueRange',{0,Len1-1}}],N)];
+ N when integer(N) ->
+ [{bit,0}]; % no encoding if only 0 or 1 alternative
+ false ->
+ [{bit,1}, % extension value
+ case set_choice_tag(Alt,L2) of
+ N2 when integer(N2) ->
+ encode_small_number(N2);
+ false ->
+ unknown_choice_alt
+ end]
+ end;
+set_choice(Alt,L,Len) ->
+ case set_choice_tag(Alt,L) of
+ N when integer(N), Len > 1 ->
+ encode_integer([{'ValueRange',{0,Len-1}}],N);
+ N when integer(N) ->
+ []; % no encoding if only 0 or 1 alternative
+ false ->
+ [unknown_choice_alt]
+ end.
+
+set_choice_tag(Alt,Choices) ->
+ set_choice_tag(Alt,Choices,0).
+
+set_choice_tag(Alt,[Alt|_Rest],Tag) ->
+ Tag;
+set_choice_tag(Alt,[_H|Rest],Tag) ->
+ set_choice_tag(Alt,Rest,Tag+1);
+set_choice_tag(_,[],_) ->
+ false.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_open_type(Constraint, Value) -> CompleteList
+%% Value = list of bytes of an already encoded value (the list must be flat)
+%% | binary
+%% Contraint = not used in this version
+%%
+encode_open_type(_Constraint, Val) when list(Val) ->
+ [encode_length(undefined,length(Val)),align,
+ {octets,Val}];
+encode_open_type(_Constraint, Val) when binary(Val) ->
+ [encode_length(undefined,size(Val)),align,
+ {octets,binary_to_list(Val)}].
+%% the binary_to_list is not optimal but compatible with the current solution
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_open_type(Buffer,Constraint) -> Value
+%% Constraint is not used in this version
+%% Buffer = [byte] with PER encoded data
+%% Value = [byte] with decoded data (which must be decoded again as some type)
+%%
+decode_open_type(Bytes, _Constraint) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ Bytes3 = align(Bytes2),
+ getoctets_as_list(Bytes3,Len).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_integer(Constraint,Value,NamedNumberList) -> CompleteList
+%% encode_integer(Constraint,Value) -> CompleteList
+%% encode_integer(Constraint,{Name,Value}) -> CompleteList
+%%
+%%
+encode_integer(C,V,NamedNumberList) when atom(V) ->
+ case lists:keysearch(V,1,NamedNumberList) of
+ {value,{_,NewV}} ->
+ encode_integer(C,NewV);
+ _ ->
+ exit({error,{asn1,{namednumber,V}}})
+ end;
+encode_integer(C,V,_NamedNumberList) when integer(V) ->
+ encode_integer(C,V).
+
+encode_integer(C,{Name,Val}) when atom(Name) ->
+ encode_integer(C,Val);
+
+encode_integer({Rc,_Ec},Val) ->
+ case (catch encode_integer(Rc,Val)) of
+ {'EXIT',{error,{asn1,_}}} ->
+ [{bit,1},encode_unconstrained_number(Val)];
+ Encoded ->
+ [{bit,0},Encoded]
+ end;
+encode_integer(C,Val ) when list(C) ->
+ case get_constraint(C,'SingleValue') of
+ no ->
+ encode_integer1(C,Val);
+ V when integer(V),V == Val ->
+ []; % a type restricted to a single value encodes to nothing
+ V when list(V) ->
+ case lists:member(Val,V) of
+ true ->
+ encode_integer1(C,Val);
+ _ ->
+ exit({error,{asn1,{illegal_value,Val}}})
+ end;
+ _ ->
+ exit({error,{asn1,{illegal_value,Val}}})
+ end.
+
+encode_integer1(C, Val) ->
+ case VR = get_constraint(C,'ValueRange') of
+ no ->
+ encode_unconstrained_number(Val);
+ {Lb,'MAX'} ->
+ encode_semi_constrained_number(Lb,Val);
+ %% positive with range
+ {Lb,Ub} when Val >= Lb,
+ Ub >= Val ->
+ encode_constrained_number(VR,Val)
+ end.
+
+decode_integer(Buffer,Range,NamedNumberList) ->
+ {Val,Buffer2} = decode_integer(Buffer,Range),
+ case lists:keysearch(Val,2,NamedNumberList) of
+ {value,{NewVal,_}} -> {NewVal,Buffer2};
+ _ -> {Val,Buffer2}
+ end.
+
+decode_integer(Buffer,{Rc,_Ec}) ->
+ {Ext,Buffer2} = getext(Buffer),
+ case Ext of
+ 0 -> decode_integer(Buffer2,Rc);
+ 1 -> decode_unconstrained_number(Buffer2)
+ end;
+decode_integer(Buffer,undefined) ->
+ decode_unconstrained_number(Buffer);
+decode_integer(Buffer,C) ->
+ case get_constraint(C,'SingleValue') of
+ V when integer(V) ->
+ {V,Buffer};
+ V when list(V) ->
+ {Val,Buffer2} = decode_integer1(Buffer,C),
+ case lists:member(Val,V) of
+ true ->
+ {Val,Buffer2};
+ _ ->
+ exit({error,{asn1,{illegal_value,Val}}})
+ end;
+ _ ->
+ decode_integer1(Buffer,C)
+ end.
+
+decode_integer1(Buffer,C) ->
+ case VR = get_constraint(C,'ValueRange') of
+ no ->
+ decode_unconstrained_number(Buffer);
+ {Lb, 'MAX'} ->
+ decode_semi_constrained_number(Buffer,Lb);
+ {_,_} ->
+ decode_constrained_number(Buffer,VR)
+ end.
+
+% X.691:10.6 Encoding of a normally small non-negative whole number
+% Use this for encoding of CHOICE index if there is an extension marker in
+% the CHOICE
+encode_small_number({Name,Val}) when atom(Name) ->
+ encode_small_number(Val);
+encode_small_number(Val) when Val =< 63 ->
+ [{bit,0},{bits,6,Val}];
+encode_small_number(Val) ->
+ [{bit,1},encode_semi_constrained_number(0,Val)].
+
+decode_small_number(Bytes) ->
+ {Bit,Bytes2} = getbit(Bytes),
+ case Bit of
+ 0 ->
+ getbits(Bytes2,6);
+ 1 ->
+ decode_semi_constrained_number(Bytes2,{0,'MAX'})
+ end.
+
+% X.691:10.7 Encoding of a semi-constrained whole number
+%% might be an optimization encode_semi_constrained_number(0,Val) ->
+encode_semi_constrained_number(C,{Name,Val}) when atom(Name) ->
+ encode_semi_constrained_number(C,Val);
+encode_semi_constrained_number({Lb,'MAX'},Val) ->
+ encode_semi_constrained_number(Lb,Val);
+encode_semi_constrained_number(Lb,Val) ->
+ Val2 = Val - Lb,
+ Octs = eint_positive(Val2),
+ [encode_length(undefined,length(Octs)),{octets,Octs}].
+
+decode_semi_constrained_number(Bytes,{Lb,_}) ->
+ decode_semi_constrained_number(Bytes,Lb);
+decode_semi_constrained_number(Bytes,Lb) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {V,Bytes3} = getoctets(Bytes2,Len),
+ {V+Lb,Bytes3}.
+
+encode_constrained_number(Range,{Name,Val}) when atom(Name) ->
+ encode_constrained_number(Range,Val);
+encode_constrained_number({Lb,Ub},Val) when Val >= Lb, Ub >= Val ->
+ Range = Ub - Lb + 1,
+ Val2 = Val - Lb,
+ if
+ Range == 2 ->
+ {bits,1,Val2};
+ Range =< 4 ->
+ {bits,2,Val2};
+ Range =< 8 ->
+ {bits,3,Val2};
+ Range =< 16 ->
+ {bits,4,Val2};
+ Range =< 32 ->
+ {bits,5,Val2};
+ Range =< 64 ->
+ {bits,6,Val2};
+ Range =< 128 ->
+ {bits,7,Val2};
+ Range =< 255 ->
+ {bits,8,Val2};
+ Range =< 256 ->
+ {octets,1,Val2};
+ Range =< 65536 ->
+ {octets,2,Val2};
+ Range =< 16#1000000 ->
+ Octs = eint_positive(Val2),
+ [encode_length({1,3},length(Octs)),{octets,Octs}];
+ Range =< 16#100000000 ->
+ Octs = eint_positive(Val2),
+ [encode_length({1,4},length(Octs)),{octets,Octs}];
+ Range =< 16#10000000000 ->
+ Octs = eint_positive(Val2),
+ [encode_length({1,5},length(Octs)),{octets,Octs}];
+ true ->
+ exit({not_supported,{integer_range,Range}})
+ end.
+
+decode_constrained_number(Buffer,{Lb,Ub}) ->
+ Range = Ub - Lb + 1,
+% Val2 = Val - Lb,
+ {Val,Remain} =
+ if
+ Range == 2 ->
+ getbits(Buffer,1);
+ Range =< 4 ->
+ getbits(Buffer,2);
+ Range =< 8 ->
+ getbits(Buffer,3);
+ Range =< 16 ->
+ getbits(Buffer,4);
+ Range =< 32 ->
+ getbits(Buffer,5);
+ Range =< 64 ->
+ getbits(Buffer,6);
+ Range =< 128 ->
+ getbits(Buffer,7);
+ Range =< 255 ->
+ getbits(Buffer,8);
+ Range =< 256 ->
+ getoctets(Buffer,1);
+ Range =< 65536 ->
+ getoctets(Buffer,2);
+ Range =< 16#1000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,3}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ Range =< 16#100000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,4}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ Range =< 16#10000000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,5}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ true ->
+ exit({not_supported,{integer_range,Range}})
+ end,
+ {Val+Lb,Remain}.
+
+% X.691:10.8 Encoding of an unconstrained whole number
+
+encode_unconstrained_number(Val) when Val >= 0 ->
+ Oct = eint(Val,[]),
+ [{debug,unconstrained_number},
+ encode_length({0,'MAX'},length(Oct)),
+ {octets,Oct}];
+encode_unconstrained_number(Val) -> % negative
+ Oct = enint(Val,[]),
+ [{debug,unconstrained_number},
+ encode_length({0,'MAX'},length(Oct)),
+ {octets,Oct}].
+
+%% used for positive Values which don't need a sign bit
+eint_positive(Val) ->
+ case eint(Val,[]) of
+ [0,B1|T] ->
+ [B1|T];
+ T ->
+ T
+ end.
+
+eint(0, [B|Acc]) when B < 128 ->
+ [B|Acc];
+eint(N, Acc) ->
+ eint(N bsr 8, [N band 16#ff| Acc]).
+
+enint(-1, [B1|T]) when B1 > 127 ->
+ [B1|T];
+enint(N, Acc) ->
+ enint(N bsr 8, [N band 16#ff|Acc]).
+
+%% used for signed positive values
+
+%eint(Val, Ack) ->
+% X = Val band 255,
+% Next = Val bsr 8,
+% if
+% Next == 0, X >= 127 ->
+% [0,X|Ack];
+% Next == 0 ->
+% [X|Ack];
+% true ->
+% eint(Next,[X|Ack])
+% end.
+
+%%% used for signed negative values
+%enint(Val, Acc) ->
+% NumOctets = if
+% -Val < 16#80 -> 1;
+% -Val < 16#8000 ->2;
+% -Val < 16#800000 ->3;
+% -Val < 16#80000000 ->4;
+% -Val < 16#8000000000 ->5;
+% -Val < 16#800000000000 ->6;
+% -Val < 16#80000000000000 ->7;
+% -Val < 16#8000000000000000 ->8;
+% -Val < 16#800000000000000000 ->9
+% end,
+% enint(Val,Acc,NumOctets).
+
+%enint(Val, Acc,0) ->
+% Acc;
+%enint(Val, Acc,NumOctets) ->
+% enint(Val bsr 8,[Val band 255|Acc],NumOctets-1).
+
+
+decode_unconstrained_number(Bytes) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {Ints,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_integer(Ints),Bytes3}.
+
+dec_pos_integer(Ints) ->
+ decpint(Ints, 8 * (length(Ints) - 1)).
+dec_integer(Ints) when hd(Ints) band 255 =< 127 -> %% Positive number
+ decpint(Ints, 8 * (length(Ints) - 1));
+dec_integer(Ints) -> %% Negative
+ decnint(Ints, 8 * (length(Ints) - 1)).
+
+decpint([Byte|Tail], Shift) ->
+ (Byte bsl Shift) bor decpint(Tail, Shift-8);
+decpint([], _) -> 0.
+
+decnint([Byte|Tail], Shift) ->
+ (-128 + (Byte band 127) bsl Shift) bor decpint(Tail, Shift-8).
+
+minimum_octets(Val) ->
+ minimum_octets(Val,[]).
+
+minimum_octets(Val,Acc) when Val > 0 ->
+ minimum_octets((Val bsr 8),[Val band 16#FF|Acc]);
+minimum_octets(0,Acc) ->
+ Acc.
+
+
+%% X.691:10.9 Encoding of a length determinant
+%%encode_small_length(undefined,Len) -> % null means no UpperBound
+%% encode_small_number(Len).
+
+%% X.691:10.9.3.5
+%% X.691:10.9.3.7
+encode_length(undefined,Len) -> % un-constrained
+ if
+ Len < 128 ->
+ {octet,Len band 16#7F};
+ Len < 16384 ->
+ {octets,2,2#1000000000000000 bor Len};
+ true ->
+ exit({error,{asn1,{encode_length,{nyi,above_16k}}}})
+ end;
+
+encode_length({0,'MAX'},Len) ->
+ encode_length(undefined,Len);
+encode_length({Lb,Ub},Len) when Ub =< 65535 ,Lb >= 0 -> % constrained
+ encode_constrained_number({Lb,Ub},Len);
+encode_length(SingleValue,_Len) when integer(SingleValue) ->
+ [].
+
+encode_small_length(Len) when Len =< 64 ->
+ [{bit,0},{bits,6,Len-1}];
+encode_small_length(Len) ->
+ [{bit,1},encode_length(undefined,Len)].
+
+decode_small_length(Buffer) ->
+ case getbit(Buffer) of
+ {0,Remain} ->
+ {Bits,Remain2} = getbits(Remain,6),
+ {Bits+1,Remain2};
+ {1,Remain} ->
+ decode_length(Remain,undefined)
+ end.
+
+decode_length(Buffer) ->
+ decode_length(Buffer,undefined).
+
+decode_length(Buffer,undefined) -> % un-constrained
+ Buffer2 = align(Buffer),
+ {Bits,_} = getbits(Buffer2,2),
+ case Bits of
+ 2 ->
+ {Val,Bytes3} = getoctets(Buffer2,2),
+ {(Val band 16#3FFF),Bytes3};
+ 3 ->
+ exit({error,{asn1,{decode_length,{nyi,above_16k}}}});
+ _ ->
+ {Val,Bytes3} = getoctet(Buffer2),
+ {Val band 16#7F,Bytes3}
+ end;
+
+decode_length(Buffer,{Lb,Ub}) when Ub =< 65535 ,Lb >= 0 -> % constrained
+ decode_constrained_number(Buffer,{Lb,Ub});
+ % X.691:10.9.3.5
+decode_length(Buffer,{_,_Lb,_Ub}) -> %when Len =< 127 -> % Unconstrained or large Ub
+ case getbit(Buffer) of
+ {0,Remain} ->
+ getbits(Remain,7);
+ {1,_Remain} ->
+ {Val,Remain2} = getoctets(Buffer,2),
+ {Val band 2#0111111111111111, Remain2}
+ end;
+decode_length(Buffer,SingleValue) when integer(SingleValue) ->
+ {SingleValue,Buffer}.
+
+
+% X.691:11
+encode_boolean({Name,Val}) when atom(Name) ->
+ encode_boolean(Val);
+encode_boolean(true) ->
+ {bit,1};
+encode_boolean(false) ->
+ {bit,0};
+encode_boolean(Val) ->
+ exit({error,{asn1,{encode_boolean,Val}}}).
+
+
+decode_boolean(Buffer) -> %when record(Buffer,buffer)
+ case getbit(Buffer) of
+ {1,Remain} -> {true,Remain};
+ {0,Remain} -> {false,Remain}
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% X.691:12
+%% ENUMERATED
+%%
+%% encode_enumerated(C,Value,NamedNumberTup) -> CompleteList
+%%
+%%
+
+encode_enumerated(C,{Name,Value},NamedNumberList) when
+ atom(Name),list(NamedNumberList) ->
+ encode_enumerated(C,Value,NamedNumberList);
+
+%% ENUMERATED with extension mark
+encode_enumerated(_C,{asn1_enum,Value},{_Nlist1,Nlist2}) when Value >= length(Nlist2) ->
+ [{bit,1},encode_small_number(Value)];
+encode_enumerated(C,Value,{Nlist1,Nlist2}) ->
+ case enum_search(Value,Nlist1,0) of
+ NewV when integer(NewV) ->
+ [{bit,0},encode_integer(C,NewV)];
+ false ->
+ case enum_search(Value,Nlist2,0) of
+ ExtV when integer(ExtV) ->
+ [{bit,1},encode_small_number(ExtV)];
+ false ->
+ exit({error,{asn1,{encode_enumerated,Value}}})
+ end
+ end;
+
+encode_enumerated(C,Value,NamedNumberList) when list(NamedNumberList) ->
+ case enum_search(Value,NamedNumberList,0) of
+ NewV when integer(NewV) ->
+ encode_integer(C,NewV);
+ false ->
+ exit({error,{asn1,{encode_enumerated,Value}}})
+ end.
+
+%% returns the ordinal number from 0 ,1 ... in the list where Name is found
+%% or false if not found
+%%
+enum_search(Name,[Name|_NamedNumberList],Acc) ->
+ Acc;
+enum_search(Name,[_H|T],Acc) ->
+ enum_search(Name,T,Acc+1);
+enum_search(_,[],_) ->
+ false. % name not found !error
+
+%% ENUMERATED with extension marker
+decode_enumerated(Buffer,C,{Ntup1,Ntup2}) when tuple(Ntup1), tuple(Ntup2) ->
+ {Ext,Buffer2} = getext(Buffer),
+ case Ext of
+ 0 -> % not an extension value
+ {Val,Buffer3} = decode_integer(Buffer2,C),
+ case catch (element(Val+1,Ntup1)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer3};
+ _Error -> exit({error,{asn1,{decode_enumerated,{Val,[Ntup1,Ntup2]}}}})
+ end;
+ 1 -> % this an extension value
+ {Val,Buffer3} = decode_small_number(Buffer2),
+ case catch (element(Val+1,Ntup2)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer3};
+ _ -> {{asn1_enum,Val},Buffer3}
+ end
+ end;
+
+decode_enumerated(Buffer,C,NamedNumberTup) when tuple(NamedNumberTup) ->
+ {Val,Buffer2} = decode_integer(Buffer,C),
+ case catch (element(Val+1,NamedNumberTup)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer2};
+ _Error -> exit({error,{asn1,{decode_enumerated,{Val,NamedNumberTup}}}})
+ end.
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Bitstring value, ITU_T X.690 Chapter 8.5
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% encode bitstring value
+%%===============================================================================
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% bitstring NamedBitList
+%% Val can be of:
+%% - [identifiers] where only named identifers are set to one,
+%% the Constraint must then have some information of the
+%% bitlength.
+%% - [list of ones and zeroes] all bits
+%% - integer value representing the bitlist
+%% C is constraint Len, only valid when identifiers
+
+%% when the value is a list of named bits
+encode_bit_string(C, [FirstVal | RestVal], NamedBitList) when atom(FirstVal) ->
+ ToSetPos = get_all_bitposes([FirstVal | RestVal], NamedBitList, []),
+ BitList = make_and_set_list(ToSetPos,0),
+ encode_bit_string(C,BitList,NamedBitList);
+
+encode_bit_string(C, [{bit,No} | RestVal], NamedBitList) ->
+ ToSetPos = get_all_bitposes([{bit,No} | RestVal], NamedBitList, []),
+ BitList = make_and_set_list(ToSetPos,0),
+ encode_bit_string(C,BitList,NamedBitList);
+
+%% when the value is a list of ones and zeroes
+
+encode_bit_string(C, BitListValue, _NamedBitList) when list(BitListValue) ->
+ %% first remove any trailing zeroes
+ Bl1 = lists:dropwhile(fun(0)->true;(1)->false end,lists:reverse(BitListValue)),
+ BitList = [{bit,X} || X <- lists:reverse(Bl1)],
+ case get_constraint(C,'SizeConstraint') of
+ 0 -> % fixed length
+ []; % nothing to encode
+ V when integer(V),V=<16 -> % fixed length 16 bits or less
+ pad_list(V,BitList);
+ V when integer(V) -> % fixed length more than 16 bits
+ [align,pad_list(V,BitList)];
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ [encode_length({Lb,Ub},length(BitList)),align,BitList];
+ no ->
+ [encode_length(undefined,length(BitList)),align,BitList]
+ end;
+
+%% when the value is an integer
+encode_bit_string(C, IntegerVal, NamedBitList) ->
+ BitList = int_to_bitlist(IntegerVal),
+ encode_bit_string(C,BitList,NamedBitList).
+
+
+
+
+%%%%%%%%%%%%%%%
+%% The result is presented as a list of named bits (if possible)
+%% else as a list of 0 and 1.
+%%
+decode_bit_string(Buffer, C, NamedNumberList) ->
+ case get_constraint(C,'SizeConstraint') of
+ 0 -> % fixed length
+ {[],Buffer}; % nothing to encode
+ V when integer(V),V=<16 -> % fixed length 16 bits or less
+ bit_list_to_named(Buffer,V,NamedNumberList);
+ V when integer(V) -> % fixed length 16 bits or less
+ Bytes2 = align(Buffer),
+ bit_list_to_named(Bytes2,V,NamedNumberList);
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ {Len,Bytes2} = decode_length(Buffer,{Lb,Ub}),
+ Bytes3 = align(Bytes2),
+ bit_list_to_named(Bytes3,Len,NamedNumberList);
+ no ->
+ {Len,Bytes2} = decode_length(Buffer,undefined),
+ Bytes3 = align(Bytes2),
+ bit_list_to_named(Bytes3,Len,NamedNumberList)
+ end.
+
+%% if no named bits are declared we will return a
+%% BitList = [0 | 1]
+
+bit_list_to_named(Buffer,Len,[]) ->
+ getbits_as_list(Len,Buffer);
+
+%% if there are named bits declared we will return a named
+%% BitList where the names are atoms and unnamed bits represented
+%% as {bit,Pos}
+%% BitList = [atom() | {bit,Pos}]
+%% Pos = integer()
+
+bit_list_to_named(Buffer,Len,NamedNumberList) ->
+ {BitList,Rest} = getbits_as_list(Len,Buffer),
+ {bit_list_to_named1(0,BitList,NamedNumberList,[]), Rest}.
+
+bit_list_to_named1(Pos,[0|Bt],Names,Acc) ->
+ bit_list_to_named1(Pos+1,Bt,Names,Acc);
+bit_list_to_named1(Pos,[1|Bt],Names,Acc) ->
+ case lists:keysearch(Pos,2,Names) of
+ {value,{Name,_}} ->
+ bit_list_to_named1(Pos+1,Bt,Names,[Name|Acc]);
+ _ ->
+ bit_list_to_named1(Pos+1,Bt,Names,[{bit,Pos}|Acc])
+ end;
+bit_list_to_named1(_Pos,[],_Names,Acc) ->
+ lists:reverse(Acc).
+
+
+
+%%%%%%%%%%%%%%%
+%%
+
+int_to_bitlist(0) ->
+ [];
+int_to_bitlist(Int) when integer(Int), Int >= 0 ->
+ [Int band 1 | int_to_bitlist(Int bsr 1)].
+
+
+%%%%%%%%%%%%%%%%%%
+%% get_all_bitposes([list of named bits to set], named_bit_db, []) ->
+%% [sorted_list_of_bitpositions_to_set]
+
+get_all_bitposes([{bit,ValPos}|Rest], NamedBitList, Ack) ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack ]);
+
+get_all_bitposes([Val | Rest], NamedBitList, Ack) ->
+ case lists:keysearch(Val, 1, NamedBitList) of
+ {value, {_ValName, ValPos}} ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack]);
+ _ ->
+ exit({error,{asn1, {bitstring_namedbit, Val}}})
+ end;
+get_all_bitposes([], _NamedBitList, Ack) ->
+ lists:sort(Ack).
+
+%%%%%%%%%%%%%%%%%%
+%% make_and_set_list([list of positions to set to 1])->
+%% returns list with all in SetPos set.
+%% in positioning in list the first element is 0, the second 1 etc.., but
+%%
+
+make_and_set_list([XPos|SetPos], XPos) ->
+ [1 | make_and_set_list(SetPos, XPos + 1)];
+make_and_set_list([Pos|SetPos], XPos) ->
+ [0 | make_and_set_list([Pos | SetPos], XPos + 1)];
+make_and_set_list([], _) ->
+ [].
+
+%%%%%%%%%%%%%%%%%
+%% pad_list(N,BitList) -> PaddedList
+%% returns a padded (with trailing {bit,0} elements) list of length N
+%% if Bitlist contains more than N significant bits set an exit asn1_error
+%% is generated
+
+pad_list(0,BitList) ->
+ case BitList of
+ [] -> [];
+ _ -> exit({error,{asn1,{range_error,{bit_string,BitList}}}})
+ end;
+pad_list(N,[Bh|Bt]) ->
+ [Bh|pad_list(N-1,Bt)];
+pad_list(N,[]) ->
+ [{bit,0},pad_list(N-1,[])].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% X.691:16
+%% encode_octet_string(Constraint,ExtensionMarker,Val)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+encode_octet_string(C,{Name,Val}) when atom(Name) ->
+ encode_octet_string(C,false,Val);
+encode_octet_string(C,Val) ->
+ encode_octet_string(C,false,Val).
+
+encode_octet_string(_C,true,_Val) ->
+ exit({error,{asn1,{'not_supported',extensionmarker}}});
+encode_octet_string(C,false,Val) ->
+ case get_constraint(C,'SizeConstraint') of
+ 0 ->
+ [];
+ 1 ->
+ [V] = Val,
+ {bits,8,V};
+ 2 ->
+ [V1,V2] = Val,
+ [{bits,8,V1},{bits,8,V2}];
+ Sv when Sv =<65535, Sv == length(Val) -> % fixed length
+ [align,{octets,Val}];
+ {Lb,Ub} ->
+ [encode_length({Lb,Ub},length(Val)),align,
+ {octets,Val}];
+ Sv when list(Sv) ->
+ [encode_length({hd(Sv),lists:max(Sv)},length(Val)),align,
+ {octets,Val}];
+ no ->
+ [encode_length(undefined,length(Val)),align,
+ {octets,Val}]
+ end.
+
+decode_octet_string(Bytes,Range) ->
+ decode_octet_string(Bytes,Range,false).
+
+decode_octet_string(Bytes,C,false) ->
+ case get_constraint(C,'SizeConstraint') of
+ 0 ->
+ {[],Bytes};
+ 1 ->
+ {B1,Bytes2} = getbits(Bytes,8),
+ {[B1],Bytes2};
+ 2 ->
+ {B1,Bytes2}= getbits(Bytes,8),
+ {B2,Bytes3}= getbits(Bytes2,8),
+ {[B1,B2],Bytes3};
+ {_,0} ->
+ {[],Bytes};
+ Sv when integer(Sv), Sv =<65535 -> % fixed length
+ Bytes2 = align(Bytes),
+ getoctets_as_list(Bytes2,Sv);
+ {Lb,Ub} ->
+ {Len,Bytes2} = decode_length(Bytes,{Lb,Ub}),
+ Bytes3 = align(Bytes2),
+ getoctets_as_list(Bytes3,Len);
+ Sv when list(Sv) ->
+ {Len,Bytes2} = decode_length(Bytes,{hd(Sv),lists:max(Sv)}),
+ Bytes3 = align(Bytes2),
+ getoctets_as_list(Bytes3,Len);
+ no ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ Bytes3 = align(Bytes2),
+ getoctets_as_list(Bytes3,Len)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Restricted char string types
+%% (NumericString, PrintableString,VisibleString,IA5String,BMPString,UniversalString)
+%% X.691:26 and X.680:34-36
+%%encode_restricted_string(aligned,'BMPString',Constraints,Extension,Val)
+
+encode_restricted_string(aligned,StringType,C,Val) ->
+encode_restricted_string(aligned,StringType,C,false,Val).
+
+
+encode_restricted_string(aligned,StringType,C,_Ext,{Name,Val}) when atom(Name) ->
+ encode_restricted_string(aligned,StringType,C,false,Val);
+encode_restricted_string(aligned,StringType,C,_Ext,Val) ->
+ Result = chars_encode(C,StringType,Val),
+ NumBits = get_NumBits(C,StringType),
+ case get_constraint(C,'SizeConstraint') of
+ Ub when integer(Ub), Ub*NumBits =< 16 ->
+ case {StringType,Result} of
+ {'BMPString',{octets,Ol}} ->
+ [{bits,8,Oct}||Oct <- Ol];
+ _ ->
+ Result
+ end;
+ 0 ->
+ [];
+ Ub when integer(Ub),Ub =<65535 -> % fixed length
+ [align,Result];
+ {Ub,Lb} ->
+ [encode_length({Ub,Lb},length(Val)),align,Result];
+ Vl when list(Vl) ->
+ [encode_length({lists:min(Vl),lists:max(Vl)},length(Val)),align,Result];
+ no ->
+ [encode_length(undefined,length(Val)),align,Result]
+ end.
+
+decode_restricted_string(Bytes,aligned,StringType,C) ->
+ decode_restricted_string(Bytes,aligned,StringType,C,false).
+
+decode_restricted_string(Bytes,aligned,StringType,C,_Ext) ->
+ NumBits = get_NumBits(C,StringType),
+ case get_constraint(C,'SizeConstraint') of
+ Ub when integer(Ub), Ub*NumBits =< 16 ->
+ chars_decode(Bytes,NumBits,StringType,C,Ub);
+ Ub when integer(Ub),Ub =<65535 -> % fixed length
+ Bytes1 = align(Bytes),
+ chars_decode(Bytes1,NumBits,StringType,C,Ub);
+ 0 ->
+ {[],Bytes};
+ Vl when list(Vl) ->
+ {Len,Bytes1} = decode_length(Bytes,{hd(Vl),lists:max(Vl)}),
+ Bytes2 = align(Bytes1),
+ chars_decode(Bytes2,NumBits,StringType,C,Len);
+ no ->
+ {Len,Bytes1} = decode_length(Bytes,undefined),
+ Bytes2 = align(Bytes1),
+ chars_decode(Bytes2,NumBits,StringType,C,Len);
+ {Lb,Ub}->
+ {Len,Bytes1} = decode_length(Bytes,{Lb,Ub}),
+ Bytes2 = align(Bytes1),
+ chars_decode(Bytes2,NumBits,StringType,C,Len)
+ end.
+
+
+
+encode_BMPString(C,Val) ->
+ encode_restricted_string(aligned,'BMPString',C,false,Val).
+decode_BMPString(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'BMPString',C,false).
+
+encode_GeneralString(C,Val) ->
+ encode_restricted_string(aligned,'GeneralString',C,false,Val).
+decode_GeneralString(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'GeneralString',C,false).
+
+encode_GraphicString(C,Val) ->
+ encode_restricted_string(aligned,'GraphicString',C,false,Val).
+decode_GraphicString(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'GraphicString',C,false).
+
+encode_IA5String(C,Val) ->
+ encode_restricted_string(aligned,'IA5String',C,false,Val).
+decode_IA5String(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'IA5String',C,false).
+
+encode_NumericString(C,Val) ->
+ encode_restricted_string(aligned,'NumericString',C,false,Val).
+decode_NumericString(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'NumericString',C,false).
+
+encode_PrintableString(C,Val) ->
+ encode_restricted_string(aligned,'PrintableString',C,false,Val).
+decode_PrintableString(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'PrintableString',C,false).
+
+encode_TeletexString(C,Val) -> % equivalent with T61String
+ encode_restricted_string(aligned,'TeletexString',C,false,Val).
+decode_TeletexString(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'TeletexString',C,false).
+
+encode_UniversalString(C,Val) ->
+ encode_restricted_string(aligned,'UniversalString',C,false,Val).
+decode_UniversalString(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'UniversalString',C,false).
+
+encode_VideotexString(C,Val) ->
+ encode_restricted_string(aligned,'VideotexString',C,false,Val).
+decode_VideotexString(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'VideotexString',C,false).
+
+encode_VisibleString(C,Val) -> % equivalent with ISO646String
+ encode_restricted_string(aligned,'VisibleString',C,false,Val).
+decode_VisibleString(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'VisibleString',C,false).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% getBMPChars(Bytes,Len) ->{BMPcharList,RemainingBytes}
+%%
+getBMPChars(Bytes,1) ->
+ {O1,Bytes2} = getbits(Bytes,8),
+ {O2,Bytes3} = getbits(Bytes2,8),
+ if
+ O1 == 0 ->
+ {[O2],Bytes3};
+ true ->
+ {[{O1,O2}],Bytes3}
+ end;
+getBMPChars(Bytes,Len) ->
+ getBMPChars(Bytes,Len,[]).
+
+getBMPChars(Bytes,0,Acc) ->
+ {lists:reverse(Acc),Bytes};
+getBMPChars(Bytes,Len,Acc) ->
+ {Octs,Bytes1} = getoctets_as_list(Bytes,2),
+ case Octs of
+ [0,O2] ->
+ getBMPChars(Bytes1,Len-1,[O2|Acc]);
+ [O1,O2]->
+ getBMPChars(Bytes1,Len-1,[{O1,O2}|Acc])
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% chars_encode(C,StringType,Value) -> ValueList
+%%
+%% encodes chars according to the per rules taking the constraint PermittedAlphabet
+%% into account.
+%% This function does only encode the value part and NOT the length
+
+chars_encode(C,StringType,Value) ->
+ case {StringType,get_constraint(C,'PermittedAlphabet')} of
+ {'UniversalString',{_,_Sv}} ->
+ exit({error,{asn1,{'not implemented',"UniversalString with PermittedAlphabet constraint"}}});
+ {'BMPString',{_,_Sv}} ->
+ exit({error,{asn1,{'not implemented',"BMPString with PermittedAlphabet constraint"}}});
+ _ ->
+ {NumBits,CharOutTab} = {get_NumBits(C,StringType),get_CharOutTab(C,StringType)},
+ chars_encode2(Value,NumBits,CharOutTab)
+ end.
+
+chars_encode2([H|T],NumBits,{Min,Max,notab}) when H =< Max, H >= Min ->
+ [{bits,NumBits,H-Min}|chars_encode2(T,NumBits,{Min,Max,notab})];
+chars_encode2([H|T],NumBits,{Min,Max,Tab}) when H =< Max, H >= Min ->
+ [{bits,NumBits,element(H-Min+1,Tab)}|chars_encode2(T,NumBits,{Min,Max,Tab})];
+chars_encode2([{A,B,C,D}|T],NumBits,{Min,Max,notab}) ->
+ %% no value range check here (ought to be, but very expensive)
+ [{bits,NumBits,(A*B*C*D)-Min}|chars_encode2(T,NumBits,{Min,Max,notab})];
+chars_encode2([{A,B,C,D}|T],NumBits,{Min,Max,Tab}) ->
+ %% no value range check here (ought to be, but very expensive)
+ [{bits,NumBits,element((A*B*C*D)-Min,Tab)}|chars_encode2(T,NumBits,{Min,Max,notab})];
+chars_encode2([H|_T],_NumBits,{_Min,_Max,_Tab}) ->
+ exit({error,{asn1,{illegal_char_value,H}}});
+chars_encode2([],_,_) ->
+ [].
+
+
+get_NumBits(C,StringType) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ {'SingleValue',Sv} ->
+ charbits(length(Sv),aligned);
+ no ->
+ case StringType of
+ 'GeneralString' ->
+ exit({error,{asn1,{not implemented,'GeneralString'}}});
+ 'GraphicString' ->
+ exit({error,{asn1,{not implemented,'GraphicString'}}});
+ 'TeletexString' ->
+ exit({error,{asn1,{not implemented,'TeletexString'}}});
+ 'VideotexString' ->
+ exit({error,{asn1,{not implemented,'VideotexString'}}});
+ 'IA5String' ->
+ charbits(128,aligned); % 16#00..16#7F
+ 'VisibleString' ->
+ charbits(95,aligned); % 16#20..16#7E
+ 'PrintableString' ->
+ charbits(74,aligned); % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+ 'NumericString' ->
+ charbits(11,aligned); % $ ,"0123456789"
+ 'UniversalString' ->
+ 32;
+ 'BMPString' ->
+ 16
+ end
+ end.
+
+%%Maybe used later
+%%get_MaxChar(C,StringType) ->
+%% case get_constraint(C,'PermittedAlphabet') of
+%% {'SingleValue',Sv} ->
+%% lists:nth(length(Sv),Sv);
+%% no ->
+%% case StringType of
+%% 'IA5String' ->
+%% 16#7F; % 16#00..16#7F
+%% 'VisibleString' ->
+%% 16#7E; % 16#20..16#7E
+%% 'PrintableString' ->
+%% $z; % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+%% 'NumericString' ->
+%% $9; % $ ,"0123456789"
+%% 'UniversalString' ->
+%% 16#ffffffff;
+%% 'BMPString' ->
+%% 16#ffff
+%% end
+%% end.
+
+%%Maybe used later
+%%get_MinChar(C,StringType) ->
+%% case get_constraint(C,'PermittedAlphabet') of
+%% {'SingleValue',Sv} ->
+%% hd(Sv);
+%% no ->
+%% case StringType of
+%% 'IA5String' ->
+%% 16#00; % 16#00..16#7F
+%% 'VisibleString' ->
+%% 16#20; % 16#20..16#7E
+%% 'PrintableString' ->
+%% $\s; % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+%% 'NumericString' ->
+%% $\s; % $ ,"0123456789"
+%% 'UniversalString' ->
+%% 16#00;
+%% 'BMPString' ->
+%% 16#00
+%% end
+%% end.
+
+get_CharOutTab(C,StringType) ->
+ get_CharTab(C,StringType,out).
+
+get_CharInTab(C,StringType) ->
+ get_CharTab(C,StringType,in).
+
+get_CharTab(C,StringType,InOut) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ {'SingleValue',Sv} ->
+ get_CharTab2(C,StringType,hd(Sv),lists:max(Sv),Sv,InOut);
+ no ->
+ case StringType of
+ 'IA5String' ->
+ {0,16#7F,notab};
+ 'VisibleString' ->
+ get_CharTab2(C,StringType,16#20,16#7F,notab,InOut);
+ 'PrintableString' ->
+ Chars = lists:sort(
+ " '()+,-./0123456789:=?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"),
+ get_CharTab2(C,StringType,hd(Chars),lists:max(Chars),Chars,InOut);
+ 'NumericString' ->
+ get_CharTab2(C,StringType,16#20,$9," 0123456789",InOut);
+ 'UniversalString' ->
+ {0,16#FFFFFFFF,notab};
+ 'BMPString' ->
+ {0,16#FFFF,notab}
+ end
+ end.
+
+get_CharTab2(C,StringType,Min,Max,Chars,InOut) ->
+ BitValMax = (1 bsl get_NumBits(C,StringType))-1,
+ if
+ Max =< BitValMax ->
+ {0,Max,notab};
+ true ->
+ case InOut of
+ out ->
+ {Min,Max,create_char_tab(Min,Chars)};
+ in ->
+ {Min,Max,list_to_tuple(Chars)}
+ end
+ end.
+
+create_char_tab(Min,L) ->
+ list_to_tuple(create_char_tab(Min,L,0)).
+create_char_tab(Min,[Min|T],V) ->
+ [V|create_char_tab(Min+1,T,V+1)];
+create_char_tab(_Min,[],_V) ->
+ [];
+create_char_tab(Min,L,V) ->
+ [false|create_char_tab(Min+1,L,V)].
+
+%% This very inefficient and should be moved to compiletime
+charbits(NumOfChars,aligned) ->
+ case charbits(NumOfChars) of
+ 1 -> 1;
+ 2 -> 2;
+ B when B > 2, B =< 4 -> 4;
+ B when B > 4, B =< 8 -> 8;
+ B when B > 8, B =< 16 -> 16;
+ B when B > 16, B =< 32 -> 32
+ end.
+
+charbits(NumOfChars) when NumOfChars =< 2 -> 1;
+charbits(NumOfChars) when NumOfChars =< 4 -> 2;
+charbits(NumOfChars) when NumOfChars =< 8 -> 3;
+charbits(NumOfChars) when NumOfChars =< 16 -> 4;
+charbits(NumOfChars) when NumOfChars =< 32 -> 5;
+charbits(NumOfChars) when NumOfChars =< 64 -> 6;
+charbits(NumOfChars) when NumOfChars =< 128 -> 7;
+charbits(NumOfChars) when NumOfChars =< 256 -> 8;
+charbits(NumOfChars) when NumOfChars =< 512 -> 9;
+charbits(NumOfChars) when NumOfChars =< 1024 -> 10;
+charbits(NumOfChars) when NumOfChars =< 2048 -> 11;
+charbits(NumOfChars) when NumOfChars =< 4096 -> 12;
+charbits(NumOfChars) when NumOfChars =< 8192 -> 13;
+charbits(NumOfChars) when NumOfChars =< 16384 -> 14;
+charbits(NumOfChars) when NumOfChars =< 32768 -> 15;
+charbits(NumOfChars) when NumOfChars =< 65536 -> 16;
+charbits(NumOfChars) when integer(NumOfChars) ->
+ 16 + charbits1(NumOfChars bsr 16).
+
+charbits1(0) ->
+ 0;
+charbits1(NumOfChars) ->
+ 1 + charbits1(NumOfChars bsr 1).
+
+
+chars_decode(Bytes,_,'BMPString',C,Len) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ no ->
+ getBMPChars(Bytes,Len);
+ _ ->
+ exit({error,{asn1,
+ {'not implemented',
+ "BMPString with PermittedAlphabet constraint"}}})
+ end;
+chars_decode(Bytes,NumBits,StringType,C,Len) ->
+ CharInTab = get_CharInTab(C,StringType),
+ chars_decode2(Bytes,CharInTab,NumBits,Len).
+
+
+chars_decode2(Bytes,CharInTab,NumBits,Len) ->
+ chars_decode2(Bytes,CharInTab,NumBits,Len,[]).
+
+chars_decode2(Bytes,_CharInTab,_NumBits,0,Acc) ->
+ {lists:reverse(Acc),Bytes};
+chars_decode2(Bytes,{Min,Max,notab},NumBits,Len,Acc) when NumBits > 8 ->
+ {Char,Bytes2} = getbits(Bytes,NumBits),
+ Result = case minimum_octets(Char+Min) of
+ [NewChar] -> NewChar;
+ [C1,C2] -> {0,0,C1,C2};
+ [C1,C2,C3] -> {0,C1,C2,C3};
+ [C1,C2,C3,C4] -> {C1,C2,C3,C4}
+ end,
+ chars_decode2(Bytes2,{Min,Max,notab},NumBits,Len -1,[Result|Acc]);
+chars_decode2(Bytes,{Min,Max,notab},NumBits,Len,Acc) ->
+ {Char,Bytes2} = getbits(Bytes,NumBits),
+ chars_decode2(Bytes2,{Min,Max,notab},NumBits,Len -1,[Char+Min|Acc]);
+
+%% BMPString and UniversalString with PermittedAlphabet is currently not supported
+chars_decode2(Bytes,{Min,Max,CharInTab},NumBits,Len,Acc) ->
+ {Char,Bytes2} = getbits(Bytes,NumBits),
+ chars_decode2(Bytes2,{Min,Max,CharInTab},NumBits,Len -1,[element(Char+1,CharInTab)|Acc]).
+
+
+ % X.691:17
+encode_null({Name,Val}) when atom(Name) ->
+ encode_null(Val);
+encode_null(_) -> []. % encodes to nothing
+
+decode_null(Bytes) ->
+ {'NULL',Bytes}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_object_identifier(Val) -> CompleteList
+%% encode_object_identifier({Name,Val}) -> CompleteList
+%% Val -> {Int1,Int2,...,IntN} % N >= 2
+%% Name -> atom()
+%% Int1 -> integer(0..2)
+%% Int2 -> integer(0..39) when Int1 (0..1) else integer()
+%% Int3-N -> integer()
+%% CompleteList -> [{bits,8,Val}|{octets,Ol}|align|...]
+%%
+encode_object_identifier(Val) ->
+ Octets = e_object_identifier(Val,notag),
+ [{debug,object_identifier},encode_length(undefined,length(Octets)),{octets,Octets}].
+
+%% This code is copied from asn1_encode.erl (BER) and corrected and modified
+
+e_object_identifier({'OBJECT IDENTIFIER',V},DoTag) ->
+ e_object_identifier(V,DoTag);
+e_object_identifier({Cname,V},DoTag) when atom(Cname),tuple(V) ->
+ e_object_identifier(tuple_to_list(V),DoTag);
+e_object_identifier({Cname,V},DoTag) when atom(Cname),list(V) ->
+ e_object_identifier(V,DoTag);
+e_object_identifier(V,DoTag) when tuple(V) ->
+ e_object_identifier(tuple_to_list(V),DoTag);
+
+% E1 = 0|1|2 and (E2 < 40 when E1 = 0|1)
+e_object_identifier([E1,E2|Tail],_DoTag) when E1 =< 2 ->
+ Head = 40*E1 + E2, % weird
+ Res = e_object_elements([Head|Tail]),
+% dotag(DoTag,[6],elength(length(Res)+1),[Head|Res]),
+ Res.
+
+e_object_elements([]) ->
+ [];
+e_object_elements([H|T]) ->
+ lists:append(e_object_element(H),e_object_elements(T)).
+
+e_object_element(Num) when Num < 128 ->
+ [Num];
+% must be changed to handle more than 2 octets
+e_object_element(Num) -> %% when Num < ???
+ Left = ((Num band 2#11111110000000) bsr 7) bor 2#10000000,
+ Right = Num band 2#1111111 ,
+ [Left,Right].
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_object_identifier(Bytes) -> {ObjId,RemainingBytes}
+%% ObjId -> {integer(),integer(),...} % at least 2 integers
+%% RemainingBytes -> [integer()] when integer() (0..255)
+decode_object_identifier(Bytes) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ [First|Rest] = dec_subidentifiers(Octs,0,[]),
+ Idlist = if
+ First < 40 ->
+ [0,First|Rest];
+ First < 80 ->
+ [1,First - 40|Rest];
+ true ->
+ [2,First - 80|Rest]
+ end,
+ {list_to_tuple(Idlist),Bytes3}.
+
+dec_subidentifiers([H|T],Av,Al) when H >=16#80 ->
+ dec_subidentifiers(T,(Av bsl 7) + (H band 16#7F),Al);
+dec_subidentifiers([H|T],Av,Al) ->
+ dec_subidentifiers(T,0,[(Av bsl 7) + H |Al]);
+dec_subidentifiers([],_Av,Al) ->
+ lists:reverse(Al).
+
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% complete(InList) -> ByteList
+%% Takes a coded list with bits and bytes and converts it to a list of bytes
+%% Should be applied as the last step at encode of a complete ASN.1 type
+%%
+complete(InList) when list(InList) ->
+ complete(InList,[],0);
+complete(InList) ->
+ complete([InList],[],0).
+
+complete([{debug,_}|T], Acc, Acclen) ->
+ complete(T,Acc,Acclen);
+complete([H|T],Acc,Acclen) when list(H) ->
+ complete(lists:concat([H,T]),Acc,Acclen);
+
+
+complete([{octets,N,Val}|T],Acc,Acclen) when N =< 4 ,integer(Val) ->
+ Newval = case N of
+ 1 ->
+ Val4 = Val band 16#FF,
+ [Val4];
+ 2 ->
+ Val3 = (Val bsr 8) band 16#FF,
+ Val4 = Val band 16#FF,
+ [Val3,Val4];
+ 3 ->
+ Val2 = (Val bsr 16) band 16#FF,
+ Val3 = (Val bsr 8) band 16#FF,
+ Val4 = Val band 16#FF,
+ [Val2,Val3,Val4];
+ 4 ->
+ Val1 = (Val bsr 24) band 16#FF,
+ Val2 = (Val bsr 16) band 16#FF,
+ Val3 = (Val bsr 8) band 16#FF,
+ Val4 = Val band 16#FF,
+ [Val1,Val2,Val3,Val4]
+ end,
+ complete([{octets,Newval}|T],Acc,Acclen);
+
+complete([{octets,Oct}|T],[],_Acclen) when list(Oct) ->
+ complete(T,lists:reverse(Oct),0);
+complete([{octets,Oct}|T],[Hacc|Tacc],Acclen) when list(Oct) ->
+ Rest = 8 - Acclen,
+ if
+ Rest == 8 ->
+ complete(T,lists:concat([lists:reverse(Oct),[Hacc|Tacc]]),0);
+ true ->
+ complete(T,lists:concat([lists:reverse(Oct),[Hacc bsl Rest|Tacc]]),0)
+ end;
+
+complete([{bit,Val}|T], Acc, Acclen) ->
+ complete([{bits,1,Val}|T],Acc,Acclen);
+complete([{octet,Val}|T], Acc, Acclen) ->
+ complete([{octets,1,Val}|T],Acc,Acclen);
+
+complete([{bits,N,Val}|T], Acc, 0) when N =< 8 ->
+ complete(T,[Val|Acc],N);
+complete([{bits,N,Val}|T], [Hacc|Tacc], Acclen) when N =< 8 ->
+ Rest = 8 - Acclen,
+ if
+ Rest >= N ->
+ complete(T,[(Hacc bsl N) + Val|Tacc],(Acclen+N) rem 8);
+ true ->
+ Diff = N - Rest,
+ NewHacc = (Hacc bsl Rest) + (Val bsr Diff),
+ Mask = element(Diff,{1,3,7,15,31,63,127,255}),
+ complete(T,[(Val band Mask),NewHacc|Tacc],(Acclen+N) rem 8)
+ end;
+complete([{bits,N,Val}|T], Acc, Acclen) -> % N > 8
+ complete([{bits,N-8,Val bsr 8},{bits,8,Val band 255}|T],Acc,Acclen);
+
+complete([align|T],Acc,0) ->
+ complete(T,Acc,0);
+complete([align|T],[Hacc|Tacc],Acclen) ->
+ Rest = 8 - Acclen,
+ complete(T,[Hacc bsl Rest|Tacc],0);
+complete([{octets,_N,Val}|T],Acc,Acclen) when list(Val) -> % no security check here
+ complete([{octets,Val}|T],Acc,Acclen);
+complete([],Acc,0) ->
+ lists:reverse(Acc);
+complete([],[Hacc|Tacc],Acclen) when Acclen > 0->
+ Rest = 8 - Acclen,
+ NewHacc = Hacc bsl Rest,
+ lists:reverse([NewHacc|Tacc]).
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per_bin.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per_bin.erl
new file mode 100644
index 0000000000..8b4512b58e
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per_bin.erl
@@ -0,0 +1,2176 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1rt_per_bin.erl,v 1.1 2008/12/17 09:53:31 mikpe Exp $
+%%
+-module(asn1rt_per_bin).
+
+%% encoding / decoding of PER aligned
+
+-include("asn1_records.hrl").
+
+-export([dec_fixup/3, cindex/3, list_to_record/2]).
+-export([setchoiceext/1, setext/1, fixoptionals/2, fixoptionals/3,
+ fixextensions/2,
+ getext/1, getextension/2, skipextensions/3, getbit/1, getchoice/3 ]).
+-export([getoptionals/2, getoptionals2/2, set_choice/3, encode_integer/2, encode_integer/3 ]).
+-export([decode_integer/2, decode_integer/3, encode_small_number/1, encode_boolean/1,
+ decode_boolean/1, encode_length/2, decode_length/1, decode_length/2,
+ encode_small_length/1, decode_small_length/1,
+ decode_compact_bit_string/3]).
+-export([decode_enumerated/3,
+ encode_bit_string/3, decode_bit_string/3 ]).
+-export([encode_octet_string/2, decode_octet_string/2,
+ encode_null/1, decode_null/1,
+ encode_object_identifier/1, decode_object_identifier/1,
+ complete/1]).
+
+
+-export([encode_open_type/2, decode_open_type/2]).
+
+-export([encode_UniversalString/2, decode_UniversalString/2,
+ encode_PrintableString/2, decode_PrintableString/2,
+ encode_GeneralString/2, decode_GeneralString/2,
+ encode_GraphicString/2, decode_GraphicString/2,
+ encode_TeletexString/2, decode_TeletexString/2,
+ encode_VideotexString/2, decode_VideotexString/2,
+ encode_VisibleString/2, decode_VisibleString/2,
+ encode_BMPString/2, decode_BMPString/2,
+ encode_IA5String/2, decode_IA5String/2,
+ encode_NumericString/2, decode_NumericString/2,
+ encode_ObjectDescriptor/2, decode_ObjectDescriptor/1
+ ]).
+-export([complete_bytes/1]).
+
+-define('16K',16384).
+-define('32K',32768).
+-define('64K',65536).
+
+dec_fixup(Terms,Cnames,RemBytes) ->
+ dec_fixup(Terms,Cnames,RemBytes,[]).
+
+dec_fixup([novalue|T],[_Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,Acc);
+dec_fixup([{_Name,novalue}|T],[_Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,Acc);
+dec_fixup([H|T],[Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,[{Hc,H}|Acc]);
+dec_fixup([],_Cnames,RemBytes,Acc) ->
+ {lists:reverse(Acc),RemBytes}.
+
+cindex(Ix,Val,Cname) ->
+ case element(Ix,Val) of
+ {Cname,Val2} -> Val2;
+ X -> X
+ end.
+
+%% converts a list to a record if necessary
+list_to_record(_Name,Tuple) when tuple(Tuple) ->
+ Tuple;
+list_to_record(Name,List) when list(List) ->
+ list_to_tuple([Name|List]).
+
+%%--------------------------------------------------------
+%% setchoiceext(InRootSet) -> [{bit,X}]
+%% X is set to 1 when InRootSet==false
+%% X is set to 0 when InRootSet==true
+%%
+setchoiceext(true) ->
+ [{debug,choiceext},{bits,1,0}];
+setchoiceext(false) ->
+ [{debug,choiceext},{bits,1,1}].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% setext(true|false) -> CompleteList
+%%
+
+setext(false) ->
+ [{debug,ext},{bits,1,0}];
+setext(true) ->
+ [{debug,ext},{bits,1,1}].
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This version of fixoptionals/2 are left only because of
+%% backward compatibility with older generates
+
+fixoptionals(OptList,Val) when tuple(Val) ->
+ fixoptionals1(OptList,Val,[]);
+
+fixoptionals(OptList,Val) when list(Val) ->
+ fixoptionals1(OptList,Val,1,[],[]).
+
+fixoptionals1([],Val,Acc) ->
+ %% return {Val,Opt}
+ {Val,lists:reverse(Acc)};
+fixoptionals1([{_,Pos}|Ot],Val,Acc) ->
+ case element(Pos+1,Val) of
+ asn1_NOVALUE -> fixoptionals1(Ot,Val,[0|Acc]);
+ asn1_DEFAULT -> fixoptionals1(Ot,Val,[0|Acc]);
+ _ -> fixoptionals1(Ot,Val,[1|Acc])
+ end.
+
+
+fixoptionals1([{Name,Pos}|Ot],[{Name,Val}|Vt],_Opt,Acc1,Acc2) ->
+ fixoptionals1(Ot,Vt,Pos+1,[1|Acc1],[{Name,Val}|Acc2]);
+fixoptionals1([{_Name,Pos}|Ot],V,Pos,Acc1,Acc2) ->
+ fixoptionals1(Ot,V,Pos+1,[0|Acc1],[asn1_NOVALUE|Acc2]);
+fixoptionals1(O,[Vh|Vt],Pos,Acc1,Acc2) ->
+ fixoptionals1(O,Vt,Pos+1,Acc1,[Vh|Acc2]);
+fixoptionals1([],[Vh|Vt],Pos,Acc1,Acc2) ->
+ fixoptionals1([],Vt,Pos+1,Acc1,[Vh|Acc2]);
+fixoptionals1([],[],_,Acc1,Acc2) ->
+ % return {Val,Opt}
+ {list_to_tuple([asn1_RECORDNAME|lists:reverse(Acc2)]),lists:reverse(Acc1)}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This is the new fixoptionals/3 which is used by the new generates
+%%
+fixoptionals(OptList,OptLength,Val) when tuple(Val) ->
+ Bits = fixoptionals(OptList,Val,0),
+ {Val,{bits,OptLength,Bits}};
+
+fixoptionals([],_Val,Acc) ->
+ %% Optbits
+ Acc;
+fixoptionals([Pos|Ot],Val,Acc) ->
+ case element(Pos,Val) of
+ asn1_NOVALUE -> fixoptionals(Ot,Val,Acc bsl 1);
+ asn1_DEFAULT -> fixoptionals(Ot,Val,Acc bsl 1);
+ _ -> fixoptionals(Ot,Val,(Acc bsl 1) + 1)
+ end.
+
+
+getext(Bytes) when tuple(Bytes) ->
+ getbit(Bytes);
+getext(Bytes) when binary(Bytes) ->
+ getbit({0,Bytes});
+getext(Bytes) when list(Bytes) ->
+ getbit({0,Bytes}).
+
+getextension(0, Bytes) ->
+ {{},Bytes};
+getextension(1, Bytes) ->
+ {Len,Bytes2} = decode_small_length(Bytes),
+ {Blist, Bytes3} = getbits_as_list(Len,Bytes2),
+ {list_to_tuple(Blist),Bytes3}.
+
+fixextensions({ext,ExtPos,ExtNum},Val) ->
+ case fixextensions(ExtPos,ExtNum+ExtPos,Val,0) of
+ 0 -> [];
+ ExtBits ->
+ [encode_small_length(ExtNum),{bits,ExtNum,ExtBits}]
+ end.
+
+fixextensions(Pos,MaxPos,_,Acc) when Pos >= MaxPos ->
+ Acc;
+fixextensions(Pos,ExtPos,Val,Acc) ->
+ Bit = case catch(element(Pos+1,Val)) of
+ asn1_NOVALUE ->
+ 0;
+ asn1_NOEXTVALUE ->
+ 0;
+ {'EXIT',_} ->
+ 0;
+ _ ->
+ 1
+ end,
+ fixextensions(Pos+1,ExtPos,Val,(Acc bsl 1)+Bit).
+
+skipextensions(Bytes,Nr,ExtensionBitPattern) ->
+ case (catch element(Nr,ExtensionBitPattern)) of
+ 1 ->
+ {_,Bytes2} = decode_open_type(Bytes,[]),
+ skipextensions(Bytes2, Nr+1, ExtensionBitPattern);
+ 0 ->
+ skipextensions(Bytes, Nr+1, ExtensionBitPattern);
+ {'EXIT',_} -> % badarg, no more extensions
+ Bytes
+ end.
+
+
+getchoice(Bytes,1,0) -> % only 1 alternative is not encoded
+ {0,Bytes};
+getchoice(Bytes,_,1) ->
+ decode_small_number(Bytes);
+getchoice(Bytes,NumChoices,0) ->
+ decode_constrained_number(Bytes,{0,NumChoices-1}).
+
+%% old version kept for backward compatibility with generates from R7B
+getoptionals(Bytes,NumOpt) ->
+ {Blist,Bytes1} = getbits_as_list(NumOpt,Bytes),
+ {list_to_tuple(Blist),Bytes1}.
+
+%% new version used in generates from r8b_patch/3 and later
+getoptionals2(Bytes,NumOpt) ->
+ getbits(Bytes,NumOpt).
+
+
+%% getbits_as_binary(Num,Bytes) -> {{Unused,BinBits},RestBytes},
+%% Num = integer(),
+%% Bytes = list() | tuple(),
+%% Unused = integer(),
+%% BinBits = binary(),
+%% RestBytes = tuple()
+getbits_as_binary(Num,Bytes) when binary(Bytes) ->
+ getbits_as_binary(Num,{0,Bytes});
+getbits_as_binary(0,Buffer) ->
+ {{0,<<>>},Buffer};
+getbits_as_binary(Num,{0,Bin}) when Num > 16 ->
+ Used = Num rem 8,
+ Pad = (8 - Used) rem 8,
+% Nbytes = Num div 8,
+ <> = Bin,
+ {{Pad,<>},RestBin};
+getbits_as_binary(Num,Buffer={_Used,_Bin}) -> % Unaligned buffer
+ %% Num =< 16,
+ {Bits2,Buffer2} = getbits(Buffer,Num),
+ Pad = (8 - (Num rem 8)) rem 8,
+ {{Pad,<>},Buffer2}.
+
+
+% integer_from_list(Int,[],BigInt) ->
+% BigInt;
+% integer_from_list(Int,[H|T],BigInt) when Int < 8 ->
+% (BigInt bsl Int) bor (H bsr (8-Int));
+% integer_from_list(Int,[H|T],BigInt) ->
+% integer_from_list(Int-8,T,(BigInt bsl 8) bor H).
+
+getbits_as_list(Num,Bytes) when binary(Bytes) ->
+ getbits_as_list(Num,{0,Bytes},[]);
+getbits_as_list(Num,Bytes) ->
+ getbits_as_list(Num,Bytes,[]).
+
+%% If buffer is empty and nothing more will be picked.
+getbits_as_list(0, B, Acc) ->
+ {lists:reverse(Acc),B};
+%% If first byte in buffer is full and at least one byte will be picked,
+%% then pick one byte.
+getbits_as_list(N,{0,Bin},Acc) when N >= 8 ->
+ <> = Bin,
+ getbits_as_list(N-8,{0,Rest},[B0,B1,B2,B3,B4,B5,B6,B7|Acc]);
+getbits_as_list(N,{Used,Bin},Acc) when N >= 4, Used =< 4 ->
+ NewUsed = Used + 4,
+ Rem = 8 - NewUsed,
+ <<_:Used,B3:1,B2:1,B1:1,B0:1,_:Rem, Rest/binary>> = Bin,
+ NewRest = case Rem of 0 -> Rest; _ -> Bin end,
+ getbits_as_list(N-4,{NewUsed rem 8,NewRest},[B0,B1,B2,B3|Acc]);
+getbits_as_list(N,{Used,Bin},Acc) when N >= 2, Used =< 6 ->
+ NewUsed = Used + 2,
+ Rem = 8 - NewUsed,
+ <<_:Used,B1:1,B0:1,_:Rem, Rest/binary>> = Bin,
+ NewRest = case Rem of 0 -> Rest; _ -> Bin end,
+ getbits_as_list(N-2,{NewUsed rem 8,NewRest},[B0,B1|Acc]);
+getbits_as_list(N,{Used,Bin},Acc) when Used =< 7 ->
+ NewUsed = Used + 1,
+ Rem = 8 - NewUsed,
+ <<_:Used,B0:1,_:Rem, Rest/binary>> = Bin,
+ NewRest = case Rem of 0 -> Rest; _ -> Bin end,
+ getbits_as_list(N-1,{NewUsed rem 8,NewRest},[B0|Acc]).
+
+
+getbit({7,<<_:7,B:1,Rest/binary>>}) ->
+ {B,{0,Rest}};
+getbit({0,Buffer = <>}) ->
+ {B,{1,Buffer}};
+getbit({Used,Buffer}) ->
+ Unused = (8 - Used) - 1,
+ <<_:Used,B:1,_:Unused,_/binary>> = Buffer,
+ {B,{Used+1,Buffer}};
+getbit(Buffer) when binary(Buffer) ->
+ getbit({0,Buffer}).
+
+
+getbits({0,Buffer},Num) when (Num rem 8) == 0 ->
+ <> = Buffer,
+ {Bits,{0,Rest}};
+getbits({Used,Bin},Num) ->
+ NumPlusUsed = Num + Used,
+ NewUsed = NumPlusUsed rem 8,
+ Unused = (8-NewUsed) rem 8,
+ case Unused of
+ 0 ->
+ <<_:Used,Bits:Num,Rest/binary>> = Bin,
+ {Bits,{0,Rest}};
+ _ ->
+ Bytes = NumPlusUsed div 8,
+ <<_:Used,Bits:Num,_UBits:Unused,_/binary>> = Bin,
+ <<_:Bytes/binary,Rest/binary>> = Bin,
+ {Bits,{NewUsed,Rest}}
+ end;
+getbits(Bin,Num) when binary(Bin) ->
+ getbits({0,Bin},Num).
+
+
+
+% getoctet(Bytes) when list(Bytes) ->
+% getoctet({0,Bytes});
+% getoctet(Bytes) ->
+% %% io:format("getoctet:Buffer = ~p~n",[Bytes]),
+% getoctet1(Bytes).
+
+% getoctet1({0,[H|T]}) ->
+% {H,{0,T}};
+% getoctet1({Pos,[_,H|T]}) ->
+% {H,{0,T}}.
+
+align({0,L}) ->
+ {0,L};
+align({_Pos,<<_H,T/binary>>}) ->
+ {0,T};
+align(Bytes) ->
+ {0,Bytes}.
+
+%% First align buffer, then pick the first Num octets.
+%% Returns octets as an integer with bit significance as in buffer.
+getoctets({0,Buffer},Num) ->
+ <> = Buffer,
+ {Val,{0,RestBin}};
+getoctets({U,<<_Padding,Rest/binary>>},Num) when U /= 0 ->
+ getoctets({0,Rest},Num);
+getoctets(Buffer,Num) when binary(Buffer) ->
+ getoctets({0,Buffer},Num).
+% getoctets(Buffer,Num) ->
+% %% io:format("getoctets:Buffer = ~p~nNum = ~p~n",[Buffer,Num]),
+% getoctets(Buffer,Num,0).
+
+% getoctets(Buffer,0,Acc) ->
+% {Acc,Buffer};
+% getoctets(Buffer,Num,Acc) ->
+% {Oct,NewBuffer} = getoctet(Buffer),
+% getoctets(NewBuffer,Num-1,(Acc bsl 8)+Oct).
+
+% getoctets_as_list(Buffer,Num) ->
+% getoctets_as_list(Buffer,Num,[]).
+
+% getoctets_as_list(Buffer,0,Acc) ->
+% {lists:reverse(Acc),Buffer};
+% getoctets_as_list(Buffer,Num,Acc) ->
+% {Oct,NewBuffer} = getoctet(Buffer),
+% getoctets_as_list(NewBuffer,Num-1,[Oct|Acc]).
+
+%% First align buffer, then pick the first Num octets.
+%% Returns octets as a binary
+getoctets_as_bin({0,Bin},Num)->
+ <> = Bin,
+ {Octets,{0,RestBin}};
+getoctets_as_bin({_U,Bin},Num) ->
+ <<_Padding,Octets:Num/binary,RestBin/binary>> = Bin,
+ {Octets,{0,RestBin}};
+getoctets_as_bin(Bin,Num) when binary(Bin) ->
+ getoctets_as_bin({0,Bin},Num).
+
+%% same as above but returns octets as a List
+getoctets_as_list(Buffer,Num) ->
+ {Bin,Buffer2} = getoctets_as_bin(Buffer,Num),
+ {binary_to_list(Bin),Buffer2}.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% set_choice(Alt,Choices,Altnum) -> ListofBitSettings
+%% Alt = atom()
+%% Altnum = integer() | {integer(),integer()}% number of alternatives
+%% Choices = [atom()] | {[atom()],[atom()]}
+%% When Choices is a tuple the first list is the Rootset and the
+%% second is the Extensions and then Altnum must also be a tuple with the
+%% lengths of the 2 lists
+%%
+set_choice(Alt,{L1,L2},{Len1,_Len2}) ->
+ case set_choice_tag(Alt,L1) of
+ N when integer(N), Len1 > 1 ->
+ [{bits,1,0}, % the value is in the root set
+ encode_integer([{'ValueRange',{0,Len1-1}}],N)];
+ N when integer(N) ->
+ [{bits,1,0}]; % no encoding if only 0 or 1 alternative
+ false ->
+ [{bits,1,1}, % extension value
+ case set_choice_tag(Alt,L2) of
+ N2 when integer(N2) ->
+ encode_small_number(N2);
+ false ->
+ unknown_choice_alt
+ end]
+ end;
+set_choice(Alt,L,Len) ->
+ case set_choice_tag(Alt,L) of
+ N when integer(N), Len > 1 ->
+ encode_integer([{'ValueRange',{0,Len-1}}],N);
+ N when integer(N) ->
+ []; % no encoding if only 0 or 1 alternative
+ false ->
+ [unknown_choice_alt]
+ end.
+
+set_choice_tag(Alt,Choices) ->
+ set_choice_tag(Alt,Choices,0).
+
+set_choice_tag(Alt,[Alt|_Rest],Tag) ->
+ Tag;
+set_choice_tag(Alt,[_H|Rest],Tag) ->
+ set_choice_tag(Alt,Rest,Tag+1);
+set_choice_tag(_Alt,[],_Tag) ->
+ false.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_fragmented_XXX; decode of values encoded fragmented according
+%% to ITU-T X.691 clause 10.9.3.8. The unit (XXX) is either bits, octets,
+%% characters or number of components (in a choice,sequence or similar).
+%% Buffer is a buffer {Used, Bin}.
+%% C is the constrained length.
+%% If the buffer is not aligned, this function does that.
+decode_fragmented_bits({0,Buffer},C) ->
+ decode_fragmented_bits(Buffer,C,[]);
+decode_fragmented_bits({_N,<<_,Bs/binary>>},C) ->
+ decode_fragmented_bits(Bs,C,[]).
+
+decode_fragmented_bits(<<3:2,Len:6,Bin/binary>>,C,Acc) ->
+ {Value,Bin2} = split_binary(Bin, Len * ?'16K'),
+ decode_fragmented_bits(Bin2,C,[Value,Acc]);
+decode_fragmented_bits(<<0:1,0:7,Bin/binary>>,C,Acc) ->
+ BinBits = list_to_binary(lists:reverse(Acc)),
+ case C of
+ Int when integer(Int),C == size(BinBits) ->
+ {BinBits,{0,Bin}};
+ Int when integer(Int) ->
+ exit({error,{asn1,{illegal_value,C,BinBits}}});
+ _ ->
+ {BinBits,{0,Bin}}
+ end;
+decode_fragmented_bits(<<0:1,Len:7,Bin/binary>>,C,Acc) ->
+ Result = {BinBits,{Used,_Rest}} =
+ case (Len rem 8) of
+ 0 ->
+ <> = Bin,
+ {list_to_binary(lists:reverse([Value|Acc])),{0,Bin2}};
+ Rem ->
+ Bytes = Len div 8,
+ U = 8 - Rem,
+ <> = Bin,
+ {list_to_binary(lists:reverse([Bits1 bsl U,Value|Acc])),
+ {Rem,<>}}
+ end,
+ case C of
+ Int when integer(Int),C == (size(BinBits) - ((8 - Used) rem 8)) ->
+ Result;
+ Int when integer(Int) ->
+ exit({error,{asn1,{illegal_value,C,BinBits}}});
+ _ ->
+ Result
+ end.
+
+
+decode_fragmented_octets({0,Bin},C) ->
+ decode_fragmented_octets(Bin,C,[]);
+decode_fragmented_octets({_N,<<_,Bs/binary>>},C) ->
+ decode_fragmented_octets(Bs,C,[]).
+
+decode_fragmented_octets(<<3:2,Len:6,Bin/binary>>,C,Acc) ->
+ {Value,Bin2} = split_binary(Bin,Len * ?'16K'),
+ decode_fragmented_octets(Bin2,C,[Value,Acc]);
+decode_fragmented_octets(<<0:1,0:7,Bin/binary>>,C,Acc) ->
+ Octets = list_to_binary(lists:reverse(Acc)),
+ case C of
+ Int when integer(Int), C == size(Octets) ->
+ {Octets,{0,Bin}};
+ Int when integer(Int) ->
+ exit({error,{asn1,{illegal_value,C,Octets}}});
+ _ ->
+ {Octets,{0,Bin}}
+ end;
+decode_fragmented_octets(<<0:1,Len:7,Bin/binary>>,C,Acc) ->
+ <> = Bin,
+ BinOctets = list_to_binary(lists:reverse([Value|Acc])),
+ case C of
+ Int when integer(Int),size(BinOctets) == Int ->
+ {BinOctets,Bin2};
+ Int when integer(Int) ->
+ exit({error,{asn1,{illegal_value,C,BinOctets}}});
+ _ ->
+ {BinOctets,Bin2}
+ end.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_open_type(Constraint, Value) -> CompleteList
+%% Value = list of bytes of an already encoded value (the list must be flat)
+%% | binary
+%% Contraint = not used in this version
+%%
+encode_open_type(_C, Val) when list(Val) ->
+ Bin = list_to_binary(Val),
+ [encode_length(undefined,size(Bin)),{octets,Bin}]; % octets implies align
+encode_open_type(_C, Val) when binary(Val) ->
+ [encode_length(undefined,size(Val)),{octets,Val}]. % octets implies align
+%% the binary_to_list is not optimal but compatible with the current solution
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_open_type(Buffer,Constraint) -> Value
+%% Constraint is not used in this version
+%% Buffer = [byte] with PER encoded data
+%% Value = [byte] with decoded data (which must be decoded again as some type)
+%%
+decode_open_type(Bytes, _C) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ getoctets_as_bin(Bytes2,Len).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_integer(Constraint,Value,NamedNumberList) -> CompleteList
+%% encode_integer(Constraint,Value) -> CompleteList
+%% encode_integer(Constraint,{Name,Value}) -> CompleteList
+%%
+%%
+encode_integer(C,V,NamedNumberList) when atom(V) ->
+ case lists:keysearch(V,1,NamedNumberList) of
+ {value,{_,NewV}} ->
+ encode_integer(C,NewV);
+ _ ->
+ exit({error,{asn1,{namednumber,V}}})
+ end;
+encode_integer(C,V,_NamedNumberList) when integer(V) ->
+ encode_integer(C,V);
+encode_integer(C,{Name,V},NamedNumberList) when atom(Name) ->
+ encode_integer(C,V,NamedNumberList).
+
+encode_integer(C,{Name,Val}) when atom(Name) ->
+ encode_integer(C,Val);
+
+encode_integer([{Rc,_Ec}],Val) when tuple(Rc) -> % XXX when is this invoked? First argument most often a list,...Ok this is the extension case...but it doesn't work.
+ case (catch encode_integer([Rc],Val)) of
+ {'EXIT',{error,{asn1,_}}} ->
+ [{bits,1,1},encode_unconstrained_number(Val)];
+ Encoded ->
+ [{bits,1,0},Encoded]
+ end;
+encode_integer(C,Val ) when list(C) ->
+ case get_constraint(C,'SingleValue') of
+ no ->
+ encode_integer1(C,Val);
+ V when integer(V),V == Val ->
+ []; % a type restricted to a single value encodes to nothing
+ V when list(V) ->
+ case lists:member(Val,V) of
+ true ->
+ encode_integer1(C,Val);
+ _ ->
+ exit({error,{asn1,{illegal_value,Val}}})
+ end;
+ _ ->
+ exit({error,{asn1,{illegal_value,Val}}})
+ end.
+
+encode_integer1(C, Val) ->
+ case VR = get_constraint(C,'ValueRange') of
+ no ->
+ encode_unconstrained_number(Val);
+ {Lb,'MAX'} ->
+ encode_semi_constrained_number(Lb,Val);
+ %% positive with range
+ {Lb,Ub} when Val >= Lb,
+ Ub >= Val ->
+ encode_constrained_number(VR,Val);
+ _ ->
+ exit({error,{asn1,{illegal_value,VR,Val}}})
+ end.
+
+decode_integer(Buffer,Range,NamedNumberList) ->
+ {Val,Buffer2} = decode_integer(Buffer,Range),
+ case lists:keysearch(Val,2,NamedNumberList) of
+ {value,{NewVal,_}} -> {NewVal,Buffer2};
+ _ -> {Val,Buffer2}
+ end.
+
+decode_integer(Buffer,[{Rc,_Ec}]) when tuple(Rc) ->
+ {Ext,Buffer2} = getext(Buffer),
+ case Ext of
+ 0 -> decode_integer(Buffer2,[Rc]);
+ 1 -> decode_unconstrained_number(Buffer2)
+ end;
+decode_integer(Buffer,undefined) ->
+ decode_unconstrained_number(Buffer);
+decode_integer(Buffer,C) ->
+ case get_constraint(C,'SingleValue') of
+ V when integer(V) ->
+ {V,Buffer};
+ V when list(V) ->
+ {Val,Buffer2} = decode_integer1(Buffer,C),
+ case lists:member(Val,V) of
+ true ->
+ {Val,Buffer2};
+ _ ->
+ exit({error,{asn1,{illegal_value,Val}}})
+ end;
+ _ ->
+ decode_integer1(Buffer,C)
+ end.
+
+decode_integer1(Buffer,C) ->
+ case VR = get_constraint(C,'ValueRange') of
+ no ->
+ decode_unconstrained_number(Buffer);
+ {Lb, 'MAX'} ->
+ decode_semi_constrained_number(Buffer,Lb);
+ {_,_} ->
+ decode_constrained_number(Buffer,VR)
+ end.
+
+ % X.691:10.6 Encoding of a normally small non-negative whole number
+ % Use this for encoding of CHOICE index if there is an extension marker in
+ % the CHOICE
+encode_small_number({Name,Val}) when atom(Name) ->
+ encode_small_number(Val);
+encode_small_number(Val) when Val =< 63 ->
+% [{bits,1,0},{bits,6,Val}];
+ [{bits,7,Val}]; % same as above but more efficient
+encode_small_number(Val) ->
+ [{bits,1,1},encode_semi_constrained_number(0,Val)].
+
+decode_small_number(Bytes) ->
+ {Bit,Bytes2} = getbit(Bytes),
+ case Bit of
+ 0 ->
+ getbits(Bytes2,6);
+ 1 ->
+ decode_semi_constrained_number(Bytes2,0)
+ end.
+
+%% X.691:10.7 Encoding of a semi-constrained whole number
+%% might be an optimization encode_semi_constrained_number(0,Val) ->
+encode_semi_constrained_number(C,{Name,Val}) when atom(Name) ->
+ encode_semi_constrained_number(C,Val);
+encode_semi_constrained_number({Lb,'MAX'},Val) ->
+ encode_semi_constrained_number(Lb,Val);
+encode_semi_constrained_number(Lb,Val) ->
+ Val2 = Val - Lb,
+ Oct = eint_positive(Val2),
+ Len = length(Oct),
+ if
+ Len < 128 ->
+ {octets,[Len|Oct]}; % equiv with encode_length(undefined,Len) but faster
+ true ->
+ [encode_length(undefined,Len),{octets,Oct}]
+ end.
+
+decode_semi_constrained_number(Bytes,{Lb,_}) ->
+ decode_semi_constrained_number(Bytes,Lb);
+decode_semi_constrained_number(Bytes,Lb) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {V,Bytes3} = getoctets(Bytes2,Len),
+ {V+Lb,Bytes3}.
+
+encode_constrained_number(Range,{Name,Val}) when atom(Name) ->
+ encode_constrained_number(Range,Val);
+encode_constrained_number({Lb,Ub},Val) when Val >= Lb, Ub >= Val ->
+ Range = Ub - Lb + 1,
+ Val2 = Val - Lb,
+ if
+ Range == 2 ->
+ {bits,1,Val2};
+ Range =< 4 ->
+ {bits,2,Val2};
+ Range =< 8 ->
+ {bits,3,Val2};
+ Range =< 16 ->
+ {bits,4,Val2};
+ Range =< 32 ->
+ {bits,5,Val2};
+ Range =< 64 ->
+ {bits,6,Val2};
+ Range =< 128 ->
+ {bits,7,Val2};
+ Range =< 255 ->
+ {bits,8,Val2};
+ Range =< 256 ->
+ {octets,[Val2]};
+ Range =< 65536 ->
+ {octets,<>};
+ Range =< 16#1000000 ->
+ Octs = eint_positive(Val2),
+ [{bits,2,length(Octs)-1},{octets,Octs}];
+ Range =< 16#100000000 ->
+ Octs = eint_positive(Val2),
+ [{bits,2,length(Octs)-1},{octets,Octs}];
+ Range =< 16#10000000000 ->
+ Octs = eint_positive(Val2),
+ [{bits,3,length(Octs)-1},{octets,Octs}];
+ true ->
+ exit({not_supported,{integer_range,Range}})
+ end;
+encode_constrained_number(Range,Val) ->
+ exit({error,{asn1,{integer_range,Range,value,Val}}}).
+
+
+decode_constrained_number(Buffer,{Lb,Ub}) ->
+ Range = Ub - Lb + 1,
+ % Val2 = Val - Lb,
+ {Val,Remain} =
+ if
+ Range == 2 ->
+ getbits(Buffer,1);
+ Range =< 4 ->
+ getbits(Buffer,2);
+ Range =< 8 ->
+ getbits(Buffer,3);
+ Range =< 16 ->
+ getbits(Buffer,4);
+ Range =< 32 ->
+ getbits(Buffer,5);
+ Range =< 64 ->
+ getbits(Buffer,6);
+ Range =< 128 ->
+ getbits(Buffer,7);
+ Range =< 255 ->
+ getbits(Buffer,8);
+ Range =< 256 ->
+ getoctets(Buffer,1);
+ Range =< 65536 ->
+ getoctets(Buffer,2);
+ Range =< 16#1000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,3}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ Range =< 16#100000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,4}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ Range =< 16#10000000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,5}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ true ->
+ exit({not_supported,{integer_range,Range}})
+ end,
+ {Val+Lb,Remain}.
+
+%% X.691:10.8 Encoding of an unconstrained whole number
+
+encode_unconstrained_number(Val) when Val >= 0 ->
+ Oct = eint(Val,[]),
+ Len = length(Oct),
+ if
+ Len < 128 ->
+ {octets,[Len|Oct]}; % equiv with encode_length(undefined,Len) but faster
+ true ->
+ [encode_length(undefined,Len),{octets,Oct}]
+ end;
+encode_unconstrained_number(Val) -> % negative
+ Oct = enint(Val,[]),
+ Len = length(Oct),
+ if
+ Len < 128 ->
+ {octets,[Len|Oct]}; % equiv with encode_length(undefined,Len) but faster
+ true ->
+ [encode_length(undefined,Len),{octets,Oct}]
+ end.
+
+
+%% used for positive Values which don't need a sign bit
+%% returns a binary
+eint_positive(Val) ->
+ case eint(Val,[]) of
+ [0,B1|T] ->
+ [B1|T];
+ T ->
+ T
+ end.
+
+
+eint(0, [B|Acc]) when B < 128 ->
+ [B|Acc];
+eint(N, Acc) ->
+ eint(N bsr 8, [N band 16#ff| Acc]).
+
+enint(-1, [B1|T]) when B1 > 127 ->
+ [B1|T];
+enint(N, Acc) ->
+ enint(N bsr 8, [N band 16#ff|Acc]).
+
+decode_unconstrained_number(Bytes) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {Ints,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_integer(Ints),Bytes3}.
+
+dec_pos_integer(Ints) ->
+ decpint(Ints, 8 * (length(Ints) - 1)).
+dec_integer(Ints) when hd(Ints) band 255 =< 127 -> %% Positive number
+ decpint(Ints, 8 * (length(Ints) - 1));
+dec_integer(Ints) -> %% Negative
+ decnint(Ints, 8 * (length(Ints) - 1)).
+
+decpint([Byte|Tail], Shift) ->
+ (Byte bsl Shift) bor decpint(Tail, Shift-8);
+decpint([], _) -> 0.
+
+decnint([Byte|Tail], Shift) ->
+ (-128 + (Byte band 127) bsl Shift) bor decpint(Tail, Shift-8).
+
+% minimum_octets(Val) ->
+% minimum_octets(Val,[]).
+
+% minimum_octets(Val,Acc) when Val > 0 ->
+% minimum_octets((Val bsr 8),[Val band 16#FF|Acc]);
+% minimum_octets(0,Acc) ->
+% Acc.
+
+
+%% X.691:10.9 Encoding of a length determinant
+%%encode_small_length(undefined,Len) -> % null means no UpperBound
+%% encode_small_number(Len).
+
+%% X.691:10.9.3.5
+%% X.691:10.9.3.7
+encode_length(undefined,Len) -> % un-constrained
+ if
+ Len < 128 ->
+ {octets,[Len]};
+ Len < 16384 ->
+ {octets,<<2:2,Len:14>>};
+ true -> % should be able to endode length >= 16384
+ exit({error,{asn1,{encode_length,{nyi,above_16k}}}})
+ end;
+
+encode_length({0,'MAX'},Len) ->
+ encode_length(undefined,Len);
+encode_length(Vr={Lb,Ub},Len) when Ub =< 65535 ,Lb >= 0 -> % constrained
+ encode_constrained_number(Vr,Len);
+encode_length({Lb,_Ub},Len) when integer(Lb), Lb >= 0 -> % Ub > 65535
+ encode_length(undefined,Len);
+encode_length({Vr={Lb,Ub},[]},Len) when Ub =< 65535 ,Lb >= 0 ->
+ %% constrained extensible
+ [{bits,1,0},encode_constrained_number(Vr,Len)];
+encode_length(SingleValue,_Len) when integer(SingleValue) ->
+ [].
+
+%% X.691 10.9.3.4 (only used for length of bitmap that prefixes extension
+%% additions in a sequence or set
+encode_small_length(Len) when Len =< 64 ->
+%% [{bits,1,0},{bits,6,Len-1}];
+ {bits,7,Len-1}; % the same as above but more efficient
+encode_small_length(Len) ->
+ [{bits,1,1},encode_length(undefined,Len)].
+
+% decode_small_length({Used,<<_:Used,0:1,Num:6,_:((8-Used+1) rem 8),Rest/binary>>}) ->
+% case Buffer of
+% <<_:Used,0:1,Num:6,_:((8-Used+1) rem 8),Rest/binary>> ->
+% {Num,
+% case getbit(Buffer) of
+% {0,Remain} ->
+% {Bits,Remain2} = getbits(Remain,6),
+% {Bits+1,Remain2};
+% {1,Remain} ->
+% decode_length(Remain,undefined)
+% end.
+
+decode_small_length(Buffer) ->
+ case getbit(Buffer) of
+ {0,Remain} ->
+ {Bits,Remain2} = getbits(Remain,6),
+ {Bits+1,Remain2};
+ {1,Remain} ->
+ decode_length(Remain,undefined)
+ end.
+
+decode_length(Buffer) ->
+ decode_length(Buffer,undefined).
+
+decode_length(Buffer,undefined) -> % un-constrained
+ {0,Buffer2} = align(Buffer),
+ case Buffer2 of
+ <<0:1,Oct:7,Rest/binary>> ->
+ {Oct,{0,Rest}};
+ <<2:2,Val:14,Rest/binary>> ->
+ {Val,{0,Rest}};
+ <<3:2,_:14,_Rest/binary>> ->
+ %% this case should be fixed
+ exit({error,{asn1,{decode_length,{nyi,above_16k}}}})
+ end;
+%% {Bits,_} = getbits(Buffer2,2),
+% case Bits of
+% 2 ->
+% {Val,Bytes3} = getoctets(Buffer2,2),
+% {(Val band 16#3FFF),Bytes3};
+% 3 ->
+% exit({error,{asn1,{decode_length,{nyi,above_16k}}}});
+% _ ->
+% {Val,Bytes3} = getoctet(Buffer2),
+% {Val band 16#7F,Bytes3}
+% end;
+
+decode_length(Buffer,{Lb,Ub}) when Ub =< 65535 ,Lb >= 0 -> % constrained
+ decode_constrained_number(Buffer,{Lb,Ub});
+decode_length(_,{Lb,_}) when integer(Lb), Lb >= 0 -> % Ub > 65535
+ exit({error,{asn1,{decode_length,{nyi,above_64K}}}});
+decode_length(Buffer,{{Lb,Ub},[]}) ->
+ case getbit(Buffer) of
+ {0,Buffer2} ->
+ decode_length(Buffer2, {Lb,Ub})
+ end;
+
+
+%When does this case occur with {_,_Lb,Ub} ??
+% X.691:10.9.3.5
+decode_length({Used,Bin},{_,_Lb,_Ub}) -> %when Len =< 127 -> % Unconstrained or large Ub NOTE! this case does not cover case when Ub > 65535
+ Unused = (8-Used) rem 8,
+ case Bin of
+ <<_:Used,0:1,Val:7,R:Unused,Rest/binary>> ->
+ {Val,{Used,<>}};
+ <<_:Used,_:Unused,2:2,Val:14,Rest/binary>> ->
+ {Val, {0,Rest}};
+ <<_:Used,_:Unused,3:2,_:14,_Rest/binary>> ->
+ exit({error,{asn1,{decode_length,{nyi,length_above_64K}}}})
+ end;
+% decode_length(Buffer,{_,_Lb,Ub}) -> %when Len =< 127 -> % Unconstrained or large Ub
+% case getbit(Buffer) of
+% {0,Remain} ->
+% getbits(Remain,7);
+% {1,Remain} ->
+% {Val,Remain2} = getoctets(Buffer,2),
+% {Val band 2#0111111111111111, Remain2}
+% end;
+decode_length(Buffer,SingleValue) when integer(SingleValue) ->
+ {SingleValue,Buffer}.
+
+
+ % X.691:11
+encode_boolean(true) ->
+ {bits,1,1};
+encode_boolean(false) ->
+ {bits,1,0};
+encode_boolean({Name,Val}) when atom(Name) ->
+ encode_boolean(Val);
+encode_boolean(Val) ->
+ exit({error,{asn1,{encode_boolean,Val}}}).
+
+decode_boolean(Buffer) -> %when record(Buffer,buffer)
+ case getbit(Buffer) of
+ {1,Remain} -> {true,Remain};
+ {0,Remain} -> {false,Remain}
+ end.
+
+
+%% ENUMERATED with extension marker
+decode_enumerated(Buffer,C,{Ntup1,Ntup2}) when tuple(Ntup1), tuple(Ntup2) ->
+ {Ext,Buffer2} = getext(Buffer),
+ case Ext of
+ 0 -> % not an extension value
+ {Val,Buffer3} = decode_integer(Buffer2,C),
+ case catch (element(Val+1,Ntup1)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer3};
+ _Error -> exit({error,{asn1,{decode_enumerated,{Val,[Ntup1,Ntup2]}}}})
+ end;
+ 1 -> % this an extension value
+ {Val,Buffer3} = decode_small_number(Buffer2),
+ case catch (element(Val+1,Ntup2)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer3};
+ _ -> {{asn1_enum,Val},Buffer3}
+ end
+ end;
+
+decode_enumerated(Buffer,C,NamedNumberTup) when tuple(NamedNumberTup) ->
+ {Val,Buffer2} = decode_integer(Buffer,C),
+ case catch (element(Val+1,NamedNumberTup)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer2};
+ _Error -> exit({error,{asn1,{decode_enumerated,{Val,NamedNumberTup}}}})
+ end.
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Bitstring value, ITU_T X.690 Chapter 8.5
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% encode bitstring value
+%%===============================================================================
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% bitstring NamedBitList
+%% Val can be of:
+%% - [identifiers] where only named identifers are set to one,
+%% the Constraint must then have some information of the
+%% bitlength.
+%% - [list of ones and zeroes] all bits
+%% - integer value representing the bitlist
+%% C is constraint Len, only valid when identifiers
+
+
+%% when the value is a list of {Unused,BinBits}, where
+%% Unused = integer(),
+%% BinBits = binary().
+
+encode_bit_string(C,Bin={Unused,BinBits},NamedBitList) when integer(Unused),
+ binary(BinBits) ->
+ encode_bin_bit_string(C,Bin,NamedBitList);
+
+%% when the value is a list of named bits
+encode_bit_string(C, LoNB=[FirstVal | _RestVal], NamedBitList) when atom(FirstVal) ->
+ ToSetPos = get_all_bitposes(LoNB, NamedBitList, []),
+ BitList = make_and_set_list(ToSetPos,0),
+ encode_bit_string(C,BitList,NamedBitList);
+
+encode_bit_string(C, BL=[{bit,_No} | _RestVal], NamedBitList) ->
+ ToSetPos = get_all_bitposes(BL, NamedBitList, []),
+ BitList = make_and_set_list(ToSetPos,0),
+ encode_bit_string(C,BitList,NamedBitList);
+
+%% when the value is a list of ones and zeroes
+
+% encode_bit_string(C, BitListValue, NamedBitList) when list(BitListValue) ->
+% Bl1 =
+% case NamedBitList of
+% [] -> % dont remove trailing zeroes
+% BitListValue;
+% _ -> % first remove any trailing zeroes
+% lists:reverse(lists:dropwhile(fun(0)->true;(1)->false end,
+% lists:reverse(BitListValue)))
+% end,
+% BitList = [{bit,X} || X <- Bl1],
+% %% BListLen = length(BitList),
+% case get_constraint(C,'SizeConstraint') of
+% 0 -> % fixed length
+% []; % nothing to encode
+% V when integer(V),V=<16 -> % fixed length 16 bits or less
+% pad_list(V,BitList);
+% V when integer(V) -> % fixed length 16 bits or more
+% [align,pad_list(V,BitList)]; % should be another case for V >= 65537
+% {Lb,Ub} when integer(Lb),integer(Ub) ->
+% [encode_length({Lb,Ub},length(BitList)),align,BitList];
+% no ->
+% [encode_length(undefined,length(BitList)),align,BitList];
+% Sc -> % extension marker
+% [encode_length(Sc,length(BitList)),align,BitList]
+% end;
+encode_bit_string(C, BitListValue, NamedBitList) when list(BitListValue) ->
+ BitListToBinary =
+ %% fun that transforms a list of 1 and 0 to a tuple:
+ %% {UnusedBitsInLastByte, Binary}
+ fun([H|T],Acc,N,Fun) ->
+ Fun(T,(Acc bsl 1)+H,N+1,Fun);
+ ([],Acc,N,_) ->
+ Unused = (8 - (N rem 8)) rem 8,
+ {Unused,<>}
+ end,
+ UnusedAndBin =
+ case NamedBitList of
+ [] -> % dont remove trailing zeroes
+ BitListToBinary(BitListValue,0,0,BitListToBinary);
+ _ ->
+ BitListToBinary(lists:reverse(
+ lists:dropwhile(fun(0)->true;(1)->false end,
+ lists:reverse(BitListValue))),
+ 0,0,BitListToBinary)
+ end,
+ encode_bin_bit_string(C,UnusedAndBin,NamedBitList);
+
+%% when the value is an integer
+encode_bit_string(C, IntegerVal, NamedBitList) when integer(IntegerVal)->
+ BitList = int_to_bitlist(IntegerVal),
+ encode_bit_string(C,BitList,NamedBitList);
+
+%% when the value is a tuple
+encode_bit_string(C,{Name,Val}, NamedBitList) when atom(Name) ->
+ encode_bit_string(C,Val,NamedBitList).
+
+
+%% encode_bin_bit_string/3, when value is a tuple of Unused and BinBits.
+%% Unused = integer(),i.e. number unused bits in least sign. byte of
+%% BinBits = binary().
+
+
+encode_bin_bit_string(C,UnusedAndBin={_Unused,_BinBits},NamedBitList) ->
+ Constr = get_constraint(C,'SizeConstraint'),
+ UnusedAndBin1 = {Unused1,Bin1} =
+ remove_trailing_bin(NamedBitList,UnusedAndBin,lower_bound(Constr)),
+ case Constr of
+ 0 ->
+ [];
+ V when integer(V),V=<16 ->
+ {Unused2,Bin2} = pad_list(V,UnusedAndBin1),
+ <> = Bin2,
+ {bits,V,BitVal};
+ V when integer(V) ->
+ [align, pad_list(V, UnusedAndBin1)];
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ [encode_length({Lb,Ub},size(Bin1)*8 - Unused1),
+ align,UnusedAndBin1];
+ no ->
+ [encode_length(undefined,size(Bin1)*8 - Unused1),
+ align,UnusedAndBin1];
+ Sc ->
+ [encode_length(Sc,size(Bin1)*8 - Unused1),
+ align,UnusedAndBin1]
+ end.
+
+remove_trailing_bin([], {Unused,Bin},_) ->
+ {Unused,Bin};
+remove_trailing_bin(NamedNumberList, {_Unused,Bin},C) ->
+ Size = size(Bin)-1,
+ <> = Bin,
+ %% clear the Unused bits to be sure
+% LastByte1 = LastByte band (((1 bsl Unused) -1) bxor 255),
+ Unused1 = trailingZeroesInNibble(LastByte band 15),
+ Unused2 =
+ case Unused1 of
+ 4 ->
+ 4 + trailingZeroesInNibble(LastByte bsr 4);
+ _ -> Unused1
+ end,
+ case Unused2 of
+ 8 ->
+ remove_trailing_bin(NamedNumberList,{0,Bfront},C);
+ _ ->
+ case C of
+ Int when integer(Int),Int > ((size(Bin)*8)-Unused2) ->
+ %% this padding see OTP-4353
+ pad_list(Int,{Unused2,Bin});
+ _ -> {Unused2,Bin}
+ end
+ end.
+
+
+trailingZeroesInNibble(0) ->
+ 4;
+trailingZeroesInNibble(1) ->
+ 0;
+trailingZeroesInNibble(2) ->
+ 1;
+trailingZeroesInNibble(3) ->
+ 0;
+trailingZeroesInNibble(4) ->
+ 2;
+trailingZeroesInNibble(5) ->
+ 0;
+trailingZeroesInNibble(6) ->
+ 1;
+trailingZeroesInNibble(7) ->
+ 0;
+trailingZeroesInNibble(8) ->
+ 3;
+trailingZeroesInNibble(9) ->
+ 0;
+trailingZeroesInNibble(10) ->
+ 1;
+trailingZeroesInNibble(11) ->
+ 0;
+trailingZeroesInNibble(12) -> %#1100
+ 2;
+trailingZeroesInNibble(13) ->
+ 0;
+trailingZeroesInNibble(14) ->
+ 1;
+trailingZeroesInNibble(15) ->
+ 0.
+
+lower_bound({{Lb,_},_}) when integer(Lb) ->
+ Lb;
+lower_bound({Lb,_}) when integer(Lb) ->
+ Lb;
+lower_bound(C) ->
+ C.
+
+%%%%%%%%%%%%%%%
+%% The result is presented as a list of named bits (if possible)
+%% else as a tuple {Unused,Bits}. Unused is the number of unused
+%% bits, least significant bits in the last byte of Bits. Bits is
+%% the BIT STRING represented as a binary.
+%%
+decode_compact_bit_string(Buffer, C, NamedNumberList) ->
+ case get_constraint(C,'SizeConstraint') of
+ 0 -> % fixed length
+ {{8,0},Buffer};
+ V when integer(V),V=<16 -> %fixed length 16 bits or less
+ compact_bit_string(Buffer,V,NamedNumberList);
+ V when integer(V),V=<65536 -> %fixed length > 16 bits
+ Bytes2 = align(Buffer),
+ compact_bit_string(Bytes2,V,NamedNumberList);
+ V when integer(V) -> % V > 65536 => fragmented value
+ {Bin,Buffer2} = decode_fragmented_bits(Buffer,V),
+ case Buffer2 of
+ {0,_} -> {{0,Bin},Buffer2};
+ {U,_} -> {{8-U,Bin},Buffer2}
+ end;
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ %% This case may demand decoding of fragmented length/value
+ {Len,Bytes2} = decode_length(Buffer,{Lb,Ub}),
+ Bytes3 = align(Bytes2),
+ compact_bit_string(Bytes3,Len,NamedNumberList);
+ no ->
+ %% This case may demand decoding of fragmented length/value
+ {Len,Bytes2} = decode_length(Buffer,undefined),
+ Bytes3 = align(Bytes2),
+ compact_bit_string(Bytes3,Len,NamedNumberList);
+ Sc ->
+ {Len,Bytes2} = decode_length(Buffer,Sc),
+ Bytes3 = align(Bytes2),
+ compact_bit_string(Bytes3,Len,NamedNumberList)
+ end.
+
+
+%%%%%%%%%%%%%%%
+%% The result is presented as a list of named bits (if possible)
+%% else as a list of 0 and 1.
+%%
+decode_bit_string(Buffer, C, NamedNumberList) ->
+ case get_constraint(C,'SizeConstraint') of
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ {Len,Bytes2} = decode_length(Buffer,{Lb,Ub}),
+ Bytes3 = align(Bytes2),
+ bit_list_or_named(Bytes3,Len,NamedNumberList);
+ no ->
+ {Len,Bytes2} = decode_length(Buffer,undefined),
+ Bytes3 = align(Bytes2),
+ bit_list_or_named(Bytes3,Len,NamedNumberList);
+ 0 -> % fixed length
+ {[],Buffer}; % nothing to encode
+ V when integer(V),V=<16 -> % fixed length 16 bits or less
+ bit_list_or_named(Buffer,V,NamedNumberList);
+ V when integer(V),V=<65536 ->
+ Bytes2 = align(Buffer),
+ bit_list_or_named(Bytes2,V,NamedNumberList);
+ V when integer(V) ->
+ Bytes2 = align(Buffer),
+ {BinBits,_} = decode_fragmented_bits(Bytes2,V),
+ bit_list_or_named(BinBits,V,NamedNumberList);
+ Sc -> % extension marker
+ {Len,Bytes2} = decode_length(Buffer,Sc),
+ Bytes3 = align(Bytes2),
+ bit_list_or_named(Bytes3,Len,NamedNumberList)
+ end.
+
+
+%% if no named bits are declared we will return a
+%% {Unused,Bits}. Unused = integer(),
+%% Bits = binary().
+compact_bit_string(Buffer,Len,[]) ->
+ getbits_as_binary(Len,Buffer); % {{Unused,BinBits},NewBuffer}
+compact_bit_string(Buffer,Len,NamedNumberList) ->
+ bit_list_or_named(Buffer,Len,NamedNumberList).
+
+
+%% if no named bits are declared we will return a
+%% BitList = [0 | 1]
+
+bit_list_or_named(Buffer,Len,[]) ->
+ getbits_as_list(Len,Buffer);
+
+%% if there are named bits declared we will return a named
+%% BitList where the names are atoms and unnamed bits represented
+%% as {bit,Pos}
+%% BitList = [atom() | {bit,Pos}]
+%% Pos = integer()
+
+bit_list_or_named(Buffer,Len,NamedNumberList) ->
+ {BitList,Rest} = getbits_as_list(Len,Buffer),
+ {bit_list_or_named1(0,BitList,NamedNumberList,[]), Rest}.
+
+bit_list_or_named1(Pos,[0|Bt],Names,Acc) ->
+ bit_list_or_named1(Pos+1,Bt,Names,Acc);
+bit_list_or_named1(Pos,[1|Bt],Names,Acc) ->
+ case lists:keysearch(Pos,2,Names) of
+ {value,{Name,_}} ->
+ bit_list_or_named1(Pos+1,Bt,Names,[Name|Acc]);
+ _ ->
+ bit_list_or_named1(Pos+1,Bt,Names,[{bit,Pos}|Acc])
+ end;
+bit_list_or_named1(_,[],_,Acc) ->
+ lists:reverse(Acc).
+
+
+
+%%%%%%%%%%%%%%%
+%%
+
+int_to_bitlist(Int) when integer(Int), Int > 0 ->
+ [Int band 1 | int_to_bitlist(Int bsr 1)];
+int_to_bitlist(0) ->
+ [].
+
+
+%%%%%%%%%%%%%%%%%%
+%% get_all_bitposes([list of named bits to set], named_bit_db, []) ->
+%% [sorted_list_of_bitpositions_to_set]
+
+get_all_bitposes([{bit,ValPos}|Rest], NamedBitList, Ack) ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack ]);
+
+get_all_bitposes([Val | Rest], NamedBitList, Ack) ->
+ case lists:keysearch(Val, 1, NamedBitList) of
+ {value, {_ValName, ValPos}} ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack]);
+ _ ->
+ exit({error,{asn1, {bitstring_namedbit, Val}}})
+ end;
+get_all_bitposes([], _NamedBitList, Ack) ->
+ lists:sort(Ack).
+
+%%%%%%%%%%%%%%%%%%
+%% make_and_set_list([list of positions to set to 1])->
+%% returns list with all in SetPos set.
+%% in positioning in list the first element is 0, the second 1 etc.., but
+%%
+
+make_and_set_list([XPos|SetPos], XPos) ->
+ [1 | make_and_set_list(SetPos, XPos + 1)];
+make_and_set_list([Pos|SetPos], XPos) ->
+ [0 | make_and_set_list([Pos | SetPos], XPos + 1)];
+make_and_set_list([], _) ->
+ [].
+
+%%%%%%%%%%%%%%%%%
+%% pad_list(N,BitList) -> PaddedList
+%% returns a padded (with trailing {bit,0} elements) list of length N
+%% if Bitlist contains more than N significant bits set an exit asn1_error
+%% is generated
+
+pad_list(N,In={Unused,Bin}) ->
+ pad_list(N, size(Bin)*8 - Unused, In).
+
+pad_list(N,Size,In={_,_}) when N < Size ->
+ exit({error,{asn1,{range_error,{bit_string,In}}}});
+pad_list(N,Size,{Unused,Bin}) when N > Size, Unused > 0 ->
+ pad_list(N,Size+1,{Unused-1,Bin});
+pad_list(N,Size,{_Unused,Bin}) when N > Size ->
+ pad_list(N,Size+1,{7,<>});
+pad_list(N,N,In={_,_}) ->
+ In.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% X.691:16
+%% encode_octet_string(Constraint,ExtensionMarker,Val)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+encode_octet_string(C,Val) ->
+ encode_octet_string(C,false,Val).
+
+encode_octet_string(C,Bool,{_Name,Val}) ->
+ encode_octet_string(C,Bool,Val);
+encode_octet_string(_,true,_) ->
+ exit({error,{asn1,{'not_supported',extensionmarker}}});
+encode_octet_string(C,false,Val) ->
+ case get_constraint(C,'SizeConstraint') of
+ 0 ->
+ [];
+ 1 ->
+ [V] = Val,
+ {bits,8,V};
+ 2 ->
+ [V1,V2] = Val,
+ [{bits,8,V1},{bits,8,V2}];
+ Sv when Sv =<65535, Sv == length(Val) -> % fixed length
+ {octets,Val};
+ {Lb,Ub} ->
+ [encode_length({Lb,Ub},length(Val)),{octets,Val}];
+ Sv when list(Sv) ->
+ [encode_length({hd(Sv),lists:max(Sv)},length(Val)),{octets,Val}];
+ no ->
+ [encode_length(undefined,length(Val)),{octets,Val}]
+ end.
+
+decode_octet_string(Bytes,Range) ->
+ decode_octet_string(Bytes,Range,false).
+
+decode_octet_string(Bytes,C,false) ->
+ case get_constraint(C,'SizeConstraint') of
+ 0 ->
+ {[],Bytes};
+ 1 ->
+ {B1,Bytes2} = getbits(Bytes,8),
+ {[B1],Bytes2};
+ 2 ->
+ {Bs,Bytes2}= getbits(Bytes,16),
+ {binary_to_list(<>),Bytes2};
+ {_,0} ->
+ {[],Bytes};
+ Sv when integer(Sv), Sv =<65535 -> % fixed length
+ getoctets_as_list(Bytes,Sv);
+ Sv when integer(Sv) -> % fragmented encoding
+ Bytes2 = align(Bytes),
+ decode_fragmented_octets(Bytes2,Sv);
+ {Lb,Ub} ->
+ {Len,Bytes2} = decode_length(Bytes,{Lb,Ub}),
+ getoctets_as_list(Bytes2,Len);
+ Sv when list(Sv) ->
+ {Len,Bytes2} = decode_length(Bytes,{hd(Sv),lists:max(Sv)}),
+ getoctets_as_list(Bytes2,Len);
+ no ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ getoctets_as_list(Bytes2,Len)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Restricted char string types
+%% (NumericString, PrintableString,VisibleString,IA5String,BMPString,UniversalString)
+%% X.691:26 and X.680:34-36
+%%encode_restricted_string(aligned,'BMPString',Constraints,Extension,Val)
+
+
+encode_restricted_string(aligned,{Name,Val}) when atom(Name) ->
+ encode_restricted_string(aligned,Val);
+
+encode_restricted_string(aligned,Val) when list(Val)->
+ [encode_length(undefined,length(Val)),{octets,Val}].
+
+encode_known_multiplier_string(aligned,StringType,C,_Ext,{Name,Val}) when atom(Name) ->
+ encode_known_multiplier_string(aligned,StringType,C,false,Val);
+
+encode_known_multiplier_string(aligned,StringType,C,_Ext,Val) ->
+ Result = chars_encode(C,StringType,Val),
+ NumBits = get_NumBits(C,StringType),
+ case get_constraint(C,'SizeConstraint') of
+ Ub when integer(Ub), Ub*NumBits =< 16 ->
+ case {StringType,Result} of
+ {'BMPString',{octets,Ol}} ->
+ [{bits,8,Oct}||Oct <- Ol];
+ _ ->
+ Result
+ end;
+ 0 ->
+ [];
+ Ub when integer(Ub),Ub =<65535 -> % fixed length
+ [align,Result];
+ {Ub,Lb} ->
+ [encode_length({Ub,Lb},length(Val)),align,Result];
+ Vl when list(Vl) ->
+ [encode_length({lists:min(Vl),lists:max(Vl)},length(Val)),align,Result];
+ no ->
+ [encode_length(undefined,length(Val)),align,Result]
+ end.
+
+decode_restricted_string(Bytes,aligned) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ getoctets_as_list(Bytes2,Len).
+
+decode_known_multiplier_string(Bytes,aligned,StringType,C,_Ext) ->
+ NumBits = get_NumBits(C,StringType),
+ case get_constraint(C,'SizeConstraint') of
+ Ub when integer(Ub), Ub*NumBits =< 16 ->
+ chars_decode(Bytes,NumBits,StringType,C,Ub);
+ Ub when integer(Ub),Ub =<65535 -> % fixed length
+ Bytes1 = align(Bytes),
+ chars_decode(Bytes1,NumBits,StringType,C,Ub);
+ 0 ->
+ {[],Bytes};
+ Vl when list(Vl) ->
+ {Len,Bytes1} = decode_length(Bytes,{hd(Vl),lists:max(Vl)}),
+ Bytes2 = align(Bytes1),
+ chars_decode(Bytes2,NumBits,StringType,C,Len);
+ no ->
+ {Len,Bytes1} = decode_length(Bytes,undefined),
+ Bytes2 = align(Bytes1),
+ chars_decode(Bytes2,NumBits,StringType,C,Len);
+ {Lb,Ub}->
+ {Len,Bytes1} = decode_length(Bytes,{Lb,Ub}),
+ Bytes2 = align(Bytes1),
+ chars_decode(Bytes2,NumBits,StringType,C,Len)
+ end.
+
+
+encode_NumericString(C,Val) ->
+ encode_known_multiplier_string(aligned,'NumericString',C,false,Val).
+decode_NumericString(Bytes,C) ->
+ decode_known_multiplier_string(Bytes,aligned,'NumericString',C,false).
+
+encode_PrintableString(C,Val) ->
+ encode_known_multiplier_string(aligned,'PrintableString',C,false,Val).
+decode_PrintableString(Bytes,C) ->
+ decode_known_multiplier_string(Bytes,aligned,'PrintableString',C,false).
+
+encode_VisibleString(C,Val) -> % equivalent with ISO646String
+ encode_known_multiplier_string(aligned,'VisibleString',C,false,Val).
+decode_VisibleString(Bytes,C) ->
+ decode_known_multiplier_string(Bytes,aligned,'VisibleString',C,false).
+
+encode_IA5String(C,Val) ->
+ encode_known_multiplier_string(aligned,'IA5String',C,false,Val).
+decode_IA5String(Bytes,C) ->
+ decode_known_multiplier_string(Bytes,aligned,'IA5String',C,false).
+
+encode_BMPString(C,Val) ->
+ encode_known_multiplier_string(aligned,'BMPString',C,false,Val).
+decode_BMPString(Bytes,C) ->
+ decode_known_multiplier_string(Bytes,aligned,'BMPString',C,false).
+
+encode_UniversalString(C,Val) ->
+ encode_known_multiplier_string(aligned,'UniversalString',C,false,Val).
+decode_UniversalString(Bytes,C) ->
+ decode_known_multiplier_string(Bytes,aligned,'UniversalString',C,false).
+
+%% end of known-multiplier strings for which PER visible constraints are
+%% applied
+
+encode_GeneralString(_C,Val) ->
+ encode_restricted_string(aligned,Val).
+decode_GeneralString(Bytes,_C) ->
+ decode_restricted_string(Bytes,aligned).
+
+encode_GraphicString(_C,Val) ->
+ encode_restricted_string(aligned,Val).
+decode_GraphicString(Bytes,_C) ->
+ decode_restricted_string(Bytes,aligned).
+
+encode_ObjectDescriptor(_C,Val) ->
+ encode_restricted_string(aligned,Val).
+decode_ObjectDescriptor(Bytes) ->
+ decode_restricted_string(Bytes,aligned).
+
+encode_TeletexString(_C,Val) -> % equivalent with T61String
+ encode_restricted_string(aligned,Val).
+decode_TeletexString(Bytes,_C) ->
+ decode_restricted_string(Bytes,aligned).
+
+encode_VideotexString(_C,Val) ->
+ encode_restricted_string(aligned,Val).
+decode_VideotexString(Bytes,_C) ->
+ decode_restricted_string(Bytes,aligned).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% getBMPChars(Bytes,Len) ->{BMPcharList,RemainingBytes}
+%%
+getBMPChars(Bytes,1) ->
+ {O1,Bytes2} = getbits(Bytes,8),
+ {O2,Bytes3} = getbits(Bytes2,8),
+ if
+ O1 == 0 ->
+ {[O2],Bytes3};
+ true ->
+ {[{0,0,O1,O2}],Bytes3}
+ end;
+getBMPChars(Bytes,Len) ->
+ getBMPChars(Bytes,Len,[]).
+
+getBMPChars(Bytes,0,Acc) ->
+ {lists:reverse(Acc),Bytes};
+getBMPChars(Bytes,Len,Acc) ->
+ {Octs,Bytes1} = getoctets_as_list(Bytes,2),
+ case Octs of
+ [0,O2] ->
+ getBMPChars(Bytes1,Len-1,[O2|Acc]);
+ [O1,O2]->
+ getBMPChars(Bytes1,Len-1,[{0,0,O1,O2}|Acc])
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% chars_encode(C,StringType,Value) -> ValueList
+%%
+%% encodes chars according to the per rules taking the constraint PermittedAlphabet
+%% into account.
+%% This function does only encode the value part and NOT the length
+
+chars_encode(C,StringType,Value) ->
+ case {StringType,get_constraint(C,'PermittedAlphabet')} of
+ {'UniversalString',{_,_Sv}} ->
+ exit({error,{asn1,{'not implemented',"UniversalString with PermittedAlphabet constraint"}}});
+ {'BMPString',{_,_Sv}} ->
+ exit({error,{asn1,{'not implemented',"BMPString with PermittedAlphabet constraint"}}});
+ _ ->
+ {NumBits,CharOutTab} = {get_NumBits(C,StringType),get_CharOutTab(C,StringType)},
+ chars_encode2(Value,NumBits,CharOutTab)
+ end.
+
+chars_encode2([H|T],NumBits,{Min,Max,notab}) when H =< Max, H >= Min ->
+ [{bits,NumBits,H-Min}|chars_encode2(T,NumBits,{Min,Max,notab})];
+chars_encode2([H|T],NumBits,{Min,Max,Tab}) when H =< Max, H >= Min ->
+ [{bits,NumBits,exit_if_false(H,element(H-Min+1,Tab))}|chars_encode2(T,NumBits,{Min,Max,Tab})];
+chars_encode2([{A,B,C,D}|T],NumBits,{Min,Max,notab}) ->
+ %% no value range check here (ought to be, but very expensive)
+% [{bits,NumBits,(A*B*C*D)-Min}|chars_encode2(T,NumBits,{Min,Max,notab})];
+ [{bits,NumBits,((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min}|chars_encode2(T,NumBits,{Min,Max,notab})];
+chars_encode2([{A,B,C,D}|T],NumBits,{Min,Max,Tab}) ->
+ %% no value range check here (ought to be, but very expensive)
+% [{bits,NumBits,element((A*B*C*D)-Min,Tab)}|chars_encode2(T,NumBits,{Min,Max,notab})];
+ [{bits,NumBits,exit_if_false({A,B,C,D},element(((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min,Tab))}|chars_encode2(T,NumBits,{Min,Max,notab})];
+chars_encode2([H|_T],_,{_,_,_}) ->
+ exit({error,{asn1,{illegal_char_value,H}}});
+chars_encode2([],_,_) ->
+ [].
+
+exit_if_false(V,false)->
+ exit({error,{asn1,{"illegal value according to Permitted alphabet constraint",V}}});
+exit_if_false(_,V) ->V.
+
+
+get_NumBits(C,StringType) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ {'SingleValue',Sv} ->
+ charbits(length(Sv),aligned);
+ no ->
+ case StringType of
+ 'IA5String' ->
+ charbits(128,aligned); % 16#00..16#7F
+ 'VisibleString' ->
+ charbits(95,aligned); % 16#20..16#7E
+ 'PrintableString' ->
+ charbits(74,aligned); % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+ 'NumericString' ->
+ charbits(11,aligned); % $ ,"0123456789"
+ 'UniversalString' ->
+ 32;
+ 'BMPString' ->
+ 16
+ end
+ end.
+
+%%Maybe used later
+%%get_MaxChar(C,StringType) ->
+%% case get_constraint(C,'PermittedAlphabet') of
+%% {'SingleValue',Sv} ->
+%% lists:nth(length(Sv),Sv);
+%% no ->
+%% case StringType of
+%% 'IA5String' ->
+%% 16#7F; % 16#00..16#7F
+%% 'VisibleString' ->
+%% 16#7E; % 16#20..16#7E
+%% 'PrintableString' ->
+%% $z; % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+%% 'NumericString' ->
+%% $9; % $ ,"0123456789"
+%% 'UniversalString' ->
+%% 16#ffffffff;
+%% 'BMPString' ->
+%% 16#ffff
+%% end
+%% end.
+
+%%Maybe used later
+%%get_MinChar(C,StringType) ->
+%% case get_constraint(C,'PermittedAlphabet') of
+%% {'SingleValue',Sv} ->
+%% hd(Sv);
+%% no ->
+%% case StringType of
+%% 'IA5String' ->
+%% 16#00; % 16#00..16#7F
+%% 'VisibleString' ->
+%% 16#20; % 16#20..16#7E
+%% 'PrintableString' ->
+%% $\s; % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+%% 'NumericString' ->
+%% $\s; % $ ,"0123456789"
+%% 'UniversalString' ->
+%% 16#00;
+%% 'BMPString' ->
+%% 16#00
+%% end
+%% end.
+
+get_CharOutTab(C,StringType) ->
+ get_CharTab(C,StringType,out).
+
+get_CharInTab(C,StringType) ->
+ get_CharTab(C,StringType,in).
+
+get_CharTab(C,StringType,InOut) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ {'SingleValue',Sv} ->
+ get_CharTab2(C,StringType,hd(Sv),lists:max(Sv),Sv,InOut);
+ no ->
+ case StringType of
+ 'IA5String' ->
+ {0,16#7F,notab};
+ 'VisibleString' ->
+ get_CharTab2(C,StringType,16#20,16#7F,notab,InOut);
+ 'PrintableString' ->
+ Chars = lists:sort(
+ " '()+,-./0123456789:=?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"),
+ get_CharTab2(C,StringType,hd(Chars),lists:max(Chars),Chars,InOut);
+ 'NumericString' ->
+ get_CharTab2(C,StringType,16#20,$9," 0123456789",InOut);
+ 'UniversalString' ->
+ {0,16#FFFFFFFF,notab};
+ 'BMPString' ->
+ {0,16#FFFF,notab}
+ end
+ end.
+
+get_CharTab2(C,StringType,Min,Max,Chars,InOut) ->
+ BitValMax = (1 bsl get_NumBits(C,StringType))-1,
+ if
+ Max =< BitValMax ->
+ {0,Max,notab};
+ true ->
+ case InOut of
+ out ->
+ {Min,Max,create_char_tab(Min,Chars)};
+ in ->
+ {Min,Max,list_to_tuple(Chars)}
+ end
+ end.
+
+create_char_tab(Min,L) ->
+ list_to_tuple(create_char_tab(Min,L,0)).
+create_char_tab(Min,[Min|T],V) ->
+ [V|create_char_tab(Min+1,T,V+1)];
+create_char_tab(_Min,[],_V) ->
+ [];
+create_char_tab(Min,L,V) ->
+ [false|create_char_tab(Min+1,L,V)].
+
+%% This very inefficient and should be moved to compiletime
+charbits(NumOfChars,aligned) ->
+ case charbits(NumOfChars) of
+ 1 -> 1;
+ 2 -> 2;
+ B when B =< 4 -> 4;
+ B when B =< 8 -> 8;
+ B when B =< 16 -> 16;
+ B when B =< 32 -> 32
+ end.
+
+charbits(NumOfChars) when NumOfChars =< 2 -> 1;
+charbits(NumOfChars) when NumOfChars =< 4 -> 2;
+charbits(NumOfChars) when NumOfChars =< 8 -> 3;
+charbits(NumOfChars) when NumOfChars =< 16 -> 4;
+charbits(NumOfChars) when NumOfChars =< 32 -> 5;
+charbits(NumOfChars) when NumOfChars =< 64 -> 6;
+charbits(NumOfChars) when NumOfChars =< 128 -> 7;
+charbits(NumOfChars) when NumOfChars =< 256 -> 8;
+charbits(NumOfChars) when NumOfChars =< 512 -> 9;
+charbits(NumOfChars) when NumOfChars =< 1024 -> 10;
+charbits(NumOfChars) when NumOfChars =< 2048 -> 11;
+charbits(NumOfChars) when NumOfChars =< 4096 -> 12;
+charbits(NumOfChars) when NumOfChars =< 8192 -> 13;
+charbits(NumOfChars) when NumOfChars =< 16384 -> 14;
+charbits(NumOfChars) when NumOfChars =< 32768 -> 15;
+charbits(NumOfChars) when NumOfChars =< 65536 -> 16;
+charbits(NumOfChars) when integer(NumOfChars) ->
+ 16 + charbits1(NumOfChars bsr 16).
+
+charbits1(0) ->
+ 0;
+charbits1(NumOfChars) ->
+ 1 + charbits1(NumOfChars bsr 1).
+
+
+chars_decode(Bytes,_,'BMPString',C,Len) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ no ->
+ getBMPChars(Bytes,Len);
+ _ ->
+ exit({error,{asn1,
+ {'not implemented',
+ "BMPString with PermittedAlphabet constraint"}}})
+ end;
+chars_decode(Bytes,NumBits,StringType,C,Len) ->
+ CharInTab = get_CharInTab(C,StringType),
+ chars_decode2(Bytes,CharInTab,NumBits,Len).
+
+
+chars_decode2(Bytes,CharInTab,NumBits,Len) ->
+ chars_decode2(Bytes,CharInTab,NumBits,Len,[]).
+
+chars_decode2(Bytes,_CharInTab,_NumBits,0,Acc) ->
+ {lists:reverse(Acc),Bytes};
+chars_decode2(Bytes,{Min,Max,notab},NumBits,Len,Acc) when NumBits > 8 ->
+ {Char,Bytes2} = getbits(Bytes,NumBits),
+ Result =
+ if
+ Char < 256 -> Char;
+ true ->
+ list_to_tuple(binary_to_list(<>))
+ end,
+ chars_decode2(Bytes2,{Min,Max,notab},NumBits,Len -1,[Result|Acc]);
+% chars_decode2(Bytes,{Min,Max,notab},NumBits,Len,Acc) when NumBits > 8 ->
+% {Char,Bytes2} = getbits(Bytes,NumBits),
+% Result = case minimum_octets(Char+Min) of
+% [NewChar] -> NewChar;
+% [C1,C2] -> {0,0,C1,C2};
+% [C1,C2,C3] -> {0,C1,C2,C3};
+% [C1,C2,C3,C4] -> {C1,C2,C3,C4}
+% end,
+% chars_decode2(Bytes2,{Min,Max,notab},NumBits,Len -1,[Result|Acc]);
+chars_decode2(Bytes,{Min,Max,notab},NumBits,Len,Acc) ->
+ {Char,Bytes2} = getbits(Bytes,NumBits),
+ chars_decode2(Bytes2,{Min,Max,notab},NumBits,Len -1,[Char+Min|Acc]);
+
+%% BMPString and UniversalString with PermittedAlphabet is currently not supported
+chars_decode2(Bytes,{Min,Max,CharInTab},NumBits,Len,Acc) ->
+ {Char,Bytes2} = getbits(Bytes,NumBits),
+ chars_decode2(Bytes2,{Min,Max,CharInTab},NumBits,Len -1,[element(Char+1,CharInTab)|Acc]).
+
+
+ % X.691:17
+encode_null(_) -> []; % encodes to nothing
+encode_null({Name,Val}) when atom(Name) ->
+ encode_null(Val).
+
+decode_null(Bytes) ->
+ {'NULL',Bytes}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_object_identifier(Val) -> CompleteList
+%% encode_object_identifier({Name,Val}) -> CompleteList
+%% Val -> {Int1,Int2,...,IntN} % N >= 2
+%% Name -> atom()
+%% Int1 -> integer(0..2)
+%% Int2 -> integer(0..39) when Int1 (0..1) else integer()
+%% Int3-N -> integer()
+%% CompleteList -> [{bits,8,Val}|{octets,Ol}|align|...]
+%%
+encode_object_identifier({Name,Val}) when atom(Name) ->
+ encode_object_identifier(Val);
+encode_object_identifier(Val) ->
+ OctetList = e_object_identifier(Val),
+ Octets = list_to_binary(OctetList), % performs a flatten at the same time
+ [{debug,object_identifier},encode_length(undefined,size(Octets)),{octets,Octets}].
+
+%% This code is copied from asn1_encode.erl (BER) and corrected and modified
+
+e_object_identifier({'OBJECT IDENTIFIER',V}) ->
+ e_object_identifier(V);
+e_object_identifier({Cname,V}) when atom(Cname),tuple(V) ->
+ e_object_identifier(tuple_to_list(V));
+e_object_identifier({Cname,V}) when atom(Cname),list(V) ->
+ e_object_identifier(V);
+e_object_identifier(V) when tuple(V) ->
+ e_object_identifier(tuple_to_list(V));
+
+%% E1 = 0|1|2 and (E2 < 40 when E1 = 0|1)
+e_object_identifier([E1,E2|Tail]) when E1 >= 0, E1 < 2, E2 < 40 ; E1==2 ->
+ Head = 40*E1 + E2, % weird
+ e_object_elements([Head|Tail],[]);
+e_object_identifier(Oid=[_,_|_Tail]) ->
+ exit({error,{asn1,{'illegal_value',Oid}}}).
+
+e_object_elements([],Acc) ->
+ lists:reverse(Acc);
+e_object_elements([H|T],Acc) ->
+ e_object_elements(T,[e_object_element(H)|Acc]).
+
+e_object_element(Num) when Num < 128 ->
+ Num;
+%% must be changed to handle more than 2 octets
+e_object_element(Num) -> %% when Num < ???
+ Left = ((Num band 2#11111110000000) bsr 7) bor 2#10000000,
+ Right = Num band 2#1111111 ,
+ [Left,Right].
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_object_identifier(Bytes) -> {ObjId,RemainingBytes}
+%% ObjId -> {integer(),integer(),...} % at least 2 integers
+%% RemainingBytes -> [integer()] when integer() (0..255)
+decode_object_identifier(Bytes) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ [First|Rest] = dec_subidentifiers(Octs,0,[]),
+ Idlist = if
+ First < 40 ->
+ [0,First|Rest];
+ First < 80 ->
+ [1,First - 40|Rest];
+ true ->
+ [2,First - 80|Rest]
+ end,
+ {list_to_tuple(Idlist),Bytes3}.
+
+dec_subidentifiers([H|T],Av,Al) when H >=16#80 ->
+ dec_subidentifiers(T,(Av bsl 7) + (H band 16#7F),Al);
+dec_subidentifiers([H|T],Av,Al) ->
+ dec_subidentifiers(T,0,[(Av bsl 7) + H |Al]);
+dec_subidentifiers([],_Av,Al) ->
+ lists:reverse(Al).
+
+get_constraint([{Key,V}],Key) ->
+ V;
+get_constraint([],_Key) ->
+ no;
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% complete(InList) -> ByteList
+%% Takes a coded list with bits and bytes and converts it to a list of bytes
+%% Should be applied as the last step at encode of a complete ASN.1 type
+%%
+
+% complete(L) ->
+% case complete1(L) of
+% {[],0} ->
+% <<0>>;
+% {Acc,0} ->
+% lists:reverse(Acc);
+% {[Hacc|Tacc],Acclen} -> % Acclen >0
+% Rest = 8 - Acclen,
+% NewHacc = Hacc bsl Rest,
+% lists:reverse([NewHacc|Tacc])
+% end.
+
+
+% complete1(InList) when list(InList) ->
+% complete1(InList,[]);
+% complete1(InList) ->
+% complete1([InList],[]).
+
+% complete1([{debug,_}|T], Acc) ->
+% complete1(T,Acc);
+% complete1([H|T],Acc) when list(H) ->
+% {NewH,NewAcclen} = complete1(H,Acc),
+% complete1(T,NewH,NewAcclen);
+
+% complete1([{0,Bin}|T],Acc,0) when binary(Bin) ->
+% complete1(T,[Bin|Acc],0);
+% complete1([{Unused,Bin}|T],Acc,0) when integer(Unused),binary(Bin) ->
+% Size = size(Bin)-1,
+% <> = Bin,
+% complete1(T,[(B bsr Unused),Bs|Acc],8-Unused);
+% complete1([{Unused,Bin}|T],[Hacc|Tacc],Acclen) when integer(Unused),binary(Bin) ->
+% Rest = 8 - Acclen,
+% Used = 8 - Unused,
+% case size(Bin) of
+% 1 ->
+% if
+% Rest >= Used ->
+% <> = Bin,
+% complete1(T,[(Hacc bsl Used) + B|Tacc],
+% (Acclen+Used) rem 8);
+% true ->
+% LeftOver = 8 - Rest - Unused,
+% <> = Bin,
+% complete1(T,[Val1,(Hacc bsl Rest) + Val2|Tacc],
+% (Acclen+Used) rem 8)
+% end;
+% N ->
+% if
+% Rest == Used ->
+% N1 = N - 1,
+% <> = Bin,
+% complete1(T,[Bs,(Hacc bsl Rest) + B|Tacc],0);
+% Rest > Used ->
+% N1 = N - 2,
+% N2 = (8 - Rest) + Used,
+% <> = Bin,
+% complete1(T,[B2,Bytes,(Hacc bsl Rest) + B1|Tacc],
+% (Acclen + Used) rem 8);
+% true -> % Rest < Used
+% N1 = N - 1,
+% N2 = Used - Rest,
+% <> = Bin,
+% complete1(T,[B2,Bytes,(Hacc bsl Rest) + B1|Tacc],
+% (Acclen + Used) rem 8)
+% end
+% end;
+
+% %complete1([{octets,N,Val}|T],Acc,Acclen) when N =< 4 ,integer(Val) ->
+% % complete1([{octets,<>}|T],Acc,Acclen);
+% complete1([{octets,N,Val}|T],Acc,Acclen) when N =< 4 ,integer(Val) ->
+% Newval = case N of
+% 1 ->
+% Val4 = Val band 16#FF,
+% [Val4];
+% 2 ->
+% Val3 = (Val bsr 8) band 16#FF,
+% Val4 = Val band 16#FF,
+% [Val3,Val4];
+% 3 ->
+% Val2 = (Val bsr 16) band 16#FF,
+% Val3 = (Val bsr 8) band 16#FF,
+% Val4 = Val band 16#FF,
+% [Val2,Val3,Val4];
+% 4 ->
+% Val1 = (Val bsr 24) band 16#FF,
+% Val2 = (Val bsr 16) band 16#FF,
+% Val3 = (Val bsr 8) band 16#FF,
+% Val4 = Val band 16#FF,
+% [Val1,Val2,Val3,Val4]
+% end,
+% complete1([{octets,Newval}|T],Acc,Acclen);
+
+% complete1([{octets,Bin}|T],Acc,Acclen) when binary(Bin) ->
+% Rest = 8 - Acclen,
+% if
+% Rest == 8 ->
+% complete1(T,[Bin|Acc],0);
+% true ->
+% [Hacc|Tacc]=Acc,
+% complete1(T,[Bin, Hacc bsl Rest|Tacc],0)
+% end;
+
+% complete1([{octets,Oct}|T],Acc,Acclen) when list(Oct) ->
+% Rest = 8 - Acclen,
+% if
+% Rest == 8 ->
+% complete1(T,[list_to_binary(Oct)|Acc],0);
+% true ->
+% [Hacc|Tacc]=Acc,
+% complete1(T,[list_to_binary(Oct), Hacc bsl Rest|Tacc],0)
+% end;
+
+% complete1([{bit,Val}|T], Acc, Acclen) ->
+% complete1([{bits,1,Val}|T],Acc,Acclen);
+% complete1([{octet,Val}|T], Acc, Acclen) ->
+% complete1([{octets,1,Val}|T],Acc,Acclen);
+
+% complete1([{bits,N,Val}|T], Acc, 0) when N =< 8 ->
+% complete1(T,[Val|Acc],N);
+% complete1([{bits,N,Val}|T], [Hacc|Tacc], Acclen) when N =< 8 ->
+% Rest = 8 - Acclen,
+% if
+% Rest >= N ->
+% complete1(T,[(Hacc bsl N) + Val|Tacc],(Acclen+N) rem 8);
+% true ->
+% Diff = N - Rest,
+% NewHacc = (Hacc bsl Rest) + (Val bsr Diff),
+% Mask = element(Diff,{1,3,7,15,31,63,127,255}),
+% complete1(T,[(Val band Mask),NewHacc|Tacc],(Acclen+N) rem 8)
+% end;
+% complete1([{bits,N,Val}|T], Acc, Acclen) -> % N > 8
+% complete1([{bits,N-8,Val bsr 8},{bits,8,Val band 255}|T],Acc,Acclen);
+
+% complete1([align|T],Acc,0) ->
+% complete1(T,Acc,0);
+% complete1([align|T],[Hacc|Tacc],Acclen) ->
+% Rest = 8 - Acclen,
+% complete1(T,[Hacc bsl Rest|Tacc],0);
+% complete1([{octets,N,Val}|T],Acc,Acclen) when list(Val) -> % no security check here
+% complete1([{octets,Val}|T],Acc,Acclen);
+
+% complete1([],Acc,Acclen) ->
+% {Acc,Acclen}.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% complete(InList) -> ByteList
+%% Takes a coded list with bits and bytes and converts it to a list of bytes
+%% Should be applied as the last step at encode of a complete ASN.1 type
+%%
+
+complete(L) ->
+ case complete1(L) of
+ {[],[]} ->
+ <<0>>;
+ {Acc,[]} ->
+ Acc;
+ {Acc,Bacc} ->
+ [Acc|complete_bytes(Bacc)]
+ end.
+
+%% this function builds the ugly form of lists [E1|E2] to avoid having to reverse it at the end.
+%% this is done because it is efficient and that the result always will be sent on a port or
+%% converted by means of list_to_binary/1
+complete1(InList) when list(InList) ->
+ complete1(InList,[],[]);
+complete1(InList) ->
+ complete1([InList],[],[]).
+
+complete1([],Acc,Bacc) ->
+ {Acc,Bacc};
+complete1([H|T],Acc,Bacc) when list(H) ->
+ {NewH,NewBacc} = complete1(H,Acc,Bacc),
+ complete1(T,NewH,NewBacc);
+
+complete1([{octets,Bin}|T],Acc,[]) ->
+ complete1(T,[Acc|Bin],[]);
+
+complete1([{octets,Bin}|T],Acc,Bacc) ->
+ complete1(T,[Acc|[complete_bytes(Bacc),Bin]],[]);
+
+complete1([{debug,_}|T], Acc,Bacc) ->
+ complete1(T,Acc,Bacc);
+
+complete1([{bits,N,Val}|T],Acc,Bacc) ->
+ complete1(T,Acc,complete_update_byte(Bacc,Val,N));
+
+complete1([{bit,Val}|T],Acc,Bacc) ->
+ complete1(T,Acc,complete_update_byte(Bacc,Val,1));
+
+complete1([align|T],Acc,[]) ->
+ complete1(T,Acc,[]);
+complete1([align|T],Acc,Bacc) ->
+ complete1(T,[Acc|complete_bytes(Bacc)],[]);
+complete1([{0,Bin}|T],Acc,[]) when binary(Bin) ->
+ complete1(T,[Acc|Bin],[]);
+complete1([{Unused,Bin}|T],Acc,[]) when integer(Unused),binary(Bin) ->
+ Size = size(Bin)-1,
+ <> = Bin,
+ NumBits = 8-Unused,
+ complete1(T,[Acc|Bs],[[B bsr Unused]|NumBits]);
+complete1([{Unused,Bin}|T],Acc,Bacc) when integer(Unused),binary(Bin) ->
+ Size = size(Bin)-1,
+ <> = Bin,
+ NumBits = 8 - Unused,
+ Bf = complete_bytes(Bacc),
+ complete1(T,[Acc|[Bf,Bs]],[[B bsr Unused]|NumBits]).
+
+
+complete_update_byte([],Val,Len) ->
+ complete_update_byte([[0]|0],Val,Len);
+complete_update_byte([[Byte|Bacc]|NumBits],Val,Len) when NumBits + Len == 8 ->
+ [[0,((Byte bsl Len) + Val) band 255|Bacc]|0];
+complete_update_byte([[Byte|Bacc]|NumBits],Val,Len) when NumBits + Len > 8 ->
+ Rem = 8 - NumBits,
+ Rest = Len - Rem,
+ complete_update_byte([[0,((Byte bsl Rem) + (Val bsr Rest)) band 255 |Bacc]|0],Val,Rest);
+complete_update_byte([[Byte|Bacc]|NumBits],Val,Len) ->
+ [[((Byte bsl Len) + Val) band 255|Bacc]|NumBits+Len].
+
+
+complete_bytes([[_Byte|Bacc]|0]) ->
+ lists:reverse(Bacc);
+complete_bytes([[Byte|Bacc]|NumBytes]) ->
+ lists:reverse([(Byte bsl (8-NumBytes)) band 255|Bacc]);
+complete_bytes([]) ->
+ [].
+
+% complete_bytes(L) ->
+% complete_bytes1(lists:reverse(L),[],[],0,0).
+
+% complete_bytes1([H={V,B}|T],Acc,ReplyAcc,NumBits,NumFields) when ((NumBits+B) rem 8) == 0 ->
+% NewReplyAcc = [complete_bytes2([H|Acc],0)|ReplyAcc],
+% complete_bytes1(T,[],NewReplyAcc,0,0);
+% complete_bytes1([H={V,B}|T],Acc,ReplyAcc,NumBits,NumFields) when NumFields == 7; (NumBits+B) div 8 > 0 ->
+% Rem = (NumBits+B) rem 8,
+% NewReplyAcc = [complete_bytes2([{V bsr Rem,B - Rem}|Acc],0)|ReplyAcc],
+% complete_bytes1([{V,Rem}|T],[],NewReplyAcc,0,0);
+% complete_bytes1([H={V,B}|T],Acc,ReplyAcc,NumBits,NumFields) ->
+% complete_bytes1(T,[H|Acc],ReplyAcc,NumBits+B,NumFields+1);
+% complete_bytes1([],[],ReplyAcc,_,_) ->
+% lists:reverse(ReplyAcc);
+% complete_bytes1([],Acc,ReplyAcc,NumBits,_) ->
+% PadBits = case NumBits rem 8 of
+% 0 -> 0;
+% Rem -> 8 - Rem
+% end,
+% lists:reverse([complete_bytes2(Acc,PadBits)|ReplyAcc]).
+
+
+% complete_bytes2([{V1,B1}],PadBits) ->
+% <>;
+% complete_bytes2([{V2,B2},{V1,B1}],PadBits) ->
+% <>;
+% complete_bytes2([{V3,B3},{V2,B2},{V1,B1}],PadBits) ->
+% <>;
+% complete_bytes2([{V4,B4},{V3,B3},{V2,B2},{V1,B1}],PadBits) ->
+% <>;
+% complete_bytes2([{V5,B5},{V4,B4},{V3,B3},{V2,B2},{V1,B1}],PadBits) ->
+% <>;
+% complete_bytes2([{V6,B6},{V5,B5},{V4,B4},{V3,B3},{V2,B2},{V1,B1}],PadBits) ->
+% <>;
+% complete_bytes2([{V7,B7},{V6,B6},{V5,B5},{V4,B4},{V3,B3},{V2,B2},{V1,B1}],PadBits) ->
+% <>;
+% complete_bytes2([{V8,B8},{V7,B7},{V6,B6},{V5,B5},{V4,B4},{V3,B3},{V2,B2},{V1,B1}],PadBits) ->
+% <>.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per_bin_rt2ct.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per_bin_rt2ct.erl
new file mode 100644
index 0000000000..9f02ad4466
--- /dev/null
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per_bin_rt2ct.erl
@@ -0,0 +1,2102 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1rt_per_bin_rt2ct.erl,v 1.1 2008/12/17 09:53:31 mikpe Exp $
+%%
+-module(asn1rt_per_bin_rt2ct).
+
+%% encoding / decoding of PER aligned
+
+-include("asn1_records.hrl").
+
+-export([dec_fixup/3, cindex/3, list_to_record/2]).
+-export([setchoiceext/1, setext/1, fixoptionals/3, fixextensions/2,
+ getext/1, getextension/2, skipextensions/3, getbit/1, getchoice/3 ]).
+-export([getoptionals/2, getoptionals2/2,
+ set_choice/3, encode_integer/2, encode_integer/3 ]).
+-export([decode_integer/2, decode_integer/3, encode_small_number/1,
+ decode_boolean/1, encode_length/2, decode_length/1, decode_length/2,
+ encode_small_length/1, decode_small_length/1,
+ decode_compact_bit_string/3]).
+-export([decode_enumerated/3,
+ encode_bit_string/3, decode_bit_string/3 ]).
+-export([encode_octet_string/2, decode_octet_string/2,
+ encode_null/1, decode_null/1,
+ encode_object_identifier/1, decode_object_identifier/1,
+ complete/1]).
+
+
+-export([encode_open_type/2, decode_open_type/2]).
+
+-export([%encode_UniversalString/2, decode_UniversalString/2,
+ %encode_PrintableString/2, decode_PrintableString/2,
+ encode_GeneralString/2, decode_GeneralString/2,
+ encode_GraphicString/2, decode_GraphicString/2,
+ encode_TeletexString/2, decode_TeletexString/2,
+ encode_VideotexString/2, decode_VideotexString/2,
+ %encode_VisibleString/2, decode_VisibleString/2,
+ %encode_BMPString/2, decode_BMPString/2,
+ %encode_IA5String/2, decode_IA5String/2,
+ %encode_NumericString/2, decode_NumericString/2,
+ encode_ObjectDescriptor/2, decode_ObjectDescriptor/1
+ ]).
+
+-export([decode_constrained_number/2,
+ decode_constrained_number/3,
+ decode_unconstrained_number/1,
+ decode_semi_constrained_number/2,
+ encode_unconstrained_number/1,
+ decode_constrained_number/4,
+ encode_octet_string/3,
+ decode_octet_string/3,
+ encode_known_multiplier_string/5,
+ decode_known_multiplier_string/5,
+ getoctets/2, getbits/2
+% start_drv/1,start_drv2/1,init_drv/1
+ ]).
+
+
+-export([eint_positive/1]).
+-export([pre_complete_bits/2]).
+
+-define('16K',16384).
+-define('32K',32768).
+-define('64K',65536).
+
+%%-define(nodriver,true).
+
+dec_fixup(Terms,Cnames,RemBytes) ->
+ dec_fixup(Terms,Cnames,RemBytes,[]).
+
+dec_fixup([novalue|T],[_Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,Acc);
+dec_fixup([{_Name,novalue}|T],[_Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,Acc);
+dec_fixup([H|T],[Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,[{Hc,H}|Acc]);
+dec_fixup([],_Cnames,RemBytes,Acc) ->
+ {lists:reverse(Acc),RemBytes}.
+
+cindex(Ix,Val,Cname) ->
+ case element(Ix,Val) of
+ {Cname,Val2} -> Val2;
+ X -> X
+ end.
+
+%% converts a list to a record if necessary
+list_to_record(_,Tuple) when tuple(Tuple) ->
+ Tuple;
+list_to_record(Name,List) when list(List) ->
+ list_to_tuple([Name|List]).
+
+%%--------------------------------------------------------
+%% setchoiceext(InRootSet) -> [{bit,X}]
+%% X is set to 1 when InRootSet==false
+%% X is set to 0 when InRootSet==true
+%%
+setchoiceext(true) ->
+% [{debug,choiceext},{bits,1,0}];
+ [0];
+setchoiceext(false) ->
+% [{debug,choiceext},{bits,1,1}].
+ [1].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% setext(true|false) -> CompleteList
+%%
+
+setext(false) ->
+% [{debug,ext},{bits,1,0}];
+ [0];
+setext(true) ->
+% [{debug,ext},{bits,1,1}];
+ [1].
+
+fixoptionals(OptList,_OptLength,Val) when tuple(Val) ->
+% Bits = fixoptionals(OptList,Val,0),
+% {Val,{bits,OptLength,Bits}};
+% {Val,[10,OptLength,Bits]};
+ {Val,fixoptionals(OptList,Val,[])};
+
+fixoptionals([],_,Acc) ->
+ %% Optbits
+ lists:reverse(Acc);
+fixoptionals([Pos|Ot],Val,Acc) ->
+ case element(Pos,Val) of
+% asn1_NOVALUE -> fixoptionals(Ot,Val,Acc bsl 1);
+% asn1_DEFAULT -> fixoptionals(Ot,Val,Acc bsl 1);
+% _ -> fixoptionals(Ot,Val,(Acc bsl 1) + 1)
+ asn1_NOVALUE -> fixoptionals(Ot,Val,[0|Acc]);
+ asn1_DEFAULT -> fixoptionals(Ot,Val,[0|Acc]);
+ _ -> fixoptionals(Ot,Val,[1|Acc])
+ end.
+
+
+getext(Bytes) when tuple(Bytes) ->
+ getbit(Bytes);
+getext(Bytes) when binary(Bytes) ->
+ getbit({0,Bytes});
+getext(Bytes) when list(Bytes) ->
+ getbit({0,Bytes}).
+
+getextension(0, Bytes) ->
+ {{},Bytes};
+getextension(1, Bytes) ->
+ {Len,Bytes2} = decode_small_length(Bytes),
+ {Blist, Bytes3} = getbits_as_list(Len,Bytes2),
+ {list_to_tuple(Blist),Bytes3}.
+
+fixextensions({ext,ExtPos,ExtNum},Val) ->
+ case fixextensions(ExtPos,ExtNum+ExtPos,Val,0) of
+ 0 -> [];
+ ExtBits ->
+% [encode_small_length(ExtNum),{bits,ExtNum,ExtBits}]
+% [encode_small_length(ExtNum),[10,ExtNum,ExtBits]]
+ [encode_small_length(ExtNum),pre_complete_bits(ExtNum,ExtBits)]
+ end.
+
+fixextensions(Pos,MaxPos,_,Acc) when Pos >= MaxPos ->
+ Acc;
+fixextensions(Pos,ExtPos,Val,Acc) ->
+ Bit = case catch(element(Pos+1,Val)) of
+ asn1_NOVALUE ->
+ 0;
+ asn1_NOEXTVALUE ->
+ 0;
+ {'EXIT',_} ->
+ 0;
+ _ ->
+ 1
+ end,
+ fixextensions(Pos+1,ExtPos,Val,(Acc bsl 1)+Bit).
+
+skipextensions(Bytes,Nr,ExtensionBitPattern) ->
+ case (catch element(Nr,ExtensionBitPattern)) of
+ 1 ->
+ {_,Bytes2} = decode_open_type(Bytes,[]),
+ skipextensions(Bytes2, Nr+1, ExtensionBitPattern);
+ 0 ->
+ skipextensions(Bytes, Nr+1, ExtensionBitPattern);
+ {'EXIT',_} -> % badarg, no more extensions
+ Bytes
+ end.
+
+
+getchoice(Bytes,1,0) -> % only 1 alternative is not encoded
+ {0,Bytes};
+getchoice(Bytes,_,1) ->
+ decode_small_number(Bytes);
+getchoice(Bytes,NumChoices,0) ->
+ decode_constrained_number(Bytes,{0,NumChoices-1}).
+
+%% old version kept for backward compatibility with generates from R7B01
+getoptionals(Bytes,NumOpt) ->
+ {Blist,Bytes1} = getbits_as_list(NumOpt,Bytes),
+ {list_to_tuple(Blist),Bytes1}.
+
+%% new version used in generates from r8b_patch/3 and later
+getoptionals2(Bytes,NumOpt) ->
+ {_,_} = getbits(Bytes,NumOpt).
+
+
+%% getbits_as_binary(Num,Bytes) -> {{Unused,BinBits},RestBytes},
+%% Num = integer(),
+%% Bytes = list() | tuple(),
+%% Unused = integer(),
+%% BinBits = binary(),
+%% RestBytes = tuple()
+getbits_as_binary(Num,Bytes) when binary(Bytes) ->
+ getbits_as_binary(Num,{0,Bytes});
+getbits_as_binary(0,Buffer) ->
+ {{0,<<>>},Buffer};
+getbits_as_binary(Num,{0,Bin}) when Num > 16 ->
+ Used = Num rem 8,
+ Pad = (8 - Used) rem 8,
+%% Nbytes = Num div 8,
+ <> = Bin,
+ {{Pad,<>},RestBin};
+getbits_as_binary(Num,Buffer={_Used,_Bin}) -> % Unaligned buffer
+ %% Num =< 16,
+ {Bits2,Buffer2} = getbits(Buffer,Num),
+ Pad = (8 - (Num rem 8)) rem 8,
+ {{Pad,<>},Buffer2}.
+
+
+% integer_from_list(Int,[],BigInt) ->
+% BigInt;
+% integer_from_list(Int,[H|T],BigInt) when Int < 8 ->
+% (BigInt bsl Int) bor (H bsr (8-Int));
+% integer_from_list(Int,[H|T],BigInt) ->
+% integer_from_list(Int-8,T,(BigInt bsl 8) bor H).
+
+getbits_as_list(Num,Bytes) when binary(Bytes) ->
+ getbits_as_list(Num,{0,Bytes},[]);
+getbits_as_list(Num,Bytes) ->
+ getbits_as_list(Num,Bytes,[]).
+
+%% If buffer is empty and nothing more will be picked.
+getbits_as_list(0, B, Acc) ->
+ {lists:reverse(Acc),B};
+%% If first byte in buffer is full and at least one byte will be picked,
+%% then pick one byte.
+getbits_as_list(N,{0,Bin},Acc) when N >= 8 ->
+ <> = Bin,
+ getbits_as_list(N-8,{0,Rest},[B0,B1,B2,B3,B4,B5,B6,B7|Acc]);
+getbits_as_list(N,{Used,Bin},Acc) when N >= 4, Used =< 4 ->
+ NewUsed = Used + 4,
+ Rem = 8 - NewUsed,
+ <<_:Used,B3:1,B2:1,B1:1,B0:1,_:Rem, Rest/binary>> = Bin,
+ NewRest = case Rem of 0 -> Rest; _ -> Bin end,
+ getbits_as_list(N-4,{NewUsed rem 8,NewRest},[B0,B1,B2,B3|Acc]);
+getbits_as_list(N,{Used,Bin},Acc) when N >= 2, Used =< 6 ->
+ NewUsed = Used + 2,
+ Rem = 8 - NewUsed,
+ <<_:Used,B1:1,B0:1,_:Rem, Rest/binary>> = Bin,
+ NewRest = case Rem of 0 -> Rest; _ -> Bin end,
+ getbits_as_list(N-2,{NewUsed rem 8,NewRest},[B0,B1|Acc]);
+getbits_as_list(N,{Used,Bin},Acc) when Used =< 7 ->
+ NewUsed = Used + 1,
+ Rem = 8 - NewUsed,
+ <<_:Used,B0:1,_:Rem, Rest/binary>> = Bin,
+ NewRest = case Rem of 0 -> Rest; _ -> Bin end,
+ getbits_as_list(N-1,{NewUsed rem 8,NewRest},[B0|Acc]).
+
+
+getbit({7,<<_:7,B:1,Rest/binary>>}) ->
+ {B,{0,Rest}};
+getbit({0,Buffer = <>}) ->
+ {B,{1,Buffer}};
+getbit({Used,Buffer}) ->
+ Unused = (8 - Used) - 1,
+ <<_:Used,B:1,_:Unused,_/binary>> = Buffer,
+ {B,{Used+1,Buffer}};
+getbit(Buffer) when binary(Buffer) ->
+ getbit({0,Buffer}).
+
+
+getbits({0,Buffer},Num) when (Num rem 8) == 0 ->
+ <> = Buffer,
+ {Bits,{0,Rest}};
+getbits({Used,Bin},Num) ->
+ NumPlusUsed = Num + Used,
+ NewUsed = NumPlusUsed rem 8,
+ Unused = (8-NewUsed) rem 8,
+ case Unused of
+ 0 ->
+ <<_:Used,Bits:Num,Rest/binary>> = Bin,
+ {Bits,{0,Rest}};
+ _ ->
+ Bytes = NumPlusUsed div 8,
+ <<_:Used,Bits:Num,_:Unused,_/binary>> = Bin,
+ <<_:Bytes/binary,Rest/binary>> = Bin,
+ {Bits,{NewUsed,Rest}}
+ end;
+getbits(Bin,Num) when binary(Bin) ->
+ getbits({0,Bin},Num).
+
+
+
+% getoctet(Bytes) when list(Bytes) ->
+% getoctet({0,Bytes});
+% getoctet(Bytes) ->
+% %% io:format("getoctet:Buffer = ~p~n",[Bytes]),
+% getoctet1(Bytes).
+
+% getoctet1({0,[H|T]}) ->
+% {H,{0,T}};
+% getoctet1({Pos,[_,H|T]}) ->
+% {H,{0,T}}.
+
+align({0,L}) ->
+ {0,L};
+align({_Pos,<<_H,T/binary>>}) ->
+ {0,T};
+align(Bytes) ->
+ {0,Bytes}.
+
+%% First align buffer, then pick the first Num octets.
+%% Returns octets as an integer with bit significance as in buffer.
+getoctets({0,Buffer},Num) ->
+ <> = Buffer,
+ {Val,{0,RestBin}};
+getoctets({U,<<_Padding,Rest/binary>>},Num) when U /= 0 ->
+ getoctets({0,Rest},Num);
+getoctets(Buffer,Num) when binary(Buffer) ->
+ getoctets({0,Buffer},Num).
+% getoctets(Buffer,Num) ->
+% %% io:format("getoctets:Buffer = ~p~nNum = ~p~n",[Buffer,Num]),
+% getoctets(Buffer,Num,0).
+
+% getoctets(Buffer,0,Acc) ->
+% {Acc,Buffer};
+% getoctets(Buffer,Num,Acc) ->
+% {Oct,NewBuffer} = getoctet(Buffer),
+% getoctets(NewBuffer,Num-1,(Acc bsl 8)+Oct).
+
+% getoctets_as_list(Buffer,Num) ->
+% getoctets_as_list(Buffer,Num,[]).
+
+% getoctets_as_list(Buffer,0,Acc) ->
+% {lists:reverse(Acc),Buffer};
+% getoctets_as_list(Buffer,Num,Acc) ->
+% {Oct,NewBuffer} = getoctet(Buffer),
+% getoctets_as_list(NewBuffer,Num-1,[Oct|Acc]).
+
+%% First align buffer, then pick the first Num octets.
+%% Returns octets as a binary
+getoctets_as_bin({0,Bin},Num)->
+ <> = Bin,
+ {Octets,{0,RestBin}};
+getoctets_as_bin({_U,Bin},Num) ->
+ <<_Padding,Octets:Num/binary,RestBin/binary>> = Bin,
+ {Octets,{0,RestBin}};
+getoctets_as_bin(Bin,Num) when binary(Bin) ->
+ getoctets_as_bin({0,Bin},Num).
+
+%% same as above but returns octets as a List
+getoctets_as_list(Buffer,Num) ->
+ {Bin,Buffer2} = getoctets_as_bin(Buffer,Num),
+ {binary_to_list(Bin),Buffer2}.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% set_choice(Alt,Choices,Altnum) -> ListofBitSettings
+%% Alt = atom()
+%% Altnum = integer() | {integer(),integer()}% number of alternatives
+%% Choices = [atom()] | {[atom()],[atom()]}
+%% When Choices is a tuple the first list is the Rootset and the
+%% second is the Extensions and then Altnum must also be a tuple with the
+%% lengths of the 2 lists
+%%
+set_choice(Alt,{L1,L2},{Len1,_Len2}) ->
+ case set_choice_tag(Alt,L1) of
+ N when integer(N), Len1 > 1 ->
+% [{bits,1,0}, % the value is in the root set
+% encode_constrained_number({0,Len1-1},N)];
+ [0, % the value is in the root set
+ encode_constrained_number({0,Len1-1},N)];
+ N when integer(N) ->
+% [{bits,1,0}]; % no encoding if only 0 or 1 alternative
+ [0]; % no encoding if only 0 or 1 alternative
+ false ->
+% [{bits,1,1}, % extension value
+ [1, % extension value
+ case set_choice_tag(Alt,L2) of
+ N2 when integer(N2) ->
+ encode_small_number(N2);
+ false ->
+ unknown_choice_alt
+ end]
+ end;
+set_choice(Alt,L,Len) ->
+ case set_choice_tag(Alt,L) of
+ N when integer(N), Len > 1 ->
+ encode_constrained_number({0,Len-1},N);
+ N when integer(N) ->
+ []; % no encoding if only 0 or 1 alternative
+ false ->
+ [unknown_choice_alt]
+ end.
+
+set_choice_tag(Alt,Choices) ->
+ set_choice_tag(Alt,Choices,0).
+
+set_choice_tag(Alt,[Alt|_Rest],Tag) ->
+ Tag;
+set_choice_tag(Alt,[_H|Rest],Tag) ->
+ set_choice_tag(Alt,Rest,Tag+1);
+set_choice_tag(_Alt,[],_Tag) ->
+ false.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_fragmented_XXX; decode of values encoded fragmented according
+%% to ITU-T X.691 clause 10.9.3.8. The unit (XXX) is either bits, octets,
+%% characters or number of components (in a choice,sequence or similar).
+%% Buffer is a buffer {Used, Bin}.
+%% C is the constrained length.
+%% If the buffer is not aligned, this function does that.
+decode_fragmented_bits({0,Buffer},C) ->
+ decode_fragmented_bits(Buffer,C,[]);
+decode_fragmented_bits({_N,<<_B,Bs/binary>>},C) ->
+ decode_fragmented_bits(Bs,C,[]).
+
+decode_fragmented_bits(<<3:2,Len:6,Bin/binary>>,C,Acc) ->
+ {Value,Bin2} = split_binary(Bin, Len * ?'16K'),
+ decode_fragmented_bits(Bin2,C,[Value,Acc]);
+decode_fragmented_bits(<<0:1,0:7,Bin/binary>>,C,Acc) ->
+ BinBits = list_to_binary(lists:reverse(Acc)),
+ case C of
+ Int when integer(Int),C == size(BinBits) ->
+ {BinBits,{0,Bin}};
+ Int when integer(Int) ->
+ exit({error,{asn1,{illegal_value,C,BinBits}}});
+ _ ->
+ {BinBits,{0,Bin}}
+ end;
+decode_fragmented_bits(<<0:1,Len:7,Bin/binary>>,C,Acc) ->
+ Result = {BinBits,{Used,_Rest}} =
+ case (Len rem 8) of
+ 0 ->
+ <> = Bin,
+ {list_to_binary(lists:reverse([Value|Acc])),{0,Bin2}};
+ Rem ->
+ Bytes = Len div 8,
+ U = 8 - Rem,
+ <> = Bin,
+ {list_to_binary(lists:reverse([Bits1 bsl U,Value|Acc])),
+ {Rem,<>}}
+ end,
+ case C of
+ Int when integer(Int),C == (size(BinBits) - ((8 - Used) rem 8)) ->
+ Result;
+ Int when integer(Int) ->
+ exit({error,{asn1,{illegal_value,C,BinBits}}});
+ _ ->
+ Result
+ end.
+
+
+decode_fragmented_octets({0,Bin},C) ->
+ decode_fragmented_octets(Bin,C,[]);
+decode_fragmented_octets({_N,<<_B,Bs/binary>>},C) ->
+ decode_fragmented_octets(Bs,C,[]).
+
+decode_fragmented_octets(<<3:2,Len:6,Bin/binary>>,C,Acc) ->
+ {Value,Bin2} = split_binary(Bin,Len * ?'16K'),
+ decode_fragmented_octets(Bin2,C,[Value,Acc]);
+decode_fragmented_octets(<<0:1,0:7,Bin/binary>>,C,Acc) ->
+ Octets = list_to_binary(lists:reverse(Acc)),
+ case C of
+ Int when integer(Int), C == size(Octets) ->
+ {Octets,{0,Bin}};
+ Int when integer(Int) ->
+ exit({error,{asn1,{illegal_value,C,Octets}}});
+ _ ->
+ {Octets,{0,Bin}}
+ end;
+decode_fragmented_octets(<<0:1,Len:7,Bin/binary>>,C,Acc) ->
+ <> = Bin,
+ BinOctets = list_to_binary(lists:reverse([Value|Acc])),
+ case C of
+ Int when integer(Int),size(BinOctets) == Int ->
+ {BinOctets,Bin2};
+ Int when integer(Int) ->
+ exit({error,{asn1,{illegal_value,C,BinOctets}}});
+ _ ->
+ {BinOctets,Bin2}
+ end.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_open_type(Constraint, Value) -> CompleteList
+%% Value = list of bytes of an already encoded value (the list must be flat)
+%% | binary
+%% Contraint = not used in this version
+%%
+encode_open_type(_Constraint, Val) when list(Val) ->
+ Bin = list_to_binary(Val),
+ case size(Bin) of
+ Size when Size>255 ->
+ [encode_length(undefined,Size),[21,<>,Bin]];
+ Size ->
+ [encode_length(undefined,Size),[20,Size,Bin]]
+ end;
+% [encode_length(undefined,size(Bin)),{octets,Bin}]; % octets implies align
+encode_open_type(_Constraint, Val) when binary(Val) ->
+% [encode_length(undefined,size(Val)),{octets,Val}]. % octets implies align
+ case size(Val) of
+ Size when Size>255 ->
+ [encode_length(undefined,size(Val)),[21,<>,Val]]; % octets implies align
+ Size ->
+ [encode_length(undefined,Size),[20,Size,Val]]
+ end.
+%% the binary_to_list is not optimal but compatible with the current solution
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_open_type(Buffer,Constraint) -> Value
+%% Constraint is not used in this version
+%% Buffer = [byte] with PER encoded data
+%% Value = [byte] with decoded data (which must be decoded again as some type)
+%%
+decode_open_type(Bytes, _Constraint) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ getoctets_as_bin(Bytes2,Len).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_integer(Constraint,Value,NamedNumberList) -> CompleteList
+%% encode_integer(Constraint,Value) -> CompleteList
+%% encode_integer(Constraint,{Name,Value}) -> CompleteList
+%%
+%%
+encode_integer(C,V,NamedNumberList) when atom(V) ->
+ case lists:keysearch(V,1,NamedNumberList) of
+ {value,{_,NewV}} ->
+ encode_integer(C,NewV);
+ _ ->
+ exit({error,{asn1,{namednumber,V}}})
+ end;
+encode_integer(C,V,_NamedNumberList) when integer(V) ->
+ encode_integer(C,V);
+encode_integer(C,{Name,V},NamedNumberList) when atom(Name) ->
+ encode_integer(C,V,NamedNumberList).
+
+encode_integer(C,{Name,Val}) when atom(Name) ->
+ encode_integer(C,Val);
+
+encode_integer([{Rc,_Ec}],Val) when tuple(Rc) -> % XXX when is this invoked? First argument most often a list,...Ok this is the extension case...but it doesn't work.
+ case (catch encode_integer([Rc],Val)) of
+ {'EXIT',{error,{asn1,_}}} ->
+% [{bits,1,1},encode_unconstrained_number(Val)];
+ [1,encode_unconstrained_number(Val)];
+ Encoded ->
+% [{bits,1,0},Encoded]
+ [0,Encoded]
+ end;
+
+encode_integer([],Val) ->
+ encode_unconstrained_number(Val);
+%% The constraint is the effective constraint, and in this case is a number
+encode_integer([{'SingleValue',V}],V) ->
+ [];
+encode_integer([{'ValueRange',VR={Lb,Ub},Range,PreEnc}],Val) when Val >= Lb,
+ Ub >= Val ->
+ %% this case when NamedNumberList
+ encode_constrained_number(VR,Range,PreEnc,Val);
+encode_integer([{'ValueRange',{Lb,'MAX'}}],Val) ->
+ encode_semi_constrained_number(Lb,Val);
+encode_integer([{'ValueRange',{'MIN',_}}],Val) ->
+ encode_unconstrained_number(Val);
+encode_integer([{'ValueRange',VR={_Lb,_Ub}}],Val) ->
+ encode_constrained_number(VR,Val);
+encode_integer(_,Val) ->
+ exit({error,{asn1,{illegal_value,Val}}}).
+
+
+
+decode_integer(Buffer,Range,NamedNumberList) ->
+ {Val,Buffer2} = decode_integer(Buffer,Range),
+ case lists:keysearch(Val,2,NamedNumberList) of
+ {value,{NewVal,_}} -> {NewVal,Buffer2};
+ _ -> {Val,Buffer2}
+ end.
+
+decode_integer(Buffer,[{Rc,_Ec}]) when tuple(Rc) ->
+ {Ext,Buffer2} = getext(Buffer),
+ case Ext of
+ 0 -> decode_integer(Buffer2,[Rc]);
+ 1 -> decode_unconstrained_number(Buffer2)
+ end;
+decode_integer(Buffer,undefined) ->
+ decode_unconstrained_number(Buffer);
+decode_integer(Buffer,C) ->
+ case get_constraint(C,'SingleValue') of
+ V when integer(V) ->
+ {V,Buffer};
+ _ ->
+ decode_integer1(Buffer,C)
+ end.
+
+decode_integer1(Buffer,C) ->
+ case VR = get_constraint(C,'ValueRange') of
+ no ->
+ decode_unconstrained_number(Buffer);
+ {Lb, 'MAX'} ->
+ decode_semi_constrained_number(Buffer,Lb);
+ {_Lb,_Ub} ->
+ decode_constrained_number(Buffer,VR)
+ end.
+
+%% X.691:10.6 Encoding of a normally small non-negative whole number
+%% Use this for encoding of CHOICE index if there is an extension marker in
+%% the CHOICE
+encode_small_number({Name,Val}) when atom(Name) ->
+ encode_small_number(Val);
+encode_small_number(Val) when Val =< 63 ->
+% [{bits,1,0},{bits,6,Val}];
+% [{bits,7,Val}]; % same as above but more efficient
+ [10,7,Val]; % same as above but more efficient
+encode_small_number(Val) ->
+% [{bits,1,1},encode_semi_constrained_number(0,Val)].
+ [1,encode_semi_constrained_number(0,Val)].
+
+decode_small_number(Bytes) ->
+ {Bit,Bytes2} = getbit(Bytes),
+ case Bit of
+ 0 ->
+ getbits(Bytes2,6);
+ 1 ->
+ decode_semi_constrained_number(Bytes2,0)
+ end.
+
+%% X.691:10.7 Encoding of a semi-constrained whole number
+%% might be an optimization encode_semi_constrained_number(0,Val) ->
+encode_semi_constrained_number(C,{Name,Val}) when atom(Name) ->
+ encode_semi_constrained_number(C,Val);
+encode_semi_constrained_number({Lb,'MAX'},Val) ->
+ encode_semi_constrained_number(Lb,Val);
+encode_semi_constrained_number(Lb,Val) ->
+ Val2 = Val - Lb,
+ Oct = eint_positive(Val2),
+ Len = length(Oct),
+ if
+ Len < 128 ->
+ %{octets,[Len|Oct]}; % equiv with encode_length(undefined,Len) but faster
+ [20,Len+1,[Len|Oct]];
+ Len < 256 ->
+ [encode_length(undefined,Len),[20,Len,Oct]];
+ true ->
+ [encode_length(undefined,Len),[21,<>,Oct]]
+ end.
+
+decode_semi_constrained_number(Bytes,{Lb,_}) ->
+ decode_semi_constrained_number(Bytes,Lb);
+decode_semi_constrained_number(Bytes,Lb) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {V,Bytes3} = getoctets(Bytes2,Len),
+ {V+Lb,Bytes3}.
+
+encode_constrained_number({Lb,_Ub},_Range,{bits,N},Val) ->
+ Val2 = Val-Lb,
+% {bits,N,Val2};
+ [10,N,Val2];
+encode_constrained_number({Lb,_Ub},_Range,{octets,N},Val) when N < 256->
+ %% N is 8 or 16 (1 or 2 octets)
+ Val2 = Val-Lb,
+% {octets,<>};
+ [20,N,Val2];
+encode_constrained_number({Lb,_Ub},_Range,{octets,N},Val) -> % N>255
+ %% N is 8 or 16 (1 or 2 octets)
+ Val2 = Val-Lb,
+% {octets,<>};
+ [21,<>,Val2];
+encode_constrained_number({Lb,_Ub},Range,_,Val) ->
+ Val2 = Val-Lb,
+ if
+ Range =< 16#1000000 -> % max 3 octets
+ Octs = eint_positive(Val2),
+% [encode_length({1,3},size(Octs)),{octets,Octs}];
+ L = length(Octs),
+ [encode_length({1,3},L),[20,L,Octs]];
+ Range =< 16#100000000 -> % max 4 octets
+ Octs = eint_positive(Val2),
+% [encode_length({1,4},size(Octs)),{octets,Octs}];
+ L = length(Octs),
+ [encode_length({1,4},L),[20,L,Octs]];
+ Range =< 16#10000000000 -> % max 5 octets
+ Octs = eint_positive(Val2),
+% [encode_length({1,5},size(Octs)),{octets,Octs}];
+ L = length(Octs),
+ [encode_length({1,5},L),[20,L,Octs]];
+ true ->
+ exit({not_supported,{integer_range,Range}})
+ end.
+
+encode_constrained_number(Range,{Name,Val}) when atom(Name) ->
+ encode_constrained_number(Range,Val);
+encode_constrained_number({Lb,Ub},Val) when Val >= Lb, Ub >= Val ->
+ Range = Ub - Lb + 1,
+ Val2 = Val - Lb,
+ if
+ Range == 2 ->
+% Size = {bits,1,Val2};
+ [Val2];
+ Range =< 4 ->
+% Size = {bits,2,Val2};
+ [10,2,Val2];
+ Range =< 8 ->
+ [10,3,Val2];
+ Range =< 16 ->
+ [10,4,Val2];
+ Range =< 32 ->
+ [10,5,Val2];
+ Range =< 64 ->
+ [10,6,Val2];
+ Range =< 128 ->
+ [10,7,Val2];
+ Range =< 255 ->
+ [10,8,Val2];
+ Range =< 256 ->
+% Size = {octets,[Val2]};
+ [20,1,Val2];
+ Range =< 65536 ->
+% Size = {octets,<>};
+ [20,2,<>];
+ Range =< 16#1000000 ->
+ Octs = eint_positive(Val2),
+% [{bits,2,length(Octs)-1},{octets,Octs}];
+ Len = length(Octs),
+ [10,2,Len-1,20,Len,Octs];
+ Range =< 16#100000000 ->
+ Octs = eint_positive(Val2),
+ Len = length(Octs),
+ [10,2,Len-1,20,Len,Octs];
+ Range =< 16#10000000000 ->
+ Octs = eint_positive(Val2),
+ Len = length(Octs),
+ [10,3,Len-1,20,Len,Octs];
+ true ->
+ exit({not_supported,{integer_range,Range}})
+ end;
+encode_constrained_number({_,_},Val) ->
+ exit({error,{asn1,{illegal_value,Val}}}).
+
+decode_constrained_number(Buffer,VR={Lb,Ub}) ->
+ Range = Ub - Lb + 1,
+ decode_constrained_number(Buffer,VR,Range).
+
+decode_constrained_number(Buffer,{Lb,_Ub},_Range,{bits,N}) ->
+ {Val,Remain} = getbits(Buffer,N),
+ {Val+Lb,Remain};
+decode_constrained_number(Buffer,{Lb,_Ub},_Range,{octets,N}) ->
+ {Val,Remain} = getoctets(Buffer,N),
+ {Val+Lb,Remain}.
+
+decode_constrained_number(Buffer,{Lb,_Ub},Range) ->
+ % Val2 = Val - Lb,
+ {Val,Remain} =
+ if
+ Range == 2 ->
+ getbits(Buffer,1);
+ Range =< 4 ->
+ getbits(Buffer,2);
+ Range =< 8 ->
+ getbits(Buffer,3);
+ Range =< 16 ->
+ getbits(Buffer,4);
+ Range =< 32 ->
+ getbits(Buffer,5);
+ Range =< 64 ->
+ getbits(Buffer,6);
+ Range =< 128 ->
+ getbits(Buffer,7);
+ Range =< 255 ->
+ getbits(Buffer,8);
+ Range =< 256 ->
+ getoctets(Buffer,1);
+ Range =< 65536 ->
+ getoctets(Buffer,2);
+ Range =< 16#1000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,3}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ Range =< 16#100000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,4}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ Range =< 16#10000000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,5}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ true ->
+ exit({not_supported,{integer_range,Range}})
+ end,
+ {Val+Lb,Remain}.
+
+%% X.691:10.8 Encoding of an unconstrained whole number
+
+encode_unconstrained_number(Val) when Val >= 0 ->
+ Oct = eint(Val,[]),
+ Len = length(Oct),
+ if
+ Len < 128 ->
+ %{octets,[Len|Oct]}; % equiv with encode_length(undefined,Len) but faster
+ [20,Len+1,[Len|Oct]];
+ Len < 256 ->
+% [encode_length(undefined,Len),20,Len,Oct];
+ [20,Len+2,<<2:2,Len:14>>,Oct];% equiv with encode_length(undefined,Len) but faster
+ true ->
+% [encode_length(undefined,Len),{octets,Oct}]
+ [encode_length(undefined,Len),[21,<>,Oct]]
+ end;
+encode_unconstrained_number(Val) -> % negative
+ Oct = enint(Val,[]),
+ Len = length(Oct),
+ if
+ Len < 128 ->
+% {octets,[Len|Oct]}; % equiv with encode_length(undefined,Len) but faster
+ [20,Len+1,[Len|Oct]];% equiv with encode_length(undefined,Len) but faster
+ Len < 256 ->
+% [encode_length(undefined,Len),20,Len,Oct];
+ [20,Len+2,<<2:2,Len:14>>,Oct];% equiv with encode_length(undefined,Len) but faster
+ true ->
+ %[encode_length(undefined,Len),{octets,Oct}]
+ [encode_length(undefined,Len),[21,<>,Oct]]
+ end.
+
+
+%% used for positive Values which don't need a sign bit
+%% returns a list
+eint_positive(Val) ->
+ case eint(Val,[]) of
+ [0,B1|T] ->
+ [B1|T];
+ T ->
+ T
+ end.
+
+
+eint(0, [B|Acc]) when B < 128 ->
+ [B|Acc];
+eint(N, Acc) ->
+ eint(N bsr 8, [N band 16#ff| Acc]).
+
+enint(-1, [B1|T]) when B1 > 127 ->
+ [B1|T];
+enint(N, Acc) ->
+ enint(N bsr 8, [N band 16#ff|Acc]).
+
+decode_unconstrained_number(Bytes) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {Ints,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_integer(Ints),Bytes3}.
+
+dec_pos_integer(Ints) ->
+ decpint(Ints, 8 * (length(Ints) - 1)).
+dec_integer(Ints) when hd(Ints) band 255 =< 127 -> %% Positive number
+ decpint(Ints, 8 * (length(Ints) - 1));
+dec_integer(Ints) -> %% Negative
+ decnint(Ints, 8 * (length(Ints) - 1)).
+
+decpint([Byte|Tail], Shift) ->
+ (Byte bsl Shift) bor decpint(Tail, Shift-8);
+decpint([], _) -> 0.
+
+decnint([Byte|Tail], Shift) ->
+ (-128 + (Byte band 127) bsl Shift) bor decpint(Tail, Shift-8).
+
+% minimum_octets(Val) ->
+% minimum_octets(Val,[]).
+
+% minimum_octets(Val,Acc) when Val > 0 ->
+% minimum_octets((Val bsr 8),[Val band 16#FF|Acc]);
+% minimum_octets(0,Acc) ->
+% Acc.
+
+
+%% X.691:10.9 Encoding of a length determinant
+%%encode_small_length(undefined,Len) -> % null means no UpperBound
+%% encode_small_number(Len).
+
+%% X.691:10.9.3.5
+%% X.691:10.9.3.7
+encode_length(undefined,Len) -> % un-constrained
+ if
+ Len < 128 ->
+% {octets,[Len]};
+ [20,1,Len];
+ Len < 16384 ->
+ %{octets,<<2:2,Len:14>>};
+ [20,2,<<2:2,Len:14>>];
+ true -> % should be able to endode length >= 16384 i.e. fragmented length
+ exit({error,{asn1,{encode_length,{nyi,above_16k}}}})
+ end;
+
+encode_length({0,'MAX'},Len) ->
+ encode_length(undefined,Len);
+encode_length(Vr={Lb,Ub},Len) when Ub =< 65535 ,Lb >= 0 -> % constrained
+ encode_constrained_number(Vr,Len);
+encode_length({Lb,_Ub},Len) when integer(Lb), Lb >= 0 -> % Ub > 65535
+ encode_length(undefined,Len);
+encode_length({Vr={Lb,Ub},[]},Len) when Ub =< 65535 ,Lb >= 0,Len=
+ %% constrained extensible
+% [{bits,1,0},encode_constrained_number(Vr,Len)];
+ [0,encode_constrained_number(Vr,Len)];
+encode_length({{Lb,_},[]},Len) ->
+ [1,encode_semi_constrained_number(Lb,Len)];
+encode_length(SingleValue,_Len) when integer(SingleValue) ->
+ [].
+
+%% X.691 10.9.3.4 (only used for length of bitmap that prefixes extension
+%% additions in a sequence or set
+encode_small_length(Len) when Len =< 64 ->
+%% [{bits,1,0},{bits,6,Len-1}];
+% {bits,7,Len-1}; % the same as above but more efficient
+ [10,7,Len-1];
+encode_small_length(Len) ->
+% [{bits,1,1},encode_length(undefined,Len)].
+ [1,encode_length(undefined,Len)].
+
+% decode_small_length({Used,<<_:Used,0:1,Num:6,_:((8-Used+1) rem 8),Rest/binary>>}) ->
+% case Buffer of
+% <<_:Used,0:1,Num:6,_:((8-Used+1) rem 8),Rest/binary>> ->
+% {Num,
+% case getbit(Buffer) of
+% {0,Remain} ->
+% {Bits,Remain2} = getbits(Remain,6),
+% {Bits+1,Remain2};
+% {1,Remain} ->
+% decode_length(Remain,undefined)
+% end.
+
+decode_small_length(Buffer) ->
+ case getbit(Buffer) of
+ {0,Remain} ->
+ {Bits,Remain2} = getbits(Remain,6),
+ {Bits+1,Remain2};
+ {1,Remain} ->
+ decode_length(Remain,undefined)
+ end.
+
+decode_length(Buffer) ->
+ decode_length(Buffer,undefined).
+
+decode_length(Buffer,undefined) -> % un-constrained
+ {0,Buffer2} = align(Buffer),
+ case Buffer2 of
+ <<0:1,Oct:7,Rest/binary>> ->
+ {Oct,{0,Rest}};
+ <<2:2,Val:14,Rest/binary>> ->
+ {Val,{0,Rest}};
+ <<3:2,_Val:14,_Rest/binary>> ->
+ %% this case should be fixed
+ exit({error,{asn1,{decode_length,{nyi,above_16k}}}})
+ end;
+%% {Bits,_} = getbits(Buffer2,2),
+% case Bits of
+% 2 ->
+% {Val,Bytes3} = getoctets(Buffer2,2),
+% {(Val band 16#3FFF),Bytes3};
+% 3 ->
+% exit({error,{asn1,{decode_length,{nyi,above_16k}}}});
+% _ ->
+% {Val,Bytes3} = getoctet(Buffer2),
+% {Val band 16#7F,Bytes3}
+% end;
+
+decode_length(Buffer,{Lb,Ub}) when Ub =< 65535 ,Lb >= 0 -> % constrained
+ decode_constrained_number(Buffer,{Lb,Ub});
+decode_length(_Buffer,{Lb,_Ub}) when integer(Lb), Lb >= 0 -> % Ub > 65535
+ exit({error,{asn1,{decode_length,{nyi,above_64K}}}});
+decode_length(Buffer,{{Lb,Ub},[]}) ->
+ case getbit(Buffer) of
+ {0,Buffer2} ->
+ decode_length(Buffer2, {Lb,Ub})
+ end;
+
+
+%When does this case occur with {_,_Lb,Ub} ??
+% X.691:10.9.3.5
+decode_length({Used,Bin},{_,_Lb,_Ub}) -> %when Len =< 127 -> % Unconstrained or large Ub NOTE! this case does not cover case when Ub > 65535
+ Unused = (8-Used) rem 8,
+ case Bin of
+ <<_:Used,0:1,Val:7,R:Unused,Rest/binary>> ->
+ {Val,{Used,<>}};
+ <<_:Used,_:Unused,2:2,Val:14,Rest/binary>> ->
+ {Val, {0,Rest}};
+ <<_:Used,_:Unused,3:2,_:14,_Rest/binary>> ->
+ exit({error,{asn1,{decode_length,{nyi,length_above_64K}}}})
+ end;
+% decode_length(Buffer,{_,_Lb,Ub}) -> %when Len =< 127 -> % Unconstrained or large Ub
+% case getbit(Buffer) of
+% {0,Remain} ->
+% getbits(Remain,7);
+% {1,Remain} ->
+% {Val,Remain2} = getoctets(Buffer,2),
+% {Val band 2#0111111111111111, Remain2}
+% end;
+decode_length(Buffer,SingleValue) when integer(SingleValue) ->
+ {SingleValue,Buffer}.
+
+
+ % X.691:11
+decode_boolean(Buffer) -> %when record(Buffer,buffer)
+ case getbit(Buffer) of
+ {1,Remain} -> {true,Remain};
+ {0,Remain} -> {false,Remain}
+ end.
+
+
+%% ENUMERATED with extension marker
+decode_enumerated(Buffer,C,{Ntup1,Ntup2}) when tuple(Ntup1), tuple(Ntup2) ->
+ {Ext,Buffer2} = getext(Buffer),
+ case Ext of
+ 0 -> % not an extension value
+ {Val,Buffer3} = decode_integer(Buffer2,C),
+ case catch (element(Val+1,Ntup1)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer3};
+ _Error -> exit({error,{asn1,{decode_enumerated,{Val,[Ntup1,Ntup2]}}}})
+ end;
+ 1 -> % this an extension value
+ {Val,Buffer3} = decode_small_number(Buffer2),
+ case catch (element(Val+1,Ntup2)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer3};
+ _ -> {{asn1_enum,Val},Buffer3}
+ end
+ end;
+
+decode_enumerated(Buffer,C,NamedNumberTup) when tuple(NamedNumberTup) ->
+ {Val,Buffer2} = decode_integer(Buffer,C),
+ case catch (element(Val+1,NamedNumberTup)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer2};
+ _Error -> exit({error,{asn1,{decode_enumerated,{Val,NamedNumberTup}}}})
+ end.
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Bitstring value, ITU_T X.690 Chapter 8.5
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% encode bitstring value
+%%===============================================================================
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% bitstring NamedBitList
+%% Val can be of:
+%% - [identifiers] where only named identifers are set to one,
+%% the Constraint must then have some information of the
+%% bitlength.
+%% - [list of ones and zeroes] all bits
+%% - integer value representing the bitlist
+%% C is constraint Len, only valid when identifiers
+
+
+%% when the value is a list of {Unused,BinBits}, where
+%% Unused = integer(),
+%% BinBits = binary().
+
+encode_bit_string(C,Bin={Unused,BinBits},NamedBitList) when integer(Unused),
+ binary(BinBits) ->
+ encode_bin_bit_string(C,Bin,NamedBitList);
+
+%% when the value is a list of named bits
+
+encode_bit_string(C, LoNB=[FirstVal | _RestVal], NamedBitList) when atom(FirstVal) ->
+ ToSetPos = get_all_bitposes(LoNB, NamedBitList, []),
+ BitList = make_and_set_list(ToSetPos,0),
+ encode_bit_string(C,BitList,NamedBitList);% consider the constraint
+
+encode_bit_string(C, BL=[{bit,_} | _RestVal], NamedBitList) ->
+ ToSetPos = get_all_bitposes(BL, NamedBitList, []),
+ BitList = make_and_set_list(ToSetPos,0),
+ encode_bit_string(C,BitList,NamedBitList);
+
+%% when the value is a list of ones and zeroes
+encode_bit_string(Int, BitListValue, _)
+ when list(BitListValue),integer(Int) ->
+ %% The type is constrained by a single value size constraint
+ [40,Int,length(BitListValue),BitListValue];
+% encode_bit_string(C, BitListValue,NamedBitList)
+% when list(BitListValue) ->
+% [encode_bit_str_length(C,BitListValue),
+% 2,45,BitListValue];
+encode_bit_string(no, BitListValue,[])
+ when list(BitListValue) ->
+ [encode_length(undefined,length(BitListValue)),
+ 2,BitListValue];
+encode_bit_string(C, BitListValue,[])
+ when list(BitListValue) ->
+ [encode_length(C,length(BitListValue)),
+ 2,BitListValue];
+encode_bit_string(no, BitListValue,_NamedBitList)
+ when list(BitListValue) ->
+ %% this case with an unconstrained BIT STRING can be made more efficient
+ %% if the complete driver can take a special code so the length field
+ %% is encoded there.
+ NewBitLVal = lists:reverse(lists:dropwhile(fun(0)->true;(1)->false end,
+ lists:reverse(BitListValue))),
+ [encode_length(undefined,length(NewBitLVal)),
+ 2,NewBitLVal];
+encode_bit_string(C,BitListValue,_NamedBitList)
+ when list(BitListValue) ->% C = {_,'MAX'}
+% NewBitLVal = lists:reverse(lists:dropwhile(fun(0)->true;(1)->false end,
+% lists:reverse(BitListValue))),
+ NewBitLVal = bit_string_trailing_zeros(BitListValue,C),
+ [encode_length(C,length(NewBitLVal)),
+ 2,NewBitLVal];
+
+% encode_bit_string(C, BitListValue, NamedBitList) when list(BitListValue) ->
+% BitListToBinary =
+% %% fun that transforms a list of 1 and 0 to a tuple:
+% %% {UnusedBitsInLastByte, Binary}
+% fun([H|T],Acc,N,Fun) ->
+% Fun(T,(Acc bsl 1)+H,N+1,Fun);
+% ([],Acc,N,_) -> % length fits in one byte
+% Unused = (8 - (N rem 8)) rem 8,
+% % case N/8 of
+% % _Len =< 255 ->
+% % [30,Unused,(Unused+N)/8,<>];
+% % _Len ->
+% % Len = (Unused+N)/8,
+% % [31,Unused,<>,<>]
+% % end
+% {Unused,<>}
+% end,
+% UnusedAndBin =
+% case NamedBitList of
+% [] -> % dont remove trailing zeroes
+% BitListToBinary(BitListValue,0,0,BitListToBinary);
+% _ ->
+% BitListToBinary(lists:reverse(
+% lists:dropwhile(fun(0)->true;(1)->false end,
+% lists:reverse(BitListValue))),
+% 0,0,BitListToBinary)
+% end,
+% encode_bin_bit_string(C,UnusedAndBin,NamedBitList);
+
+%% when the value is an integer
+encode_bit_string(C, IntegerVal, NamedBitList) when integer(IntegerVal)->
+ BitList = int_to_bitlist(IntegerVal),
+ encode_bit_string(C,BitList,NamedBitList);
+
+%% when the value is a tuple
+encode_bit_string(C,{Name,Val}, NamedBitList) when atom(Name) ->
+ encode_bit_string(C,Val,NamedBitList).
+
+bit_string_trailing_zeros(BitList,C) when integer(C) ->
+ bit_string_trailing_zeros1(BitList,C,C);
+bit_string_trailing_zeros(BitList,{Lb,Ub}) when integer(Lb) ->
+ bit_string_trailing_zeros1(BitList,Lb,Ub);
+bit_string_trailing_zeros(BitList,{{Lb,Ub},_}) when integer(Lb) ->
+ bit_string_trailing_zeros1(BitList,Lb,Ub);
+bit_string_trailing_zeros(BitList,_) ->
+ BitList.
+
+bit_string_trailing_zeros1(BitList,Lb,Ub) ->
+ case length(BitList) of
+ Lb -> BitList;
+ B when B BitList++lists:duplicate(Lb-B,0);
+ D -> F = fun(L,LB,LB,_,_)->lists:reverse(L);
+ ([0|R],L1,LB,UB,Fun)->Fun(R,L1-1,LB,UB,Fun);
+ (L,L1,_,UB,_)when L1 =< UB -> lists:reverse(L);
+ (_,_L1,_,_,_) ->exit({error,{list_length_BIT_STRING,
+ BitList}}) end,
+ F(lists:reverse(BitList),D,Lb,Ub,F)
+ end.
+
+%% encode_bin_bit_string/3, when value is a tuple of Unused and BinBits.
+%% Unused = integer(),i.e. number unused bits in least sign. byte of
+%% BinBits = binary().
+encode_bin_bit_string(C,{_,BinBits},_NamedBitList)
+ when integer(C),C=<16 ->
+ [45,C,size(BinBits),BinBits];
+encode_bin_bit_string(C,{_Unused,BinBits},_NamedBitList)
+ when integer(C) ->
+ [2,45,C,size(BinBits),BinBits];
+encode_bin_bit_string(C,UnusedAndBin={_,_},NamedBitList) ->
+% UnusedAndBin1 = {Unused1,Bin1} =
+ {Unused1,Bin1} =
+ %% removes all trailing bits if NamedBitList is not empty
+ remove_trailing_bin(NamedBitList,UnusedAndBin),
+ case C of
+% case get_constraint(C,'SizeConstraint') of
+
+% 0 ->
+% []; % borde avgöras i compile-time
+% V when integer(V),V=<16 ->
+% {Unused2,Bin2} = pad_list(V,UnusedAndBin1),
+% <> = Bin2,
+% % {bits,V,BitVal};
+% [10,V,BitVal];
+% V when integer(V) ->
+% %[align, pad_list(V, UnusedAndBin1)];
+% {Unused2,Bin2} = pad_list(V, UnusedAndBin1),
+% <> = Bin2,
+% [2,octets_unused_to_complete(Unused2,size(Bin2),Bin2)];
+
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+% [encode_length({Lb,Ub},size(Bin1)*8 - Unused1),
+% align,UnusedAndBin1];
+ Size=size(Bin1),
+ [encode_length({Lb,Ub},Size*8 - Unused1),
+ 2,octets_unused_to_complete(Unused1,Size,Bin1)];
+ no ->
+ Size=size(Bin1),
+ [encode_length(undefined,Size*8 - Unused1),
+ 2,octets_unused_to_complete(Unused1,Size,Bin1)];
+ Sc ->
+ Size=size(Bin1),
+ [encode_length(Sc,Size*8 - Unused1),
+ 2,octets_unused_to_complete(Unused1,Size,Bin1)]
+ end.
+
+remove_trailing_bin([], {Unused,Bin}) ->
+ {Unused,Bin};
+remove_trailing_bin(NamedNumberList, {_Unused,Bin}) ->
+ Size = size(Bin)-1,
+ <> = Bin,
+ %% clear the Unused bits to be sure
+% LastByte1 = LastByte band (((1 bsl Unused) -1) bxor 255),% why this???
+ Unused1 = trailingZeroesInNibble(LastByte band 15),
+ Unused2 =
+ case Unused1 of
+ 4 ->
+ 4 + trailingZeroesInNibble(LastByte bsr 4);
+ _ -> Unused1
+ end,
+ case Unused2 of
+ 8 ->
+ remove_trailing_bin(NamedNumberList,{0,Bfront});
+ _ ->
+ {Unused2,Bin}
+ end.
+
+
+trailingZeroesInNibble(0) ->
+ 4;
+trailingZeroesInNibble(1) ->
+ 0;
+trailingZeroesInNibble(2) ->
+ 1;
+trailingZeroesInNibble(3) ->
+ 0;
+trailingZeroesInNibble(4) ->
+ 2;
+trailingZeroesInNibble(5) ->
+ 0;
+trailingZeroesInNibble(6) ->
+ 1;
+trailingZeroesInNibble(7) ->
+ 0;
+trailingZeroesInNibble(8) ->
+ 3;
+trailingZeroesInNibble(9) ->
+ 0;
+trailingZeroesInNibble(10) ->
+ 1;
+trailingZeroesInNibble(11) ->
+ 0;
+trailingZeroesInNibble(12) -> %#1100
+ 2;
+trailingZeroesInNibble(13) ->
+ 0;
+trailingZeroesInNibble(14) ->
+ 1;
+trailingZeroesInNibble(15) ->
+ 0.
+
+%%%%%%%%%%%%%%%
+%% The result is presented as a list of named bits (if possible)
+%% else as a tuple {Unused,Bits}. Unused is the number of unused
+%% bits, least significant bits in the last byte of Bits. Bits is
+%% the BIT STRING represented as a binary.
+%%
+decode_compact_bit_string(Buffer, C, NamedNumberList) ->
+ case get_constraint(C,'SizeConstraint') of
+ 0 -> % fixed length
+ {{8,0},Buffer};
+ V when integer(V),V=<16 -> %fixed length 16 bits or less
+ compact_bit_string(Buffer,V,NamedNumberList);
+ V when integer(V),V=<65536 -> %fixed length > 16 bits
+ Bytes2 = align(Buffer),
+ compact_bit_string(Bytes2,V,NamedNumberList);
+ V when integer(V) -> % V > 65536 => fragmented value
+ {Bin,Buffer2} = decode_fragmented_bits(Buffer,V),
+ case Buffer2 of
+ {0,_} -> {{0,Bin},Buffer2};
+ {U,_} -> {{8-U,Bin},Buffer2}
+ end;
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ %% This case may demand decoding of fragmented length/value
+ {Len,Bytes2} = decode_length(Buffer,{Lb,Ub}),
+ Bytes3 = align(Bytes2),
+ compact_bit_string(Bytes3,Len,NamedNumberList);
+ no ->
+ %% This case may demand decoding of fragmented length/value
+ {Len,Bytes2} = decode_length(Buffer,undefined),
+ Bytes3 = align(Bytes2),
+ compact_bit_string(Bytes3,Len,NamedNumberList);
+ Sc ->
+ {Len,Bytes2} = decode_length(Buffer,Sc),
+ Bytes3 = align(Bytes2),
+ compact_bit_string(Bytes3,Len,NamedNumberList)
+ end.
+
+
+%%%%%%%%%%%%%%%
+%% The result is presented as a list of named bits (if possible)
+%% else as a list of 0 and 1.
+%%
+decode_bit_string(Buffer, C, NamedNumberList) ->
+ case get_constraint(C,'SizeConstraint') of
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ {Len,Bytes2} = decode_length(Buffer,{Lb,Ub}),
+ Bytes3 = align(Bytes2),
+ bit_list_or_named(Bytes3,Len,NamedNumberList);
+ no ->
+ {Len,Bytes2} = decode_length(Buffer,undefined),
+ Bytes3 = align(Bytes2),
+ bit_list_or_named(Bytes3,Len,NamedNumberList);
+ 0 -> % fixed length
+ {[],Buffer}; % nothing to encode
+ V when integer(V),V=<16 -> % fixed length 16 bits or less
+ bit_list_or_named(Buffer,V,NamedNumberList);
+ V when integer(V),V=<65536 ->
+ Bytes2 = align(Buffer),
+ bit_list_or_named(Bytes2,V,NamedNumberList);
+ V when integer(V) ->
+ Bytes2 = align(Buffer),
+ {BinBits,_Bytes3} = decode_fragmented_bits(Bytes2,V),
+ bit_list_or_named(BinBits,V,NamedNumberList);
+ Sc -> % extension marker
+ {Len,Bytes2} = decode_length(Buffer,Sc),
+ Bytes3 = align(Bytes2),
+ bit_list_or_named(Bytes3,Len,NamedNumberList)
+ end.
+
+
+%% if no named bits are declared we will return a
+%% {Unused,Bits}. Unused = integer(),
+%% Bits = binary().
+compact_bit_string(Buffer,Len,[]) ->
+ getbits_as_binary(Len,Buffer); % {{Unused,BinBits},NewBuffer}
+compact_bit_string(Buffer,Len,NamedNumberList) ->
+ bit_list_or_named(Buffer,Len,NamedNumberList).
+
+
+%% if no named bits are declared we will return a
+%% BitList = [0 | 1]
+
+bit_list_or_named(Buffer,Len,[]) ->
+ getbits_as_list(Len,Buffer);
+
+%% if there are named bits declared we will return a named
+%% BitList where the names are atoms and unnamed bits represented
+%% as {bit,Pos}
+%% BitList = [atom() | {bit,Pos}]
+%% Pos = integer()
+
+bit_list_or_named(Buffer,Len,NamedNumberList) ->
+ {BitList,Rest} = getbits_as_list(Len,Buffer),
+ {bit_list_or_named1(0,BitList,NamedNumberList,[]), Rest}.
+
+bit_list_or_named1(Pos,[0|Bt],Names,Acc) ->
+ bit_list_or_named1(Pos+1,Bt,Names,Acc);
+bit_list_or_named1(Pos,[1|Bt],Names,Acc) ->
+ case lists:keysearch(Pos,2,Names) of
+ {value,{Name,_}} ->
+ bit_list_or_named1(Pos+1,Bt,Names,[Name|Acc]);
+ _ ->
+ bit_list_or_named1(Pos+1,Bt,Names,[{bit,Pos}|Acc])
+ end;
+bit_list_or_named1(_Pos,[],_Names,Acc) ->
+ lists:reverse(Acc).
+
+
+
+%%%%%%%%%%%%%%%
+%%
+
+int_to_bitlist(Int) when integer(Int), Int > 0 ->
+ [Int band 1 | int_to_bitlist(Int bsr 1)];
+int_to_bitlist(0) ->
+ [].
+
+
+%%%%%%%%%%%%%%%%%%
+%% get_all_bitposes([list of named bits to set], named_bit_db, []) ->
+%% [sorted_list_of_bitpositions_to_set]
+
+get_all_bitposes([{bit,ValPos}|Rest], NamedBitList, Ack) ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack ]);
+
+get_all_bitposes([Val | Rest], NamedBitList, Ack) ->
+ case lists:keysearch(Val, 1, NamedBitList) of
+ {value, {_ValName, ValPos}} ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack]);
+ _ ->
+ exit({error,{asn1, {bitstring_namedbit, Val}}})
+ end;
+get_all_bitposes([], _NamedBitList, Ack) ->
+ lists:sort(Ack).
+
+%%%%%%%%%%%%%%%%%%
+%% make_and_set_list([list of positions to set to 1])->
+%% returns list with all in SetPos set.
+%% in positioning in list the first element is 0, the second 1 etc.., but
+%%
+
+make_and_set_list([XPos|SetPos], XPos) ->
+ [1 | make_and_set_list(SetPos, XPos + 1)];
+make_and_set_list([Pos|SetPos], XPos) ->
+ [0 | make_and_set_list([Pos | SetPos], XPos + 1)];
+make_and_set_list([], _) ->
+ [].
+
+%%%%%%%%%%%%%%%%%
+%% pad_list(N,BitList) -> PaddedList
+%% returns a padded (with trailing {bit,0} elements) list of length N
+%% if Bitlist contains more than N significant bits set an exit asn1_error
+%% is generated
+
+% pad_list(N,In={Unused,Bin}) ->
+% pad_list(N, size(Bin)*8 - Unused, In).
+
+% pad_list(N,Size,In={Unused,Bin}) when N < Size ->
+% exit({error,{asn1,{range_error,{bit_string,In}}}});
+% pad_list(N,Size,{Unused,Bin}) when N > Size, Unused > 0 ->
+% pad_list(N,Size+1,{Unused-1,Bin});
+% pad_list(N,Size,{Unused,Bin}) when N > Size ->
+% pad_list(N,Size+1,{7,<>});
+% pad_list(N,N,In={Unused,Bin}) ->
+% In.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% X.691:16
+%% encode_octet_string(Constraint,ExtensionMarker,Val)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+encode_octet_string(C,Val) ->
+ encode_octet_string(C,false,Val).
+
+encode_octet_string(C,Bool,{_Name,Val}) ->
+ encode_octet_string(C,Bool,Val);
+encode_octet_string(_C,true,_Val) ->
+ exit({error,{asn1,{'not_supported',extensionmarker}}});
+encode_octet_string(SZ={_,_},false,Val) ->
+% [encode_length(SZ,length(Val)),align,
+% {octets,Val}];
+ Len = length(Val),
+ [encode_length(SZ,Len),2,
+ octets_to_complete(Len,Val)];
+encode_octet_string(SZ,false,Val) when list(SZ) ->
+ Len = length(Val),
+ [encode_length({hd(SZ),lists:max(SZ)},Len),2,
+ octets_to_complete(Len,Val)];
+encode_octet_string(no,false,Val) ->
+ Len = length(Val),
+ [encode_length(undefined,Len),2,
+ octets_to_complete(Len,Val)];
+encode_octet_string(C,_,_) ->
+ exit({error,{not_implemented,C}}).
+
+
+decode_octet_string(Bytes,Range) ->
+ decode_octet_string(Bytes,Range,false).
+
+decode_octet_string(Bytes,1,false) ->
+ {B1,Bytes2} = getbits(Bytes,8),
+ {[B1],Bytes2};
+decode_octet_string(Bytes,2,false) ->
+ {Bs,Bytes2}= getbits(Bytes,16),
+ {binary_to_list(<