plof2js.php

<?PHP
/* Copyright (c) 2006  Gregor Richards
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

if (!isset($_POST['code'])) {
    
?>
    <HTML><head><title>Plof to JS</title></head>
    <body>
    <form action='plof2js.php' method='post'>
    <textarea name='code' style='width: 100%; height: 75%'>
    a = 0;
    b = 1;
    while({a &lt;= 10000}, {
        println(a);
        var c = a;
        a += b;
        b = c;
    });
    </textarea><br>
    <input type='submit' value='Submit'>
    </form>
    <a href="src.php">Source</a>
    </body>
    </HTML>
    <?PHP
} else {
    
?>
    <HTML><head><title>Plof Compiled Code</title></head>
    <body>
    <?PHP
    
// 1) tokenize
    
$symtoks = array( // an array of multi-symbol tokens
                      
"++""--",
                      
"==""!=",
                      
"<="">=",
                      
"&&""||",
                      
"+=""-=""*=""/=""%=",
                      
"..""...",
                      
":[");
    
    
// load in std.plof
    
$code file_get_contents("std.plof");
    
// and the provided code
    
$code .= stripslashes($_POST['code']);
    
    
$tokens = array();
    
$tok "";
    
$tokan true;
    
$inq false;
    
    for (
$i 0$i strlen($code); $i++) {
        
$c $code[$i];
        if (
$inq) {
            if (
$c == "\"") {
                if (
substr($tokstrlen($tok) - 11) != "\\") {
                    
$tok .= $c;
                    
$tokens[] = $tok;
                    
$tok "";
                    
$tokan true;
                    
$inq false;
                } else {
                    
$tok .= $c;
                }
            } else {
                
$tok .= $c;
            }
            
        } else if (
$c == "\"") {
            if (
$tok != ""$tokens[] = $tok;
            
$tok $c;
            
$tokan false;
            
$inq true;
            
        } else if ((
$c >= 'A' && $c <= 'Z') ||
            (
$c >= 'a' && $c <= 'z') ||
            (
$c >= '0' && $c <= '9') ||
            
$c == '_') {
            if (
$tokan) {
                
$tok .= $c;
            } else {
                if (
$tok != ""$tokens[] = $tok;
                
$tok $c;
                
$tokan true;
            }
            
        } else if (
$c == " " ||
                   
$c == "\n" ||
                   
$c == "\r" ||
                   
$c == "\t") {
            if (
$tok != "") {
                
$tokens[] = $tok;
                
$tok "";
                
$tokan true;
            }
            
        } else {
            if (
$tokan) {
                if (
$tok != ""$tokens[] = $tok;
                
$tok $c;
                
$tokan false;
            } else {
                
$ftok $tok $c;
                if (
strlen($ftok) == ||
                    
in_array($ftok$symtoks)) {
                    
$tok $ftok;
                } else {
                    if (
$tok != ""$tokens[] = $tok;
                    
$tok $c;
                }
            }
        }
    }
    
    class 
Tree {
        function 
Tree($stok ""$sl false$sc false$sr false) {
            
$this->tok $stok;
            
$this->$sl;
            
$this->$sc;
            
$this->$sr;
        }
        var 
$tok "";
        var 
$l false;
        var 
$c false;
        var 
$r false;
    }
    
    
/*print "TOKENS:<br><pre>";
    print_r($tokens);
    print "</pre>";*/
    
    // 2) Parse it
    
$prec = array(
        array(
";"),
        array(
",""..."),
        array(
"=""+=""-=""*=""/=""%="),
        array(
"&&""||"),
        array(
"==""!="),
        array(
"<""<="">"">="),
        array(
"+""-"),
        array(
"*""/""%"),
        array(
"!""++""--"),
        array(
"as""is"),
        array(
"var""global""next"),
        array(
"int""float""str""obj""func""array""ref"),
        array(
"{"),
        array(
"("),
        array(
"["":["),
        array(
".")
        );
    
$ras = array(".");
    
    function 
