LINUX.ORG.RU

Взятие выражения в скобках

 , ,


0

1

Есть такая, например, строка:

ABC X=(...) Y=a+b*(...) Z=123

X, Y, Z можно назвать параметрами.

Как бы так изловчиться, чтобы вылавливать эти параметры и всё содержимое каждого вместе со скобками (т.е от «=» до следующего параметра)? Внутри скобок может быть что угодно, в том числе другие выражения в скобках, но скобки обязательно сбалансированы.

Перерыл уже весь гуголь и документацию, но везде только примеры инфиксных калькуляторов, мне же не нужно вычислять выражение, а только убедиться в балансе скобок в нём и поместить куда-нибудь.

Пока имеется такое:

.l

name [a-zA-Z][a-zA-Z0-9]*

{name}"="  { yylval.sval = strdup(yytext); return PARAM; }
"("        { return LBRK; }
")"        { return RBRK; }

.y

param:
    PARAM expr { printf(" {%s%s}", $1, $2); }
    ;


Ответ на: комментарий от Deleted

Ну это часть парсера какбэ. Предлагаете раскрывать парные скобки регэкспами?

Пробелы внутри данных выражений кстати тоже имеют право быть.

mosfet
() автор топика
Ответ на: комментарий от mosfet

И число параметров может быть любым, а их имена произвольными.

mosfet
() автор топика

Ни бизон ни yacc для проектирования не пригодны. ЕМНИП даже в документации на yacc это написано. Они предназначены для рутинного преобразования синтаксиса в таблицы переходов.

Ещё раз: yacc предназначен для упрощения внесения изменений в уже готовую схему!

ziemin ★★
()
Ответ на: комментарий от ziemin

А что обычно используют? (на уровне сишечки)

anonymous
()

банальный алгоритм - бежишь по строке, хранишь уровень вложенности, проверяешь, что он не ниже 0 после ')' и не выше 0 после перехода к следующему значению/завершению строки, ты все равно полную грамматику не выписываешь для парсера

wota ★★
()
$ echo 'ABC X=(...) Y=a+b*(...) Z=123' | awk -F'[A-Z]+=' '{for(i=2;i<=NF;i++) {print $i}}'
(...) 
a+b*(...) 
123

Считай скобки в каждом отдельном выражении
$ echo 'ABC X=(...) Y=a+b*(...) Z=123' | grep -Eo '[A-Z]+='
X=
Y=
Z=

Список имен параметров
anonymous
()
Ответ на: комментарий от anonymous

Это конечно хорошо, но я пытаюсь освоить сабжевые инструменты, видимо я выбрал не очень хороший пример для этого.

mosfet
() автор топика
Ответ на: комментарий от wota

А как примерно это сделать в yacc/bison? Я их только несколько дней назад увидел. Или это всё в лексере надобно выполнять?

mosfet
() автор топика

ЕМНИП, чтобы соблюсти сбалансированность скобок, хватит ленивой рекуррентной грамматики.

dmfd
()

Для ясности - я пытаюсь распарсить SPICE-файл межсоединений в дерево объектов

*.GLOBAL VDD, VSS
*
* NA2
.SUBCKT NA2 Z / A B
MP1 Z A VDD VDD P W=12 L=14
MP2 Z B VDD VDD P W=12 L=14
MN1 Z A 5 VSS N W=12 L=1.75*(3+5)
MN2 5 B VSS VSS N W=12 L=14
X1 n1 n2 n3 n4 / NA3
.ENDS NA2
mosfet
() автор топика
Ответ на: комментарий от mosfet

test1.l

%{
# include "test1.tab.h"
%}

%%
"("     { return OP; }
")"     { return CP; }
[\+\-\*\/a-zA-Z0-9]+ {return SYM;}
\n      { return EOL; }
[ \t]   { }
.	{ yyerror("Unknonw character %c\n", *yytext); }
%%

test1.y

%token OP CP
%token SYM
%token EOL

%%

calclist: /* nothing */
 | calclist exp EOL { }
 ;

exp: term
 | exp term
 ;

term: SYM
 | OP exp CP { $$ = $2;}
 ;

%%
main()
{
  yyparse();
}

yyerror(char *s)
{
//  fprintf(stderr, "error: %s\n", s);
}

Makefile

