在 Xcode 中将 lex 和 bison 与 swift 一起使用

问题描述 投票:0回答:1

我正在致力于将 Swift 与在 Xcode 16.1 项目中使用 lex 和 bison 生成的简单解析器集成。

下面是我当前的代码:

calc.l

%{
#include "calc.tab.h"
#include <stdlib.h>    
%}


%%
[0-9]+      { yylval.num = atoi(yytext); return NUMBER; }
[ \t\n]+    ;  
"+"         return '+';
"-"         return '-';
"*"         return '*';
"/"         return '/';
"("         return '(';
")"         return ')';
.           return yytext[0]; 
%%

calc.y

%{
#include <stdio.h>
#include <stdlib.h>

#include "calc.h"

void yyerror(const char *s);  
int result;  
%}


%union {
    int num;
}

/* Define tokens with types */
%token <num> NUMBER
%left '+' '-'
%left '*' '/'

/* Use %type to declare the type of non-terminal symbols */
%type <num> expr

%%
expr:
    expr '+' expr { $$ = $1 + $3; }
  | expr '-' expr { $$ = $1 - $3; }
  | expr '*' expr { $$ = $1 * $3; }
  | expr '/' expr { $$ = $1 / $3; }
  | '(' expr ')' { $$ = $2; }
  | NUMBER       { $$ = $1; }
  ;
%%

int main(void) {
    return yyparse();
}

void yyerror(const char *s) {
    fprintf(stderr, "Error: %s\n", s);
}

calc.h

#ifndef CALC_H
#define CALC_H



extern int yylex(void);
extern int yyparse(void);
void yyerror(const char *s);
int parseExpression(void);

#endif

calc.c

#include "calc.h"
#include <stdio.h>

int result;

int parseExpression(void) {
    if (yyparse() == 0) {
        return result;
    } else {
        return -1; // Error
    }
}

void yyerror(const char *s) {
    fprintf(stderr, "Error: %s\n", s);
}

桥接标头.h

#import "calc.h"

这是一个调用解析器的简单 SwiftUI 视图:


struct ContentView: View {
    @State var result: Int32 = 0
    
    var body: some View {
        VStack {
            Text("\(result)")
            
            Button("Calculate") {
                let expression = "3 + 5 * (10 - 4)"
                result = evaluate(expression: expression)
                print("Result: \(result)")
            }
        }
        .padding()
    }
    
    
    func evaluate(expression: String) -> Int32 {
        
        expression.withCString { cString in
            freopen("/dev/stdin", "r", stdin)
            fputs(cString, stdin)
        }
        return parseExpression()
    }

}

我从终端手动运行这些命令:

flex -o lex.yy.c calc.l 
bison -d calc.y -o calc.tab.c

当我在Xcode中编译项目时,遇到以下错误,但我无法解决:


Undefined symbols for architecture arm64:
  "_yywrap", referenced from:
      _yylex in lex.yy.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

有人可以提供一些指导或指出我正确的方向吗?

swift xcode macos bison lex
1个回答
0
投票

我的问题不是重复现有问题,只是添加选项:

%option noyywrap

按照建议并没有解决问题。事实上,由于重复的变量和函数定义,它引入了多个错误。

这是因为在 Xcode 项目中同时包含 .l (Flex) 和 .y (Bison) 文件会在幕后自动触发 Flex 和 Bison。

解决方案是更新分词器和解析器定义以避免引用不可用的 calc.tab.h。

以下是最终的工作文件:

calc.l

%option noyywrap

%{
#include <stdio.h>
#include <stdlib.h>

enum {
    NUMBER = 258,
    PLUS,
    MINUS,
    MULTIPLY,
    DIVIDE,
    LEFTPAR,
    RIGHTPAR
};
    
union YYSTYPE {
    int num;
};
    
extern union YYSTYPE yylval;
extern int expression_value;
extern void yyerror(const char *s);

%}


%%
[0-9]+      { yylval.num = atoi(yytext); return NUMBER; }
[ \t\n]+    ;
"+"         return PLUS;
"-"         return MINUS;
"*"         return MULTIPLY;
"/"         return DIVIDE;
"("         return LEFTPAR;
")"         return RIGHTPAR;
.           return yytext[0];
%%

calc.y

%{
#include <stdio.h>
#include <stdlib.h>

void yyerror(const char *s);
int yylex(void);

int expression_value;

%}


%union {
    int num;
}

%token <num> NUMBER
%token PLUS
%token MINUS
%token MULTIPLY
%token DIVIDE
%token LEFTPAR
%token RIGHTPAR
%left PLUS MINUS
%left MULTIPLY DIVIDE

%type <num> expr

%%
    
input:
  |  expr { expression_value = $1; }
  ;
    
expr:
    expr PLUS expr  { $$ = $1 + $3; }
  | expr MINUS expr { $$ = $1 - $3; }
  | expr MULTIPLY expr { $$ = $1 * $3; }
  | expr DIVIDE expr { $$ = $1 / $3; }
  | LEFTPAR expr RIGHTPAR { $$ = $2; }
  | NUMBER       { $$ = $1; }
  ;
%%

void yyerror(const char *s) {
    fprintf(stderr, "Error: %s\n", s);
}

calc.h

#ifndef CALC_H
#define CALC_H

int evaluate_expression(const char *expression, int *result);

#endif

calc.c

#include "calc.h"
#include <stdio.h>

extern int yyparse(void);
extern void yy_scan_string(const char *str);
extern void yylex_destroy(void);
extern int expression_value;

int evaluate_expression(const char *expression, int *result) {
    
    yy_scan_string(expression);

    // Parse and evaluate the expression
    int status = yyparse();

    yylex_destroy();
    
    if (status == 0) {
        *result = expression_value;
    }
    
    return  status;
}

这是调用解析器的 Swift(UI) 代码:

struct ContentView: View {
    @State var expression: String = ""
    @State var result: Int32?

    var body: some View {
        VStack {
            TextEditor(text: $expression)

            if let result {
                Text("\(result)")
            }

            Button("Calculate") {
                result = evaluate(expression: expression)
            }
        }
        .padding()
    }

    func evaluate(expression: String) -> Int32? {
        var result: Int32 = 0

        let status = expression.withCString { cString in
            evaluate_expression(UnsafeMutablePointer(mutating: cString), &result)
        }

        if status != 0 {
            // The parse returned an error
            return nil
        }

        return result
    }
}

请注意,调用evaluate_expression函数需要Swift-Bridging.h文件。

© www.soinside.com 2019 - 2024. All rights reserved.