Code

My Coolest Code. Ever.

No Comments »Written on March 20th, 2011 by
Categories: Code
My Coolest Code. Ever.

Code? Cool? Well OK, hang on.

The first online app I ever wrote was a chess/checkers game called Acies. It didn’t have AI, but it used raw TCP to keep the two game boards in sync, and allow players to chat and send ‘nudges’.

I was hunting around for some other code this morning, and I found a draft of the code used to enforce chess/checkers rules. It sets up a board, keeps track of the game, and tells you which moves are legal, and when you’re in check, checkmate or stalemate (and whatever they call it when you win checkers).

Slightly more interesting is that I wrote it in VB5 when I was 14. It seems I was smarter then than I am now, because although it’s somewhat unstructured and undisciplined, it’s more creatively crafted than anything I’d write today. This is the proof I need that alcohol kills brain cells!

Don’t ask me for the source for the rest of the game, let alone the game itself; no way am I looking that far back!

Option Explicit

Public Enum ePieceColor
    Black = 0
    White = 10
End Enum

Public Enum ePieceType
    None = 0
    Pawn = 1
    Knight = 2
    Bishop = 3
    Rook = 4
    Queen = 5
    King = 6
    Checker = 7
    CheckerKing = 8
End Enum

Public Enum eGameType
    Chess = 0
    Checkers = 1
End Enum

Public Enum eGameState
    NotStarted = 0
    InProgress = 1
    Check = 2
    CheckMate = 3
    stalemate = 4
End Enum

Private Square(1 To 8, 1 To 8) As Byte
Private pGameType As eGameType
Private pGameState As eGameState
Private pWhoseMove As ePieceColor
Private pCanCastle(1 To 18) As Boolean
Private pCanBeTakenEnPassent(10, 1 To 8) As Boolean

Public Property Get MoveIsValid(ByVal StartX As Byte, ByVal StartY As Byte, ByVal TargetX As Byte, ByVal TargetY As Byte, ParamArray Additional() As Variant)
MoveIsValid = False
Dim MD As String, Ad As String, i As Integer
If Abs(UBound(Additional) Mod 2) <> 1 Then Exit Property
If UBound(Additional) >= 0 Then
    For i = 0 To UBound(Additional) Step 2
        Ad = Ad & Chr(Val(Additional(i)) + Val(Additional(i + 1)) * 16)
    Next i
End If
MD = GetValidMD(StartX, StartY, TargetX, TargetY, Ad)
If MD = "" Then Exit Property
MoveIsValid = IsValid(MD)
End Property

Public Function MovePiece(ByVal StartX As Byte, ByVal StartY As Byte, ByVal TargetX As Byte, ByVal TargetY As Byte, ParamArray Additional() As Variant) As Boolean
MovePiece = False
Dim MD As String, i As Integer
If Abs(UBound(Additional) Mod 2) <> 1 Then Exit Function
If UBound(Additional) >= 0 Then
    For i = 0 To UBound(Additional) Step 2
        MD = MD & Chr(Val(Additional(i)) + Val(Additional(i + 1)) * 16)
    Next i
End If
MD = GetValidMD(StartX, StartY, TargetX, TargetY, MD)
If MD = "" Then Exit Function
MovePiece = IsValid(MD)
If Not MovePiece Then Exit Function
If IsCheck(MD, 10 - pWhoseMove) Then pGameState = Check
pGameState = InProgress
ResetEnPassent pWhoseMove
If PT(StartX, StartY) = Pawn And Abs(StartY - TargetY) = 2 Then pCanBeTakenEnPassent(pWhoseMove, StartX) = True
If PT(StartX, StartY) = King Then
    pCanCastle(pWhoseMove + 1) = False
    pCanCastle(pWhoseMove + 8) = False
End If
If PT(StartX, StartY) = Rook Then pCanCastle(pWhoseMove + StartX) = False
ForceMove MD
pWhoseMove = 10 - pWhoseMove
If Not CanMove Then
    If pGameState = Check Then pGameState = CheckMate Else pGameState = stalemate
End If
End Function

Public Property Get WhoseMove() As ePieceColor
WhoseMove = pWhoseMove
End Property

