# Example of Scope Analysis in Egg

This code was developed by David Afonso Dorta.

See https://github.com/ULL-ESIT-PL-1920/TFA-davafons (opens new window) and https://github.com/ULL-ESIT-PL-1920/TFA-davafons (opens new window)

const estraverse = require("estraverse");
1
const { TopEnv, SpecialForms } = require("../interp/environment.js");
const { Apply, Word } = require("../interp/ast.js");

const SCOPE_OPERATORS = ["do", "if", "while", "for", "foreach", "fun", "->", "object"];
const DEFINE_OPERATORS = [":=", "define", "def"];
const SET_OPERATORS = ["set", "<-"];
const FUNCTION_OPERATORS = ["fun", "->"];
const OBJECT_OPERATORS = ["object", "obj"];
1
2
3
4
5
6
7
8
class Semantic {
  constructor(symboltable) {
    if (symboltable === undefined) {
      this.symboltable = Object.create(null);
    } else {
      this.symboltable = symboltable;
    }
  }

  check(tree) {
    const methods = this;

    tree = estraverse.replace(tree, {
      enter: function(node, parent) {
        if (node.type === "apply") {
          node = methods.checkApplyEnter(node);
        } else if (node.type === "word") {
          node = methods.checkWordEnter(node, parent);
        }

        return node;
      },
      leave: function(node) {
        if (node.type === "apply") {
          node = methods.checkApplyLeave(node);
        }

        return node;
      },
      keys: {
        apply: ["args"],
        word: [],
        value: [],
        regex: []
      },
      fallback: "iteration"
    });

    return tree;
  }

  static check(tree) {
    return new Semantic().check(tree);
  }

  checkApplyEnter(node) {
    if (node.operator.type == "word") {
      const operator_name = node.operator.name;

      // Assert that the arguments passed to the apply are correct
      // this.assertApplyArgs(node);

      // If entered on a new scope, create a child symboltable
      if (SCOPE_OPERATORS.includes(operator_name)) {
        this.symboltable = Object.create(this.symboltable);
      }

      // If the operator is a "define" operator, add the new symbol to the symboltable
      if (DEFINE_OPERATORS.includes(operator_name)) {
        node.args[0] = this.addToSymboltable(node.args[0]);
      }

      if (SET_OPERATORS.includes(operator_name)) {
        const symbol = this.findInSymboltable(node.args[0].name);

        if (symbol && symbol.const) {
          throw new TypeError(`${node.args[0].name} is const and can't be reassigned!`);
        }
      }

      // Add function arguments to function symboltable
      if (FUNCTION_OPERATORS.includes(operator_name)) {
        for (let i = 0; i < node.args.length - 1; ++i) {
          this.addToSymboltable(node.args[i]);
        }
      }

      // Add "this" symbol, and object properties, to object symboltable
      if (OBJECT_OPERATORS.includes(operator_name)) {
        const apply = new Apply(new Word({ value: "const" }));
        apply.args = [new Word({ value: "this" })];

        this.addToSymboltable(apply);

        for (let i = 0; i < node.args.length; i += 2) {
          this.addToSymboltable(new Word({ value: node.args[i].value }));
        }
      }

      // Add the "iterator" symbol to foreach symboltable
      if (operator_name === "foreach") {
        this.addToSymboltable(node.args[0]);
      }
    }

    return node;
  }

  assertApplyArgs(node) {
    /* ... */
  }

  addToSymboltable(node) {
    /* ... */
  }

  findInSymboltable(name) {
    // Find an element on the symboltable or the parent ones
    for (let table = this.symboltable; table; table = Object.getPrototypeOf(table)) {
      if (Object.prototype.hasOwnProperty.call(table, name)) {
        return table[name];
      }
    }

    return undefined;
  }

  checkWordEnter(node, parent) {
      /* ... */
  }

  checkApplyLeave(node) {
    if (node.operator.type == "word") {
      const operator_name = node.operator.name;

      // If left the scope, remove the last symboltable
      if (SCOPE_OPERATORS.includes(operator_name)) {
        this.symboltable = Object.getPrototypeOf(this.symboltable);
      }
    }

    return node;
  }
}

module.exports = {
  Semantic
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138

See also davafons TFA

Last Updated: 2 months ago