Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #2 from unu/master

LadyPHP updated
  • Loading branch information...
commit af8618fe80af22b3ffb0470e4d4c1611fd34619e 2 parents 74b9add + 523e542
@juzna authored
Showing with 214 additions and 228 deletions.
  1. +10 −8 examples/05-ladyphp/classes/Fruit.php
  2. +204 −220 examples/05-ladyphp/lady.php
View
18 examples/05-ladyphp/classes/Fruit.php
@@ -1,23 +1,25 @@
<?
-# This should not be a .php file, but it is for the sake of autoloader
class Fruit
var apples = 0
var numbers = [
1: 'one',
2: 'two',
- 3: 'three',
+ 3: 'three'
]
fn addApples(n = 1)
- this.apples += n
+ if (n >= 0)
+ this.apples += n
return this
fn countApples()
apples = this.apples
out = 'You have '
- out .= isset(this.numbers[apples]) ? this.numbers[apples] : apples
- if (this.apples == 1)
- return out . ' apple.'
- else
- return "$out apples."
+ out .= isset(this.numbers[apples])
+ ? this.numbers[apples] : apples
+ switch (apples)
+ case 1
+ return out . ' apple.'
+ default
+ return "$out apples."
View
424 examples/05-ladyphp/lady.php
@@ -1,278 +1,262 @@
-<?php # DO NOT EDIT THIS FILE. It was generated by LadyPHP.
+<?php /* DO NOT EDIT THIS FILE. It was generated by LadyPHP. */
-class Lady{
+# LadyPHP - type PHP with elegance
+# ================================
+# http://github.com/unu/ladyphp
+# Unumin 2012 WTFPL
+# Everyone is permitted to copy and distribute verbatim
+# or modified copies of this license document, and changing
+# it is allowed as long as the name is changed.
+class Lady{
# ---------------------------------------------
# constants
# ---------------------------------------------
- const REGEX_CODE = '/.*[^<\?|<\?php|\{|\}|\s].*/' ;# some php code on line
- const REGEX_OPEN_TAG = '/^\s*(<\?|<\?php)\s*$/' ;# <? or <?php
- const REGEX_COMMENT = '/^\s*(#|\/\/).*/' ;# full line comment
- const REGEX_EMPTY = '/^\s*$/' ;# only spaces
- const REGEX_CONTINUE = '/^\s*[&\|\.(->)].*/' ;# continue from previous line
- const REGEX_CONTINUE_END = '/[&\|\.,\(\[=]\s*$/' ;# continue to next line
- const REGEX_CLOSING = '/^\s*[\]\)\}]+\s*$/' ;# closing bracket on line
- const REGEX_OPENING = '/(^[^(]*|.*\))\s*$/' ;# 'else' is opening
- const REGEX_VARIABLE = '/^[_a-z].*/' ;# variable name
- const REGEX_CLASS = '/^([A-Z].*|self|parent)$/' ;# class name
- const REGEX_NOVARIABLE = '/^(false|true|self|parent|null)$/' ;# not variables
-
- const PRESERVE = 0 ;# preserve code formating and comments
- const STRIP = 1 ;# strip comments, keep line numbers
- const COMPRESS = 2 ;# compress output code
-
- const OUTPUT_HEAD = '# DO NOT EDIT THIS FILE. It was generated by LadyPHP.';
- const INDENT_WIDTH = 2;
- static $debug = false;
-
+ const ENDING = '; { } ( [ <?'; # do not add ; { } after these
+ const CONTINUING = ') ]'; # line is continuing if starts with these
+ const JOINING = '&& || & | -> . + - , / * % = ? :'; # joining symbols
+ const REGEX_CLASS = ';^([A-Z].*|self|parent)$;'; # it is class
+ const REGEX_VAR = ';^[_a-z].*$;'; # it is variable
+ const REGEX_NOVAR = ';^(false|true|self|parent|null)$;'; # it isn't variable
+ const HEAD = '/* DO NOT EDIT THIS FILE. It was generated by LadyPHP. */';
+
+ # flags
+ const COMPRESS = 1; # compress output code
+ const NOCACHE = 2; # always overwrite cache file
# ---------------------------------------------
# parse
- # convert lady script to php
+ # convert LadyPHP from string to PHP code
# ---------------------------------------------
- static public function parse($source, $shrink = self::PRESERVE){
- $code = $noVar = null;
- $dump = $comment = array();
-
- # get tokens
- $tokens = token_get_all($source);
+ static function parse($source, $flags = 0){
+ $source = str_replace("\r", '', $source);
+ $tokens = self::tokenize($source);
+ $openingBracket = false;
+ $closingBrackets = [];
- # convert string tokens to arrays
- foreach ($tokens as $n => $token){
- if (!is_array($token)){
- $tokens[$n] = array(null, $token, null);}}
-
# process tokens
foreach ($tokens as $n => $token){
- list($name, $string, $line) = $token;
-
- # convert `fn` to `function`
- if ($name == T_STRING
- && $string == 'fn'){
- $code .= 'function';}
-
- # add `new` before `Foo()`
- elseif ($name == T_STRING
- && $tokens[$n + 1][1] == '('
- && $tokens[$n - 2][0] != T_NEW
- && $tokens[$n - 1][0] != T_NS_SEPARATOR
- && !preg_match(self::REGEX_VARIABLE, $string)){
- $code .= 'new ' . $string;}
-
- # add $ to variables
- elseif ($name == T_STRING
- && $tokens[$n + 1][1] != '('
- && $tokens[$n - 1][1] != '->'
- && $tokens[$n - 1][1] != '.'
- && preg_match(self::REGEX_VARIABLE, $string)
- && !preg_match(self::REGEX_NOVARIABLE, $string)){
- $code .= '$' . $string;}
+ extract($token, EXTR_OVERWRITE | EXTR_REFS);
+
+ # skip last dummy token
+ if ($n > count($tokens) - 2){
+ break;}
+
+ # convert 'fn' to 'function'
+ if ($str == 'fn'){
+ $str = 'function';}
# convert . to -> or ::
- elseif ($name == null
- && $string == '.'
- && ($tokens[$n - 1][0] != T_WHITESPACE
- || $tokens[$n + 1][0] != T_WHITESPACE)){
- if ($tokens[$n - 1][0] == T_STRING
- && preg_match(self::REGEX_CLASS, $tokens[$n - 1][1])){
- $code .= '::';}
+ elseif ($str == '.'
+ && (!$hasBlank || !$tokens[$n + 1]['hasBlank'])){
+ if (preg_match(self::REGEX_CLASS, $tokens[$n - 1]['str'])){
+ $str = '::';}
else{
- $code .= '->';}}
+ $str = '->';}}
# convert : to =>
- elseif ($name == null
- && $string == ':'
- && $tokens[$n - 1][0] != T_WHITESPACE){
- $code .= ' =>';}
-
- # strip comments
- elseif ($name == T_COMMENT){
- $comments[$line - 1] = $string;
- if (substr($string, -1) == "\n"){
- $code .= "\n";}}
-
- # convert php open tag
- elseif ($name == T_OPEN_TAG){
- $code .= '<?php ';
- if ($line == 1){
- $code .= self::OUTPUT_HEAD;}}
-
- # just copy
- else{
- $code .= $string;}
-
- # save token dump
- if (self::$debug){
- $dump[] = $line . ': ' . token_name($name) . ' = ' . $string;}}
-
- # lines
- $lines = explode("\n", $code);
- $indent = 0;
-
- # shrink lines
- $newLineNum = 0;
- foreach ($lines as $n => $line){
- if (!isset($emptyLines[$newLineNum])){
- $emptyLines[$newLineNum] = null;}
- if (preg_match(self::REGEX_CODE, $line)){
- $shrinkedLines[$newLineNum] = $line;
- $newLineNum++;}
- else {
- $emptyLines[$newLineNum] .= $line . "\n";}}
- $shrinkedLines[] = 'true;';
- $lines = $shrinkedLines;
-
- # edit lines
- foreach ($lines as $n => $line){
- if (preg_match(self::REGEX_CODE, trim($line))
- && !preg_match(self::REGEX_CONTINUE, trim($line))
- && !preg_match(self::REGEX_CONTINUE_END, trim($lines[$n - 1]))){
-
- $indentBefore = $indent;
- $indent = (strlen($line) - strlen(ltrim($line))) / self::INDENT_WIDTH;
- $jump = $indent - $indentBefore;}
- else {
- $jump = 0;}
-
- # trim spaces
- $spaceBefore = str_repeat(' ', strlen($line) - strlen(ltrim($line)));
- $spaceAfter = str_repeat(' ', strlen($line) - strlen(rtrim($line)));
- $line = trim($line);
-
- # move ending bracket
- if ($n > 0
- && preg_match(self::REGEX_CLOSING, $line)){
- $lines[$n - 1] .= $line;
- $line = $lines[$n] = '';}
-
- # add semicolon
- if ($jump <= 0
- && $n > 0
- && !preg_match(self::REGEX_CONTINUE, $line) # line starting && ||...
- && preg_match(self::REGEX_CODE, $lines[$n - 1])
- && !preg_match(self::REGEX_OPEN_TAG, $lines[$n - 1])
- && !preg_match(self::REGEX_COMMENT, $lines[$n - 1])
- && !preg_match(self::REGEX_CONTINUE_END, $lines[$n - 1])){
- $lines[$n - 1] .= ';';}
-
- # add opening bracket
- if ($jump > 0 && $n > 0
- && preg_match(self::REGEX_OPENING, $lines[$n - 1])){
-
- if (preg_match(self::REGEX_EMPTY, $lines[$n - 1])) {# bracket was moved
- $lines[$n - 2] = rtrim($lines[$n - 2], ';') . '{';}
- else{
- $lines[$n - 1] .= '{';}}
+ elseif ($str == ':' && !$hasBlank && !$isLast){
+ $str = ' =>';}
+
+ # add $ before variables
+ elseif ($type == T_STRING
+ && $tokens[$n + 1]['str'] != '('
+ && $tokens[$n - 1]['str'] != '->'
+ && preg_match(self::REGEX_VAR, $str)
+ && !preg_match(self::REGEX_NOVAR, $str)){
+ $str = '$' . $str;}
+
+ # add 'new' before 'Foo\Bar()'
+ $i = 0;
+ while ((($tokens[$n + $i]['type'] == T_STRING
+ && preg_match(self::REGEX_CLASS, $tokens[$n + $i]['str']))
+ || $tokens[$n + $i]['type'] == T_NS_SEPARATOR)
+ && $hasBlank
+ && (!$tokens[$n + $i]['hasBlank'] || $i == 0)
+ && $tokens[$n - 1]['type'] != T_NEW){
+ if ($tokens[$n + $i]['type'] == T_STRING
+ && $tokens[$n + $i + 1]['str'] == '('){
+ $str = 'new ' . $str;
+ break;}
+ $i++;}
+
+ # add semicolon and brackets
+ if ($isLast
+ && !in_array($str, explode(' ', self::JOINING . ' ' . self::ENDING))
+ && $type != T_CLOSE_TAG){
+
+ # sort list of closing brackets
+ $closingBrackets = array_unique($closingBrackets);
+ rsort($closingBrackets);
+
+ # switch block
+ $isSwitch = false;
+ $i = 0;
+ while (isset($tokens[$n - $i]['y'])
+ && $tokens[$n - $i]['y'] == $y){
+ if (in_array($tokens[$n - $i]['type'], [T_CASE, T_DEFAULT])){
+ $isSwitch = true;
+ break;}
+ $i++;}
+ if ($isSwitch){
+ $str .= ':';}
+
+ # next line is indented
+ elseif ($tokens[$n + 1]['indent'] > $indent){
+ # add opening bracket
+ if (!in_array($tokens[$n + 1]['str'], explode(' ', self::JOINING . ' ' . self::CONTINUING))){
+ $str .= '{';
+ $closingBrackets[] = $indent;
+ $openingBracket = false;}
+ # save opening bracket
+ else{
+ $openingBracket = $indent;}}
+
+ # line doesn't continue
+ elseif (!in_array($tokens[$n + 1]['str'], explode(' ', self::JOINING . ' ' . self::CONTINUING))){
+ # there is saved opening bracket
+ if ($openingBracket !== false
+ && $tokens[$n + 1]['indent'] > $openingBracket){
+ $str .= '{';
+ $closingBrackets[] = $openingBracket;
+ $openingBracket = false;}
+ # add semicolon
+ else{
+ $str .= ';';}}
+
+ # add closing brackets
+ if ($indent > $tokens[$n + 1]['indent']){
+ while (isset($closingBrackets[0])
+ && $closingBrackets[0] >= $tokens[$n + 1]['indent']){
+ $str .= '}';
+ $closingBrackets = array_slice($closingBrackets, 1);}}}
+
+ # convert php open tags
+ if ($type == T_OPEN_TAG){
+ $str = '<?php ';
+ if ($y == 0){
+ $str .= self::HEAD;}}
+ if ($type == T_OPEN_TAG_WITH_ECHO){
+ $str = '<?php echo ';}
+
+ # save token
+ $tokens[$n] = $token;}
+
+ # glue code
+ $code = null;
+ foreach ($tokens as $token){
+ $code .= $token['blank'] . $token['str'];}
+
+ # return (and compress)
+ return ($flags & self::COMPRESS) ? self::compress($code) : $code;}
- # add closing brackets
- if ($jump < 0
- && !preg_match(self::REGEX_CONTINUE_END, $lines[$n - 1])){
- $lines[$n - 1] .= str_repeat('}', -$jump);}
-
- # add spaces (PRESERVE)
- if ($shrink == self::PRESERVE){
- $line = $spaceBefore . $line . $spaceAfter;}
-
- # change line
- $lines[$n] = $line;}
+ # ---------------------------------------------
+ # tokenize
+ # ---------------------------------------------
+ static function tokenize($source){
+ $tokens = [];
+ $blank = null;
- # add empty lines
- foreach ($lines as $n => $line){
- $lines[$n] = $emptyLines[$n] . $lines[$n];}
+ # prepare tokens
+ foreach (token_get_all($source) as $n => $token){
- # reindex lines
- $lines = explode("\n", implode("\n", $lines));
+ # convert to associative array
+ if (is_array($token)){
+ $token = ['str' => $token[1], 'type' => $token[0]];}
+ else{
+ $token = ['str' => $token, 'type' => null];}
- # add comments (PRESERVE)
- if ($shrink == self::PRESERVE){
- foreach ($lines as $n => $line){
- if (isset($comments[$n])){
- $lines[$n] = $lines[$n] . rtrim($comments[$n]);}}}
+ # save whitespaces and comments into tokens
+ if (in_array($token['type'], [T_COMMENT, T_DOC_COMMENT, T_WHITESPACE, T_INLINE_HTML])){
+ $blank .= $token['str'];}
+ else{
+ $token['blank'] = $blank;
+ $token['hasBlank'] = ($blank != null);
+ $blank = null;
+ $tokens[] = $token;}}
- # trim spaces (STRIP)
- if ($shrink == self::STRIP){
- foreach ($lines as $n => $line){
- $lines[$n] = rtrim($line, ' ');}}
+ # save remaining blank
+ $tokens[] = ['str' => null, 'type' => null, 'blank' => $blank, 'isLast' => true];
- # format code
- $code = implode("\n", array_slice($lines, 0, -1));
+ # get positions
+ foreach ($tokens as $n => $token){
+ $token['n'] = $n;
+ if ($n == 0){
+ $token['indent'] = $token['x'] = $token['y'] = 0;
+ $token['isFirst'] = true;}
+ else{
+ $token['y'] = $tokens[$n - 1]['y'] + count(explode("\n", $tokens[$n - 1]['str'] . $token['blank'])) - 1;
+ $token['isFirst'] = $tokens[$n - 1]['isLast'] = ($tokens[$n - 1]['y'] != $token['y']);
+ $token['x'] = mb_strlen(array_slice(explode("\n", $token['blank']), -1)[0]);
+ $token['x'] += !$token['isFirst'] ? $tokens[$n - 1]['x'] + mb_strlen($tokens[$n - 1]['str']) : null;
+ $token['indent'] = $token['isFirst'] ? $token['x'] : $tokens[$n - 1]['indent'];}
+ $tokens[$n] = $token;}
- # minify code (COMPRESS)
- if ($shrink == self::COMPRESS){
- $code = self::compress($code);}
+ return $tokens;}
- # output
- if (self::$debug){
- NDebugger::barDump($dump, 'Lady tokens');}
- return $code;}
-
+ # ---------------------------------------------
+ # cacheFile
+ # check cacheFile, parse if it's old
+ # ---------------------------------------------
+ static function cacheFile($file, $cacheFile, $flags = 0){
+ if (!is_dir(dirname($cacheFile))){
+ mkdir(dirname($cacheFile), 0755, true);}
+ if (!is_file($cacheFile) || filemtime($cacheFile) <= filemtime($file) || $flags & self::NOCACHE){
+ file_put_contents($cacheFile, self::parseFile($file, null, $flags));}
+ return $cacheFile;}
# ---------------------------------------------
# parseFile
# load file and parse it
# ---------------------------------------------
- static public function parseFile($file, $cacheFile = null, $shrink = self::PRESERVE){
- if ($cacheFile == null){
- return self::parse(file_get_contents($file), $shrink);}
+ static function parseFile($file, $cacheFile = null, $flags = 0){
+ if ($cacheFile){
+ return file_get_contents(self::cacheFile($file, $cacheFile, $flags));}
else{
- if (!is_file($cacheFile) || filemtime($cacheFile) <= filemtime($file)){
- file_put_contents($cacheFile, self::parseFile($file, null, $shrink));}
- file_get_contents($cacheFile);}}
-
+ return self::parse(file_get_contents($file), $flags);}}
# ---------------------------------------------
# includeFile
# parse file and execute it
# ---------------------------------------------
- static public function includeFile($file, $cacheFile = null, $shrink = self::PRESERVE){
- if ($cacheFile == null){
- return eval('?>' . self::parseFile($file, null, $shrink));}
+ static function includeFile($file, $cacheFile = null, $flags = 0){
+ if ($cacheFile){
+ return require_once(self::cacheFile($file, $cacheFile, $flags));}
else{
- if (!is_file($cacheFile) || filemtime($cacheFile) <= filemtime($file)){
- file_put_contents($cacheFile, self::parseFile($file, null, $shrink));}
- require_once($cacheFile);}}
-
+ return eval('?>' . self::parseFile($file, null, $flags));}}
+
# ---------------------------------------------
# testFile
+ # parse file and show input and output as html
# ---------------------------------------------
- static public function testFile($file, $shrink = self::PRESERVE){
+ static function testFile($file, $flags = 0){
$input = file_get_contents($file);
- $output = self::parseFile($file, null, $shrink);
- $pre = '<pre style="max-height:40em;max-width:30em;float:left;overflow:auto;font-size:12px;border:1px solid gray;padding:.2em;background-color:#fafafa">';
+ $output = self::parseFile($file, null, $flags);
+ $pre = '<pre style="max-height:40em;max-width:30em;float:left;overflow:auto;font-size:12px;border:1px solid gray;padding:.2em;background-color:#fff">';
$html = '<div><h3 style="margin:0">' . $file . '</h3>';
- foreach (array($input, $output) as $text){
+ foreach ([$input, $output] as $text){
$html .= $pre;
foreach (explode("\n", $text) as $n => $line){
$html .= sprintf('%3d: %s', $n, htmlspecialchars($line)) . "\n";}
$html .= '</pre>';}
- $html .= '<hr style="height=0;border:none;clear:both"></div>';
- return $html;}
+ return $html . '<hr style="height=0;border:none;clear:both"></div>';}
# ---------------------------------------------
# compress
- # strip comments and compress php source
+ # remove comments and whitespaces from PHP code
# ---------------------------------------------
- static public function compress($php){
- if (!defined('T_DOC_COMMENT')){
- define('T_DOC_COMMENT', -1);}
- if (!defined('T_ML_COMMENT')){
- define('T_ML_COMMENT', -1);}
-
- $space = $output = '';
- $set = '!"#$&\'()*+,-./:;<=>?@[\]^`{|}';
- $set = array_flip(preg_split('//',$set));
-
+ static function compress($php){
+ $space = $output = null;
+ $set = array_flip(preg_split('//', '!"#$&\'()*+,-./:;<=>?@[\]^`{|}'));
foreach (token_get_all($php) as $token){
if (!is_array($token)){
- $token = array(0, $token);}
-
- if (in_array($token[0], array(T_COMMENT, T_ML_COMMENT, T_DOC_COMMENT, T_WHITESPACE))){
+ $token = [0, $token];}
+ if (in_array($token[0], [T_COMMENT, T_DOC_COMMENT, T_WHITESPACE])){
$space = "\n";}
else{
if (isset($set[substr($output, -1)]) || isset($set[$token[1]{0}])){
- $space = '';}
+ $space = null;}
$output .= $space . $token[1];
- $space = '';}}
+ $space = null;}}
return $output;}}
-
Please sign in to comment.
Something went wrong with that request. Please try again.