summaryrefslogtreecommitdiff
path: root/dice-lang/src/bjc/dicelang/v1/ast/DiceASTInliner.java
blob: 38e13617d8f5c0331bad4ee89c3c98ba04130c0f (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
package bjc.dicelang.v1.ast;

import bjc.dicelang.v1.ast.nodes.DiceASTType;
import bjc.dicelang.v1.ast.nodes.IDiceASTNode;
import bjc.dicelang.v1.ast.nodes.VariableDiceNode;
import bjc.utils.data.ITree;
import bjc.utils.funcdata.FunctionalList;
import bjc.utils.funcdata.IList;
import bjc.utils.funcdata.IMap;
import bjc.utils.data.Tree;

/**
 * Inline variables in a dice AST
 * 
 * @author ben
 *
 */
public class DiceASTInliner {
	/**
	 * Inline all the variables in the AST
	 * 
	 * @param ast
	 *                The AST to inline variables into
	 * @param enviroment
	 *                The enviroment to inline from
	 * @return The inlined AST
	 */
	public static ITree<IDiceASTNode> inlineAll(ITree<IDiceASTNode> ast,
			IMap<String, ITree<IDiceASTNode>> enviroment) {
		// Tell the compiler that the null is for the entire varargs
		// parameter, not a single one with a null value
		return selectiveInline(ast, enviroment, (String[]) null);
	}

	private static ITree<IDiceASTNode> inlineNode(IDiceASTNode node, IMap<String, ITree<IDiceASTNode>> enviroment,
			boolean specificInline, IList<String> variableNames) {
		// Only variables get inlined
		if (node.getType() != DiceASTType.VARIABLE) {
			return new Tree<>(node);
		}

		// Get the name of what we're inlining
		String variableName = ((VariableDiceNode) node).getVariable();

		// If we're inlining only certain variables, do so
		if (specificInline) {
			// Only inline the variable if we're supposed to
			if (variableNames.contains(variableName)) {
				// You can't inline non-existent variables
				if (!enviroment.containsKey(variableName)) {
					throw new UnsupportedOperationException(
							"Attempted to inline non-existant variable " + variableName);
				}

				// Return the tree for the variable
				return enviroment.get(variableName);
			}

			// We're not inlining this particular variable
			return new Tree<>(node);
		}

		// You can't inline non-existent variables
		if (!enviroment.containsKey(variableName)) {
			throw new UnsupportedOperationException(
					"Attempted to inline non-existant variable " + variableName);
		}

		// Return the tree for the variable
		return enviroment.get(variableName);
	}

	/**
	 * Inline the specified variables in the AST
	 * 
	 * @param ast
	 *                The AST to inline variables into
	 * @param enviroment
	 *                The enviroment to inline from
	 * @param variables
	 *                The variables to inline
	 * @return The inlined AST
	 */
	public static ITree<IDiceASTNode> selectiveInline(ITree<IDiceASTNode> ast,
			IMap<String, ITree<IDiceASTNode>> enviroment, IList<String> variables) {
		// Inline the specified variables
		return selectiveInline(ast, enviroment, variables.toArray(new String[0]));
	}

	/**
	 * Inline the specified variables in the AST
	 * 
	 * @param ast
	 *                The AST to inline variables into
	 * @param enviroment
	 *                The enviroment to inline from
	 * @param variables
	 *                The variables to inline
	 * @return The inlined AST
	 */
	public static ITree<IDiceASTNode> selectiveInline(ITree<IDiceASTNode> ast,
			IMap<String, ITree<IDiceASTNode>> enviroment, String... variables) {
		// If we're selectively inlining, do so
		if (variables != null && variables.length > 0) {
			IList<String> variableNames = new FunctionalList<>(variables);

			// Selectively inline each tree node
			return ast.flatMapTree((node) -> {
				return inlineNode(node, enviroment, true, variableNames);
			});
		}

		// Inline everything in each node
		return ast.flatMapTree((node) -> {
			return inlineNode(node, enviroment, false, null);
		});
	}
}