From 0faf2b7a53a49a83d31f68a57954c4c4427e5394 Mon Sep 17 00:00:00 2001
From: daarond
Date: Tue, 12 May 2015 21:35:49 -0500
Subject: [PATCH] still working on tests, lists are the only unfinished
---
core/names.js | 12 ++-
generators/php.js | 78 +++++++--------
generators/php/lists.js | 61 ++++++++----
generators/php/loops.js | 16 +--
generators/php/math.js | 68 +++++++++----
generators/php/procedures.js | 25 ++++-
generators/php/text.js | 34 ++++---
generators/php/variables.js | 21 ++--
tests/generators/index.html | 18 ++++
tests/generators/unittest_php.js | 162 +++++++++++++++++++++++++++++++
10 files changed, 381 insertions(+), 114 deletions(-)
create mode 100644 tests/generators/unittest_php.js
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';
+};
|