How do ParseKit Assembler callbacks work? Where should I keep the work that I do in them?

How to use callback functions in parsekit? Suppose I have the following rule:

expr_s = expr_p '+' expr_s | expr_p ; 

Should I put 3 characters from the resulting PKAssembly and add the first and last numbers, and then put the answer back on the stack?
And for the above rule, how should I know that this is the first or second rule that caused the match?
I do not understand the order in which ParseKit calls the callback functions. I really could use some help.

Thanks to Todd for your answer, bearing in mind your instructions, I wrote the following grammar and callback functions for a simple math expression that includes addition and multiplication:

 - (IBAction)press_equals:(id)sender { NSString *g = @"@start = expr_s; expr_s = expr_p ('+'! expr_p)+ ; expr_p = Number ('*'! Number)+ ;"; PKParser *p = [[PKParserFactory factory] parserFromGrammar:g assembler:self]; NSString *s = @"3*4+4*8"; [p parse:s]; PKAssembly *res = [p parse:s]; NSLog(@"res %@", res); } - (void)parser:(PKParser *)p didMatchExpr_s:(PKAssembly *)a { NSLog(@"%s %@", __PRETTY_FUNCTION__, a); NSArray *toks = [a objectsAbove:nil]; double total = 0.0; for (PKToken *tok in toks) { total += tok.floatValue; } a.target = [NSNumber numberWithDouble:total]; } - (void)parser:(PKParser *)p didMatchExpr_p:(PKAssembly *)a { NSLog(@"%s %@", __PRETTY_FUNCTION__, a); NSArray *toks = [a objectsAbove:nil]; double total = 1.0; for (PKToken *tok in toks) { total *= tok.floatValue; } a.target = [NSNumber numberWithDouble:total]; } 

and here is the result that I get:

 2012-04-06 22:54:31.975 Calculator[1070:207] -[CalculatorViewController parser:didMatchExpr_p:] [3, 4]3/*/4^+/4/*/8 2012-04-06 22:54:31.976 Calculator[1070:207] -[CalculatorViewController parser:didMatchExpr_p:] [4, 8]3/*/4/+/4/*/8^ 2012-04-06 22:54:31.977 Calculator[1070:207] -[CalculatorViewController parser:didMatchExpr_s:] []3/*/4/+/4/*/8^ 2012-04-06 22:54:31.977 Calculator[1070:207] -[CalculatorViewController parser:didMatchExpr_p:] [3, 4]3/*/4^+/4/*/8 2012-04-06 22:54:31.978 Calculator[1070:207] -[CalculatorViewController parser:didMatchExpr_p:] [4, 8]3/*/4/+/4/*/8^ 2012-04-06 22:54:31.978 Calculator[1070:207] -[CalculatorViewController parser:didMatchExpr_s:] []3/*/4/+/4/*/8^ 2012-04-06 22:54:31.979 Calculator[1070:207] res 0 

Why is my res 0?

+6
source share
1 answer

ParseKit developer is here.

Firstly, the best way to understand this material is the book of Stephen Metsker , on which ParseKit is based.


Secondly, check out my answer to another question about the PKAssembly stack and target .


Thirdly, here is my answer to another PaseKit question about unexpected callbacks .


