# This file is part of NIT ( http://www.nitlanguage.org ). # # Copyright 2012 Alexis Laferrière # # Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Tools and utilities for language targetted FFI implementations module ffi_base import c_tools import parser import modelbuilder import nitni redef class ToolContext var ffi_language_assignation_phase: Phase = new FFILanguageAssignationPhase(self, null) end class FFILanguageAssignationPhase super Phase # All supported languages var languages = new Array[FFILanguage] redef fun process_nmodule(nmodule) do for block in nmodule.n_extern_code_blocks do verify_foreign_code_on_node( block ) end end redef fun process_npropdef(npropdef) do if npropdef isa AExternPropdef then var code_block = npropdef.n_extern_code_block if code_block != null then verify_foreign_code_on_node( code_block ) end end end redef fun process_nclassdef(nclassdef) do if nclassdef isa AStdClassdef and nclassdef.n_extern_code_block != null then verify_foreign_code_on_node( nclassdef.n_extern_code_block.as(not null) ) end end private fun verify_foreign_code_on_node(n: AExternCodeBlock) do var found = false for v in languages do var identified = v.identify_language(n) if identified then if found and identified then toolcontext.error(n.location, "Two languages identified as possible handlers.") end n.language = v found = true end end if not found then toolcontext.error(n.location, "Unsupported language.") end end redef class AModule var ffi_files = new Array[ExternFile] end redef class AExternCodeBlock fun language_name: nullable String do if n_in_language == null then return null return n_in_language.n_string.without_quotes end fun language_name_lowered: nullable String do if language_name == null then return null return language_name.to_lower end fun code: String do return n_extern_code_segment.without_guard var language: nullable FFILanguage = null end # Visitor for a specific languages. Works kinda like a `Phase` and is executed # by a `Phase`. class FFILanguage init(ffi_language_assignation_phase: FFILanguageAssignationPhase) do ffi_language_assignation_phase.languages.add(self) end # Is this `block` written in this language? fun identify_language(block: AExternCodeBlock ): Bool is abstract # Generate wrapper code for this module/header code block fun compile_module_block(block: AExternCodeBlock, ecc: CCompilationUnit, nmodule: AModule) is abstract # Generate wrapper code for this extern method fun compile_extern_method(block: AExternCodeBlock, m: AExternPropdef, ecc: CCompilationUnit, nmodule: AModule) is abstract # Generate wrapper code for this extern class fun compile_extern_class(block: AExternCodeBlock, m: AClassdef, ecc: CCompilationUnit, nmodule: AModule) is abstract # Get the foreign type of this extern class definition fun get_ftype(block: AExternCodeBlock, m: AClassdef): ForeignType is abstract # Generate the code to offer this callback if foreign code fun compile_callback(callback: NitniCallback, nmodule: AModule, mainmmodule: MModule, ecc: CCompilationUnit) is abstract # Complete compilation of generated code fun compile_to_files(amodule: AModule, directory: String) do end end redef class TString # Returns the content of this node without both of the surrounding " fun without_quotes: String do assert text.length >= 2 return text.substring(1, text.length-2) end end redef class TExternCodeSegment # Returns the content of this node without the surrounding `{ and `} fun without_guard: String do assert text.length >= 4 return text.substring(2, text.length-4) end end # An extern file to compile class ExternFile # The filename of the file var filename: String fun makefile_rule_name: String is abstract fun makefile_rule_content: String is abstract end redef class CCompilationUnit fun write_as_impl( amodule: AModule, compdir: String ) do var base_name = "{amodule.mmodule.name}._ffi" var h_file = "{base_name}.h" var guard = "{amodule.cname.to_s.to_upper}_NIT_H" write_header_to_file( amodule, "{compdir}/{h_file}", new Array[String], guard) var c_file = "{base_name}.c" write_body_to_file( amodule, "{compdir}/{c_file}", ["", "", "\"{h_file}\""] ) files.add( "{compdir}/{c_file}" ) end fun write_header_to_file(amodule: AModule, file: String, includes: Array[String], guard: String) do var stream = new OFStream.open( file ) # header comments var module_info = "/*\n\tExtern implementation of Nit module {amodule.mmodule.name}\n*/\n" stream.write( module_info ) stream.write( "#ifndef {guard}\n" ) stream.write( "#define {guard}\n\n" ) for incl in includes do stream.write( "#include {incl}\n" ) compile_header_core( stream ) # header file guard close stream.write( "#endif\n" ) stream.close end fun write_body_to_file(amodule: AModule, file: String, includes: Array[String]) do var stream = new OFStream.open(file) var module_info = "/*\n\tExtern implementation of Nit module {amodule.mmodule.name}\n*/\n" stream.write( module_info ) for incl in includes do stream.write( "#include {incl}\n" ) compile_body_core( stream ) stream.close end end class ForeignType fun ctype: String do return "void*" end