Adding a new Eiffel built-in routine |
![]() ![]() |
The following sections will describe step by step what needs to be done in order to add support for new built-in routines in the Eiffel compiler.
The first thing to do is to declare the name of the built-in feature and the name of its class. Let's assume that the built-in feature foo has been declared in class BAR. These feature and class names will have to be declared as follows in class ET_TOKEN_CONSTANTS if not already done:
It is a good practice to keep these features in alphabetic order in their respective feature clauses. Note that capitalized_bar_name and foo_name also need to be listed in feature strings from class ET_EIFFEL_SCANNER_SKELETON:feature -- Class names bar_class_name: ET_CLASS_NAME -- "BAR" class name once create {ET_IDENTIFIER} Result.make (capitalized_bar_name) ensure bar_class_name_not_void: Result /= Void end feature -- Feature names foo_feature_name: ET_FEATURE_NAME -- 'foo' feature name once create {ET_IDENTIFIER} Result.make (foo_name) ensure foo_feature_name_not_void: Result /= Void end feature -- Keyword and symbol names capitalized_bar_name: STRING = "BAR" -- Name of Eiffel class "BAR" foo_name: STRING = "foo" -- Name of Eiffel feature 'foo'
Again, try to keep them in alphabetic order in their respective sections.strings: DS_HASH_TABLE [INTEGER, STRING] -- Strings known by the current scanner, and the associated -- hash codes when they are used as identifier once ... -- Class names. Result.force_new (-1, tokens.capitalized_bar_name) ... -- Feature names. Result.force_new (-1, tokens.foo_name) ... end
Each built-in feature is associated with a code which is made up of a class identifier and a feature identifier within this class. Still assuming that the built-in feature foo has been declared in class BAR, the following features need to be declared in class ET_TOKEN_CODES if not already done:
where XX is the class identifier whose value should be different of all other builtin_*_class declared in this feature clause, and YY is a unique identifier within the built-in features of this class. Note that the value of YY should be between 1 and builtin_capacity - 1. The value of builtin_capacity will need to be adjusted if there are more built-in features within a given class.feature -- Built-in codes builtin_bar_feature (a_feature_code: INTEGER): INTEGER -- Full code for built-in feature from class "BAR" do Result := builtin_bar_class * builtin_capacity + a_feature_code end builtin_bar_class: INTEGER = XX builtin_bar_foo: INTEGER = YY -- Codes for built-in features from class "BAR"
When features are flattened, the Eiffel compiler checks whether features declared as built-in are known and if they have the expected signature. This is done in class ET_BUILTIN_FEATURE_CHECKER. Let's assume first that the feature foo in class BAR is a built-in procedure with one argument of type 'like Current'. The following features need to be modified or added in class ET_BUILTIN_FEATURE_CHECKER:
check_builtin_function_validity (a_feature: ET_EXTERNAL_FUNCTION) -- Check validity of built-in `a_feature', written in `current_class'. -- Set `has_fatal_error' if a fatal error occurred. require a_feature_not_void: a_feature /= Void local l_name: ET_CLASS_NAME do l_name := current_class.name if ... then ... elseif l_name.same_class_name (tokens.bar_class_name) then check_builtin_bar_function_validity (a_feature) ... else -- Unknown built-in routine. a_feature.set_builtin_code (tokens.builtin_unknown) if unknown_builtin_reported then set_fatal_error error_handler.report_gvkbu1a_error (current_class, a_feature) end end end check_builtin_bar_function_validity (a_feature: ET_EXTERNAL_FUNCTION) -- Check validity of built-in `a_feature' from class "BAR". -- Set `has_fatal_error' if a fatal error occurred. require a_feature_not_void: a_feature /= Void local l_formals: ET_FORMAL_ARGUMENT_LIST do -- List function names first, then procedure names. if ... then ... elseif a_feature.name.same_feature_name (tokens.foo_feature_name) then -- 'BAR.foo' should be a procedure. a_feature.set_builtin_code (tokens.builtin_bar_feature (tokens.builtin_bar_foo)) set_fatal_error error_handler.report_gvkbs0a_error (current_class, a_feature, <<tokens.like_current.type>>, Void) ... else -- Unknown built-in routine. a_feature.set_builtin_code (tokens.builtin_unknown) if unknown_builtin_reported then set_fatal_error error_handler.report_gvkbu1a_error (current_class, a_feature) end end end check_builtin_procedure_validity (a_feature: ET_EXTERNAL_PROCEDURE) -- Check validity of built-in `a_feature', written in `current_class'. -- Set `has_fatal_error' if a fatal error occurred. require a_feature_not_void: a_feature /= Void local l_name: ET_CLASS_NAME do l_name := current_class.name if ... then ... elseif l_name.same_class_name (tokens.bar_class_name) then check_builtin_bar_procedure_validity (a_feature) ... else -- Unknown built-in routine. a_feature.set_builtin_code (tokens.builtin_unknown) if unknown_builtin_reported then set_fatal_error error_handler.report_gvkbu1a_error (current_class, a_feature) end end end check_builtin_bar_procedure_validity (a_feature: ET_EXTERNAL_PROCEDURE) -- Check validity of built-in `a_feature' from class "BAR". -- Set `has_fatal_error' if a fatal error occurred. require a_feature_not_void: a_feature /= Void local l_formals: ET_FORMAL_ARGUMENT_LIST do -- List procedure names first, then function names. if ... then ... elseif a_feature.name.same_feature_name (tokens.foo_feature_name) then a_feature.set_builtin_code (tokens.builtin_bar_feature (tokens.builtin_bar_foo)) l_formals := a_feature.arguments if l_formals = Void or else l_formals.count /= 1 then -- The signature should be 'foo (a: like Current)'. set_fatal_error error_handler.report_gvkbs0a_error (current_class, a_feature, <<tokens.like_current.type>>, Void) elseif not l_formals.formal_argument (1).type.same_syntactical_type (tokens.like_current, current_class, current_class) then -- The signature should be 'foo (a: like Current)'. set_fatal_error error_handler.report_gvkbs0a_error (current_class, a_feature, <<tokens.like_current.type>>, Void) end ... else -- Unknown built-in routine. a_feature.set_builtin_code (tokens.builtin_unknown) if unknown_builtin_reported then set_fatal_error error_handler.report_gvkbu1a_error (current_class, a_feature) end end end
Now if feature foo is a built-in function of type INTEGER, here is how the features check_builtin_bar_function_validity and check_builtin_bar_procedure_validity of class ET_BUILTIN_FEATURE_CHECKER will look like:
check_builtin_bar_function_validity (a_feature: ET_EXTERNAL_FUNCTION) -- Check validity of built-in `a_feature' from class "BAR". -- Set `has_fatal_error' if a fatal error occurred. require a_feature_not_void: a_feature /= Void local l_formals: ET_FORMAL_ARGUMENT_LIST do -- List function names first, then procedure names. if ... then ... elseif a_feature.name.same_feature_name (tokens.foo_feature_name) then a_feature.set_builtin_code (tokens.builtin_bar_feature (tokens.builtin_bar_foo)) l_formals := a_feature.arguments if l_formals = Void or else l_formals.count /= 1 then -- The signature should be 'foo (a: like Current): INTEGER'. set_fatal_error error_handler.report_gvkbs0a_error (current_class, a_feature, <<tokens.like_current.type>>, current_universe.integer_type) elseif not l_formals.formal_argument (1).type.same_syntactical_type (tokens.like_current, current_class, current_class) then -- The signature should be 'foo (a: like Current): INTEGER'. set_fatal_error error_handler.report_gvkbs0a_error (current_class, a_feature, <<tokens.like_current.type>>, current_universe.integer_type) elseif not a_feature.type.same_syntactical_type (current_universe.integer_type, current_class, current_class) then -- The signature should be 'foo (a: like Current): INTEGER'. set_fatal_error error_handler.report_gvkbs0a_error (current_class, a_feature, <<tokens.like_current.type>>, current_universe.integer_type) end ... else -- Unknown built-in routine. a_feature.set_builtin_code (tokens.builtin_unknown) if unknown_builtin_reported then set_fatal_error error_handler.report_gvkbu1a_error (current_class, a_feature) end end end check_builtin_bar_procedure_validity (a_feature: ET_EXTERNAL_PROCEDURE) -- Check validity of built-in `a_feature' from class "BAR". -- Set `has_fatal_error' if a fatal error occurred. require a_feature_not_void: a_feature /= Void local l_formals: ET_FORMAL_ARGUMENT_LIST do -- List procedure names first, then function names. if ... then ... elseif a_feature.name.same_feature_name (tokens.foo_feature_name) then -- 'BAR.foo' should be a function. a_feature.set_builtin_code (tokens.builtin_bar_feature (tokens.builtin_bar_foo)) set_fatal_error error_handler.report_gvkbs0a_error (current_class, a_feature, <<tokens.like_current.type>>, current_universe.integer_type) ... else -- Unknown built-in routine. a_feature.set_builtin_code (tokens.builtin_unknown) if unknown_builtin_reported then set_fatal_error error_handler.report_gvkbu1a_error (current_class, a_feature) end end end
Various examples with different combinations of argument and result types can be found in class ET_BUILTIN_FEATURE_CHECKER.
The Gobo Eiffel compiler uses the dynamic type sets of the target of the feature calls to implement dynamic binding and CAT-call checking. Each expression is associated with a dynamic type set which is then propagated when there is an assignment or argument passing. The dynamic type set of the 'Result' entity of built-in functions needs to be built. If it is only made up of exactly one type which is the declared type of the 'Result' entity of the built-in function, then nothing special needs to be done. The default implementation will take care of this case. On the other hand, if the dynamic type set is more complicated or if the built-in feature calls internally another feature whose dynamic type sets need to be updated as well (for example feature twin calls feature copy), then the following code needs to appear in class ET_DYNAMIC_TYPE_BUILDER:
By default, nothing needs to be done for built-in procedures. But there are some special cases when the built-in procedure will call internally another feature or will have an effect on the dynamic type set of some entities (for example in class SPECIAL, the procedure put will have an effect on the dynamic type set of item). In this case, code similar to the one above will be needed for these built-in procedures. Examples can be found in class ET_DYNAMIC_TYPE_BUILDER.feature {NONE} -- Feature validity check_external_builtin_function_validity (a_feature: ET_EXTERNAL_FUNCTION) -- Check validity of `a_feature'. -- `a_feature' is a built-in function. -- Set `has_fatal_error' if a fatal error occurred. require a_feature_not_void: a_feature /= Void a_feature_is_builtin: a_feature.is_builtin builtin_feature_known: not a_feature.is_builtin_unknown local l_builtin_class: INTEGER do l_builtin_class := a_feature.builtin_code // builtin_capacity inspect l_builtin_class ... when builtin_bar_class then inspect a_feature.builtin_code \\ builtin_capacity when builtin_bar_foo then report_builtin_bar_foo (a_feature) else report_builtin_function (a_feature) end ... else report_builtin_function (a_feature) end end feature {NONE} -- Built-in features report_builtin_bar_foo (a_feature: ET_EXTERNAL_FUNCTION) -- Report that built-in feature 'BAR.foo' is being analyzed. require no_error: not has_fatal_error a_feature_not_void: a_feature /= Void local l_result_type: ET_DYNAMIC_TYPE do if current_type = current_dynamic_type.base_type then ... l_result_type := result_type_set.static_type propagate_builtin_result_dynamic_types (l_result_type, current_dynamic_feature) ... end end
The last thing to do is to make sure that the C code of the built-in feature is properly generated. Let's assume again that the built-in feature is a function foo declared in class BAR. Then the following code needs to appear in class ET_C_GENERATOR:
In some cases, the C code can be inlined. This is when the call to the C function can be avoided by calling the C code directly. In that case the code above in class ET_C_GENERATOR can be changed by replacing print_builtin_bar_foo_body by print_builtin_bar_foo_call, by adding features print_builtin_query_call and print_builtin_bar_query_call, and by modifying feature print_external_builtin_bar_function_body as follows:feature {NONE} -- Feature generation print_external_builtin_function_body (a_feature: ET_EXTERNAL_ROUTINE) -- Print to `current_file' the body of built-in feature `a_feature'. -- `a_feature' is a built-in function. require a_feature_not_void: a_feature /= Void a_feature_is_function: a_feature.is_function a_feature_is_builtin: a_feature.is_builtin valid_feature: current_feature.static_feature = a_feature local l_builtin_class: INTEGER do l_builtin_class := a_feature.builtin_code // builtin_capacity inspect l_builtin_class ... when builtin_bar_class then print_external_builtin_bar_function_body (a_feature) ... else -- Internal error: unknown built-in feature. -- This error should already have been reported in ET_FEATURE_FLATTENER. set_fatal_error error_handler.report_giaaa_error end end print_external_builtin_bar_function_body (a_feature: ET_EXTERNAL_ROUTINE) -- Print to `current_file' the body of built-in feature `a_feature'. -- `a_feature' is a built-in function introduced in class "BAR". require a_feature_not_void: a_feature /= Void a_feature_is_function: a_feature.is_function a_feature_is_builtin: a_feature.is_builtin a_feature_is_builtin_platform: (a_feature.builtin_code // builtin_capacity) = builtin_bar_class valid_feature: current_feature.static_feature = a_feature do inspect a_feature.builtin_code \\ builtin_capacity ... when builtin_bar_foo then print_builtin_bar_foo_body (a_feature) ... else -- Internal error: unknown built-in feature. -- This error should already have been reported in ET_FEATURE_FLATTENER. set_fatal_error error_handler.report_giaaa_error end end feature {NONE} -- Built-in feature generation print_builtin_bar_foo_body (a_feature: ET_EXTERNAL_ROUTINE) -- Print to `current_file' the body of `a_feature' corresponding -- to built-in feature 'BAR.foo'. require a_feature_not_void: a_feature /= Void valid_feature: current_feature.static_feature = a_feature do ... end
The code is very similar when the built-in feature is a procedure. First the case where the C code cannot be inlined:feature {NONE} -- Feature generation print_external_builtin_bar_function_body (a_feature: ET_EXTERNAL_ROUTINE) -- Print to `current_file' the body of built-in feature `a_feature'. -- `a_feature' is a built-in function introduced in class "BAR". require a_feature_not_void: a_feature /= Void a_feature_is_function: a_feature.is_function a_feature_is_builtin: a_feature.is_builtin a_feature_is_builtin_platform: (a_feature.builtin_code // builtin_capacity) = builtin_bar_class valid_feature: current_feature.static_feature = a_feature do inspect a_feature.builtin_code \\ builtin_capacity ... when builtin_bar_foo then fill_call_formal_arguments (a_feature) print_indentation_assign_to_result print_builtin_bar_foo_call (current_feature, current_type, False) print_semicolon_newline call_operands.wipe_out ... else -- Internal error: unknown built-in feature. -- This error should already have been reported in ET_FEATURE_FLATTENER. set_fatal_error error_handler.report_giaaa_error end end feature {NONE} -- Query call generation print_builtin_query_call (a_feature: ET_DYNAMIC_FEATURE; a_target_type: ET_DYNAMIC_TYPE; a_check_void_target: BOOLEAN) -- Print to `current_file' a call to query `a_feature' (static binding). -- `a_feature' is a built-in feature. -- `a_target_type' is the dynamic type of the target. -- `a_check_void_target' means that we need to check whether the target is Void or not. -- Operands can be found in `call_operands'. -- Note that the result of the query is not adapted to match the kind -- of result type expected by the caller. It is recommended to use -- `print_adapted_query_call' whenever possible. require a_feature_not_void: a_feature /= Void a_feature_is_query: a_feature.result_type_set /= Void a_feature_is_builtin: a_feature.is_builtin a_target_type_not_void: a_target_type /= Void call_operands_not_empty: not call_operands.is_empty local l_builtin_class: INTEGER do l_builtin_class := a_feature.builtin_code // builtin_capacity inspect l_builtin_class ... when builtin_bar_class then print_builtin_bar_query_call (a_feature, a_target_type, a_check_void_target) ... else print_non_inlined_query_call (a_feature, a_target_type, a_check_void_target) end end print_builtin_bar_query_call (a_feature: ET_DYNAMIC_FEATURE; a_target_type: ET_DYNAMIC_TYPE; a_check_void_target: BOOLEAN) -- Print to `current_file' a call to query `a_feature' (static binding). -- `a_feature' is a built-in feature introduced in class "BAR". -- `a_target_type' is the dynamic type of the target. -- `a_check_void_target' means that we need to check whether the target is Void or not. -- Operands can be found in `call_operands'. -- Note that the result of the query is not adapted to match the kind -- of result type expected by the caller. It is recommended to use -- `print_adapted_query_call' whenever possible. require a_feature_not_void: a_feature /= Void a_feature_is_query: a_feature.result_type_set /= Void a_feature_is_builtin: a_feature.is_builtin a_feature_is_builtin_platform: (a_feature.builtin_code // builtin_capacity) = builtin_bar_class a_target_type_not_void: a_target_type /= Void call_operands_not_empty: not call_operands.is_empty do inspect a_feature.builtin_code \\ builtin_capacity ... when builtin_bar_foo then print_builtin_bar_foo_call (a_feature, a_target_type, a_check_void_target) ... else print_non_inlined_query_call (a_feature, a_target_type, a_check_void_target) end end feature {NONE} -- Built-in feature generation print_builtin_bar_foo_call (a_feature: ET_DYNAMIC_FEATURE; a_target_type: ET_DYNAMIC_TYPE; a_check_void_target: BOOLEAN) -- Print to `current_file' a call (static binding) to `a_feature' -- corresponding to built-in feature 'BAR.foo'. -- `a_target_type' is the dynamic type of the target. -- `a_check_void_target' means that we need to check whether the target is Void or not. -- Operands can be found in `call_operands'. require a_feature_not_void: a_feature /= Void a_target_type_not_void: a_target_type /= Void call_operands_not_empty: not call_operands.is_empty do ... end
And finally when the C code of the built-in procedure can be inlined, replacing print_builtin_bar_foo_body by print_builtin_bar_foo_call, adding features print_builtin_procedure_call and print_builtin_bar_procedure_call, and by modifying feature print_external_builtin_bar_procedure_body as follows::feature {NONE} -- Feature generation print_external_builtin_procedure_body (a_feature: ET_EXTERNAL_ROUTINE) -- Print to `current_file' the body of built-in feature `a_feature'. -- `a_feature' is a built-in procedure. require a_feature_not_void: a_feature /= Void a_feature_is_procedure: a_feature.is_procedure a_feature_is_builtin: a_feature.is_builtin valid_feature: current_feature.static_feature = a_feature local l_builtin_class: INTEGER do l_builtin_class := a_feature.builtin_code // builtin_capacity inspect l_builtin_class ... when builtin_bar_class then print_external_builtin_bar_procedure_body (a_feature) ... else -- Internal error: unknown built-in feature. -- This error should already have been reported in ET_FEATURE_FLATTENER. set_fatal_error error_handler.report_giaaa_error end end print_external_builtin_bar_procedure_body (a_feature: ET_EXTERNAL_ROUTINE) -- Print to `current_file' the body of built-in feature `a_feature'. -- `a_feature' is a built-in procedure introduced in class "BAR". require a_feature_not_void: a_feature /= Void a_feature_is_procedure: a_feature.is_procedure a_feature_is_builtin: a_feature.is_builtin a_feature_is_builtin_any: (a_feature.builtin_code // builtin_capacity) = builtin_bar_class valid_feature: current_feature.static_feature = a_feature do inspect a_feature.builtin_code \\ builtin_capacity ... when builtin_bar_foo then print_builtin_bar_foo_body (a_feature) ... else -- Internal error: unknown built-in feature. -- This error should already have been reported in ET_FEATURE_FLATTENER. set_fatal_error error_handler.report_giaaa_error end end feature {NONE} -- Built-in feature generation print_builtin_bar_foo_body (a_feature: ET_EXTERNAL_ROUTINE) -- Print to `current_file' the body of `a_feature' corresponding -- to built-in feature 'BAR.foo'. require a_feature_not_void: a_feature /= Void valid_feature: current_feature.static_feature = a_feature do ... end
feature {NONE} -- Feature generation print_external_builtin_bar_procedure_body (a_feature: ET_EXTERNAL_ROUTINE) -- Print to `current_file' the body of built-in feature `a_feature'. -- `a_feature' is a built-in procedure introduced in class "BAR". require a_feature_not_void: a_feature /= Void a_feature_is_procedure: a_feature.is_procedure a_feature_is_builtin: a_feature.is_builtin a_feature_is_builtin_any: (a_feature.builtin_code // builtin_capacity) = builtin_bar_class valid_feature: current_feature.static_feature = a_feature do inspect a_feature.builtin_code \\ builtin_capacity ... when builtin_bar_foo then fill_call_formal_arguments (a_feature) print_builtin_bar_foo_call (current_feature, current_type, False) call_operands.wipe_out ... else -- Internal error: unknown built-in feature. -- This error should already have been reported in ET_FEATURE_FLATTENER. set_fatal_error error_handler.report_giaaa_error end end feature {NONE} -- Procedure call generation print_builtin_procedure_call (a_feature: ET_DYNAMIC_FEATURE; a_target_type: ET_DYNAMIC_TYPE; a_check_void_target: BOOLEAN) -- Print to `current_file' a call to procedure `a_feature' (static binding). -- `a_feature' is a built-in feature. -- `a_target_type' is the dynamic type of the target. -- `a_check_void_target' means that we need to check whether the target is Void or not. -- Operands can be found in `call_operands'. require a_feature_not_void: a_feature /= Void a_feature_is_builtin: a_feature.is_builtin a_target_type_not_void: a_target_type /= Void call_operands_not_empty: not call_operands.is_empty local l_builtin_class: INTEGER do l_builtin_class := a_feature.builtin_code // builtin_capacity inspect l_builtin_class ... when builtin_bar_class then print_builtin_bar_procedure_call (a_feature, a_target_type, a_check_void_target) ... else print_non_inlined_procedure_call (a_feature, a_target_type, a_check_void_target) end end print_builtin_bar_procedure_call (a_feature: ET_DYNAMIC_FEATURE; a_target_type: ET_DYNAMIC_TYPE; a_check_void_target: BOOLEAN) -- Print to `current_file' a call to procedure `a_feature' (static binding). -- `a_feature' is a built-in feature introduced in class "BAR". -- `a_target_type' is the dynamic type of the target. -- `a_check_void_target' means that we need to check whether the target is Void or not. -- Operands can be found in `call_operands'. require a_feature_not_void: a_feature /= Void a_feature_is_builtin: a_feature.is_builtin a_feature_is_builtin_any: (a_feature.builtin_code // builtin_capacity) = builtin_bar_class a_target_type_not_void: a_target_type /= Void call_operands_not_empty: not call_operands.is_empty do inspect a_feature.builtin_code \\ builtin_capacity when builtin_bar_foo then print_builtin_bar_foo_call (a_feature, a_target_type, a_check_void_target) ... else print_non_inlined_procedure_call (a_feature, a_target_type, a_check_void_target) end end feature {NONE} -- Built-in feature generation print_builtin_bar_foo_call (a_feature: ET_DYNAMIC_FEATURE; a_target_type: ET_DYNAMIC_TYPE; a_check_void_target: BOOLEAN) -- Print to `current_file' a call (static binding) to `a_feature' -- corresponding to built-in feature 'BAR.foo'. -- `a_target_type' is the dynamic type of the target. -- `a_check_void_target' means that we need to check whether the target is Void or not. -- Operands can be found in `call_operands'. require a_feature_not_void: a_feature /= Void a_target_type_not_void: a_target_type /= Void call_operands_not_empty: not call_operands.is_empty do ... end
Copyright © 2010, Eric Bezault mailto:ericb@gobosoft.com http://www.gobosoft.com Last Updated: 27 June 2010 |
![]() ![]() ![]() ![]() |