Fourth, check the TDArithmeticParser.m file in the ParseKit Tests Target tag (included in ParseKit Xcode This class has callbacks that implement the exact same arithmetic logic that you seem to be looking for.

Also check the arithmetic.grammar file (also in the ParseKit Tests Target tag). This is an example of how to create arithmetic grammar in ParseKit syntax.


Finally, here are some thoughts more specific to your example above.

Explain your grammar a bit, as the question you ask is pretty simple, and I don't think it requires complicated grammar. Here's the basic arithmetic grammar, which gives the advantage of multiplying and dividing operators over additions and subtractions:

 @start = expr; expr = term (plusTerm | minusTerm)*; term = factor (timesFactor | divFactor)*; plusTerm = '+'! term; minusTerm = '-'! term; timesFactor = '*'! factor; divFactor = '/'! factor; factor = Number; 

What ! after '+' tells ParseKit to automatically cancel this token . This makes things a little more convenient for you when writing callbacks.

Note If you want your grammar to have only operator priority from left to right (for example, a calculator), this grammar will not work. If you need this, please ask a separate question with the #ParseKit tag here in StackOverflow, and I will answer it quickly.

I would define these callbacks:

 - (void)parser:(PKParser *)p didMatchExpr:(PKAssembly *)a { NSLog(@"%s %@", __PRETTY_FUNCTION__, a); NSNumber *n = [a pop]; // the expr is complete, and its value is on the stack. // important! wrap things up by // storing your work in `a.target`. not in an ivar. a.target = n; } - (void)parser:(PKParser *)p didMatchFactor:(PKAssembly *)a { NSLog(@"%s %@", __PRETTY_FUNCTION__, a); // a number token was found. store its number value on the stack PKToken *tok = [a pop]; [a push:[NSNumber numberWithDouble:tok.floatValue]]; } - (void)parser:(PKParser *)p didMatchPlusTerm:(PKAssembly *)a { NSLog(@"%s %@", __PRETTY_FUNCTION__, a); // a '+' expr was found. pop off the two operands and add them // store the result on the stack temporarily NSNumber *n2 = [a pop]; NSNumber *n1 = [a pop]; [a push:[NSNumber numberWithDouble:[n1 doubleValue] + [n2 doubleValue]]]; } - (void)parser:(PKParser *)p didMatchMinusTerm:(PKAssembly *)a { NSLog(@"%s %@", __PRETTY_FUNCTION__, a); // a '-' expr was found. pop off the two operands and subtract them // store the result on the stack temporarily NSNumber *n2 = [a pop]; NSNumber *n1 = [a pop]; [a push:[NSNumber numberWithDouble:[n1 doubleValue] - [n2 doubleValue]]]; } - (void)parser:(PKParser *)p didMatchTimesFactor:(PKAssembly *)a { NSLog(@"%s %@", __PRETTY_FUNCTION__, a); // a '*' expr was found. pop off the two operands and multiply them // store the result on the stack temporarily NSNumber *n2 = [a pop]; NSNumber *n1 = [a pop]; [a push:[NSNumber numberWithDouble:[n1 doubleValue] * [n2 doubleValue]]]; } - (void)parser:(PKParser *)p didMatchDivideFactor:(PKAssembly *)a { NSLog(@"%s %@", __PRETTY_FUNCTION__, a); // a '/' expr was found. pop off the two operands and divide them // store the result on the stack temporarily NSNumber *n2 = [a pop]; NSNumber *n1 = [a pop]; [a push:[NSNumber numberWithDouble:[n1 doubleValue] / [n2 doubleValue]]]; } 

Two important points :

  • Don't worry about how many times these callbacks are called . They can be called more than you expect, or in a strange way.
  • Do not save the results of work done on these callbacks in ivar . Always save your work with either a target or stack . I usually save temporary values ​​on stack and the final result on target , as I find this the most convenient. But you have the flexibility.

I would write this driver code:

 NSString *g = .. // fetch grammar above PKParser *p = [[PKParserFactory factory] parserFromGrammar:g assembler:self]; NSString *s = @"3*4+4*8"; PKAssembly *res = [p parse:s]; NSLog(@"res %@", res); 

I see this log output:

 -[DebugAppDelegate parser:didMatchFactor:] [3]3^*/4/+/4/*/8 -[DebugAppDelegate parser:didMatchFactor:] [3, 4]3/*/4^+/4/*/8 -[DebugAppDelegate parser:didMatchTimesFactor:] [3, 4]3/*/4^+/4/*/8 -[DebugAppDelegate parser:didMatchFactor:] [12, 4]3/*/4/+/4^*/8 -[DebugAppDelegate parser:didMatchFactor:] [12, 4, 8]3/*/4/+/4/*/8^ -[DebugAppDelegate parser:didMatchTimesFactor:] [12, 4, 8]3/*/4/+/4/*/8^ -[DebugAppDelegate parser:didMatchPlusTerm:] [12, 4]3/*/4/+/4^*/8 -[DebugAppDelegate parser:didMatchPlusTerm:] [12, 32]3/*/4/+/4/*/8^ -[DebugAppDelegate parser:didMatchExpr:] [3]3^*/4/+/4/*/8 -[DebugAppDelegate parser:didMatchExpr:] [12]3/*/4^+/4/*/8 -[DebugAppDelegate parser:didMatchExpr:] [16]3/*/4/+/4^*/8 -[DebugAppDelegate parser:didMatchExpr:] [44]3/*/4/+/4/*/8^ res 44 

Hope this helps.

+8
source

Source: https://habr.com/ru/post/912612/


All Articles