Public Property Let WhoseMove(vData As ePieceColor)
pWhoseMove = vData
ResetEnPassent vData
End Property

Public Property Get GameType() As eGameType
GameType = pGameType
End Property

Public Property Get GameState() As eGameState
GameState = pGameState
End Property

Public Property Get Free(ByVal XIndex As Byte, ByVal YIndex As Byte) As Boolean
Free = (PieceType(XIndex, YIndex) = None)
End Property

Public Property Get PieceColor(ByVal XIndex As Byte, ByVal YIndex As Byte) As ePieceColor
PieceColor = PC(XIndex, YIndex)
End Property

Public Property Let PieceColor(ByVal XIndex As Byte, ByVal YIndex As Byte, ByVal vData As ePieceColor)
Square(XIndex, YIndex) = PT(XIndex, YIndex) + vData
End Property

Public Property Get PieceType(ByVal XIndex As Byte, ByVal YIndex As Byte) As ePieceType
PieceType = PT(XIndex, YIndex)
End Property

Public Property Let PieceType(ByVal XIndex As Byte, ByVal YIndex As Byte, ByVal vData As ePieceType)
Square(XIndex, YIndex) = PC(XIndex, YIndex) + vData
End Property

Public Sub NewGame(NewGameType As eGameType, Optional WhoStarts As ePieceColor = -1)
Dim i As Integer
If WhoStarts = -1 Then
    If NewGameType = Chess Then WhoStarts = White Else WhoStarts = Black
End If
pWhoseMove = WhoStarts
pGameType = NewGameType
pGameState = NotStarted
pCanCastle(White + 3) = True
pCanCastle(Black + 3) = True
pCanCastle(White + 7) = True
pCanCastle(Black + 7) = True
ResetEnPassent Black
ResetEnPassent White
Dim x, y, pCol As Integer
If pGameType = Chess Then
    For x = 1 To 8
    For y = 1 To 8
        If y < 4 Then pCol = White Else pCol = Black
        Select Case y
            Case 3, 4, 5, 6: Square(x, y) = 0
            Case 2, 7: Square(x, y) = pCol + Pawn
            Case 1, 8
            Select Case x
                Case 1, 8: Square(x, y) = pCol + Rook
                Case 2, 7: Square(x, y) = pCol + Knight
                Case 3, 6: Square(x, y) = pCol + Bishop
                Case 4: Square(x, y) = pCol + Queen
                Case 5: Square(x, y) = pCol + King
            End Select
        End Select
    Next y
    Next x
Else
    For x = 1 To 8
    For y = 1 To 8
        If y < 4 Then pCol = White Else pCol = Black
        If y <> 4 And y <> 5 And (x + y) Mod 2 = 0 Then
            Square(x, y) = Checker + pCol
        Else
            Square(x, y) = 0
        End If
    Next y
    Next x
End If
End Sub

Private Function IsValid(MoveDescription As String) As Boolean
IsValid = False
If pGameState = CheckMate Or pGameState = stalemate Then Exit Function
Dim pCol As ePieceColor
pCol = PC(Asc(Left(MoveDescription, 1)) And 15, (Asc(Left(MoveDescription, 1)) And 240) / 16)
If Not pCol = pWhoseMove Then Exit Function
If Not CanGoToSquare(MoveDescription) Then Exit Function
If IsCheck(MoveDescription, pCol) Then Exit Function
IsValid = True
End Function

Private Function CanMove() As Boolean
CanMove = False
If pGameType = Checkers Then Exit Function
Dim i, j, k, l As Byte, MD As String
For i = 1 To 8
    For j = 1 To 8
        If Not Free(i, j) And PieceColor(i, j) = WhoseMove Then
            For k = 1 To 8
                For l = 1 To 8
                    If Free(k, l) And IsValid(GetValidMD(i, j, k, l)) Then
                        CanMove = True
                    End If
                Next l
            Next k
        End If
    Next j
Next i
End Function