getclose(&$tokar$lb) {
        
// get the closing location of a tag
        
$depth 0;
        
$inq false;
        for (
$i $lb 1$i count($tokar); $i++) {
            
$tok $tokar[$i];
            if (
$depth == 0) {
                if (
$tok == "}" ||
                    
$tok == "]" ||
                    
$tok == ")") {
                    
                    return 
$i// done!
                
} else if ($tok == "{" ||
                           
$tok == "[" ||
                           
$tok == ":[" ||
                           
$tok == "(") {
                    
$depth++;
                }
            } else {
                if (
$tok == "}" ||
                    
$tok == "]" ||
                    
$tok == ")") {
                    
$depth--;
                } else if (
$tok == "{" ||
                           
$tok == "[" ||
                           
$tok == ":[" ||
                           
$tok == "(") {
                    
$depth++;
                }
            }
        }
        return 
false;
    }
    
    function 
parse(&$tokar, &$for$lb$ub) {
        global 
$ras;
        
$depth 0;
        
        
$step 1;
        
$begin $lb;
        
$end $ub;
        if (
in_array($for[0], $ras)) {
            
// this is right-associative
            
$step = -1;
            
$begin $ub 1;
            
$end $lb 1;
        }
        
        for (
$i $begin$i != $end$i += $step) {
            
$tok $tokar[$i];
            if (
$depth == 0) {
                
// first look for depth modifiers
                
if ($tok == "{" ||
                    
$tok == "[" ||
                    
$tok == ":[" ||
                    
$tok == "(") {
                    if (
in_array($tok$for)) {
                        
// actually, we want this
                        
$ret = new Tree();
                        
$ret->tok $tok;
                        
                        
// get the close of it
                        
$cl getclose($tokar$i);
                        
                        
// then parse the other bits
                        
$ret->parseall($tokar$lb$i);
                        
$ret->parseall($tokar$i 1$cl);
                        
$ret->parseall($tokar$cl 1$ub);
                        return 
$ret;
                    } else {
                        
$depth++;
                    }
                } else {
                    
// then look for our token
                    
if (in_array($tok$for)) {
                        
$ret = new Tree();
                        
$ret->tok $tok;
                        
                        
$ret->parseall($tokar$lb$i);
                        
$ret->parseall($tokar$i 1$ub);
                        return 
$ret;
                    }
                }
            } else {
                
// check if we can change depth
                
if ($tok == "}" ||
                    
$tok == "]" ||
                    
$tok == ")") {
                    
$depth--;
                } else if (
$tok == "{" ||
                           
$tok == "[" ||
                           
$tok == ":[" ||
                           
$tok == "(") {
                    
$depth++;
                }
            }
        }
        
        return 
false;
    }
    
    function 
parseall(&$tokar$lb$ub) {
        global 
$prec;
        foreach (
$prec as $for) {
            
$ret parse($tokar$for$lb$ub);
            if (
$ret !== false) return $ret;
        }
        if (
$lb == $ub 1) {
            
$ret = new Tree();
            
$ret->tok $tokar[$lb];
            return 
$ret;
        } else {
            return 
false;
        }
    }
    
    
$tree parseall($tokens0count($tokens));
    
/*print "TREE:<br><pre>";
    print_r($tree);
    print "</pre>";*/
    
    // now output the actual code
    
