diff --git a/core/names.js b/core/names.js index cc2740f37..c7fbf8c8f 100644 --- a/core/names.js +++ b/core/names.js @@ -32,8 +32,10 @@ goog.provide('Blockly.Names'); * @param {string} reservedWords A comma-separated string of words that are * illegal for use as names in a language (e.g. 'new,if,this,...'). * @constructor + * @param namesPrependDollar boolean indicating whether the languages requires dollar signs ($) before a variable */ -Blockly.Names = function(reservedWords) { +Blockly.Names = function(reservedWords, namesPrependDollar) { + this.prependDollar = namesPrependDollar || false; this.reservedDict_ = Object.create(null); if (reservedWords) { var splitWords = reservedWords.split(','); @@ -70,11 +72,12 @@ Blockly.Names.prototype.reset = function() { */ Blockly.Names.prototype.getName = function(name, type) { var normalized = name.toLowerCase() + '_' + type; + var prepend = type=='VARIABLE' && this.prependDollar ? '$' : ''; if (normalized in this.db_) { - return this.db_[normalized]; + return prepend + this.db_[normalized]; } var safeName = this.getDistinctName(name, type); - this.db_[normalized] = safeName; + this.db_[normalized] = type=='VARIABLE' && this.prependDollar ? safeName.substr(1) : safeName; return safeName; }; @@ -98,7 +101,8 @@ Blockly.Names.prototype.getDistinctName = function(name, type) { } safeName += i; this.dbReverse_[safeName] = true; - return safeName; + var prepend = type=='VARIABLE' && this.prependDollar ? '$' : ''; + return prepend + safeName; }; /** diff --git a/generators/php.js b/generators/php.js index 6263a5892..1002def62 100644 --- a/generators/php.js +++ b/generators/php.js @@ -2,7 +2,7 @@ * @license * Visual Blocks Language * - * Copyright 2015 Google Inc. + * Copyright 2012 Google Inc. * https://developers.google.com/blockly/ * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ /** * @fileoverview Helper functions for generating PHP for blocks. - * @author daarond@gmail.com (Daaron Dwyer) + * @author fraser@google.com (Neil Fraser) */ 'use strict'; @@ -43,19 +43,36 @@ Blockly.PHP = new Blockly.Generator('PHP'); * @private */ Blockly.PHP.addReservedWords( - // http://php.net/manual/en/reserved.keywords.php - '__halt_compiler,abstract,and,array,as,break,callable,case,catch,class,clone,const,continue,declare,default,die,do,echo,else,elseif,empty,enddeclare,endfor,endforeach,endif,endswitch,endwhile,eval,exit,extends,final,for,foreach,function,global,goto,if,implements,include,include_once,instanceof,insteadof,interface,isset,list,namespace,new,or,print,private,protected,public,require,require_once,return,static,switch,throw,trait,try,unset,use,var,while,xor,' + - // http://php.net/manual/en/reserved.constants.php - 'PHP_VERSION,PHP_MAJOR_VERSION,PHP_MINOR_VERSION,PHP_RELEASE_VERSION,PHP_VERSION_ID,PHP_EXTRA_VERSION,PHP_ZTS,PHP_DEBUG,PHP_MAXPATHLEN,PHP_OS,PHP_SAPI,PHP_EOL,PHP_INT_MAX,PHP_INT_SIZE,DEFAULT_INCLUDE_PATH,PEAR_INSTALL_DIR,PEAR_EXTENSION_DIR,PHP_EXTENSION_DIR,PHP_PREFIX,PHP_BINDIR,PHP_BINARY,PHP_MANDIR,PHP_LIBDIR,PHP_DATADIR,PHP_SYSCONFDIR,PHP_LOCALSTATEDIR,PHP_CONFIG_FILE_PATH,PHP_CONFIG_FILE_SCAN_DIR,PHP_SHLIB_SUFFIX,E_ERROR,E_WARNING,E_PARSE,E_NOTICE,E_CORE_ERROR,E_CORE_WARNING,E_COMPILE_ERROR,E_COMPILE_WARNING,E_USER_ERROR,E_USER_WARNING,E_USER_NOTICE,E_DEPRECATED,E_USER_DEPRECATED,E_ALL,E_STRICT,__COMPILER_HALT_OFFSET__,TRUE,FALSE,NULL,__CLASS__,__DIR__,__FILE__,__FUNCTION__,__LINE__,__METHOD__,__NAMESPACE__,__TRAIT__'); + 'Blockly,' + // In case JS is evaled in the current window. + // https://developer.mozilla.org/en/PHP/Reference/Reserved_Words + 'break,case,catch,continue,debugger,default,delete,do,else,finally,for,function,if,in,instanceof,new,return,switch,this,throw,try,typeof,var,void,while,with,' + + 'class,enum,export,extends,import,super,implements,interface,let,package,private,protected,public,static,yield,' + + 'const,null,true,false,' + + // https://developer.mozilla.org/en/PHP/Reference/Global_Objects + 'Array,ArrayBuffer,Boolean,Date,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Error,eval,EvalError,Float32Array,Float64Array,Function,Infinity,Int16Array,Int32Array,Int8Array,isFinite,isNaN,Iterator,JSON,Math,NaN,Number,Object,parseFloat,parseInt,RangeError,ReferenceError,RegExp,StopIteration,String,SyntaxError,TypeError,Uint16Array,Uint32Array,Uint8Array,Uint8ClampedArray,undefined,uneval,URIError,' + + // https://developer.mozilla.org/en/DOM/window + 'applicationCache,closed,Components,content,_content,controllers,crypto,defaultStatus,dialogArguments,directories,document,frameElement,frames,fullScreen,globalStorage,history,innerHeight,innerWidth,length,location,locationbar,localStorage,menubar,messageManager,mozAnimationStartTime,mozInnerScreenX,mozInnerScreenY,mozPaintCount,name,navigator,opener,outerHeight,outerWidth,pageXOffset,pageYOffset,parent,performance,personalbar,pkcs11,returnValue,screen,screenX,screenY,scrollbars,scrollMaxX,scrollMaxY,scrollX,scrollY,self,sessionStorage,sidebar,status,statusbar,toolbar,top,URL,window,' + + 'addEventListener,alert,atob,back,blur,btoa,captureEvents,clearImmediate,clearInterval,clearTimeout,close,confirm,disableExternalCapture,dispatchEvent,dump,enableExternalCapture,escape,find,focus,forward,GeckoActiveXObject,getAttention,getAttentionWithCycleCount,getComputedStyle,getSelection,home,matchMedia,maximize,minimize,moveBy,moveTo,mozRequestAnimationFrame,open,openDialog,postMessage,print,prompt,QueryInterface,releaseEvents,removeEventListener,resizeBy,resizeTo,restore,routeEvent,scroll,scrollBy,scrollByLines,scrollByPages,scrollTo,setCursor,setImmediate,setInterval,setResizable,setTimeout,showModalDialog,sizeToContent,stop,unescape,updateCommands,XPCNativeWrapper,XPCSafeJSObjectWrapper,' + + 'onabort,onbeforeunload,onblur,onchange,onclick,onclose,oncontextmenu,ondevicemotion,ondeviceorientation,ondragdrop,onerror,onfocus,onhashchange,onkeydown,onkeypress,onkeyup,onload,onmousedown,onmousemove,onmouseout,onmouseover,onmouseup,onmozbeforepaint,onpaint,onpopstate,onreset,onresize,onscroll,onselect,onsubmit,onunload,onpageshow,onpagehide,' + + 'Image,Option,Worker,' + + // https://developer.mozilla.org/en/Gecko_DOM_Reference + 'Event,Range,File,FileReader,Blob,BlobBuilder,' + + 'Attr,CDATASection,CharacterData,Comment,console,DocumentFragment,DocumentType,DomConfiguration,DOMError,DOMErrorHandler,DOMException,DOMImplementation,DOMImplementationList,DOMImplementationRegistry,DOMImplementationSource,DOMLocator,DOMObject,DOMString,DOMStringList,DOMTimeStamp,DOMUserData,Entity,EntityReference,MediaQueryList,MediaQueryListListener,NameList,NamedNodeMap,Node,NodeFilter,NodeIterator,NodeList,Notation,Plugin,PluginArray,ProcessingInstruction,SharedWorker,Text,TimeRanges,Treewalker,TypeInfo,UserDataHandler,Worker,WorkerGlobalScope,' + + 'HTMLDocument,HTMLElement,HTMLAnchorElement,HTMLAppletElement,HTMLAudioElement,HTMLAreaElement,HTMLBaseElement,HTMLBaseFontElement,HTMLBodyElement,HTMLBRElement,HTMLButtonElement,HTMLCanvasElement,HTMLDirectoryElement,HTMLDivElement,HTMLDListElement,HTMLEmbedElement,HTMLFieldSetElement,HTMLFontElement,HTMLFormElement,HTMLFrameElement,HTMLFrameSetElement,HTMLHeadElement,HTMLHeadingElement,HTMLHtmlElement,HTMLHRElement,HTMLIFrameElement,HTMLImageElement,HTMLInputElement,HTMLKeygenElement,HTMLLabelElement,HTMLLIElement,HTMLLinkElement,HTMLMapElement,HTMLMenuElement,HTMLMetaElement,HTMLModElement,HTMLObjectElement,HTMLOListElement,HTMLOptGroupElement,HTMLOptionElement,HTMLOutputElement,HTMLParagraphElement,HTMLParamElement,HTMLPreElement,HTMLQuoteElement,HTMLScriptElement,HTMLSelectElement,HTMLSourceElement,HTMLSpanElement,HTMLStyleElement,HTMLTableElement,HTMLTableCaptionElement,HTMLTableCellElement,HTMLTableDataCellElement,HTMLTableHeaderCellElement,HTMLTableColElement,HTMLTableRowElement,HTMLTableSectionElement,HTMLTextAreaElement,HTMLTimeElement,HTMLTitleElement,HTMLTrackElement,HTMLUListElement,HTMLUnknownElement,HTMLVideoElement,' + + 'HTMLCanvasElement,CanvasRenderingContext2D,CanvasGradient,CanvasPattern,TextMetrics,ImageData,CanvasPixelArray,HTMLAudioElement,HTMLVideoElement,NotifyAudioAvailableEvent,HTMLCollection,HTMLAllCollection,HTMLFormControlsCollection,HTMLOptionsCollection,HTMLPropertiesCollection,DOMTokenList,DOMSettableTokenList,DOMStringMap,RadioNodeList,' + + 'SVGDocument,SVGElement,SVGAElement,SVGAltGlyphElement,SVGAltGlyphDefElement,SVGAltGlyphItemElement,SVGAnimationElement,SVGAnimateElement,SVGAnimateColorElement,SVGAnimateMotionElement,SVGAnimateTransformElement,SVGSetElement,SVGCircleElement,SVGClipPathElement,SVGColorProfileElement,SVGCursorElement,SVGDefsElement,SVGDescElement,SVGEllipseElement,SVGFilterElement,SVGFilterPrimitiveStandardAttributes,SVGFEBlendElement,SVGFEColorMatrixElement,SVGFEComponentTransferElement,SVGFECompositeElement,SVGFEConvolveMatrixElement,SVGFEDiffuseLightingElement,SVGFEDisplacementMapElement,SVGFEDistantLightElement,SVGFEFloodElement,SVGFEGaussianBlurElement,SVGFEImageElement,SVGFEMergeElement,SVGFEMergeNodeElement,SVGFEMorphologyElement,SVGFEOffsetElement,SVGFEPointLightElement,SVGFESpecularLightingElement,SVGFESpotLightElement,SVGFETileElement,SVGFETurbulenceElement,SVGComponentTransferFunctionElement,SVGFEFuncRElement,SVGFEFuncGElement,SVGFEFuncBElement,SVGFEFuncAElement,SVGFontElement,SVGFontFaceElement,SVGFontFaceFormatElement,SVGFontFaceNameElement,SVGFontFaceSrcElement,SVGFontFaceUriElement,SVGForeignObjectElement,SVGGElement,SVGGlyphElement,SVGGlyphRefElement,SVGGradientElement,SVGLinearGradientElement,SVGRadialGradientElement,SVGHKernElement,SVGImageElement,SVGLineElement,SVGMarkerElement,SVGMaskElement,SVGMetadataElement,SVGMissingGlyphElement,SVGMPathElement,SVGPathElement,SVGPatternElement,SVGPolylineElement,SVGPolygonElement,SVGRectElement,SVGScriptElement,SVGStopElement,SVGStyleElement,SVGSVGElement,SVGSwitchElement,SVGSymbolElement,SVGTextElement,SVGTextPathElement,SVGTitleElement,SVGTRefElement,SVGTSpanElement,SVGUseElement,SVGViewElement,SVGVKernElement,' + + 'SVGAngle,SVGColor,SVGICCColor,SVGElementInstance,SVGElementInstanceList,SVGLength,SVGLengthList,SVGMatrix,SVGNumber,SVGNumberList,SVGPaint,SVGPoint,SVGPointList,SVGPreserveAspectRatio,SVGRect,SVGStringList,SVGTransform,SVGTransformList,' + + 'SVGAnimatedAngle,SVGAnimatedBoolean,SVGAnimatedEnumeration,SVGAnimatedInteger,SVGAnimatedLength,SVGAnimatedLengthList,SVGAnimatedNumber,SVGAnimatedNumberList,SVGAnimatedPreserveAspectRatio,SVGAnimatedRect,SVGAnimatedString,SVGAnimatedTransformList,' + + 'SVGPathSegList,SVGPathSeg,SVGPathSegArcAbs,SVGPathSegArcRel,SVGPathSegClosePath,SVGPathSegCurvetoCubicAbs,SVGPathSegCurvetoCubicRel,SVGPathSegCurvetoCubicSmoothAbs,SVGPathSegCurvetoCubicSmoothRel,SVGPathSegCurvetoQuadraticAbs,SVGPathSegCurvetoQuadraticRel,SVGPathSegCurvetoQuadraticSmoothAbs,SVGPathSegCurvetoQuadraticSmoothRel,SVGPathSegLinetoAbs,SVGPathSegLinetoHorizontalAbs,SVGPathSegLinetoHorizontalRel,SVGPathSegLinetoRel,SVGPathSegLinetoVerticalAbs,SVGPathSegLinetoVerticalRel,SVGPathSegMovetoAbs,SVGPathSegMovetoRel,ElementTimeControl,TimeEvent,SVGAnimatedPathData,' + + 'SVGAnimatedPoints,SVGColorProfileRule,SVGCSSRule,SVGExternalResourcesRequired,SVGFitToViewBox,SVGLangSpace,SVGLocatable,SVGRenderingIntent,SVGStylable,SVGTests,SVGTextContentElement,SVGTextPositioningElement,SVGTransformable,SVGUnitTypes,SVGURIReference,SVGViewSpec,SVGZoomAndPan'); /** * Order of operation ENUMs. - * http://php.net/manual/en/language.operators.precedence.php + * https://developer.mozilla.org/en/PHP/Reference/Operators/Operator_Precedence */ Blockly.PHP.ORDER_ATOMIC = 0; // 0 "" ... -Blockly.PHP.ORDER_CLONE = 1; // clone +Blockly.PHP.ORDER_MEMBER = 1; // . [] Blockly.PHP.ORDER_NEW = 1; // new -Blockly.PHP.ORDER_MEMBER = 2; // () Blockly.PHP.ORDER_FUNCTION_CALL = 2; // () Blockly.PHP.ORDER_INCREMENT = 3; // ++ Blockly.PHP.ORDER_DECREMENT = 3; // -- @@ -63,6 +80,9 @@ Blockly.PHP.ORDER_LOGICAL_NOT = 4; // ! Blockly.PHP.ORDER_BITWISE_NOT = 4; // ~ Blockly.PHP.ORDER_UNARY_PLUS = 4; // + Blockly.PHP.ORDER_UNARY_NEGATION = 4; // - +Blockly.PHP.ORDER_TYPEOF = 4; // typeof +Blockly.PHP.ORDER_VOID = 4; // void +Blockly.PHP.ORDER_DELETE = 4; // delete Blockly.PHP.ORDER_MULTIPLICATION = 5; // * Blockly.PHP.ORDER_DIVISION = 5; // / Blockly.PHP.ORDER_MODULUS = 5; // % @@ -76,10 +96,10 @@ Blockly.PHP.ORDER_EQUALITY = 9; // == != === !== Blockly.PHP.ORDER_BITWISE_AND = 10; // & Blockly.PHP.ORDER_BITWISE_XOR = 11; // ^ Blockly.PHP.ORDER_BITWISE_OR = 12; // | -Blockly.PHP.ORDER_CONDITIONAL = 13; // ?: -Blockly.PHP.ORDER_ASSIGNMENT = 14; // = += -= *= /= %= <<= >>= ... -Blockly.PHP.ORDER_LOGICAL_AND = 15; // && -Blockly.PHP.ORDER_LOGICAL_OR = 16; // || +Blockly.PHP.ORDER_LOGICAL_AND = 13; // && +Blockly.PHP.ORDER_LOGICAL_OR = 14; // || +Blockly.PHP.ORDER_CONDITIONAL = 15; // ?: +Blockly.PHP.ORDER_ASSIGNMENT = 16; // = += -= *= /= %= <<= >>= ... Blockly.PHP.ORDER_COMMA = 17; // , Blockly.PHP.ORDER_NONE = 99; // (...) @@ -96,36 +116,18 @@ Blockly.PHP.init = function(workspace) { if (!Blockly.PHP.variableDB_) { Blockly.PHP.variableDB_ = - new Blockly.Names(Blockly.PHP.RESERVED_WORDS_); + new Blockly.Names(Blockly.PHP.RESERVED_WORDS_, true); } else { Blockly.PHP.variableDB_.reset(); } -}; -Blockly.PHP.getDistinctName = function(name, type) { - var safeName = this.variableDB_.safeName_(name); - var i = ''; - while ((type == Blockly.Procedures.NAME_TYPE && (safeName + i) in this.variableDB_.reservedDict_) - || this.variableDB_.dbReverse_[safeName + i]) { - // Collision with existing name. Create a unique name. - i = i ? i + 1 : 2; + var defvars = []; + var variables = Blockly.Variables.allVariables(workspace); + for (var x = 0; x < variables.length; x++) { + defvars[x] = Blockly.PHP.variableDB_.getName(variables[x], + Blockly.Variables.NAME_TYPE) + ';'; } - safeName += i; - this.variableDB_.dbReverse_[safeName] = true; - if (type == Blockly.Variables.NAME_TYPE) { - safeName = '$' + safeName; - } - return safeName; -}; - -Blockly.PHP.getName = function(name, type) { - var normalized = name.toLowerCase() + '_' + type; - if (normalized in this.variableDB_.db_) { - return this.variableDB_.db_[normalized]; - } - var safeName = this.getDistinctName(name, type); - this.variableDB_[normalized] = safeName; - return safeName; + Blockly.PHP.definitions_['variables'] = defvars.join('\n'); }; /** diff --git a/generators/php/lists.js b/generators/php/lists.js index f0d4d298a..e3d93c283 100644 --- a/generators/php/lists.js +++ b/generators/php/lists.js @@ -68,48 +68,73 @@ Blockly.PHP['lists_repeat'] = function(block) { Blockly.PHP['lists_length'] = function(block) { // List length. var argument0 = Blockly.PHP.valueToCode(block, 'VALUE', - Blockly.PHP.ORDER_FUNCTION_CALL) || '[]'; + Blockly.PHP.ORDER_FUNCTION_CALL) || 'array()'; return ['count(' + argument0 + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; }; Blockly.PHP['lists_isEmpty'] = function(block) { // Is the list empty? var argument0 = Blockly.PHP.valueToCode(block, 'VALUE', - Blockly.PHP.ORDER_FUNCTION_CALL) || '[]'; + Blockly.PHP.ORDER_FUNCTION_CALL) || 'array()'; return ['empty(' + argument0 + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; }; Blockly.PHP['lists_indexOf'] = function(block) { // Find an item in the list. - var operator = block.getFieldValue('END') == 'FIRST' ? - 'indexOf' : 'lastIndexOf'; + var operator = block.getFieldValue('END'); var argument0 = Blockly.PHP.valueToCode(block, 'FIND', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; + Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; var argument1 = Blockly.PHP.valueToCode(block, 'VALUE', - Blockly.PHP.ORDER_FUNCTION_CALL) || '[]'; - var code = argument1 + '.' + operator + '(' + argument0 + ') + 1'; + Blockly.PHP.ORDER_FUNCTION_CALL) || 'array()'; + + var code; + if (operator == 'FIRST'){ + var functionName = Blockly.PHP.provideFunction_( + 'indexOf', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($list, $item) {', + ' for($i=0; $i < count($list); $i++){', + ' if ($list[$i] == $item) { return $i + 1; }', + ' }', + ' return 0;', + '}']); + code = functionName + '(' + argument1 + ', ' + argument0 + ')'; + } else { + var functionName = Blockly.PHP.provideFunction_( + 'lastIndexOf', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($list, $item) {', + ' $last = -1;', + ' for($i=0; $i < count($list); $i++){', + ' if ($list[$i] == $item) { $last = $i; }', + ' }', + ' return $last;', + '}']); + code = functionName + '(' + argument1 + ', ' + argument0 + ') + 1'; + } + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; Blockly.PHP['lists_getIndex'] = function(block) { // Get element at index. - // Note: Until January 2013 this block did not have MODE or WHERE inputs. var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; var at = Blockly.PHP.valueToCode(block, 'AT', Blockly.PHP.ORDER_UNARY_NEGATION) || '1'; var list = Blockly.PHP.valueToCode(block, 'VALUE', - Blockly.PHP.ORDER_FUNCTION_CALL) || '[]'; + Blockly.PHP.ORDER_FUNCTION_CALL) || 'array()'; if (where == 'FIRST') { if (mode == 'GET') { var code = list + '[0]'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; } else if (mode == 'GET_REMOVE') { - var code = 'array_shift(' + list + ', 1)'; + var code = 'array_shift(' + list + ')'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; } else if (mode == 'REMOVE') { - var code = 'array_shift(' + list + ', 1)'; + var code = 'array_shift(' + list + ');\n'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; } } else if (where == 'LAST') { if (mode == 'GET') { @@ -179,7 +204,7 @@ Blockly.PHP['lists_setIndex'] = function(block) { // Set element at index. // Note: Until February 2013 this block did not have MODE or WHERE inputs. var list = Blockly.PHP.valueToCode(block, 'LIST', - Blockly.PHP.ORDER_MEMBER) || '[]'; + Blockly.PHP.ORDER_MEMBER) || 'array()'; var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; var at = Blockly.PHP.valueToCode(block, 'AT', @@ -192,7 +217,7 @@ Blockly.PHP['lists_setIndex'] = function(block) { if (list.match(/^\w+$/)) { return ''; } - var listVar = Blockly.PHP.getDistinctName( + var listVar = Blockly.PHP.variableDB_.getDistinctName( 'tmp_list', Blockly.Variables.NAME_TYPE); var code = listVar + ' = ' + list + ';\n'; list = listVar; @@ -210,7 +235,7 @@ Blockly.PHP['lists_setIndex'] = function(block) { code += list + '[count(' + list + ') - 1] = ' + value + ';\n'; return code; } else if (mode == 'INSERT') { - return list + 'array_push(' + list + ', ' + value + ');\n'; + return 'array_push(' + list + ', ' + value + ');\n'; } } else if (where == 'FROM_START') { // Blockly uses one-based indicies. @@ -237,7 +262,7 @@ Blockly.PHP['lists_setIndex'] = function(block) { } } else if (where == 'RANDOM') { var code = cacheList(); - var xVar = Blockly.PHP.getDistinctName( + var xVar = Blockly.PHP.variableDB_.getDistinctName( 'tmp_x', Blockly.Variables.NAME_TYPE); code += xVar + ' = rand(0, count(' + list + ')-1);\n'; if (mode == 'SET') { @@ -254,7 +279,7 @@ Blockly.PHP['lists_setIndex'] = function(block) { Blockly.PHP['lists_getSublist'] = function(block) { // Get sublist. var list = Blockly.PHP.valueToCode(block, 'LIST', - Blockly.PHP.ORDER_MEMBER) || '[]'; + Blockly.PHP.ORDER_MEMBER) || 'array()'; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); var at1 = Blockly.PHP.valueToCode(block, 'AT1', @@ -262,7 +287,7 @@ Blockly.PHP['lists_getSublist'] = function(block) { var at2 = Blockly.PHP.valueToCode(block, 'AT2', Blockly.PHP.ORDER_NONE) || '1'; if (where1 == 'FIRST' && where2 == 'LAST') { - var code = list + '.concat()'; + var code = list; } else { var functionName = Blockly.PHP.provideFunction_( 'lists_get_sublist', @@ -311,7 +336,7 @@ Blockly.PHP['lists_split'] = function(block) { var functionName = 'explode'; } else if (mode == 'JOIN') { if (!value_input) { - value_input = '[]'; + value_input = 'array()'; } var functionName = 'implode'; } else { diff --git a/generators/php/loops.js b/generators/php/loops.js index dfb29c6fe..84ec10414 100644 --- a/generators/php/loops.js +++ b/generators/php/loops.js @@ -34,7 +34,7 @@ Blockly.PHP['controls_repeat'] = function(block) { var repeats = Number(block.getFieldValue('TIMES')); var branch = Blockly.PHP.statementToCode(block, 'DO'); branch = Blockly.PHP.addLoopTrap(branch, block.id); - var loopVar = Blockly.PHP.getDistinctName( + var loopVar = Blockly.PHP.variableDB_.getDistinctName( 'count', Blockly.Variables.NAME_TYPE); var code = 'for (' + loopVar + ' = 0; ' + loopVar + ' < ' + repeats + '; ' + @@ -50,11 +50,11 @@ Blockly.PHP['controls_repeat_ext'] = function(block) { var branch = Blockly.PHP.statementToCode(block, 'DO'); branch = Blockly.PHP.addLoopTrap(branch, block.id); var code = ''; - var loopVar = Blockly.PHP.getDistinctName( + var loopVar = Blockly.PHP.variableDB_.getDistinctName( 'count', Blockly.Variables.NAME_TYPE); var endVar = repeats; if (!repeats.match(/^\w+$/) && !Blockly.isNumber(repeats)) { - var endVar = Blockly.PHP.getDistinctName( + var endVar = Blockly.PHP.variableDB_.getDistinctName( 'repeat_end', Blockly.Variables.NAME_TYPE); code += endVar + ' = ' + repeats + ';\n'; } @@ -81,7 +81,7 @@ Blockly.PHP['controls_whileUntil'] = function(block) { Blockly.PHP['controls_for'] = function(block) { // For loop. - var variable0 = Blockly.PHP.getName( + var variable0 = Blockly.PHP.variableDB_.getName( block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); var argument0 = Blockly.PHP.valueToCode(block, 'FROM', Blockly.PHP.ORDER_ASSIGNMENT) || '0'; @@ -111,19 +111,19 @@ Blockly.PHP['controls_for'] = function(block) { // Cache non-trivial values to variables to prevent repeated look-ups. var startVar = argument0; if (!argument0.match(/^\w+$/) && !Blockly.isNumber(argument0)) { - startVar = Blockly.PHP.getDistinctName( + startVar = Blockly.PHP.variableDB_.getDistinctName( variable0 + '_start', Blockly.Variables.NAME_TYPE); code += startVar + ' = ' + argument0 + ';\n'; } var endVar = argument1; if (!argument1.match(/^\w+$/) && !Blockly.isNumber(argument1)) { - var endVar = Blockly.PHP.getDistinctName( + var endVar = Blockly.PHP.variableDB_.getDistinctName( variable0 + '_end', Blockly.Variables.NAME_TYPE); code += endVar + ' = ' + argument1 + ';\n'; } // Determine loop direction at start, in case one of the bounds // changes during loop execution. - var incVar = Blockly.PHP.getDistinctName( + var incVar = Blockly.PHP.variableDB_.getDistinctName( variable0 + '_inc', Blockly.Variables.NAME_TYPE); code += incVar + ' = '; if (Blockly.isNumber(increment)) { @@ -146,7 +146,7 @@ Blockly.PHP['controls_for'] = function(block) { Blockly.PHP['controls_forEach'] = function(block) { // For each loop. - var variable0 = Blockly.PHP.getName( + var variable0 = Blockly.PHP.variableDB_.getName( block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); var argument0 = Blockly.PHP.valueToCode(block, 'LIST', Blockly.PHP.ORDER_ASSIGNMENT) || '[]'; diff --git a/generators/php/math.js b/generators/php/math.js index d0296d992..a10290445 100644 --- a/generators/php/math.js +++ b/generators/php/math.js @@ -52,7 +52,7 @@ Blockly.PHP['math_arithmetic'] = function(block) { var code; // Power in PHP requires a special case since it has no operator. if (!operator) { - code = 'Math.pow(' + argument0 + ', ' + argument1 + ')'; + code = 'pow(' + argument0 + ', ' + argument1 + ')'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; } code = argument0 + operator + argument1; @@ -146,8 +146,8 @@ Blockly.PHP['math_single'] = function(block) { Blockly.PHP['math_constant'] = function(block) { // Constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY. var CONSTANTS = { - 'PI': ['pi()', Blockly.PHP.ORDER_FUNCTION_CALL], - 'E': ['exp()', Blockly.PHP.ORDER_FUNCTION_CALL], + 'PI': ['M_PI', Blockly.PHP.ORDER_ATOMIC], + 'E': ['M_E', Blockly.PHP.ORDER_ATOMIC], 'GOLDEN_RATIO': ['(1 + sqrt(5)) / 2', Blockly.PHP.ORDER_DIVISION], 'SQRT2': ['M_SQRT2', Blockly.PHP.ORDER_ATOMIC], @@ -164,7 +164,32 @@ Blockly.PHP['math_number_property'] = function(block) { Blockly.PHP.ORDER_MODULUS) || '0'; var dropdown_property = block.getFieldValue('PROPERTY'); var code; - + if (dropdown_property == 'PRIME') { + // Prime is a special case as it is not a one-liner test. + var functionName = Blockly.PHP.provideFunction_( + 'math_isPrime', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($n) {', + ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', + ' if ($n == 2 || $n == 3) {', + ' return true;', + ' }', + ' // False if n is NaN, negative, is 1, or not whole.', + ' // And false if n is divisible by 2 or 3.', + ' if (!is_numeric($n) || $n <= 1 || $n % 1 != 0 || $n % 2 == 0 ||' + + ' $n % 3 == 0) {', + ' return false;', + ' }', + ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', + ' for ($x = 6; $x <= sqrt($n) + 1; $x += 6) {', + ' if ($n % ($x - 1) == 0 || $n % ($x + 1) == 0) {', + ' return false;', + ' }', + ' }', + ' return true;', + '}']); + code = functionName + '(' + number_to_check + ')'; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; + } switch (dropdown_property) { case 'EVEN': code = number_to_check + ' % 2 == 0'; @@ -172,11 +197,8 @@ Blockly.PHP['math_number_property'] = function(block) { case 'ODD': code = number_to_check + ' % 2 == 1'; break; - case 'PRIME': - code = 'mp_prob_prime('+number_to_check + ')'; - break; case 'WHOLE': - code = number_to_check + ' % 1 == 0'; + code = 'is_int(' + number_to_check + ')'; break; case 'POSITIVE': code = number_to_check + ' > 0'; @@ -197,7 +219,7 @@ Blockly.PHP['math_change'] = function(block) { // Add to a variable in place. var argument0 = Blockly.PHP.valueToCode(block, 'DELTA', Blockly.PHP.ORDER_ADDITION) || '0'; - var varName = Blockly.PHP.getName( + var varName = Blockly.PHP.variableDB_.getName( block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); return varName + ' += ' + argument0 + ';\n'; }; @@ -228,7 +250,6 @@ Blockly.PHP['math_on_list'] = function(block) { code = 'max(' + list + ')'; break; case 'AVERAGE': - // math_median([null,null,1,3]) == 2.0. var functionName = Blockly.PHP.provideFunction_( 'math_mean', [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + @@ -240,14 +261,13 @@ Blockly.PHP['math_on_list'] = function(block) { code = functionName + '(' + list + ')'; break; case 'MEDIAN': - // math_median([null,null,1,3]) == 2.0. var functionName = Blockly.PHP.provideFunction_( 'math_median', [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '($myList) {', - ' rsort($myList);', - ' $middle = round(count($myList) / 2);', - ' return $myList[$middle-1]; ', + '($arr) {', + ' sort($arr,SORT_NUMERIC);', + ' return (count($arr) % 2) ? $arr[floor(count($arr)/2)] : ', + ' ($arr[floor(count($arr)/2)] + $arr[floor(count($arr)/2) - 1]) / 2;', '}']); list = Blockly.PHP.valueToCode(block, 'LIST', Blockly.PHP.ORDER_NONE) || '[]'; @@ -264,23 +284,33 @@ Blockly.PHP['math_on_list'] = function(block) { ' $v = array_count_values($values);', ' arsort($v);', ' foreach($v as $k => $v){$total = $k; break;}', - ' return $total;', + ' return array($total);', '}']); list = Blockly.PHP.valueToCode(block, 'LIST', Blockly.PHP.ORDER_NONE) || '[]'; code = functionName + '(' + list + ')'; break; case 'STD_DEV': + var functionName = Blockly.PHP.provideFunction_( + 'math_standard_deviation', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($numbers) {', + ' $n = count($numbers);', + ' if (!$n) return null;', + ' $mean = array_sum($numbers) / count($numbers);', + ' foreach($numbers as $key => $num) $devs[$key] = pow($num - $mean, 2);', + ' return sqrt(array_sum($devs) / (count($devs) - 1));', + '}']); list = Blockly.PHP.valueToCode(block, 'LIST', - Blockly.PHP.ORDER_MEMBER) || 'array()'; - code = 'stats_standard_deviation(' + list + ')'; + Blockly.PHP.ORDER_NONE) || '[]'; + code = functionName + '(' + list + ')'; break; case 'RANDOM': var functionName = Blockly.PHP.provideFunction_( 'math_random_list', [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($list) {', - ' $x = floor(rand() * count($list));', + ' $x = rand(0, count($list)-1);', ' return $list[$x];', '}']); list = Blockly.PHP.valueToCode(block, 'LIST', diff --git a/generators/php/procedures.js b/generators/php/procedures.js index 9744735dd..75903e31c 100644 --- a/generators/php/procedures.js +++ b/generators/php/procedures.js @@ -30,7 +30,22 @@ goog.require('Blockly.PHP'); Blockly.PHP['procedures_defreturn'] = function(block) { // Define a procedure with a return value. - var funcName = Blockly.PHP.getName( + // First, add a 'global' statement for every variable that is assigned. + var globals = Blockly.Variables.allVariables(block); + for (var i = globals.length - 1; i >= 0; i--) { + var varName = globals[i]; + if (block.arguments_.indexOf(varName) == -1) { + globals[i] = Blockly.PHP.variableDB_.getName(varName, + Blockly.Variables.NAME_TYPE); + } else { + // This variable is actually a parameter name. Do not include it in + // the list of globals, thus allowing it be of local scope. + globals.splice(i, 1); + } + } + globals = globals.length ? ' global ' + globals.join(', ') + ';\n' : ''; + + var funcName = Blockly.PHP.variableDB_.getName( block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var branch = Blockly.PHP.statementToCode(block, 'STACK'); if (Blockly.PHP.STATEMENT_PREFIX) { @@ -49,11 +64,11 @@ Blockly.PHP['procedures_defreturn'] = function(block) { } var args = []; for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.PHP.getName(block.arguments_[x], + args[x] = Blockly.PHP.variableDB_.getName(block.arguments_[x], Blockly.Variables.NAME_TYPE); } var code = 'function ' + funcName + '(' + args.join(', ') + ') {\n' + - branch + returnValue + '}'; + globals + branch + returnValue + '}'; code = Blockly.PHP.scrub_(block, code); Blockly.PHP.definitions_[funcName] = code; return null; @@ -66,7 +81,7 @@ Blockly.PHP['procedures_defnoreturn'] = Blockly.PHP['procedures_callreturn'] = function(block) { // Call a procedure with a return value. - var funcName = Blockly.PHP.getName( + var funcName = Blockly.PHP.variableDB_.getName( block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var args = []; for (var x = 0; x < block.arguments_.length; x++) { @@ -79,7 +94,7 @@ Blockly.PHP['procedures_callreturn'] = function(block) { Blockly.PHP['procedures_callnoreturn'] = function(block) { // Call a procedure with no return value. - var funcName = Blockly.PHP.getName( + var funcName = Blockly.PHP.variableDB_.getName( block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var args = []; for (var x = 0; x < block.arguments_.length; x++) { diff --git a/generators/php/text.js b/generators/php/text.js index a167a4d06..835ec1292 100644 --- a/generators/php/text.js +++ b/generators/php/text.js @@ -58,14 +58,14 @@ Blockly.PHP['text_join'] = function(block) { code[n] = Blockly.PHP.valueToCode(block, 'ADD' + n, Blockly.PHP.ORDER_COMMA) || '\'\''; } - code = 'implode(\',\'' + code.join(',') + ')'; + code = 'implode(\'\', array(' + code.join(',') + '))'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; } }; Blockly.PHP['text_append'] = function(block) { // Append to a variable in place. - var varName = Blockly.PHP.getName( + var varName = Blockly.PHP.variableDB_.getName( block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', Blockly.PHP.ORDER_NONE) || '\'\''; @@ -94,7 +94,16 @@ Blockly.PHP['text_indexOf'] = function(block) { Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; var argument1 = Blockly.PHP.valueToCode(block, 'VALUE', Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - var code = operator + '(' + argument0 + ', ' + argument1 + ') + 1'; + var code = operator + '(' + argument1 + ', ' + argument0 + ') + 1'; + + var functionName = Blockly.PHP.provideFunction_( + block.getFieldValue('END')=='FIRST'?'text_indexOf':'text_lastIndexOf', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($text, $search) {', + ' $pos = ' + operator + '($text, $search);', + ' return $pos===false?0:$pos+1;', + '}']); + code = functionName + '(' + argument1 + ', ' + argument0 + ')'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; @@ -156,6 +165,15 @@ Blockly.PHP['text_getSubstring'] = function(block) { 'text_get_substring', [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($text, $where1, $at1, $where2, $at2) {', + ' if ($where2 == \'FROM_START\') {', + ' $at2--;', + ' } else if ($where2 == \'FROM_END\') {', + ' $at2 = $at2 - $at1;', + ' } else if ($where2 == \'FIRST\') {', + ' $at2 = 0;', + ' } else if ($where2 == \'LAST\') {', + ' $at2 = strlen($text);', + ' } else { $at2 = 0; }', ' if ($where1 == \'FROM_START\') {', ' $at1--;', ' } else if ($where1 == \'FROM_END\') {', @@ -165,14 +183,6 @@ Blockly.PHP['text_getSubstring'] = function(block) { ' } else if ($where1 == \'LAST\') {', ' $at1 = strlen($text) - 1;', ' } else { $at1 = 0; }', - ' if ($where2 == \'FROM_START\') {', - ' } else if ($where2 == \'FROM_END\') {', - ' $at2 = strlen($text) - $at2;', - ' } else if ($where2 == \'FIRST\') {', - ' $at2 = 0;', - ' } else if ($where2 == \'LAST\') {', - ' $at2 = strlen($text);', - ' } else { $at2 = 0; }', ' return substr($text, $at1, $at2);', '}']); var code = functionName + '(' + text + ', \'' + @@ -195,7 +205,7 @@ Blockly.PHP['text_changeCase'] = function(block) { } else if (block.getFieldValue('CASE')=='TITLECASE') { var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - code = 'ucwords(' + argument0 + ')'; + code = 'ucwords(strtolower(' + argument0 + '))'; } return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; diff --git a/generators/php/variables.js b/generators/php/variables.js index b8c8a63db..dd68f4ead 100644 --- a/generators/php/variables.js +++ b/generators/php/variables.js @@ -30,16 +30,17 @@ goog.require('Blockly.PHP'); Blockly.PHP['variables_get'] = function(block) { - // Variable getter. - var code = '$' + block.getFieldValue('VAR'); - return [code, Blockly.PHP.ORDER_ATOMIC]; + // Variable getter. + var code = Blockly.PHP.variableDB_.getName(block.getFieldValue('VAR'), + Blockly.Variables.NAME_TYPE); + return [code, Blockly.PHP.ORDER_ATOMIC]; }; Blockly.PHP['variables_set'] = function(block) { - // Variable setter. - var argument0 = Blockly.PHP.valueToCode(block, 'VALUE', - Blockly.PHP.ORDER_ASSIGNMENT) || '0'; - var varName = Blockly.PHP.getName( - block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); - return varName + ' = ' + argument0 + ';\n'; -}; + // Variable setter. + var argument0 = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_ASSIGNMENT) || '0'; + var varName = Blockly.PHP.variableDB_.getName( + block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); + return varName + ' = ' + argument0 + ';\n'; +}; \ No newline at end of file diff --git a/tests/generators/index.html b/tests/generators/index.html index 8d135ab65..2645e3011 100644 --- a/tests/generators/index.html +++ b/tests/generators/index.html @@ -24,6 +24,18 @@ + + + + + + + + + + + + @@ -126,6 +138,11 @@ function toPython() { setOutput(code); } +function toPhp() { + var code = Blockly.PHP.workspaceToCode(workspace); + setOutput(code); +} + function toDart() { var code = Blockly.Dart.workspaceToCode(workspace); setOutput(code); @@ -406,6 +423,7 @@ h1 { +

diff --git a/tests/generators/unittest_php.js b/tests/generators/unittest_php.js new file mode 100644 index 000000000..5a4a5bc4f --- /dev/null +++ b/tests/generators/unittest_php.js @@ -0,0 +1,162 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2015 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Generating PHP for unit test blocks. + * @author daarond@gmail.com (Neil Fraser) + */ +'use strict'; + +Blockly.PHP['unittest_main'] = function(block) { + // Container for unit tests. + var resultsVar = Blockly.PHP.variableDB_.getName('unittestResults', + Blockly.Variables.NAME_TYPE); + var functionName = Blockly.PHP.provideFunction_( + 'unittest_report', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '() {', + 'global ' + resultsVar + ';', + ' // Create test report.', + ' $report = array();', + ' $summary = array();', + ' $fails = 0;', + ' for ($x = 0; $x < count(' + resultsVar + '); $x++) {', + ' if (' + resultsVar + '[$x][0]) {', + ' array_push($summary, ".");', + ' } else {', + ' array_push($summary, "F");', + ' $fails++;', + ' array_push($report,"");', + ' array_push($report, "FAIL: " . ' + resultsVar + '[$x][2]);', + ' array_push($report, ' + resultsVar + '[$x][1]);', + ' }', + ' }', + ' array_unshift($report, implode("",$summary));', + ' array_push($report, "");', + ' array_push($report, "Number of tests run: " . count(' + resultsVar + '));', + ' array_push($report, "");', + ' if ($fails) {', + ' array_push($report, "FAILED (failures=" . $fails + ")");', + ' } else {', + ' array_push($report, "OK");', + ' }', + ' return implode("\\n", $report);', + '}']); + // Setup global to hold test results. + var code = resultsVar + ' = array();\n'; + // Run tests (unindented). + code += Blockly.PHP.statementToCode(block, 'DO') + .replace(/^ /, '').replace(/\n /g, '\n'); + var reportVar = Blockly.PHP.variableDB_.getDistinctName( + 'report', Blockly.Variables.NAME_TYPE); + code += reportVar + ' = ' + functionName + '();\n'; + // Destroy results. + code += resultsVar + ' = null;\n'; + // Send the report to the console (that's where errors will go anyway). + code += 'print(' + reportVar + ');\n'; + return code; +}; + +Blockly.PHP['unittest_main'].defineAssert_ = function(block) { + var resultsVar = Blockly.PHP.variableDB_.getName('unittestResults', + Blockly.Variables.NAME_TYPE); + var functionName = Blockly.PHP.provideFunction_( + 'assertEquals', + [ ' function equals($a, $b) {', + ' if ($a === $b) {', + ' return true;', + ' } else if ((is_numeric($a)) && (is_numeric($b)) &&', + ' (round($a,15) == round($b,15))) {', + ' return true;', + ' } else if (is_array($a) && is_array($b)) {', + ' if (count($a) != count($b)) {', + ' return false;', + ' }', + ' for ($i = 0; $i < count($a); $i++) {', + ' if (!equals($a[$i], $b[$i])) {', + ' return false;', + ' }', + ' }', + ' return true;', + ' }', + ' return false;', + ' }', + 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($actual, $expected, $message) {', + 'global ' + resultsVar + ';', + ' // Asserts that a value equals another value.', + ' if (!is_array(' + resultsVar + ')) {', + ' throw new Exception("Orphaned assert: " . $message);', + ' }', + ' if (equals($actual, $expected)) {', + ' array_push(' + resultsVar + ', [true, "OK", $message]);', + ' } else {', + ' array_push(' + resultsVar + ', [false, ' + + '"Expected: " . $expected . "\\nActual: " . $actual, $message]);', + ' }', + '}']); + return functionName; +}; + +Blockly.PHP['unittest_assertequals'] = function(block) { + // Asserts that a value equals another value. + var message = Blockly.PHP.quote_(block.getFieldValue('MESSAGE')); + var actual = Blockly.PHP.valueToCode(block, 'ACTUAL', + Blockly.PHP.ORDER_COMMA) || 'null'; + var expected = Blockly.PHP.valueToCode(block, 'EXPECTED', + Blockly.PHP.ORDER_COMMA) || 'null'; + return Blockly.PHP['unittest_main'].defineAssert_() + + '(' + actual + ', ' + expected + ', ' + message + ');\n'; +}; + +Blockly.PHP['unittest_assertvalue'] = function(block) { + // Asserts that a value is true, false, or null. + var message = Blockly.PHP.quote_(block.getFieldValue('MESSAGE')); + var actual = Blockly.PHP.valueToCode(block, 'ACTUAL', + Blockly.PHP.ORDER_COMMA) || 'null'; + var expected = block.getFieldValue('EXPECTED'); + if (expected == 'TRUE') { + expected = 'true'; + } else if (expected == 'FALSE') { + expected = 'false'; + } else if (expected == 'NULL') { + expected = 'null'; + } + return Blockly.PHP['unittest_main'].defineAssert_() + + '(' + actual + ', ' + expected + ', ' + message + ');\n'; +}; + +Blockly.PHP['unittest_fail'] = function(block) { + // Always assert an error. + var resultsVar = Blockly.PHP.variableDB_.getName('unittestResults', + Blockly.Variables.NAME_TYPE); + var message = Blockly.PHP.quote_(block.getFieldValue('MESSAGE')); + var functionName = Blockly.PHP.provideFunction_( + 'unittest_fail', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($message) {', + 'global ' + resultsVar + ';', + ' // Always assert an error.', + ' if (!' + resultsVar + ') {', + ' throw new Exception("Orphaned assert fail: " . $message);', + ' }', + ' array_push(' + resultsVar + ', [false, "Fail.", $message]);', + '}']); + return functionName + '(' + message + ');\n'; +};