0

Let's say we have a simple grammar:

  1. Program ::= Expression
  2. Expression ::= Number
  3. ::= - ( Expression , Expression )

With this expression: -(-(8,3)4)
Returning 1.

My token stream(I splice parens and commas out) looks like this
(MINUS -)
(MINUS -)
(INTEGER 8)
(INTEGER 3)
(INTEGER 4)

So the AST would look like so
. . -
. - . 4
8..3

My question is, regarding the recursive nature of the grammar. How would a java example work given the difference expression has 2 evaluated expressions.

I've tried passing in expressions to a class constructor like so:

public class DiffExp implements LetLangExp {
  LetLangExp left, right;

  public DiffExp(LetLangExp l, LetLangExp r) {
    left = l;
    right = r;
    eval();
  }
}

This works for just a difference expression of -(number,number) but recursively it doesn't, because I can't seem to wrap head around the recursive nature of parsing it seems. I'm stuck on this example and i've looked online but i can't seem to equivocate this type of grammar to anything i've seen.

Essentially how do I implement a Difference Expression that is handled recursively that can take a difference expression as an operand and calculate that accordingly?

Edit: Per Markspace's request, i'm attempting to build a node structure for the parse tree. Here is the class I have right now.

class ExprNode{
String c;
static String operator;
static ExprNode operand1;
static ExprNode operand2;

public ExprNode(String num){
    c = num;
    operand1 = operand2 = null;
}

public static void Expr(String op, ExprNode e1, ExprNode e2){
    operator = op;
    operand1 = e1;
    operand2 = e2;
}
}
James
  • 15
  • 6
  • I don't see how you can `eval()` an expression until all sub-nodes are parsed, so I'm having trouble seeing how calling `eval()` in the ctor is a good idea. – markspace Oct 23 '17 at 14:31
  • That's the issue i'm struggling with, i am unable to know how to keep track of the tokens as i parse them into recursive representations. I've edited my post with a token stream output if that might help. But as far as i'm aware, i would need to build this as a tree structure first before I evaluate it? – James Oct 23 '17 at 15:07
  • I'm not sure at all what you're "struggling" with. Make a tree and put the nodes/expressions in it. Can you do that much? Show the code that does it please. – markspace Oct 23 '17 at 16:27
  • `-(-(8,3)4)` is not a valid expression per your grammar. Ist there a comma missing? – JimmyB Oct 23 '17 at 16:28
  • There is a comma in rule 3, is that not a valid representation of an expression. – James Oct 23 '17 at 17:27
  • @markspace Sure, i've been trying to build the tree on its own without evaluating yet and i've struggling on that as well. Apologies for the delay. – James Oct 23 '17 at 17:36

2 Answers2

0

You should create methods for each rule from the grammar, like:

parseProgram(String program) {
  return parseExpression(program)
}

parseExpression(String expression) {

  if ( isNumber(expression) ) {
    return parseNumber(expression);
  } else 
  if ( isSignedExpression(expression) ) {
    String left = getLeftExpression(expression);
    String right = getRightExpression(expression);

    return parseExpression(left) - parseExpression(right);
  } 

}

parseNumber(String number) {
  parsedNumber = ...
  return parsedNumber;
}
JimmyB
  • 12,101
  • 2
  • 28
  • 44
  • I edited my post above to include my token stream, so with this example, if i'm parsing tokens which then need to call expressions and evaluate, how would i keep track of the tokens after a recursive call? – James Oct 23 '17 at 15:06
  • You can put your tokens into a queue. You look at the head of the queue and decide which rule applies, remove the head of the queue and pass the rest of the queue to the respective rule's method. You just consume the tokens sequentially, the recursion does the job for you of handling where in the tree you currently are and how to traverse it. – JimmyB Oct 23 '17 at 16:29
0

Looks good but you'll want to separate tree building and evaluation:

public class DiffExp implements LetLangExp {
  LetLangExp left, right;

  public DiffExp(LetLangExp l, LetLangExp r) {
    left = l;
    right = r;
  }

  public double eval() {
    return left.eval() - right.eval();
  }
}

p.s. Parsing should be roughly as follows:

LetLangExpr parseProgram(LinkedList<String> tokens) {
  return parseExpression(tokens);
}

LetLangExpr parseExpression(LinkedList<String> tokens) {
  if ("-".equals(tokenStream.peekFirst())) {
    return parseDiff(tokens);
  } else {
    return parseNumber(tokens);
  }
}

LetLangExpr parseDiff(LinkedList<String> tokens) {
  tokens.pollFirst();  // Consume "-"
  LetLangExpr left = parseExpression(tokens);
  LetLangExpr right = parseExpression(tokens);
  return new DiffExpr(left, right);
}

LetLangExpr parseNumber(LinkedList<String> tokens) {
  String numberStr = tokens.pollFirs();
  double number = Double.parseDouble(numberStr);
  return new NumberExpr(number);
}
Stefan Haustein
  • 18,427
  • 3
  • 36
  • 51
  • I have that class defined pretty much exactly. The problem comes in whenever I need to build the tree from parsing the tokens on getting expressions that are NonTerminds(IE DiffExp) again. So, essentially, when I reach the inner diffexp, how do I make sure i know the tokens i am getting are its inner tokens? – James Oct 23 '17 at 17:53
  • Typically the token stream is consumed as you go and the current position is part of its state. Does this answer the question? I have extended the answer slightly to cover parsing, but without seeing your TokenStream interface it can't be exact. – Stefan Haustein Oct 23 '17 at 18:06
  • I'm using a LinkedList as a token stream right now, for reference. – James Oct 23 '17 at 18:08
  • On the return values, if these rules(?) are of type ExprNode and my DiffExp is of a type of LetLangExp, how is the return of an expression going to validate for a node? – James Oct 23 '17 at 18:17
  • Have removed ExprNode, didn't immediate realize that it was supposed to be for an alternative approach thats unrelated to LetLangExpr / DiffExpr. – Stefan Haustein Oct 23 '17 at 18:32
  • It all clicked for me just now, thanks so much. Appreciate it! – James Oct 23 '17 at 19:13