Private Function IsCheck(MoveDescription As String, CheckToWho As ePieceColor) As Boolean
IsCheck = False
If pGameType = Checkers Then Exit Function
Dim i, j As Byte, CheckMove As String
Dim OldSquare(1 To 8, 1 To 8) As Integer
If MoveDescription <> "" Then
    For i = 1 To 8
        For j = 1 To 8
            OldSquare(i, j) = Square(i, j)
        Next j
    Next i
    ForceMove MoveDescription
End If
For i = 1 To 8
    For j = 1 To 8
        If Square(i, j) = CheckToWho + King Then
            CheckMove = Chr(i + j * 16)
        End If
    Next j
Next i
For i = 1 To 8
    For j = 1 To 8
        If Not Free(i, j) And PC(i, j) = 10 - CheckToWho Then
            IsCheck = IsCheck Or CanGoToSquare(Chr(i + j * 16) & CheckMove)
        End If
    Next j
Next i
If MoveDescription <> "" Then
    For i = 1 To 8
        For j = 1 To 8
            Square(i, j) = OldSquare(i, j)
        Next j
    Next i
End If
End Function

Private Sub ForceMove(MoveDescription As String)
Dim i As Integer
If pGameType = Checkers Then
    Dim x(255) As Byte
    Dim y(255) As Byte
    For i = 0 To Len(MoveDescription) - 1
        x(i) = Asc(Mid(MoveDescription, i + 1, 1)) And 15
        y(i) = (Asc(Mid(MoveDescription, i + 1, 1)) And 240) / 16
    Next i
    For i = 0 To Len(MoveDescription) - 2
        Square(x(i + 1), y(i + 1)) = Square(x(i), y(i))
        Square(x(i), y(i)) = None
        If Abs(x(i) - x(i + 1)) = 2 Then Square((x(i) + x(i + 1)) / 2, (y(i) + y(i + 1)) / 2) = None
    Next i
Else
    Dim X1, X2, Y1, Y2 As Integer
    X1 = Asc(Left(MoveDescription, 1)) And 15
    Y1 = (Asc(Left(MoveDescription, 1)) And 240) / 16
    X2 = Asc(Right(MoveDescription, 1)) And 15
    Y2 = (Asc(Right(MoveDescription, 1)) And 240) / 16
    If PT(X1, Y1) = King And Abs(X1 - X2) = 2 Then
        If X2 = 3 Then
            Square(1, Y1) = None
            Square(4, Y1) = Rook + PC(X1, Y1)
        Else
            Square(8, Y1) = None
            Square(6, Y1) = Rook + PC(X1, Y1)
        End If
    End If
    If IsEnPassent(MoveDescription) Then Square(X2, Y1) = None
    Square(X2, Y2) = Square(X1, Y1)
    Square(X1, Y1) = None
End If
End Sub

Private Function PT(ByVal XIndex As Byte, ByVal YIndex As Byte) As ePieceType
PT = Square(XIndex, YIndex) Mod 10
End Function

Private Function PC(ByVal XIndex As Byte, ByVal YIndex As Byte) As ePieceColor
PC = Int(Square(XIndex, YIndex) / 10) * 10
End Function