?>
    <script type='text/javascript'>
    <?PHP readfile("plof.js"); ?>
    
    document.writeln("<pre>");
    try {
    
        <?PHP
        
function codegen(&$tree$lval false) {
            if (
$tree === false) return;
            
$tok $tree->tok;
            switch (
$tok) {
                case 
";":
                    
codegen($tree->l);
                    print 
";\n";
                    
codegen($tree->r);
                    break;
                
                case 
"=":
                    print 
"set(scope,";
                    
codegen($tree->ltrue);
                    print 
",plof_pass(";
                    
codegen($tree->r);
                    print 
")) ";
                    break;
                    
                case 
"-=":
                case 
"*=":
                case 
"/=":
                case 
"%=":
                    print 
"set(scope,";
                    
codegen($tree->ltrue);
                    print 
",";
                    
codegen($tree->l);
                    print 
$tok[0];
                    
codegen($tree->r);
                    print 
") ";
                    break;
                    
                case 
"+=":
                    print 
"set(scope,";
                    
codegen($tree->ltrue);
                    print 
",plof_add(";
                    
codegen($tree->l);
                    print 
",";
                    
codegen($tree->r);
                    print 
")) ";
                    break;
                    
                case 
"var":
                    
// a plof var scoped locally
                    
if (!$lval) {
                        print 
"get(scope,";
                    }
                    print 
"new plof_Var(scope,\"" $tree->r->tok "\") ";
                    if (!
$lval) {
                        print 
") ";
                    }
                    break;
                
                case 
"{":
                    
// a function
                    
print "new plof_Func(scope,function(scope){\n";
                    
/* if there's only an expression, no statement, then it
                     * needs to be translated into a return */
                    
if ($tree->c->tok == ";") {
                        
codegen($tree->c);
                    } else {
                        print 
"callf(scope,get(scope,new plof_Var(null,\"return\")),[";
                        
codegen_call($tree->c);
                        print 
"]) ";
                    }
                    print 
"},";
                    
// arguments
                    
if ($tree->!== false) {
                        
codegen_args($tree->l);
                    } else {
                        print 
"[],[],[]";
                    }
                    print 
") ";
                    break;
                
                case 
"(":
                    
// a function call or just parens
                    
if ($tree->!== false) {
                        
// function call
                        
print "callf(scope,";
                        
codegen($tree->l);
                        print 
",[";
                        
// now the arguments
                        
codegen_call($tree->c);
                        print 
"],";
                        
// now the added scope
                        
if ($tree->!== false) {
                            
codegen($tree->r);
                        } else {
                            print 
"null";
                        }
                        print 
") ";
                    } else {
                        
// just parens
                        
print "(";
                        
codegen($tree->c);
                        print 
") ";
                    }
                    break;
                    
                case 
"[":
                    if (
$tree->!== false) {
                        
// an array access
                        
if (!$lval) {
                            print 
"plof_aaccess(";
                            
codegen($tree->l);
                            print 
",";
                            
codegen($tree->c);
                            print 
") ";
                        } else {
                            
// FIXME: this should do a check
                            
codegen($tree->l);
                            if (
$tree->!= false) {
                                print 
"[";
                                
codegen($tree->c);
                                print 
".val] ";
                            } else {
                                
// append.  FIXME: inefficient
                                
print "[";
                                
codegen($tree->l);
                                print 
".length] ";
                            }
                        }
                    } else {
                        
// an anonymous object, which is just a scope
                        
print "new Scope(scope,";
                    
                        
// generate it like arguments to a function
                        
$newtree = new Tree();
                        
$newtree->tok "(";
                        
$newtree->$tree->c;
                        
codegen_args($newtree);
                    
                        print 
") ";
                    }
                    break;
                    
                case 
".":
                    
// an object accessor, but could be .size for array or string as well
                    
if ($tree->!== false && $tree->r->tok == "size") {
                        
// get the size
                        
if (!$lval) {
                            print 
"plof_getsize(";
                        } else {
                            
// Urk: FIXME
                        
}
                        
codegen($tree->l);
                        if (!
$lval){
                            print 
") ";
                        } else {
                            
// FIXME
                        
}
                    } else {
                        if (!
$lval) {
                            print 
"get(scope,";
                        }
                        print 
"new plof_Var(";
                        
codegen($tree->l);
                        print 
",\"" $tree->r->tok "\") ";
                        if (!
$lval) {
                            print 
") ";
                        }
                    }
                    break;
                    
                case 
":[":
                    
// an object accessor by-string
                    
if (!$lval) {
                        print 
"get(scope,";
                    }
                    print 
"new plof_Var(";
                    
codegen($tree->l);
                    print 
",";
                    
codegen($tree->c);
                    print 
".val) ";
                    if (!
$lval) {
                        print 
") ";
                    }
                    break;
                    
                case 
"++":
                case 
"--":
                    
// increase the value by one
                    
$newtree = new Tree(
                        
"=",
                        ((
$tree->!== false) ? $tree->$tree->r), false,
                        new 
Tree(
                            (
$tok == "++" "+" "-"),
                            ((
$tree->!== false) ? $tree->$tree->r), false,
                            new 
Tree(
                                
"1"
                                
)
                            )
                        );
                    
codegen($newtree);
                    
                    break;
                    
                case 
",":
                    
codegen($tree->l);
                    print 
",";
                    
codegen($tree->r);
                    break;
                    
                case 
"-":
                case 
"*":
                case 
"/":
                case 
"%":
                    
// simple
                    
print "new plof_Num(";
                    
codegen($tree->l);
                    print 
".val $tok ";
                    
codegen($tree->r);
                    print 
".val) ";
                    break;
                    
                case 
"==":
                case 
"!=":
                case 
"<":
                case 
"<=":
                case 
">":
                case 
">=":
                case 
"&&":
                case 
"||":
                    
// needs to become a number (not bool)
                    
print "new plof_Num((";
                    
codegen($tree->l);
                    print 
".val $tok ";
                    
codegen($tree->r);
                    print 
".val) ? 1 : 0) ";
                    break;
                    
                case 
"+":
                    
// this can do more complex things, so use a function
                    
print "plof_add(";
                    
codegen($tree->l);
                    print 
",";
                    
codegen($tree->r);
                    print 
") ";
                    break;
                    
                case 
"is":
                    print 
"new plof_Num(plof_typecheck(";
                    
codegen($tree->l);
                    print 
",\"" $tree->r->tok "\") ? 1 : 0) ";
                    break;
                    
                default:
                    
// variable name, number, or string
                    
if ($tok[0] >= '0' && $tok[0] <= '9') {
                        print 
"new plof_Num(" $tok ") ";
                    } else if (
$tok[0] == '"') {
                        print 
"new plof_Str(" $tok ") ";
                    } else if ((
$tok[0] >= 'A' && $tok[0] <= 'Z') ||
                               (
$tok[0] >= 'a' && $tok[0] <= 'z') ||
                               
$tok[0] == '_') {
                        if (!
$lval) {
                            print 
"get(scope,";
                        }
                        print 
"new plof_Var(null, \"" $tok "\") ";
                        if (!
$lval) {
                            print 
") ";
                        }
                    } else {
                        
// unsupported token!
                        
print "\n\nUnsupported token: $tok\n\n";
                        die();
                    }
            }
        }
        
        function 
codegen_args(&$tree$defs false$as false) {
            
/* we have a list of arguments, which we need to convert into an
             * array of names and an array of defaults */
            
if ($tree === false) return;
            
            if (
$tree->tok == "(") {
                
// we're at the top level
                
print "[";
                
codegen_args($tree->c);
                print 
"],[";
                
codegen_args($tree->ctrue);
                print 
"],[";
                
codegen_args($tree->cfalsetrue);
                print 
"]";
            } else if (
$tree->tok == ",") {
                
// more arguments
                
codegen_args($tree->l$defs$as);
                print 
",";
                
codegen_args($tree->r$defs$as);
            } else if (
$tree->tok == "=") {
                
// we either want the left or the right
                
if ($defs) {
                    
// the right
                    
codegen($tree->r);
                } else {
                    
// the left
                    
codegen_args($tree->l$defs$as);
                }
            } else if (
$tree->tok == "as") {
                if (
$as) {
                    
// the right
                    
print "\"" $tree->r->tok "\" ";
                } else {
                    
// the left
                    
codegen_args($tree->l$defs$as);
                }
            } else if (
$tree->tok == "...") {
                if (