summaryrefslogtreecommitdiff
path: root/src/www/syntax-editor/js/ast.js
blob: de9e9d3ebb088f36a06e5ed6aa52f8527f4b71ec (plain)
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
/* --- Tree representation -------------------------------------------------- */
function Tree(value) {

    // Create node as JS object
    var createNode = function(value, children) {
        var node = {
            value: value,
            children: []
        };
        if (children != undefined)
            for (c in children)
                node.children.push( createNode(children[c],[]) );
        return node;
    }

    this.root = createNode(value, []);

    // add value as child of id
    this.add = function(id, value, children) {
        var x = this.find(id);
        x.children.push( createNode(value, children) );
    }

    // id should be a list of child indices [0,1,0]
    // or a string separated by commas "0,1,0"
    this.find = function(_id) {
        var id = undefined
        switch (typeof _id) {
        case "number": id = [_id]; break;
        case "string": id = _id.split(","); break;
        case "object": id = _id.get().slice(); break; // clone NodeID array
        }
        var node = this.root;
        if (id[0] == 0) id.shift();
        while (id.length>0 && node.children.length>0) {
            node = node.children[id.shift()];
        }
        if (id.length>0)
            return undefined;
        return node;
    }
}

/* --- ID for a node in a tree ---------------------------------------------- */
function NodeID(x) {
    this.id = new Array();
    this.id.push(0);

    // Initialize from input
    if (x) {
        switch (typeof x) {
        case "number": this.id = [x]; break;
        case "string": this.id = map(function(s){return parseInt(s)}, x.split(",")); break;
        case "object": this.id = x.get().slice(); break; // another NodeID
        }
    }

    // get id
    this.get = function() {
        return this.id;
    }
    
    // Add child node to id
    this.add = function(x) {
        this.id.push(parseInt(x));
        return this.id;
    }

    // compare with other id
    this.equals = function(other) {
        return JSON.stringify(this.id)==JSON.stringify(other.id);
    }

}

/* --- Abstract Syntax Tree (with state)------------------------------------- */
function AST(fun, cat) {

    function Node(fun, cat) {
        this.fun = fun;
        this.cat = cat;
    }

    this.tree = new Tree(new Node(fun, cat));
    this.current = new NodeID(); // current id in tree

    this.getFun = function() {
        return this.tree.find(this.current).value.fun;
    }
    this.setFun = function(f) {
        this.tree.find(this.current).value.fun = f;
    }
    this.getCat = function() {
        return this.tree.find(this.current).value.cat;
    }
    this.setCat = function(c) {
        this.tree.find(this.current).value.cat = c;
    }

    this.add = function(fun, cat) {
        this.tree.add(this.current, new Node(fun,cat));
    }
    
    // Clear children of current node
    this.removeChildren = function() {
        this.tree.find(this.current).children = [];
    }

    // Move current id to child number i
    this.toChild = function(i) {
        this.current.add(i);
    }

    // Return tree as string
    this.toString = function() {
        var s = "";
        function visit(node) {
            s += node.value.fun ? node.value.fun : "?" ;
            if (node.children.length == 0)
                return;
            for (i in node.children) {
            s += "(";
                visit(node.children[i]);
            s += ")";
            }
        }
        visit(this.tree.root);
        return s;
    }
}