Private Function CanGoToSquare(MoveDescription As String) As Boolean
CanGoToSquare = False
Dim X1, X2, Y1, Y2, i, StepX, StepY As Integer, pCol As ePieceColor, pType As ePieceType
X1 = Asc(Left(MoveDescription, 1)) And 15
Y1 = (Asc(Left(MoveDescription, 1)) And 240) / 16
X2 = Asc(Right(MoveDescription, 1)) And 15
Y2 = (Asc(Right(MoveDescription, 1)) And 240) / 16
pCol = PC(X1, Y1)
pType = PT(X1, Y1)
If PC(X2, Y2) = PC(X1, Y1) And Not Free(X2, Y2) Then Exit Function
If X1 = X2 And Y1 = Y2 Then Exit Function
If pGameType = Checkers And Not Free(X2, Y2) Then Exit Function
Select Case pType
    Case Checker, CheckerKing
        If pType = Checker Then
            If Y2 < Y1 And pCol = White Then Exit Function
            If Y2 > Y1 And pCol = Black Then Exit Function
        End If
        If Abs(X1 - X2) = 1 And Abs(Y1 - Y2) = 1 Then CanGoToSquare = True
        If Abs(X1 - X2) = 2 And Abs(Y1 - Y2) = 2 And PC((X1 + X2) / 2, (Y1 + Y2) / 2) <> pCol Then CanGoToSquare = True
        Exit Function
    Case Knight
        If X1 = X2 Or Y1 = Y2 Then Exit Function
        If Abs(X1 - X2) + Abs(Y1 - Y2) <> 3 Then Exit Function
    Case Bishop
        If Abs(X1 - X2) <> Abs(Y1 - Y2) Then Exit Function
        If X1 < X2 Then StepX = 1 Else StepX = -1
        If Y1 < Y2 Then StepY = 1 Else StepY = -1
        For i = X1 + StepX To X2 - StepX Step StepX
            If Not Free(i, Y1 + (StepY * i)) Then Exit Function
        Next i
    Case Queen
        For i = 3 To 4
            PieceType(X1, Y1) = i
            If CanGoToSquare(Chr(X1 + Y1 * 16) & Chr(X2 + Y2 * 16)) Then CanGoToSquare = True
        Next i
        PieceType(X1, Y1) = Queen
        If Not CanGoToSquare Then Exit Function
    Case King
        If pGameState <> Check And pCanCastle(pCol + X2) And Y1 = Y2 And Abs(X1 - X2) = 2 And X1 = 5 Then
            If X2 = 3 Then
                StepX = 2: StepY = 4: i = 1
            Else
                StepX = 6: StepY = 7: i = 8
            End If
            If Square(i, Y1) <> Rook + pCol Then Exit Function
            For i = StepX To StepY
                If Not Free(i, Y1) Then Exit Function
            Next i
        End If
        If Abs(X1 - X2) > 1 Or Abs(Y1 - Y2) > 1 Then Exit Function
    Case Rook
        If X1 <> X2 And Y1 <> Y2 Then Exit Function
        If Abs(X1 - X2) = 1 Or Abs(Y1 - Y2) = 1 Then
            CanGoToSquare = True
            Exit Function
        Else
            If Y1 = Y2 Then
                If X1 < X2 Then StepX = 1 Else StepX = -1
                For i = X1 + StepX To X2 - StepX Step StepX
                    If Not Free(i, Y1) Then Exit Function
                Next i
            Else
                If Y1 < Y2 Then StepY = 1 Else StepY = -1
                For i = Y1 + StepY To Y2 - StepY Step StepY
                    If Not Free(X1, i) Then Exit Function
                Next i
            End If
        End If
    Case Pawn
        Dim pDir As Integer: pDir = ((pCol / 5) - 1)
        If Y2 = Y1 + pDir And X1 = X2 And Free(X2, Y2) Then CanGoToSquare = True
        If ((Y1 = 2 And pCol = White) Or (Y1 = 7 And pCol = Black)) And Y2 = Y1 + 2 * pDir And X1 = X2 And Free(X2, Y2) Then CanGoToSquare = True
        If Y2 = Y1 + pDir And Abs(X1 - X2) = 1 And PC(X2, Y2) <> pCol And Not Free(X2, Y2) Then CanGoToSquare = True
        CanGoToSquare = CanGoToSquare Or IsEnPassent(MoveDescription)
        Exit Function
End Select
CanGoToSquare = True
End Function

Private Function IsEnPassent(MoveDescription As String) As Boolean
IsEnPassent = False
Dim X1, X2, Y1, Y2, pDir As Integer, pCol As ePieceColor, pType As ePieceType
X1 = Asc(Left(MoveDescription, 1)) And 15
Y1 = (Asc(Left(MoveDescription, 1)) And 240) / 16
X2 = Asc(Right(MoveDescription, 1)) And 15
Y2 = (Asc(Right(MoveDescription, 1)) And 240) / 16
pCol = PC(X1, Y1)
pType = PT(X1, Y1)
pDir = ((pCol / 5) - 1) * -1
If Y2 <> Y1 + pDir Then Exit Function
If Abs(X1 - X2) <> 1 Then Exit Function
If Square(X2, Y1) <> Pawn + (10 - pCol) Then Exit Function
If Not Free(X2, Y2) Then Exit Function
If Not pCanBeTakenEnPassent(10 - pCol, X2) Then Exit Function
IsEnPassent = True
End Function

