summaryrefslogtreecommitdiff
path: root/src/ste/Scope.php
diff options
context:
space:
mode:
Diffstat (limited to 'src/ste/Scope.php')
-rw-r--r--src/ste/Scope.php146
1 files changed, 146 insertions, 0 deletions
diff --git a/src/ste/Scope.php b/src/ste/Scope.php
new file mode 100644
index 0000000..0da2724
--- /dev/null
+++ b/src/ste/Scope.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace kch42\ste;
+
+class Scope implements \ArrayAccess {
+ private $parent = NULL;
+ private $vars = array();
+
+ private static function parse_name($name) {
+ $remain = $name;
+ $fields = array();
+
+ while($remain !== "") {
+ $br_open = strpos($remain, '[');
+ if($br_open === false) {
+ $fields[] = $remain;
+ break;
+ }
+
+ $br_close = strpos($remain, ']', $br_open);
+ if($br_close === false) {
+ throw new RuntimeError("Invalid varname \"$name\". Missing closing \"]\".");
+ }
+
+ $fields[] = substr($remain, 0, $br_open);
+
+ $field = substr($remain, $br_open+1, $br_close-$br_open-1);
+ $more = substr($remain, $br_close+1);
+
+ if(strpos($field, '[') !== false) {
+ throw new RuntimeError("A variable field must not contain a '[' character.");
+ }
+
+ if((strlen($more) > 0) && ($more[0] !== '[')) {
+ // TODO: better error message, not very non-programmer friendly...
+ throw new RuntimeError("A variable name must be of format name('[' name ']')*.");
+ }
+
+ $remain = $field . $more;
+ }
+
+ return $fields;
+ }
+
+ private function &get_topvar_reference($name, $localonly) {
+ if(array_key_exists($name, $this->vars)) {
+ $ref = &$this->vars[$name];
+ return $ref;
+ }
+
+ if((!$localonly) && ($this->parent !== NULL)) {
+ $ref = &$this->parent->get_topvar_reference($name, $localonly);
+ return $ref;
+ }
+
+ throw new VarNotInScope();
+ }
+
+ public function &get_var_reference($name, $create_if_not_exist, $localonly=false) {
+ $nullref = NULL;
+
+ $fields = self::parse_name($name);
+ if(count($fields) == 0) {
+ return $nullref; // TODO: or should we throw an exception here?
+ }
+
+ $first = $fields[0];
+
+ $ref = NULL;
+ try {
+ $ref = &$this->get_topvar_reference($first, $localonly);
+ } catch(VarNotInScope $e) {
+ if($create_if_not_exist) {
+ $this->vars[$first] = (count($fields) > 0) ? array() : "";
+ $ref = &$this->vars[$first];
+ } else {
+ return $nullref;
+ }
+ }
+
+ for($i = 1; $i < count($fields); $i++) {
+ $field = $fields[$i];
+
+ if(!is_array($ref)) {
+ return $nullref;
+ }
+
+ if(!array_key_exists($field, $ref)) {
+ if(!$create_if_not_exist) {
+ return $nullref;
+ }
+
+ if($i < count($fields) - 1) {
+ $ref[$field] = array();
+ } else {
+ $ref[$field] = "";
+ }
+ }
+
+ $ref = &$ref[$field];
+ }
+
+ return $ref;
+ }
+
+ public function set_var_by_name($name, $val) {
+ $ref = &$this->get_var_reference($name, true);
+ $ref = $val;
+ }
+
+ public function set_local_var($name, $val) {
+ $ref = &$this->get_var_reference($name, true, true);
+ $ref = $val;
+ }
+
+ public function get_var_by_name($name) {
+ $ref = $this->get_var_reference($name, false);
+ return $ref === NULL ? "" : $ref;
+ }
+
+ public function new_subscope() {
+ $o = new self();
+ $o->parent = $this;
+ return $o;
+ }
+
+ /* implementing ArrayAccess */
+
+ public function offsetSet($offset, $value) {
+ $this->set_var_by_name($offset, $value);
+ }
+ public function offsetGet($offset) {
+ return $this->get_var_by_name($offset);
+ }
+ public function offsetExists($offset) {
+ try {
+ $this->get_topvar_reference($offset);
+ return true;
+ } catch(VarNotInScope $e) {
+ return false;
+ }
+ }
+ public function offsetUnset($offset) {
+ unset($this->vars[$offset]);
+ }
+}