test1:	test1.l test1.y
		bison -d test1.y
		flex test1.l
		cc -o $@ test1.tab.c lex.yy.c -lfl

Usage:

$ ls
Makefile  test1.l  test1.y
$ make
bison -d test1.y
flex test1.l
cc -o test1 test1.tab.c lex.yy.c -lfl

$ ls
lex.yy.c  Makefile  test1  test1.l  test1.tab.c  test1.tab.h  test1.y

# good syntax
$ echo '(a+b)+(a+b)*9' | ./test1 ; echo $?
0

# bad syntax
$ echo '(a+b)+(a+b)*9)' | ./test1 ; echo $?
1

anonymous
()
Ответ на: комментарий от dmfd

Пример (правда на парсеке, а не бизоне):

{-# LANGUAGE FlexibleContexts #-}
import Text.Parsec

data Ast = Leaf String | Block [Ast]
    deriving Show

atom = many1 alphaNum >>= return.Leaf

block = do 
    char '{' 
    many space
    s <- sentence 
    many space
    char '}'
    return $ Block s

sentence = do
    many1 $ do
        a <- (atom <|> block)
        many space
        char ';'
        many space
        return a

main = mapM_ (\a -> putStrLn a >> parseTest block a) 
            ["{aa; bb;}", "{aaa;}", "{a; {a; b;}; a;}", "{a;{a;}"]

Вывод:

{aa; bb;}
Block [Leaf "aa",Leaf "bb"]
{aaa;}
Block [Leaf "aaa"]
{a; {a; b;}; a;}
Block [Leaf "a",Block [Leaf "a",Leaf "b"],Leaf "a"]
{a;{a;}
parse error at (line 1, column 8):
unexpected end of input
expecting space or ";"
dmfd
()
Ответ на: комментарий от mosfet
%{
#include <string.h>
#include "cdlparser.tab.h"

int lineno = 1;
%}

%%
[ \t]           ;
^[A-Z]+         { yylval.sval = strdup(yytext); return ELEM; }
[a-z]"="        { yylval.sval = strdup(yytext); return PARAM; }
[a-z0-9*/+-]+   { yylval.sval = strdup(yytext); return NAME; }
"("             { return LP; }
")"             { return RP; }
\n              { ++lineno; return EOL; }
.               { yylval.sval = strdup(yytext); return OTHER; }
%%
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

extern int lineno;
char temp[50];
%}

%union {
    int     ival;
    float   fval;
    char   *sval;
}

%token LP RP EOL
%token <sval> ELEM PARAM NAME OTHER
%type <sval> params param expr subexpr term

%%
lines:
    lines elem
|   elem
;

elem:       /* 'name: params' */
    ELEM params EOL { printf("%s: %s\n", $1, $2); }
;

params:     /* 'param1 param2 ...' */
    params param    { sprintf($$, "%s %s", $1, $2); }
|   param
;

param:      /* 'name=expression' */
    PARAM expr      { sprintf($$, "%s%s", $1, $2); }
;

expr:       /* 'expression' */
    /* empty */     { $$ = "<empty expression>"; }
|   expr subexpr    { $$ = $2; }
;

subexpr:
    term
|   subexpr term    { sprintf($$, "%s%s", $1, $2); }
;

term:
    NAME
|   LP subexpr RP   { sprintf(temp, "(%s)", $2); $$ = strdup(temp); }
|   LP RP           { $$ = "()"; }
;
%%

void yyerror(const char *s) {
    printf("Parse error on line %d: %s\n", lineno, s);
    exit(-1);
}

int main(int argc, char **argv) {
    FILE *myfile = fopen("test.cdl", "r");
    yyin = myfile;
    do {
        yyparse();
    } while (!feof(yyin));
}
D r=(a+b)+(a+b)*(9)+() t=
E a=(a*((c)+(d)+())) b=(123) c=a d=(bb)
F z=((())())()
G x=(2*(3+4))+((5)
  D: r=(a+b)+(a+b)*(9)+() t=<empty expression>
  E: a=(a*((c)+(d)+())) b=(123) c=a d=(bb)
  F: z=((())())()
  Parse error on line 5: syntax error

Тем не менее, есть две неприятности:

conflicts: 2 shift/reduce
и нельзя ли как-то избавиться от переменной temp?

mosfet
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.