Private Sub ResetEnPassent(WhichColor As ePieceColor)
Dim i As Integer
For i = 1 To 8
    pCanBeTakenEnPassent(WhichColor, i) = False
Next i
End Sub

Private Function GetValidMD(ByVal StartX As Byte, ByVal StartY As Byte, ByVal TargetX As Byte, ByVal TargetY As Byte, Optional Additional As String = "") As String
GetValidMD = ""
Dim i As Integer, MD As String
If Val(StartX) > 8 Or Val(StartX) < 1 Then Exit Function
If Val(StartY) > 8 Or Val(StartY) < 1 Then Exit Function
If Val(TargetX) > 8 Or Val(TargetX) < 1 Then Exit Function
If Val(TargetY) > 8 Or Val(TargetY) < 1 Then Exit Function
If Additional <> "" Then
    If pGameType = Chess Then Exit Function
    For i = 1 To Len(Additional)
        MD = CStr(Asc(Mid(Additional, i, 1)) And 15)
        If Val(MD) < 1 Or Val(MD) > 8 Then Exit Function
        MD = CStr((Asc(Mid(Additional, i, 1)) And 240) / 16)
        If Val(MD) < 1 Or Val(MD) > 8 Then Exit Function
    Next i
End If
MD = Chr(StartX + StartY * 16) & Chr(TargetX + TargetY * 16) & Additional
GetValidMD = MD
End Function

JavaScript, CSS and PHP Compacter for Python

No Comments »Written on March 5th, 2011 by
Categories: Python

Here’s a port of the Hx compacter library. Originally written in PHP, it’s now available as a Python module.

You can get the original PHP version of the JavaScript compacter here.

This is a work-in-progress. It’s been superficially tested. Please report any issues you find!

#! /usr/bin/python

import re
import sys

__author__="Neil E. Pearson"
__date__ ="$04/05/2007 1:22:19 PM$"

def js(input):
	cd=re.match(r"^\s*/[/*]\s*<!\[CDATA\[\s*(?:\*/)?(.*)/[/*]\s*\]\]>\s*(?:\*/)?\s*$", input, re.DOTALL)
	if cd: input=cd.group(1)
	ph=0
	while str(ph) in input: ph+=1
	lines=re.split(r"(?s)\s*[\r\n]+\s*", input)
	subs=(
		(r"\s*//.*$"		, r""),
		(r"\s*([^\w$])\s*"	, r"\1"),
		(r";}"				, r"}")
	)
	s=[]
	cm=False
	for k in range(len(lines)-1):
		l=lines[k]
		if cm:
			if '*/' in l:
				l=re.sub(r"^.*?\*/","",l)
				cm=False
			else: l=""
		if not cm:
			c=0
			while c<len(l):
				n=l1
				if n in "\"'/":
					if re.search(r"([=(?:]\s*/|[\'\"])$", l[:c+1]):
						cl=findDelimiter(l,c)
						if cl is not False:
							s.append(l1)
							l="%s%s.%s%s" % (l[:c], ph, len(s)-1, l[cl+1:])
							c=0
				c+=1

			l=re.sub(r"/\*.*?\*/","",l)
			if "/*" in l:
				l=re.sub(r"/\*.*$","",l)
				cm=True
			for x in subs: l=re.sub(x[0], x[1], l)
			l=l.strip()
			if k<len(lines)-1:
				if lines[k+1]:
					if not re.search(r"^\s*[-{};:+=&|^,?.]", lines[k+1]):
						if re.search(r"([^-{};:+=&|^,.?([\\]]|[-+]{2})$", l): l+=";"
						m=re.search(r"(if|for|while|with)(\(.*?\));$", l, re.IGNORECASE)
						if m:
							if findDelimiter(m.group(2))==len(m.group(2))-1:
								l=l[:-1]
		l=l.replace('else;','else ')
		lines[k]=l
	ret="".join(lines)
	f=-1
	while(True):
		f=ret.lower().find("do{",f+1)
		if(f<0): break
		b=findDelimiter(ret,f+2)
		if b is not False:
			b=ret.lower().find("while(",b)
			if b is not False:
				b+=5
				b=findDelimiter(ret,b)
				ret="%s;%s" % (ret[:b+1], ret[b+1:])
	i=0
	while i<len(ret):
		if ret[i] in "=:":
			o=i+1
			if ret[o:o+9].lower()=="function(":
				o=findDelimiter(ret,o+8)+1
			if ret[o]=="{":
				cl=findDelimiter(ret,o)
				if cl<len(ret):
					if re.match(r"""[-\w(${["']""",ret[cl+1]):
						ret="%s;%s" % (ret[:cl+1], ret[cl+1:])
		i+=1
	ret=ret.replace(";}","}")
	ret=re.sub(str(ph)+r"\.(\d+)", lambda m: s[int(m.group(1))], ret)
	if cd:
		ret="/*<![CDATA[*/%s//]]>" % ret
	return ret

