Rename REAMDE to README.md
[nit.git] / src / ffi / light_ffi_base.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Tools and utilities for implement FFI with different languages
18 module light_ffi_base
19
20 import c_tools
21 import parser
22 import modelbuilder
23 import nitni::nitni_utilities
24
25 redef class ToolContext
26 # Phase that assign a `FFILanguage` to all `AExternCodeBlock`
27 var ffi_language_assignation_phase: Phase = new FFILanguageAssignationPhase(self, null)
28 end
29
30 # Phase that assign a `FFILanguage` to all `AExternCodeBlock`
31 #
32 # It will also report errors when using an unknown foreign langages.
33 class FFILanguageAssignationPhase
34 super Phase
35
36 # All supported languages
37 var languages = new Array[FFILanguage]
38
39 redef fun process_nmodule(nmodule)
40 do
41 for block in nmodule.n_extern_code_blocks do
42 verify_foreign_code_on_node( block )
43 end
44 end
45
46 redef fun process_npropdef(npropdef)
47 do
48 if npropdef isa AMethPropdef then
49 var code_block = npropdef.n_extern_code_block
50 if code_block != null then
51 verify_foreign_code_on_node( code_block )
52 end
53 end
54 end
55
56 redef fun process_nclassdef(nclassdef)
57 do
58 if nclassdef isa AStdClassdef and nclassdef.n_extern_code_block != null then
59 verify_foreign_code_on_node( nclassdef.n_extern_code_block.as(not null) )
60 end
61 end
62
63 private fun verify_foreign_code_on_node(n: AExternCodeBlock)
64 do
65 var found = false
66 for v in languages do
67 var identified = v.identify_language(n)
68 if identified then
69 if found and identified then
70 toolcontext.error(n.location, "FFI Error: two languages identified as possible handlers.")
71 end
72 n.language = v
73 found = true
74 end
75 end
76
77 if not found then toolcontext.error(n.location, "FFI Error: unsupported language.")
78 end
79 end
80
81 redef class MModule
82 # All FFI files linked to this module
83 var ffi_files = new Array[ExternFile]
84 end
85
86 redef class AExternCodeBlock
87 # User entered name for the language of this block
88 fun language_name: nullable String do
89 if n_in_language == null then return null
90 return n_in_language.n_string.without_quotes
91 end
92
93 # `language_name`, in lower case
94 protected fun language_name_lowered: nullable String do
95 if language_name == null then return null
96 return language_name.to_lower
97 end
98
99 # User entered foreign code in the block
100 fun code: String do return n_extern_code_segment.without_guard
101
102 # `FFILanguage` assigned to this block
103 var language: nullable FFILanguage = null
104 end
105
106 # Visitor for a specific languages. Works kinda like a `Phase` and is executed
107 # by a `Phase`.
108 class FFILanguage
109 # `FFILanguageAssignationPhase` assigning `self` to `AExternCodeBlock`s
110 var ffi_language_assignation_phase: FFILanguageAssignationPhase
111
112 init
113 do
114 ffi_language_assignation_phase.languages.add(self)
115 end
116
117 # Is this `block` written in this language?
118 fun identify_language(block: AExternCodeBlock ): Bool is abstract
119
120 # Generate wrapper code for this module/header code block
121 fun compile_module_block(block: AExternCodeBlock, ecc: CCompilationUnit, mmodule: MModule) is abstract
122
123 # Generate wrapper code for this extern method
124 fun compile_extern_method(block: AExternCodeBlock, m: AMethPropdef,
125 ecc: CCompilationUnit, nmodule: MModule) is abstract
126
127 # Generate wrapper code for this extern class
128 fun compile_extern_class(block: AExternCodeBlock, m: AClassdef,
129 ecc: CCompilationUnit, mmodule: MModule) is abstract
130
131 # Get the foreign type of this extern class definition
132 fun get_ftype(block: AExternCodeBlock, m: AClassdef): ForeignType is abstract
133
134 # Complete compilation of generated code
135 fun compile_to_files(mmodule: MModule, directory: String) do end
136 end
137
138 redef class TString
139 # Returns the content of this node without both of the surrounding "
140 fun without_quotes: String
141 do
142 assert text.length >= 2
143 return text.substring(1, text.length-2)
144 end
145 end
146
147 redef class TExternCodeSegment
148 # Returns the content of this node without the surrounding `{ and `}
149 fun without_guard: String
150 do
151 assert text.length >= 4
152 return text.substring(2, text.length-4)
153 end
154 end
155
156 redef class CCompilationUnit
157 # Compile as `_ffi` files which contains the implementation of extern methods
158 fun write_as_impl(mmodule: MModule, compdir: String)
159 do
160 var base_name = "{mmodule.c_name}._ffi"
161
162 var h_file = "{base_name}.h"
163 var guard = "{mmodule.c_name.to_upper}_NIT_H"
164 write_header_to_file(mmodule, "{compdir}/{h_file}", new Array[String], guard)
165
166 var c_file = "{base_name}.c"
167 write_body_to_file(mmodule, "{compdir}/{c_file}", ["<stdlib.h>", "<stdio.h>", "\"{h_file}\""])
168
169 files.add( "{compdir}/{c_file}" )
170 end
171
172 # Write the header part to `file` including all `includes` using the `guard`
173 fun write_header_to_file(mmodule: MModule, file: String, includes: Array[String], guard: String)
174 do
175 var stream = new FileWriter.open( file )
176
177 # header comments
178 var module_info = "/*\n\tExtern implementation of Nit module {mmodule.name}\n*/\n"
179
180 stream.write( module_info )
181
182 stream.write( "#ifndef {guard}\n" )
183 stream.write( "#define {guard}\n\n" )
184
185 for incl in includes do stream.write( "#include {incl}\n" )
186
187 compile_header_core( stream )
188
189 # header file guard close
190 stream.write( "#endif\n" )
191 stream.close
192 end
193
194 # Write the body part to `file` including all `includes`
195 fun write_body_to_file(mmodule: MModule, file: String, includes: Array[String])
196 do
197 var stream = new FileWriter.open(file)
198
199 var module_info = "/*\n\tExtern implementation of Nit module {mmodule.name}\n*/\n"
200
201 stream.write( module_info )
202 for incl in includes do stream.write( "#include {incl}\n" )
203
204 compile_body_core( stream )
205
206 stream.close
207 end
208 end
209
210 # Foreign equivalent types of extern classes
211 class ForeignType
212 # C type of `self`, by default it is `void*`
213 fun ctype: String do return "void*"
214 end