reka.js icon indicating copy to clipboard operation
reka.js copied to clipboard

Resolving variable ambiguity

Open prevwong opened this issue 3 months ago • 0 comments

Overview

Currently, Reka resolve variables by their names and scope:


{
  type: "Program",
  globals: [ { type: "Val", name: "counter", ... }
  components: [
    {
      type: "RekaComponent", 
      name: "App", 
      state: [ { type: "Val", name: "counter", ... }
      template: {
	type: "TagTemplate",
        tag: "text",
        props: { 
	  "value": { 
	      type: "Identifier",
	      name: "counter" // refers to the state variable
	   } 
        },
      }
    }
  ]
}

In the above example, the counter variable used as prop refers the counter state variable in the App component, rather than the global variable. This design is simple and straightforward and it's based on what programmers typically expect when writing code.

However, this does make referencing the global counter variable impossible. More importantly, when building a page editor — the names of these variables are going to be defined by users and chances are we’re going to need to build some user friendly UI (ie: a dropdown to list variables). In which case, having variables to be resolved by names and scopes is going to cause a lot of confusion.


Removing ambiguity in the State

To solve this, we need a more explicit way to resolve variables. We can achieve this by requiring identifiers to specify the id of the variable explicitly:

{
  type: "Program",
  globals: [ { id: "hhghak1", type: "Val", name: "counter", ... }
  components: [
    {
      type: "RekaComponent", 
      name: "App", 
      state: [ { id: "kbamk10", type: "Val", name: "counter", ... }
      template: {
	type: "TagTemplate",
        tag: "text",
        props: { 
	   "value": { 
		type: "Identifier",
		ref: "hhghak1" // refers to the global variable
	    } 
        },
      }
    }
  ]
}

Parser

To support this change, we will need to make some changes to the Parser as well. An easy way to achieve a non-ambiguous to require identifiers to specify the “path” of the variable they are referencing:

val counter = 0;

component App() {
  val counter = 1;
} => (
  <text value={$state.counter} />
)

Here, $state.counter explicitly specifies that we’re referencing the state variable. Similarly we can allow the following:

  • $counter to reference a global counter variable
  • $prop.counter to reference a prop counter variable

One other place where we need to take care of this is in templates with the @each directive. Currently consumers can create a variable to reference each item in the iterator:

<div @each={item in [1,2,3]}>
   <div @each={item in [1,2,3]}>
	<text value={item} /> // item refers to the nested item 
   </div>
</div>

In this case, it is also impossible to reference the outer iterators item using our existing model. Using the same solution with the counter example, we can do the following:

<div @each={item in [1,2,3]}>
   <div @each={item in [1,2,3]}>
       <text value={$each.0.item} /> // item refers to the outer item 
   </div>
</div>

prevwong avatar Mar 08 '24 14:03 prevwong