def css(input):
	subs=(
		(r"\s+"					, r" "),
		(r"([{;,}:])\s"			, r"\1"),
		(r"\s([}{,;])"			, r"\1"),
		(r"(?s)/\*.*?\*/"		, r""),
		(r";\s*(}|$)"			, r"\1"),
		(r"(?i)#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3(\s|\}|;|$)" , r"#\1\2\3\4"),
		(r"\b0[a-z]{2}\b"		, r"0"),
		(r"\b0(\.\d+[a-z]{2})\b", r"\1")
	)
	ret=input
	for i in subs: ret=re.sub(i[0],i[1],ret)
	return ret.strip()

def php(input):
	subs=(
		("(?m)(?<!:)//.*$",					""),
		(r"(?s)/\*.*\*/",					""),
		(r"(\s)\s+",						r"\1"),
		(r"""([^\w\s'"])\s+([^\w\s'"])""",	r"\1\2")
	)
	ret=input
	for i in subs: ret=re.sub(i[0],i[1],ret)
	return ret.strip()

def findDelimiter(str, pos=0, dir=0, esc="\\"):
	if pos>len(str): return False
	pairs="\"\"''//<>[]{}()"
	pp=pairs.find(str[pos])
	if pp<0: return False
	op=pp%2 and -1 or 1
	o=None
	if pairs[pp]!=pairs[pp+op]:
		dir=op
		o=pairs[pp]
	if not dir: dir=1
	c=pairs[pp+op]
	nest=1
	pos+=dir
	while pos>=0 and pos<len(str):
		if esc:
			if pos<len(str)-1:
				if str[pos+dir-1]==esc: pos+=dir*2
			if pos>=0 and pos<len(str):
				if str[pos]==o: nest+=1
				if str[pos]==c: nest-=1
		if not nest: return pos
		pos+=dir
	return False

if __name__ == "__main__":
	if len(sys.argv)==2:
		filename=sys.argv[1]
		func=css if filename.lower().endswith(".css") else js
		f=open(filename,"r")
		print func(f.read())
		f.close()

JavaScript Compactor for PHP

No Comments »Written on February 25th, 2011 by
Categories: JavaScript, PHP

There’s minify and a bunch of other JS compactors/compressors out there, but nothing that I’ve found works with any of the massive scripts I’ve written. Enter the Hx JavaScript compactor. It’s too easy to use so I won’t bother explaining it.

Right now it’s just PHP. I’ll be porting it to Python when I have a chance.

