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 <= 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($tok, strlen($tok) - 1, 1) != "\\") {
$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) == 1 ||
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->l = $sl;
$this->c = $sc;
$this->r = $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->l = parseall($tokar, $lb, $i);
$ret->c = parseall($tokar, $i + 1, $cl);
$ret->r = 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->l = parseall($tokar, $lb, $i);
$ret->r = 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($tokens, 0, count($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->l, true);
print ",plof_pass(";
codegen($tree->r);
print ")) ";
break;
case "-=":
case "*=":
case "/=":
case "%=":
print "set(scope,";
codegen($tree->l, true);
print ",";
codegen($tree->l);
print $tok[0];
codegen($tree->r);
print ") ";
break;
case "+=":
print "set(scope,";
codegen($tree->l, true);
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->l !== false) {
codegen_args($tree->l);
} else {
print "[],[],[]";
}
print ") ";
break;
case "(":
// a function call or just parens
if ($tree->l !== false) {
// function call
print "callf(scope,";
codegen($tree->l);
print ",[";
// now the arguments
codegen_call($tree->c);
print "],";
// now the added scope
if ($tree->r !== false) {
codegen($tree->r);
} else {
print "null";
}
print ") ";
} else {
// just parens
print "(";
codegen($tree->c);
print ") ";
}
break;
case "[":
if ($tree->l !== 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->c != 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->c = $tree->c;
codegen_args($newtree);
print ") ";
}
break;
case ".":
// an object accessor, but could be .size for array or string as well
if ($tree->r !== 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->l !== false) ? $tree->l : $tree->r), false,
new Tree(
($tok == "++" ? "+" : "-"),
(($tree->l !== false) ? $tree->l : $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->c, true);
print "],[";
codegen_args($tree->c, false, true);
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 (