The script in (almost) full

As opposed to wasting space by printing such a long script several times over, I have only been printing parts so far. However, as we are done, here is the complete script, sans the gettoken() function which is unchanged:

<?php
    define("FOO_NUMBER", 0);
    define("FOO_VARIABLE", 1);
    define("FOO_ASSIGNEQUALS", 2);
    define("FOO_PRINT", 3);
    define("FOO_STRING", 4);
    define("FOO_SEMICOLON", 5);
    define("FOO_PLUS", 6);
    define("FOO_MULTIPLY", 7);
    define("IS_OPERATOR", 0);
    define("IS_OPERAND", 1);

    // you can change the values in $precedence, as long you keep the order the same
    $precedence = array (FOO_PRINT => 0, FOO_ASSIGNEQUALS => 1, FOO_PLUS => 2, FOO_MULTIPLY => 3);

    class token {
        public $type;
        public $token;
        public $val;

        public function __construct($type, $token, $val) {
            $this->type = $type;
            $this->token = $token;
            $this->val = $val;
        }
    }

    // this function converts variables to values as appropriate
    function getval($token) {
        GLOBAL $variables;
        if ($token->token == FOO_VARIABLE) {
            return $variables[$token->val];
        } else {
            return $token->val;
        }
    }

    function execute(&$stack) {
        GLOBAL $variables;
        $operator = array_pop($stack);

        if ($stack[count($stack) - 1]->type == IS_OPERATOR) {
            $right = execute($stack);
        } else {
            $right = array_pop($stack);
        }

        if (count($stack)) {
            if ($stack[count($stack) - 1]->type == IS_OPERATOR) {
                $left = execute($stack);
            } else {
                $left = array_pop($stack);
            }
        }

        switch($operator->token) {
            case FOO_ASSIGNEQUALS:
                $variables[$left->val] = getval($right);
                break;
            case FOO_PLUS:
                return new token(IS_OPERAND, FOO_NUMBER, getval($left) + getval($right));
            case FOO_MULTIPLY:
                return new token(IS_OPERAND, FOO_NUMBER, getval($left) * getval($right));
            case FOO_PRINT:
                print getval($right);
                print "\n";
        }
    }

    function main() {
        GLOBAL $lasttoken, $precedence;

        while(1) {
            $stack = array();
            $operators = array();

            do {
                $token = gettoken();

                switch($token) {
                    case FOO_NUMBER:
                        case FOO_VARIABLE:
                        case FOO_STRING:
                            $stack[] = new token(IS_OPERAND, $token, $lasttoken);
                            break;
                        default:
                            if ($token != FOO_SEMICOLON) {
                                // this removes higher-precedence operators in places of a new, lower-precedence one

                                while (count($operators) && $precedence[$operators[count($operators) - 1]->token] > $precedence[$token]) {
                                    $higher_op = array_pop($operators);
                                    array_push($stack, $higher_op);
                                }

                                $operators[] = new token(IS_OPERATOR, $token, NULL);
                            }
                }
            } while ($token != FOO_SEMICOLON);

            while (count($operators)) array_push($stack, array_pop($operators));

            execute($stack);
        }
    }

    function gettoken() {
        // this is unchanged
    }

    $script = fopen("script.foo", "r");
    $characters = array_merge(range('a', 'z'), range('A', 'z'));
    $characters[] = "_";
    $variables = array();

    function cleanup() {
        GLOBAL $script;
        fclose($script);
    }

    register_shutdown_function("cleanup");
    main();
?>

Even with the gettoken() function all but removed, that is still probably the longest script in the entire book. Still, you like typing, right?

 

Next chapter: Mini-language conclusion >>

Previous chapter: Operator precedence

Jump to:

 

Home: Table of Contents

Copyright ©2015 Paul Hudson. Follow me: @twostraws.