class Hx {
	public static function compact_js($js) {
		$cp='`^\s*/[/*]\s*<!\[CDATA\[\s*(?:\*/)?(.*)/[/*]\s*\]\]>\s*(?:\*/)?\s*$`s';
		$cd=0;
		if(preg_match($cp,$js)) {
			$cd=1;
			$js=preg_replace($cp,'\1',$js,1);
		}
		$ph=0;
		while(strpos($js,strval($ph))!==false) $ph++;
		$lines=preg_split('`\s*[\r\n]+\s*`',trim($js));
		$fr=array(
			'`\s*//.*$`'		=> '',
			'`\s*([^\w$])\s*`'	=> '\1',
			'`;}`'				=> '}'
		);
		$find=array_keys($fr);
		$repl=array_values($fr);
		$s=array();
		$cm=null;
		foreach($lines as $k=>$l) {
			if($cm) {
				if(strpos($l,'*/')!==false) {
					$l=preg_replace('`^.*?\*/`','',$l);
					$cm=0;
				} else $l='';
			}
			if(!$cm) {
				for($c=0;$c<strlen($l);$c++) {
					$n=$l{$c};
					if($n=='"'||$n=="'"||$n=='/') if(preg_match('`([=(?:]\s*/|[\'"])$`',substr($l,0,$c+1))) {
						$cl=self::find_delimiter($l,$c);
						if($cl!==false) {
							$s[]=substr($l,$c,$cl-$c+1);
							$l=substr($l,0,$c).$ph. '.' .strval(count($s)-1).substr($l,$cl+1);
						}
					}
				}
				$l=preg_replace('`/\*.*?\*/`','',$l);
				if(strpos($l,'/*')!==false) {
					$l=preg_replace('`/\*.*$`','',$l);
					$cm=1;
				}
				$l=trim(preg_replace($find,$repl,$l));
				if($k<count($lines)-1) if($nl=$lines[$k+1]) if(!preg_match('`^\s*[-{};:+=&|^,?.]`',$nl)) {
					if(preg_match('`([^-{};:+=&|^,.?([\\]]|[-+]{2})$`',$l)) $l.=';';
					if(preg_match('`^(.*)(if|for|while|with)(\(.*?\));$`i',$l,$r)) if(self::find_delimiter($r[3])==strlen($r[3])-1) $l=$r[1].$r[2].$r[3];
				}
			}
			$l=str_ireplace('else;','else ',$l);
			$lines[$k]=$l;
		}
		$ret=implode('',$lines);
		$f=-1;
		while($f=stripos($ret,'do{',$f+1)) {
			$b=self::find_delimiter($ret,$f+2);
			if($b!==false) {
				$b=strpos($ret,'while(',$b);
				if($b!==false) {
					$b+=5;
					$b=self::find_delimiter($ret,$b);
					$ret=substr($ret,0,$b+1).';'.substr($ret,$b+1);
				}
			}
		}
		for($i=0;$i<strlen($ret);$i++) if($ret{$i}==':'||$ret{$i}=='=') {
			$o=$i+1;
			if(strtolower(substr($ret, $o, 9))=='function(') $o=self::find_delimiter($ret,$o+8)+1;
			if($ret{$o}=='{') {
				$cl=self::find_delimiter($ret,$o);
				if($cl<strlen($ret)) if(preg_match('`[-\w(${["\']`',$ret{$cl+1})) $ret=substr($ret,0,$cl+1).';'.substr($ret,$cl+1);
			}
		}
		$ret=str_replace(';}','}',$ret);
		$ret=preg_replace("`$ph\.(\d+)`e",'$s[\1]',$ret);
		if($cd) $ret="/*<![CDATA[*/$ret//]]>";
		return $ret;
	}

	private static function find_delimiter($str,$pos=0,$dir=0,$esc='\\') {
		if($pos>=strlen($str)) return false;
		static $pairs='\'\'""//<>[]{}()';
		$pp=strpos($pairs,$str{$pos});
		if($pp===false) return false;
		$op=$pp%2?-1:1;
		$o=null;
		if($pairs{$pp}!=$pairs{$pp+$op}) {
			$dir=$op;
			$o=$pairs{$pp};
		}
		if(!$dir) $dir=1;
		$c=$pairs{$pp+$op};
		$nest=1;
		for($pos+=$dir;$pos>=0&&$pos<strlen($str);$pos+=$dir) {
			if($esc) if($pos<(strlen($str)-1)) if($str{$pos+$dir-1}==$esc) $pos+=($dir*2);
			if($str{$pos}==$o) $nest++;
			if($str{$pos}==$c) $nest--;
			if(!$nest) return $pos;
		}
		return false;
	}
}