Top Level Namespace

Defined Under Namespace

Modules: CTokenizer, CType, DBC, OCL, ParseErrorHandler, Preprocessor Classes: Module, SearchPath, String

Instance Method Summary collapse

Instance Method Details

#expand_function(conditions, context, body, start_line, line_info) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/dbc/expand_function.rb', line 8

def expand_function(conditions, context, body, start_line, line_info)
	raise 'should be a function' unless context.function?

	context_str = context.short_name
	f_cond = conditions[context_str]

	pre_text = ''		# preconditions
	post_text = ''	# postconditions
	inv_text = ''		# invariants
	init_text = ''	# initializations
	final_text = ''	# stuff at the end

	if f_cond
		# get the conditions
		f_cond.conditions.each do |c|
			case c.type
				when "pre"
					pre_text
				when "post"
					post_text
				else
					raise ParseError, "unexpected condition type: #{c.type} for #{context_str}"
			end << c.to_cexp("\t")
		end
		# set up old values
		f_cond.old_values.each do |identifier, var_used|
			# figures out the type of identifiers such as a->b->c
			begin
				p_type = context.evaluate(identifier)
			rescue CType::EvaluationError
				raise CType::EvaluationError, "#{line_info}: in #{context_str}, #{$!} for #{identifier}"
			end
			init_text << "\t" << p_type.to_init_s(var_used)
			init_text << ' = ' << identifier << ";\n"
		end
	end

	context.parameters.each do |p|
		inv = conditions[p.short_name]
		if inv then inv.conditions.each do |c|
			raise ParseError, "expecting invariant got #{c.long_type} for #{p.to_s}" \
				unless c.type == "inv"
			exp_str = c.to_cexp("\t", context_str)
			# fix me - this could be better
			sub_str = '\1' + p.identifier + '\2'
			exp_str.gsub!(/(\W)self(\W+)/, sub_str)
			inv_text << exp_str
		end; end
	end

	void_return_type = (context.base_type.to_s == 'void')

	unless void_return_type or \
				(post_text.empty? and inv_text.empty?)
		init_text << "\t" << context.base_type.to_init_s('ocl__ret_val') << ";\n"
	end
	
	outstr = ''

	if pre_text.empty? and post_text.empty? and inv_text.empty?
		# we're done early
		body.each do |t|
			outstr << t[1]
		end
		return outstr
	end

	outstr << "\n" << init_text

	# output invariants and preconditions
	outstr << "\t/* Invariants: */\n" << inv_text unless inv_text.empty?
	outstr << "\t/* Preconditions: */\n" << pre_text unless pre_text.empty?

	outstr << "{\n"
	outstr << '#line ' << start_line.to_s << "\n" if line_info

	# output function body
	if post_text.empty? and inv_text.empty?
		body.each do |t|
			outstr << t[1]
			start_line += CTokenizer.line_count(t[1])
		end
	else
		output_label = false
		body = body.dup
		until body.empty?
			t = body.shift
			start_line += CTokenizer.line_count(t[1])
			# replace return statements with goto statements.
			# if a value is returned this value is saved in ocl__ret_val
			if t[1] == 'return'
				output_label = true
				if void_return_type
					outstr << 'goto ocl__return_here'
				else
					outstr << '{ ocl__ret_val = ( '
					loop do
						raise "syntax error" if body.empty?
						t = body.shift
						start_line += CTokenizer.line_count(t[1])
						break if t[1] == ';'
						outstr << t[1]
					end
					outstr << ' ); goto ocl__return_here; }'
				end
			else # output all other tokens
				outstr << t[1]
			end
		end
		outstr << "\nocl__return_here:\n" if output_label
		# must explicitly return the return value unless return value is void
		final_text << "\treturn ocl__ret_val;\n" unless void_return_type
	end
	outstr << "\n"

	# output postconditions and invariants
	outstr << "\t/* Postconditions: */\n" << post_text unless post_text.empty?
	outstr << "\t/* Invariants: */\n" << inv_text unless inv_text.empty?

	outstr << final_text
	outstr << "}\n"
	outstr << '#line ' << start_line.to_s << "\n" if line_info
	outstr
end