ParseKit的汇编程序回调如何工作?我应该在哪里存储我在其中的工作?

时间:2012-04-06 15:24:37

标签: objective-c parsekit

我应该如何在parsekit中使用回调函数?假设我有以下规则:

expr_s = expr_p '+' expr_s | expr_p ; 

我应该从生成的PKAssembly中弹出3个符号并添加第一个和最后一个数字,然后将答案推回堆栈?
对于上述规则,我应该如何知道它是导致匹配的第一个或第二个规则? 我不明白ParseKit调用回调函数的顺序。我真的可以使用一些帮助。

感谢Todd的回复,请记住你的指示我为包含加法和乘法的简单数学表达式编写了以下语法和回调函数:

- (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];
}

这是我得到的输出:

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

为什么我的res 0?

1 个答案:

答案 0 :(得分:8)

ParseKit的开发人员。

首先,理解这些东西的最好方法是Steven Metsker's book,ParseKit就是基于此。


其次,结帐my answer to another question about PKAssembly's stack and target.


第三,这是my answer to another PaseKit question about unexpected callbacks


第四,检查ParseKit中的TDArithmeticParser.m file 测试目标(包含在ParseKit Xcode项目中。这个类有回调,它实现了你看起来相同的算术逻辑对

同时结帐arithmetic.grammar file(也在ParseKit测试目标中)。这是如何在ParseKit语法中设计算术语法的一个示例。


最后,这里有一些对上面例子更具体的想法。

让我们稍微澄清一下你的语法,因为你提出的问题非常基本,而且我认为它不需要非常复杂的语法来解决。这是一个基本的算术语法,它给乘法和除法运算符优先于加法和减法:

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

!'+'告诉ParseKit 自动丢弃此令牌。这使你在编写回调时更加方便。

注意如果您希望语法只有从左到右的运算符优先级(如计算器),则此语法将不起作用。如果您需要,请在StackOverflow上单独提出一个标记为#ParseKit的问题,我会及时回答。

我会定义这些回调:

- (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]]];
}

两个要点

  1. 不要担心这些回调被调用的次数。他们可能被召唤的次数超出您的预期,或者看起来很奇怪。
  2. 请勿将这些回调中完成的工作结果存储在ivar 中。始终将您的工作存储在a参数的targetstack上。我通常会在stack上存储临时值,并在target上存储最终结果,因为我觉得最方便。但你在那里有灵活性。
  3. 我会写这个驱动程序代码:

    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);
    

    我看到了这个日志输出:

    -[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
    

    希望这有帮助。