source: EcnlProtoTool/trunk/webapp/webmrbc/Ruby/Math.cs@ 270

Last change on this file since 270 was 270, checked in by coas-nagasima, 7 years ago

mruby版ECNLプロトタイピング・ツールを追加

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-csharp
File size: 11.2 KB
Line 
1// Porting from
2// https://github.com/jeanlazarou/blockly2ruby
3// Copyright (c) 2014 Jean Lazarou
4// MIT Lisence
5using System;
6using Bridge;
7using System.Collections.Generic;
8
9namespace WebMrbc
10{
11 partial class Ruby
12 {
13 public node math_number(MathNumberBlock block)
14 {
15 // Numeric value.
16 var num = block.getFieldValue("NUM");
17 return new_num_node(num);
18 }
19
20 static Dictionary<string, string> ARITHMETIC_OPERATORS = new Dictionary<string, string>() {
21 { "ADD", "+" },
22 { "MINUS", "-" },
23 { "MULTIPLY", "*" },
24 { "DIVIDE", "/" },
25 { "POWER", "**" }
26 };
27
28 public node math_arithmetic(MathArithmeticBlock block)
29 {
30 // Basic arithmetic operators, and power.
31 var @operator = ARITHMETIC_OPERATORS[block.getFieldValue("OP")];
32 var argument0 = valueToCode(block, "A");
33 if (argument0 == null) argument0 = new int_node(this, 0);
34 var argument1 = valueToCode(block, "B");
35 if (argument1 == null) argument1 = new int_node(this, 0);
36 var code = argument0 + @operator + argument1;
37 return new call_node(this, argument0, intern(@operator), argument1);
38 }
39
40 public node math_single(Block block)
41 {
42 // Math operators with single operand.
43 var @operator = block.getFieldValue("OP");
44 node code = null;
45 node arg;
46 if (@operator == "NEG") {
47 // Negation is a special case given its different operator precedence.
48 code = valueToCode(block, "NUM");
49 if (code == null) code = new int_node(this, 0);
50 return new call_node(this, code, intern("-@"), (node)null);
51 }
52 if (@operator == "SIN" || @operator == "COS" || @operator == "TAN") {
53 arg = valueToCode(block, "NUM");
54 if (arg == null) arg = new int_node(this, 0);
55 }
56 else {
57 arg = valueToCode(block, "NUM");
58 if (arg == null) arg = new int_node(this, 0);
59 }
60 // First, handle cases which generate values that don't need parentheses
61 // wrapping the code.
62 var math = new const_node(this, intern("Math"));
63 switch (@operator) {
64 case "ABS":
65 code = new call_node(this, arg, intern("abs"));
66 break;
67 case "ROOT":
68 code = new call_node(this, math, intern("sqrt"), new node[] { arg }, null);
69 break;
70 case "LN":
71 code = new call_node(this, math, intern("log"), new node[] { arg }, null);
72 break;
73 case "LOG10":
74 code = new call_node(this, math, intern("log10"), new node[] { arg }, null);
75 break;
76 case "EXP":
77 code = new call_node(this, math, intern("exp"), new node[] { arg }, null);
78 break;
79 case "POW10":
80 code = new call_node(this, new int_node(this, 10), intern("exp"), new node[] { arg }, null);
81 break;
82 case "ROUND":
83 code = new call_node(this, arg, intern("round"));
84 break;
85 case "ROUNDUP":
86 code = new call_node(this, arg, intern("ceil"));
87 break;
88 case "ROUNDDOWN":
89 code = new call_node(this, arg, intern("floor"));
90 break;
91 case "SIN":
92 arg = new call_node(this, arg, intern("/"), new float_node(this, 180.0));
93 arg = new call_node(this, arg, intern("*"), new colon2_node(this, math, intern("PI")));
94 code = new call_node(this, math, intern("sin"), new node[] { arg }, null);
95 break;
96 case "COS":
97 arg = new call_node(this, arg, intern("/"), new float_node(this, 180.0));
98 arg = new call_node(this, arg, intern("*"), new colon2_node(this, math, intern("PI")));
99 code = new call_node(this, math, intern("cos"), new node[] { arg }, null);
100 break;
101 case "TAN":
102 arg = new call_node(this, arg, intern("/"), new float_node(this, 180.0));
103 arg = new call_node(this, arg, intern("*"), new colon2_node(this, math, intern("PI")));
104 code = new call_node(this, math, intern("tan"), new node[] { arg }, null);
105 break;
106 }
107 if (code != null) {
108 return code;
109 }
110 // Second, handle cases which generate values that may need parentheses
111 // wrapping the code.
112 switch (@operator) {
113 case "ASIN":
114 code = new call_node(this, math, intern("asin"), new node[] { arg }, null);
115 code = new call_node(this, code, intern("/"), new colon2_node(this, math, intern("PI")));
116 code = new call_node(this, code, intern("*"), new float_node(this, 180.0));
117 break;
118 case "ACOS":
119 code = new call_node(this, math, intern("acos"), new node[] { arg }, null);
120 code = new call_node(this, code, intern("/"), new colon2_node(this, math, intern("PI")));
121 code = new call_node(this, code, intern("*"), new float_node(this, 180.0));
122 break;
123 case "ATAN":
124 code = new call_node(this, math, intern("atan"), new node[] { arg }, null);
125 code = new call_node(this, code, intern("/"), new colon2_node(this, math, intern("PI")));
126 code = new call_node(this, code, intern("*"), new float_node(this, 180.0));
127 break;
128 default:
129 throw new Exception("Unknown math operator: " + @operator);
130 }
131 return new begin_node(this, code, true);
132 }
133
134 public node math_constant(MathConstantBlock block)
135 {
136 // Constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY.
137 var constant = block.getFieldValue("CONSTANT");
138 var math = new const_node(this, intern("Math"));
139 node code;
140 switch (constant) {
141 case "PI":
142 return new colon2_node(this, math, intern("PI"));
143 case "E":
144 return new colon2_node(this, math, intern("E"));
145 case "GOLDEN_RATIO":
146 code = new call_node(this, math, intern("sqrt"), new node[] { new float_node(this, 5) }, null);
147 code = new call_node(this, new float_node(this, 1), intern("+"), code);
148 code = new call_node(this, new begin_node(this, code, true), intern("/"), new float_node(this, 2));
149 return code;
150 case "SQRT2":
151 return new call_node(this, math, intern("sqrt"), new node[] { new float_node(this, 2) }, null);
152 case "SQRT1_2":
153 code = new call_node(this, new float_node(this, 1), intern("/"), new float_node(this, 2));
154 return new call_node(this, math, intern("sqrt"), new node[] { code }, null);
155 case "INFINITY":
156 return new call_node(this, new float_node(this, 1), intern("/"), new float_node(this, 0));
157 }
158 return null;
159 }
160
161 public node math_number_property(MathNumberPropertyBlock block)
162 {
163 // Check if a number is even, odd, prime, whole, positive, or negative
164 // or if it is divisible by certain number. Returns true or false.
165 var number_to_check = valueToCode(block, "NUMBER_TO_CHECK");
166 if (number_to_check == null) number_to_check = new int_node(this, 0);
167 var dropdown_property = block.getFieldValue("PROPERTY");
168 node code = null;
169 if (dropdown_property == "PRIME") {
170 return new fcall_node(this, intern("is_prime"), new node[] { number_to_check }, null);
171 }
172 switch (dropdown_property) {
173 case "EVEN":
174 return new call_node(this, number_to_check, intern("even?"));
175 case "ODD":
176 return new call_node(this, number_to_check, intern("odd?"));
177 case "WHOLE":
178 code = new call_node(this, number_to_check, intern("%"), new int_node(this, 1));
179 return new call_node(this, code, intern("=="), new int_node(this, 0));
180 case "POSITIVE":
181 return new call_node(this, number_to_check, intern(">"), new node[] { new int_node(this, 0) }, null);
182 case "NEGATIVE":
183 return new call_node(this, number_to_check, intern("<"), new node[] { new int_node(this, 0) }, null);
184 case "DIVISIBLE_BY":
185 var divisor = valueToCode(block, "DIVISOR");
186 // If "divisor" is some code that evals to 0, Ruby will raise an error.
187 if (divisor == null || (divisor is int_node && ((int_node)divisor).to_i() == 0)
188 || (divisor is float_node && ((float_node)divisor).to_f() == 0.0)) {
189 return new false_node(this);
190 }
191 code = new call_node(this, number_to_check, intern("%"), divisor);
192 return new call_node(this, code, intern("=="), new int_node(this, 0));
193 }
194 return null;
195 }
196
197 public node math_change(MathChangeBlock block)
198 {
199 // Add to a variable in place.
200 var argument0 = valueToCode(block, "DELTA");
201 if (argument0 == null) argument0 = new int_node(this, 0);
202 var varName = get_var_name(block.getFieldValue("VAR"));
203 return new op_asgn_node(this, new_var_node(varName), intern("+"), argument0);
204 }
205
206 // Rounding functions have a single operand.
207 public node math_round(MathRoundBlock block) { return math_single(block); }
208 // Trigonometry functions have a single operand.
209 public node math_trig(MathTrigBlock block) { return math_single(block); }
210
211 public node math_on_list(MathOnListBlock block)
212 {
213 // Math functions for lists.
214 var func = block.getFieldValue("OP");
215 var list = valueToCode(block, "LIST");
216 if (list == null) list = new array_node(this, new node[0]);
217 node code;
218 switch (func) {
219 case "SUM":
220 code = new call_node(this, list, intern("sum"));
221 break;
222 case "MIN":
223 code = new call_node(this, list, intern("numbers"));
224 code = new call_node(this, code, intern("min"));
225 break;
226 case "MAX":
227 code = new call_node(this, list, intern("numbers"));
228 code = new call_node(this, code, intern("max"));
229 break;
230 case "AVERAGE":
231 code = new call_node(this, list, intern("average"));
232 break;
233 case "MEDIAN":
234 code = new call_node(this, list, intern("median"));
235 break;
236 case "MODE":
237 // As a list of numbers can contain more than one mode,
238 // the returned result is provided as an array.
239 // Mode of [3, "x", "x", 1, 1, 2, "3"] -> ["x", 1].
240 code = new fcall_node(this, intern("math_modes"), new node[] { list }, null);
241 break;
242 case "STD_DEV":
243 code = new call_node(this, list, intern("standard_deviation"));
244 break;
245 case "RANDOM":
246 code = new call_node(this, list, intern("size"));
247 code = new fcall_node(this, intern("rand"), new node[] { code }, null);
248 code = new call_node(this, list, intern("[]"), new node[] { code }, null);
249 break;
250 default:
251 throw new Exception("Unknown operator: " + func);
252 }
253 return code;
254 }
255
256 public node math_modulo(MathModuloBlock block)
257 {
258 // Remainder computation.
259 var argument0 = valueToCode(block, "DIVIDEND");
260 if (argument0 == null) argument0 = new int_node(this, 0);
261 var argument1 = valueToCode(block, "DIVISOR");
262 if (argument1 == null) argument1 = new int_node(this, 0);
263 return new call_node(this, argument0, intern("%"), argument1);
264 }
265
266 public node math_constrain(MathConstrainBlock block)
267 {
268 // Constrain a number between two limits.
269 var argument0 = valueToCode(block, "VALUE");
270 if (argument0 == null) argument0 = new int_node(this, 0);
271 var argument1 = valueToCode(block, "LOW");
272 if (argument1 == null) argument1 = new int_node(this, 0);
273 var argument2 = valueToCode(block, "HIGH");
274 if (argument2 == null) argument2 = new fcall_node(this, intern("Float"), new node[] { new str_node(this, "inf") }, null);
275 node code = new array_node(this, new node[] { argument0, argument1 });
276 code = new call_node(this, code, intern("max"));
277 code = new array_node(this, new node[] { code, argument2 });
278 return new call_node(this, code, intern("min"));
279 }
280
281 public node math_random_int(MathRandomIntBlock block)
282 {
283 // Random integer between [X] and [Y].
284 var argument0 = valueToCode(block, "FROM");
285 if (argument0 == null) argument0 = new int_node(this, 0);
286 var argument1 = valueToCode(block, "TO");
287 if (argument1 == null) argument1 = new int_node(this, 0);
288 var code = new dot2_node(this, argument0, argument1);
289 return new fcall_node(this, intern("rand"), new node[] { code }, null);
290 }
291
292 public node math_random_float(MathRandomFloatBlock block)
293 {
294 // Random fraction between 0 and 1.
295 return new fcall_node(this, intern("rand"));
296 }
297 }
298}
Note: See TracBrowser for help on using the repository browser.