Please see a simplified version written in dart, it produces both RPN and AST.
Used pseudocode from wikipedia for the implementation.
Here is a nice visual explanation of the algorithm.
Try it out
void main() {
final ast = shunting('(2*3*x+5*y-3*z)/(1+3+2*2)'.split(''), 'xyz'.split(''));
print(ast);
}
/// imm - immediate value
Ast shunting(
List<String> body,
List<String> arguments,
) {
final tree = <Ast>[];
final outputQueue = <String>[];
final operatorStack = <String>[];
for (final token in body) {
if (int.tryParse(token) is int) {
final operand = UnOp('imm', int.parse(token));
outputQueue.add(token);
tree.add(operand);
} else if (arguments.contains(token)) {
final operand = UnOp('arg', arguments.indexOf(token));
outputQueue.add(token);
tree.add(operand);
} else if (token.isOperator) {
while (operatorStack.isNotEmpty && (operatorStack.last > token || operatorStack.last.isSamePrecedence(token))) {
final lastOp = operatorStack.removeLast();
outputQueue.add(lastOp);
final second = tree.removeLast();
final first = tree.removeLast();
tree.add(BinOp(lastOp, first, second));
}
operatorStack.add(token);
} else if (token == '(') {
operatorStack.add(token);
} else if (token == ')') {
assert(operatorStack.isNotEmpty, 'mismatched parenthesis');
while (operatorStack.last != '(') {
final lastOp = operatorStack.removeLast();
outputQueue.add(lastOp);
final second = tree.removeLast();
final first = tree.removeLast();
tree.add(BinOp(lastOp, first, second));
}
operatorStack.removeLast();
}
}
while (operatorStack.isNotEmpty) {
final lastOp = operatorStack.removeLast();
outputQueue.add(lastOp);
final second = tree.removeLast();
final first = tree.removeLast();
tree.add(BinOp(lastOp, first, second));
}
print('RPN: ${outputQueue.join()}');
return tree.first;
}
abstract class Ast {
abstract final String op;
}
class BinOp implements Ast {
BinOp(
this.op,
this._a,
this._b,
);
final Ast _a;
final Ast _b;
@override
final String op;
Ast a(Ast a) => _a;
Ast b(Ast b) => _b;
@override
String toString() => '{op: $op, a: $_a, b: $_b}';
}
class UnOp implements Ast {
UnOp(this.op, this.n);
final int n;
@override
final String op;
@override
String toString() => '{op: $op, n: $n}';
}
extension Operators on String {
bool operator >(Object other) {
assert(other is String, 'Incompatible type comparison');
return '*/'.split('').contains(this) && '+-'.split('').contains(other);
}
bool operator <(Object other) {
assert(other is String, 'Incompatible type comparison');
return '-+'.split('').contains(this) && '*/'.split('').contains(other);
}
bool get isOperator => '*/+-'.split('').contains(this);
bool isSamePrecedence(Object other) {
assert(other is String, 'Incompatible type comparison');
if ('+-'.split('').contains(this) && '+-'.split('').contains(other)) return true;
if ('/*'.split('').contains(this) && '/*'.split('').contains(other)) return true;
return false;
}
}
dict.lookup
calls, it makes an AST. This does not use the shunting yard algorithm, but a recursive descent parser. – Deberadeberry