public node colour_blend(ColourBlendBlock block) { // Blend two colours together. var colour1 = valueToCode(block, "COLOUR1"); if (colour1 == null) { colour1 = new str_node(this, "#000000"); } var colour2 = valueToCode(block, "COLOUR2"); if (colour2 == null) { colour2 = new str_node(this, "#000000"); } var ratio = valueToCode(block, "RATIO"); if (ratio == null) { ratio = new int_node(this, 0); } var p = new JsArray <node>() { colour1, colour2, ratio }; return(new fcall_node(this, intern("colour_blend"), p, null)); }
public node logic_compare(LogicCompareBlock block) { // Comparison operator. var OPERATORS = new Dictionary <string, string>() { { "EQ", "==" }, { "NEQ", "!=" }, { "LT", "<" }, { "LTE", "<=" }, { "GT", ">" }, { "GTE", ">=" }, }; var @operator = OPERATORS[block.getFieldValue("OP")]; var argument0 = valueToCode(block, "A"); if (argument0 == null) { argument0 = new int_node(this, 0); } var argument1 = valueToCode(block, "B"); if (argument1 == null) { argument1 = new int_node(this, 0); } return(new call_node(this, argument0, intern(@operator), argument1)); }
public node colour_rgb(ColourRGBBlock block) { // Compose a colour from RGB components expressed as percentages. var r = valueToCode(block, "RED"); if (r == null) { r = new int_node(this, 0); } var g = valueToCode(block, "GREEN"); if (g == null) { g = new int_node(this, 0); } var b = valueToCode(block, "BLUE"); if (b == null) { b = new int_node(this, 0); } var p = new JsArray <node>() { r, g, b }; return(new fcall_node(this, intern("colour_rgb"), p, null)); }
public node math_number_property(MathNumberPropertyBlock block) { // Check if a number is even, odd, prime, whole, positive, or negative // or if it is divisible by certain number. Returns true or false. var number_to_check = valueToCode(block, "NUMBER_TO_CHECK"); if (number_to_check == null) { number_to_check = new int_node(this, 0); } var dropdown_property = block.getFieldValue("PROPERTY"); node code = null; if (dropdown_property == "PRIME") { return(new fcall_node(this, intern("is_prime"), new JsArray <node>() { number_to_check }, null)); } switch (dropdown_property) { case "EVEN": return(new call_node(this, number_to_check, intern("even?"))); case "ODD": return(new call_node(this, number_to_check, intern("odd?"))); case "WHOLE": code = new call_node(this, number_to_check, intern("%"), new int_node(this, 1)); return(new call_node(this, code, intern("=="), new int_node(this, 0))); case "POSITIVE": return(new call_node(this, number_to_check, intern(">"), new JsArray <node>() { new int_node(this, 0) }, null)); case "NEGATIVE": return(new call_node(this, number_to_check, intern("<"), new JsArray <node>() { new int_node(this, 0) }, null)); case "DIVISIBLE_BY": var divisor = valueToCode(block, "DIVISOR"); // If "divisor" is some code that evals to 0, Ruby will raise an error. if (divisor == null || (divisor is int_node && ((int_node)divisor).to_i() == 0) || (divisor is float_node && ((float_node)divisor).to_f() == 0.0)) { return(new false_node(this)); } code = new call_node(this, number_to_check, intern("%"), divisor); return(new call_node(this, code, intern("=="), new int_node(this, 0))); } return(null); }
public node controls_repeat(ControlsRepeatBlock block) { // Repeat n times (internal number). var times = block.getFieldValue("TIMES"); var repeats = new int_node(this, times == null ? 0 : Int32.Parse(times)); var branch = statementToCode(block, "DO"); return(new call_node(this, repeats, intern("times"), new JsArray <node>(), new block_node(this, new JsArray <node>(), branch, false))); }
public node switch_case_number(SwitchCaseNumberBlock block) { // case/when/else condition. var argument0 = valueToCode(block, "SWITCH"); if (argument0 == null) { argument0 = new int_node(this, -1); } var code = new JsArray <case_node.when_t>(); for (int n = 0; n <= block.cases_.Length; n++) { var branch = statementToCode(block, "DO" + n); var argument1 = block.getFieldValue("CONST" + n); if (argument1 != null) { var when = new case_node.when_t() { body = branch }; when.value.Push(new int_node(this, argument1 == null ? 0 : Int32.Parse(argument1))); code.Push(when); } else { var min = block.getFieldValue("RANGE_MIN" + n); var max = block.getFieldValue("RANGE_MAX" + n); if ((min != null) && (max != null)) { var when = new case_node.when_t() { body = branch }; when.value.Push(new dot2_node(this, new int_node(this, min == null ? 0 : Int32.Parse(min)), new int_node(this, max == null ? 0 : Int32.Parse(max)))); code.Push(when); } } } if (block.defaultCount_ != 0) { var branch = statementToCode(block, "DEFAULT_DO"); if (branch != null) { var when = new case_node.when_t() { body = branch }; code.Push(when); } } return(new case_node(this, argument0, code)); }
public node variables_set(VariablesSetBlock block) { // Variable setter. var argument0 = valueToCode(block, "VALUE"); if (argument0 == null) { argument0 = new int_node(this, 0); } var varName = get_var_name(block.getFieldValue("VAR")); return(new asgn_node(this, new_var_node(varName), argument0)); }
public node math_change(MathChangeBlock block) { // Add to a variable in place. var argument0 = valueToCode(block, "DELTA"); if (argument0 == null) { argument0 = new int_node(this, 0); } var varName = get_var_name(block.getFieldValue("VAR")); return(new op_asgn_node(this, new_var_node(varName), intern("+"), argument0)); }
public node math_modulo(MathModuloBlock block) { // Remainder computation. var argument0 = valueToCode(block, "DIVIDEND"); if (argument0 == null) { argument0 = new int_node(this, 0); } var argument1 = valueToCode(block, "DIVISOR"); if (argument1 == null) { argument1 = new int_node(this, 0); } return(new call_node(this, argument0, intern("%"), argument1)); }
public node controls_repeat_ext(ControlsRepeatExtBlock block) { // Repeat n times (external number). var repeats = valueToCode(block, "TIMES"); if (repeats == null) { repeats = new int_node(this, 0); } if (repeats is int_node) { } else { repeats = new call_node(this, repeats, intern("to_i")); } var branch = statementToCode(block, "DO"); return(new call_node(this, repeats, intern("times"), new JsArray <node>(), new block_node(this, new JsArray <node>(), branch, false))); }
public node math_arithmetic(MathArithmeticBlock block) { // Basic arithmetic operators, and power. var @operator = ARITHMETIC_OPERATORS[block.getFieldValue("OP")]; var argument0 = valueToCode(block, "A"); if (argument0 == null) { argument0 = new int_node(this, 0); } var argument1 = valueToCode(block, "B"); if (argument1 == null) { argument1 = new int_node(this, 0); } var code = argument0 + @operator + argument1; return(new call_node(this, argument0, intern(@operator), argument1)); }
public node math_random_int(MathRandomIntBlock block) { // Random integer between [X] and [Y]. var argument0 = valueToCode(block, "FROM"); if (argument0 == null) { argument0 = new int_node(this, 0); } var argument1 = valueToCode(block, "TO"); if (argument1 == null) { argument1 = new int_node(this, 0); } var code = new dot2_node(this, argument0, argument1); return(new fcall_node(this, intern("rand"), new JsArray <node>() { code }, null)); }
public node lists_repeat(ListsRepeatBlock block) { // Create a list with one element repeated. var argument0 = valueToCode(block, "ITEM"); if (argument0 == null) { argument0 = new nil_node(this); } var argument1 = valueToCode(block, "NUM"); if (argument1 == null) { argument1 = new int_node(this, 0); } var a = new array_node(this, new JsArray <node>() { argument0 }); return(new call_node(this, a, intern("*"), argument1)); }
public node math_constrain(MathConstrainBlock block) { // Constrain a number between two limits. var argument0 = valueToCode(block, "VALUE"); if (argument0 == null) { argument0 = new int_node(this, 0); } var argument1 = valueToCode(block, "LOW"); if (argument1 == null) { argument1 = new int_node(this, 0); } var argument2 = valueToCode(block, "HIGH"); if (argument2 == null) { argument2 = new fcall_node(this, intern("Float"), new JsArray <node>() { new str_node(this, "inf") }, null); } node code = new array_node(this, new JsArray <node>() { argument0, argument1 }); code = new call_node(this, code, intern("max")); code = new array_node(this, new JsArray <node>() { code, argument2 }); return(new call_node(this, code, intern("min"))); }
public node text_charAt(TextCharAtBlock block) { // Get letter at index. // Note: Until January 2013 this block did not have the WHERE input. var where = block.getFieldValue("WHERE"); if (String.IsNullOrEmpty(where)) { where = "FROM_START"; } var at = valueToCode(block, "AT"); if (at == null) { at = new int_node(this, 1); } var text = valueToCode(block, "VALUE"); if (text == null) { text = new str_node(this, ""); } // Blockly uses one-based indicies. if (at is int_node) { // If the index is a naked number, decrement it right now. at = new int_node(this, (int)(((int_node)at).to_i() - 1)); } else { // If the index is dynamic, decrement it in code. at = new call_node(this, at, intern("to_i")); at = new call_node(this, at, intern("-"), new int_node(this, 1)); } switch (where) { case "FIRST": return(new call_node(this, text, intern("[]"), new JsArray <node>() { new int_node(this, 0) }, null)); case "LAST": return(new call_node(this, text, intern("[]"), new JsArray <node>() { new int_node(this, -1) }, null)); case "FROM_START": return(new fcall_node(this, intern("text_get_from_start"), new JsArray <node>() { text, at }, null)); case "FROM_END": return(new fcall_node(this, intern("text_get_from_end"), new JsArray <node>() { text, at }, null)); case "RANDOM": return(new fcall_node(this, intern("text_random_letter"), new JsArray <node>() { text }, null)); } throw new Exception("Unhandled option (text_charAt)."); }
public node lists_getIndex(ListsGetIndexBlock block) { // Get element at index. var mode = block.getFieldValue("MODE"); if (String.IsNullOrEmpty(mode)) { mode = "GET"; } var where = block.getFieldValue("WHERE"); if (String.IsNullOrEmpty(where)) { where = "FROM_START"; } var at = valueToCode(block, "AT"); if (at == null) { at = new int_node(this, 1); } var list = valueToCode(block, "VALUE"); if (list == null) { list = new array_node(this, new JsArray <node>()); } if (where == "FIRST") { if (mode == "GET") { return(new call_node(this, list, intern("first"))); } else { if (mode == "GET_REMOVE") { return(new call_node(this, list, intern("shift"))); } else if (mode == "REMOVE") { return(new call_node(this, list, intern("shift"))); } } } else if (where == "LAST") { if (mode == "GET") { return(new call_node(this, list, intern("last"))); } else { var code = list + ".pop"; if (mode == "GET_REMOVE") { return(new call_node(this, list, intern("pop"))); } else if (mode == "REMOVE") { return(new call_node(this, list, intern("pop"))); } } } else if (where == "FROM_START") { // Blockly uses one-based indicies. if (at is int_node) { // If the index is a naked number, decrement it right now. at = new int_node(this, (int)(((int_node)at).to_i() - 1)); } else { // If the index is dynamic, decrement it in code. at = new begin_node(this, new call_node(this, at, intern("-"), new int_node(this, 1)), true); at = new call_node(this, at, intern("to_i")); } if (mode == "GET") { return(new call_node(this, list, intern("[]"), new JsArray <node>() { at }, null)); } else if (mode == "GET_REMOVE") { return(new call_node(this, list, intern("delete_at"), new JsArray <node>() { at }, null)); } else if (mode == "REMOVE") { return(new call_node(this, list, intern("delete_at"), new JsArray <node>() { at }, null)); } } else if (where == "FROM_END") { at = new call_node(this, at, intern("-@"), (node)null); if (mode == "GET") { return(new call_node(this, list, intern("[]"), new JsArray <node>() { at }, null)); } else if (mode == "GET_REMOVE") { return(new call_node(this, list, intern("delete_at"), new JsArray <node>() { at }, null)); } else if (mode == "REMOVE") { return(new call_node(this, list, intern("delete_at"), new JsArray <node>() { at }, null)); } } else if (where == "RANDOM") { if (mode == "GET") { return(new fcall_node(this, intern("lists_random_item"), new JsArray <node>() { list }, null)); } else { if (mode == "GET_REMOVE") { return(new fcall_node(this, intern("lists_remove_random_item"), new JsArray <node>() { list }, null)); } else if (mode == "REMOVE") { return(new fcall_node(this, intern("lists_remove_random_item"), new JsArray <node>() { list }, null)); } } } throw new Exception("Unhandled combination (lists_getIndex)."); }
public node math_single(Block block) { // Math operators with single operand. var @operator = block.getFieldValue("OP"); node code = null; node arg; if (@operator == "NEG") { // Negation is a special case given its different operator precedence. code = valueToCode(block, "NUM"); if (code == null) { code = new int_node(this, 0); } return(new call_node(this, code, intern("-@"), (node)null)); } if (@operator == "SIN" || @operator == "COS" || @operator == "TAN") { arg = valueToCode(block, "NUM"); if (arg == null) { arg = new int_node(this, 0); } } else { arg = valueToCode(block, "NUM"); if (arg == null) { arg = new int_node(this, 0); } } // First, handle cases which generate values that don't need parentheses // wrapping the code. var math = new const_node(this, intern("Math")); switch (@operator) { case "ABS": code = new call_node(this, arg, intern("abs")); break; case "ROOT": code = new call_node(this, math, intern("sqrt"), new JsArray <node>() { arg }, null); break; case "LN": code = new call_node(this, math, intern("log"), new JsArray <node>() { arg }, null); break; case "LOG10": code = new call_node(this, math, intern("log10"), new JsArray <node>() { arg }, null); break; case "EXP": code = new call_node(this, math, intern("exp"), new JsArray <node>() { arg }, null); break; case "POW10": code = new call_node(this, new int_node(this, 10), intern("exp"), new JsArray <node>() { arg }, null); break; case "ROUND": code = new call_node(this, arg, intern("round")); break; case "ROUNDUP": code = new call_node(this, arg, intern("ceil")); break; case "ROUNDDOWN": code = new call_node(this, arg, intern("floor")); break; case "SIN": arg = new call_node(this, arg, intern("/"), new float_node(this, 180.0)); arg = new call_node(this, arg, intern("*"), new colon2_node(this, math, intern("PI"))); code = new call_node(this, math, intern("sin"), new JsArray <node>() { arg }, null); break; case "COS": arg = new call_node(this, arg, intern("/"), new float_node(this, 180.0)); arg = new call_node(this, arg, intern("*"), new colon2_node(this, math, intern("PI"))); code = new call_node(this, math, intern("cos"), new JsArray <node>() { arg }, null); break; case "TAN": arg = new call_node(this, arg, intern("/"), new float_node(this, 180.0)); arg = new call_node(this, arg, intern("*"), new colon2_node(this, math, intern("PI"))); code = new call_node(this, math, intern("tan"), new JsArray <node>() { arg }, null); break; } if (code != null) { return(code); } // Second, handle cases which generate values that may need parentheses // wrapping the code. switch (@operator) { case "ASIN": code = new call_node(this, math, intern("asin"), new JsArray <node>() { arg }, null); code = new call_node(this, code, intern("/"), new colon2_node(this, math, intern("PI"))); code = new call_node(this, code, intern("*"), new float_node(this, 180.0)); break; case "ACOS": code = new call_node(this, math, intern("acos"), new JsArray <node>() { arg }, null); code = new call_node(this, code, intern("/"), new colon2_node(this, math, intern("PI"))); code = new call_node(this, code, intern("*"), new float_node(this, 180.0)); break; case "ATAN": code = new call_node(this, math, intern("atan"), new JsArray <node>() { arg }, null); code = new call_node(this, code, intern("/"), new colon2_node(this, math, intern("PI"))); code = new call_node(this, code, intern("*"), new float_node(this, 180.0)); break; default: throw new Exception("Unknown math operator: " + @operator); } return(new begin_node(this, code, true)); }
public node lists_setIndex(ListsSetIndexBlock block) { // Set element at index. var list = valueToCode(block, "LIST"); if (list == null) { list = new array_node(this, new JsArray <node>()); } var mode = block.getFieldValue("MODE"); if (String.IsNullOrEmpty(mode)) { mode = "GET"; } var where = block.getFieldValue("WHERE"); if (String.IsNullOrEmpty(where)) { where = "FROM_START"; } var at = valueToCode(block, "AT"); if (at == null) { at = new int_node(this, 1); } var value = valueToCode(block, "TO"); if (value == null) { value = new nil_node(this); } if (where == "FIRST") { if (mode == "SET") { return(new asgn_node(this, new call_node(this, list, intern("[]"), new JsArray <node>() { new int_node(this, 0) }, null), value)); } else if (mode == "INSERT") { return(new call_node(this, list, intern("unshift"), new JsArray <node>() { value }, null)); } } else if (where == "LAST") { if (mode == "SET") { return(new asgn_node(this, new call_node(this, list, intern("[]"), new JsArray <node>() { new int_node(this, -1) }, null), value)); } else if (mode == "INSERT") { return(new call_node(this, list, intern("push"), new JsArray <node>() { value }, null)); } } else if (where == "FROM_START") { // Blockly uses one-based indicies. if (at is int_node) { // If the index is a naked number, decrement it right now. at = new int_node(this, (int)(((int_node)at).to_i() - 1)); } else { // If the index is dynamic, decrement it in code. at = new begin_node(this, new call_node(this, at, intern("-"), new int_node(this, 1)), true); at = new call_node(this, at, intern("to_i")); } if (mode == "SET") { return(new asgn_node(this, new call_node(this, list, intern("[]"), new JsArray <node>() { at }, null), value)); } else if (mode == "INSERT") { return(new call_node(this, list, intern("insert"), new JsArray <node>() { at, value }, null)); } } else if (where == "FROM_END") { if (mode == "SET") { // Blockly uses one-based indicies. if (at is int_node) { // If the index is a naked number, decrement it right now. } else { // If the index is dynamic, decrement it in code. at = new call_node(this, at, intern("to_i")); } return(new asgn_node(this, new call_node(this, list, intern("[]"), new JsArray <node>() { at }, null), value)); } else if (mode == "INSERT") { // Blockly uses one-based indicies. if (at is int_node) { // If the index is a naked number, decrement it right now. at = new int_node(this, (int)(((int_node)at).to_i() + 1)); } else { // If the index is dynamic, decrement it in code. at = new begin_node(this, new call_node(this, at, intern("+"), new int_node(this, 1)), true); at = new call_node(this, at, intern("to_i")); } at = new call_node(this, at, intern("-@"), (node)null); return(new call_node(this, list, intern("insert"), new JsArray <node>() { at, value }, null)); } } else if (where == "RANDOM") { if (mode == "SET") { return(new fcall_node(this, intern("lists_set_random_item"), new JsArray <node>() { list, value }, null)); } else if (mode == "INSERT") { return(new fcall_node(this, intern("lists_insert_random_item"), new JsArray <node>() { list, value }, null)); } } throw new Exception("Unhandled combination (lists_setIndex)."); }
public node text_getSubstring(TextGetSubstringBlock block) { // Get substring. var text = valueToCode(block, "STRING"); if (text == null) { text = new str_node(this, ""); } var where1 = block.getFieldValue("WHERE1"); var where2 = block.getFieldValue("WHERE2"); var at1 = valueToCode(block, "AT1"); if (at1 == null) { at1 = new int_node(this, 1); } var at2 = valueToCode(block, "AT2"); if (at2 == null) { at2 = new int_node(this, 1); } if (where1 == "FIRST" || (where1 == "FROM_START" && at1 is int_node && ((int_node)at1).to_i() == 1)) { at1 = new int_node(this, 0); } else if (where1 == "FROM_START") { // Blockly uses one-based indicies. if (at1 is int_node) { // If the index is a naked number, decrement it right now. at1 = new int_node(this, (int)(((int_node)at1).to_i() - 1)); } else { // If the index is dynamic, decrement it in code. at1 = new call_node(this, at1, intern("to_i")); at1 = new call_node(this, at1, intern("-"), new int_node(this, 1)); } } else if (where1 == "FROM_END") { if (at1 is int_node) { at1 = new int_node(this, (int)(-((int_node)at1).to_i())); } else { at1 = new call_node(this, at1, intern("-@"), (node)null); at1 = new call_node(this, at1, intern("to_i")); } } if (where2 == "LAST" || (where2 == "FROM_END" && at2 is int_node && ((int_node)at2).to_i() == 1)) { at2 = new int_node(this, -1); } else if (where2 == "FROM_START") { if (at2 is int_node) { at2 = new int_node(this, (int)(((int_node)at2).to_i() - 1)); } else { at2 = new call_node(this, at2, intern("to_i")); at2 = new call_node(this, at2, intern("-"), new int_node(this, 1)); } } else if (where2 == "FROM_END") { if (at2 is int_node) { at2 = new int_node(this, (int)(-((int_node)at2).to_i())); } else { at2 = new call_node(this, at2, intern("-@"), (node)null); at2 = new call_node(this, at2, intern("to_i")); } } var code = new dot2_node(this, at1, at2); return(new call_node(this, text, intern("[]"), new JsArray <node>() { code }, null)); }
public node lists_getSublist(ListsGetSublistBlock block) { // Get sublist. var list = valueToCode(block, "LIST"); if (list == null) { list = new array_node(this, new JsArray <node>()); } var where1 = block.getFieldValue("WHERE1"); var where2 = block.getFieldValue("WHERE2"); var at1 = valueToCode(block, "AT1"); if (at1 == null) { at1 = new int_node(this, 1); } var at2 = valueToCode(block, "AT2"); if (at2 == null) { at2 = new int_node(this, 1); } if (where1 == "FIRST" || (where1 == "FROM_START" && at1 is int_node && ((int_node)at1).to_i() == 1)) { at1 = new int_node(this, 0); } else if (where1 == "FROM_START") { // Blockly uses one-based indicies. if (at1 is int_node) { at1 = new int_node(this, (int)(((int_node)at1).to_i() - 1)); } else { at1 = new call_node(this, at1, intern("to_i")); at1 = new call_node(this, at1, intern("-"), new int_node(this, 1)); } } else if (where1 == "FROM_END") { if (at1 is int_node) { at1 = new int_node(this, (int)(-((int_node)at1).to_i())); } else { at1 = new call_node(this, at1, intern("-@"), (node)null); at1 = new call_node(this, at1, intern("to_i")); } } if (where2 == "LAST" || (where2 == "FROM_END" && at2 is int_node && ((int_node)at2).to_i() == 1)) { at2 = new int_node(this, -1); } else if (where2 == "FROM_START") { if (at2 is int_node) { at2 = new int_node(this, (int)(((int_node)at2).to_i() - 1)); } else { at2 = new call_node(this, at2, intern("to_i")); at2 = new call_node(this, at2, intern("-"), new int_node(this, 1)); } } else if (where2 == "FROM_END") { if (at2 is int_node) { at2 = new int_node(this, (int)(-((int_node)at2).to_i())); } else { at2 = new call_node(this, at2, intern("-@"), (node)null); at2 = new call_node(this, at2, intern("to_i")); } } return(new fcall_node(this, intern("lists_sublist"), new JsArray <node>() { list, at1, at2 }, null)); }
public node controls_for(ControlsForBlock block) { // For loop. var lv = local_switch(); var loopVar = local_add_f(block.getFieldValue("VAR")); var fromVal = valueToCode(block, "FROM"); if (fromVal == null) { fromVal = new int_node(this, 0); } var toVal = valueToCode(block, "TO"); if (toVal == null) { toVal = new int_node(this, 0); } var increment = valueToCode(block, "BY"); var branch = statementToCode(block, "DO"); if (fromVal is int_node && toVal is int_node && (increment == null || increment is int_node)) { if (increment == null) { increment = new int_node(this, 1); } // All parameters are simple numbers. } else { fromVal = new call_node(this, fromVal, intern("to_f")); toVal = new call_node(this, toVal, intern("to_f")); if (increment == null) { increment = new float_node(this, 1); } else { increment = new call_node(this, increment, intern("to_f")); } } local_resume(lv); var arg = new hash_node(this, new JsArray <hash_node.kv_t>() { new hash_node.kv_t(new sym_node(this, intern("from")), fromVal), new hash_node.kv_t(new sym_node(this, intern("to")), toVal), new hash_node.kv_t(new sym_node(this, intern("by")), increment), }); var exec = new block_node(this, new JsArray <node>() { new arg_node(this, loopVar) }, branch, false); return(new fcall_node(this, intern("for_loop"), new JsArray <node>() { arg }, exec)); }