1/* valaccodefunction.vala
2 *
3 * Copyright (C) 2006-2012  Jürg Billeter
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Lesser General Public License for more details.
14
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
18 *
19 * Author:
20 * 	Jürg Billeter <j@bitron.ch>
21 */
22
23using GLib;
24
25/**
26 * Represents a function declaration in the C code.
27 */
28public class Vala.CCodeFunction : CCodeNode {
29	/**
30	 * The name of this function.
31	 */
32	public string name { get; set; }
33
34	/**
35	 * The function return type.
36	 */
37	public string return_type { get; set; }
38
39	public bool is_declaration { get; set; }
40
41	/**
42	 * The function body.
43	 */
44	public CCodeBlock block { get; set; }
45
46	/**
47	 * The current line directive.
48	 */
49	public CCodeLineDirective current_line { get; set; }
50
51	/**
52	 * The current block to be written into.
53	 */
54	public CCodeBlock current_block { get; set; }
55
56	private List<CCodeParameter> parameters = new ArrayList<CCodeParameter> ();
57
58	List<CCodeStatement> statement_stack = new ArrayList<CCodeStatement> ();
59
60	public CCodeFunction (string name, string return_type = "void") {
61		this.name = name;
62		this.return_type = return_type;
63		this.block = new CCodeBlock ();
64		current_block = block;
65	}
66
67	/**
68	 * Appends the specified parameter to the list of function parameters.
69	 *
70	 * @param param a formal parameter
71	 */
72	public void add_parameter (CCodeParameter param) {
73		parameters.add (param);
74	}
75
76	public void insert_parameter (int position, CCodeParameter param) {
77		parameters.insert (position, param);
78	}
79
80	public int get_parameter_count () {
81		return parameters.size;
82	}
83
84	public CCodeParameter get_parameter (int position) {
85		return parameters[position];
86	}
87
88	/**
89	 * Returns a copy of this function.
90	 *
91	 * @return copied function
92	 */
93	public CCodeFunction copy () {
94		var func = new CCodeFunction (name, return_type);
95		func.modifiers = modifiers;
96
97		/* no deep copy for lists available yet
98		 * func.parameters = parameters.copy ();
99		 */
100		foreach (CCodeParameter param in parameters) {
101			func.parameters.add (param);
102		}
103
104		func.is_declaration = is_declaration;
105		func.block = block;
106		return func;
107	}
108
109	public override void write (CCodeWriter writer) {
110		writer.write_indent (line);
111		if (CCodeModifiers.INTERNAL in modifiers) {
112			writer.write_string ("G_GNUC_INTERNAL ");
113		}
114		if (!is_declaration && CCodeModifiers.NO_INLINE in modifiers) {
115			writer.write_string ("G_GNUC_NO_INLINE ");
116		}
117		if (CCodeModifiers.STATIC in modifiers) {
118			writer.write_string ("static ");
119		}
120		if (CCodeModifiers.INLINE in modifiers) {
121			writer.write_string ("inline ");
122		}
123		writer.write_string (return_type);
124		if (is_declaration) {
125			writer.write_string (" ");
126		} else {
127			writer.write_newline ();
128		}
129		writer.write_string (name);
130		writer.write_string (" (");
131		int param_pos_begin = (is_declaration ? return_type.char_count () + 1 : 0 ) + name.char_count () + 2;
132
133		bool has_args = (CCodeModifiers.PRINTF in modifiers || CCodeModifiers.SCANF in modifiers);
134		int i = 0;
135		int format_arg_index = -1;
136		int args_index = -1;
137		foreach (CCodeParameter param in parameters) {
138			if (i > 0) {
139				writer.write_string (",");
140				writer.write_newline ();
141				writer.write_nspaces (param_pos_begin);
142			}
143			param.write (writer);
144			if (CCodeModifiers.FORMAT_ARG in param.modifiers) {
145				format_arg_index = i;
146			}
147			if (has_args && param.ellipsis) {
148				args_index = i;
149			} else if (has_args && param.type_name == "va_list" && format_arg_index < 0) {
150				format_arg_index = i - 1;
151			}
152			i++;
153		}
154		if (i == 0) {
155			writer.write_string ("void");
156		}
157
158		writer.write_string (")");
159
160		if (is_declaration) {
161			if (CCodeModifiers.DEPRECATED in modifiers) {
162				writer.write_string (" G_GNUC_DEPRECATED");
163			}
164
165			if (CCodeModifiers.PRINTF in modifiers) {
166				format_arg_index = (format_arg_index >= 0 ? format_arg_index + 1 : args_index);
167				writer.write_string (" G_GNUC_PRINTF(%d,%d)".printf (format_arg_index, args_index + 1));
168			} else if (CCodeModifiers.SCANF in modifiers) {
169				format_arg_index = (format_arg_index >= 0 ? format_arg_index + 1 : args_index);
170				writer.write_string (" G_GNUC_SCANF(%d,%d)".printf (format_arg_index, args_index + 1));
171			} else if (format_arg_index >= 0) {
172				writer.write_string (" G_GNUC_FORMAT(%d)".printf (format_arg_index + 1));
173			}
174
175			if (CCodeModifiers.CONST in modifiers) {
176				writer.write_string (" G_GNUC_CONST");
177			}
178			if (CCodeModifiers.UNUSED in modifiers) {
179				writer.write_string (" G_GNUC_UNUSED");
180			}
181
182			if (CCodeModifiers.CONSTRUCTOR in modifiers) {
183				writer.write_string (" __attribute__((constructor))");
184			} else if (CCodeModifiers.DESTRUCTOR in modifiers) {
185				writer.write_string (" __attribute__((destructor))");
186			}
187
188			writer.write_string (";");
189		} else {
190			writer.write_newline ();
191			block.write (writer);
192			writer.write_newline ();
193		}
194		writer.write_newline ();
195	}
196
197	public void add_statement (CCodeNode stmt) {
198		stmt.line = current_line;
199		current_block.add_statement (stmt);
200	}
201
202	public void open_block () {
203		statement_stack.add (current_block);
204		var parent_block = current_block;
205
206		current_block = new CCodeBlock ();
207
208		parent_block.add_statement (current_block);
209	}
210
211	public void open_if (CCodeExpression condition) {
212		statement_stack.add (current_block);
213		var parent_block = current_block;
214
215		current_block = new CCodeBlock ();
216
217		var cif = new CCodeIfStatement (condition, current_block);
218		cif.line = current_line;
219		statement_stack.add (cif);
220
221		parent_block.add_statement (cif);
222	}
223
224	public void add_else () {
225		current_block = new CCodeBlock ();
226
227		var cif = (CCodeIfStatement) statement_stack[statement_stack.size - 1];
228		cif.line = current_line;
229		assert (cif.false_statement == null);
230		cif.false_statement = current_block;
231	}
232
233	public void else_if (CCodeExpression condition) {
234		var parent_if = (CCodeIfStatement) statement_stack.remove_at (statement_stack.size - 1);
235		assert (parent_if.false_statement == null);
236
237		current_block = new CCodeBlock ();
238
239		var cif = new CCodeIfStatement (condition, current_block);
240		cif.line = current_line;
241		parent_if.false_statement = cif;
242		statement_stack.add (cif);
243	}
244
245	public void open_while (CCodeExpression condition) {
246		statement_stack.add (current_block);
247		var parent_block = current_block;
248
249		current_block = new CCodeBlock ();
250
251		var cwhile = new CCodeWhileStatement (condition, current_block);
252		cwhile.line = current_line;
253		parent_block.add_statement (cwhile);
254	}
255
256	public void open_for (CCodeExpression? initializer, CCodeExpression condition, CCodeExpression? iterator) {
257		statement_stack.add (current_block);
258		var parent_block = current_block;
259
260		current_block = new CCodeBlock ();
261
262		var cfor = new CCodeForStatement (condition, current_block);
263		cfor.line = current_line;
264		if (initializer != null) {
265			cfor.add_initializer (initializer);
266		}
267		if (iterator != null) {
268			cfor.add_iterator (iterator);
269		}
270
271		parent_block.add_statement (cfor);
272	}
273
274	public void open_switch (CCodeExpression expression) {
275		statement_stack.add (current_block);
276		var parent_block = current_block;
277
278		var cswitch = new CCodeSwitchStatement (expression);
279		cswitch.line = current_line;
280		current_block = cswitch;
281
282		parent_block.add_statement (cswitch);
283	}
284
285	public void add_label (string label) {
286		add_statement (new CCodeLabel (label));
287	}
288
289	public void add_case (CCodeExpression expression) {
290		add_statement (new CCodeCaseStatement (expression));
291	}
292
293	public void add_default () {
294		add_statement (new CCodeLabel ("default"));
295	}
296
297	public void add_goto (string target) {
298		add_statement (new CCodeGotoStatement (target));
299	}
300
301	public void add_expression (CCodeExpression expression) {
302		add_statement (new CCodeExpressionStatement (expression));
303	}
304
305	public void add_assignment (CCodeExpression left, CCodeExpression right) {
306		add_expression (new CCodeAssignment (left, right));
307	}
308
309	public void add_return (CCodeExpression? expression = null) {
310		add_statement (new CCodeReturnStatement (expression));
311	}
312
313	public void add_break () {
314		add_statement (new CCodeBreakStatement ());
315	}
316
317	public void add_continue () {
318		add_statement (new CCodeContinueStatement ());
319	}
320
321	public void add_declaration (string type_name, CCodeDeclarator declarator, CCodeModifiers modifiers = 0) {
322		var stmt = new CCodeDeclaration (type_name);
323		stmt.add_declarator (declarator);
324		stmt.modifiers = modifiers;
325		add_statement (stmt);
326	}
327
328	public void close () {
329		do {
330			var top = statement_stack.remove_at (statement_stack.size - 1);
331			current_block = top as CCodeBlock;
332		} while (current_block == null);
333	}
334}
335