%{ package main %} %token tkWORD %token tkASSIGNMENT_WORD %token tkNEWLINE %token tkIO_NUMBER %token tkBACKGROUND %token tkPIPE tkSEMI %token tkAND tkOR tkSEMISEMI %token tkLT tkGT tkLTLT tkGTGT tkLTAND tkGTAND tkLTGT tkLTLTDASH tkGTPIPE %token tkIF tkTHEN tkELSE tkELIF tkFI tkDO tkDONE %token tkCASE tkESAC tkWHILE tkUNTIL tkFOR %token tkLPAREN tkRPAREN tkLBRACE tkRBRACE tkEXCLAM %token tkIN %union { IONum int List *MkShList AndOr *MkShAndOr Pipeline *MkShPipeline Command *MkShCommand CompoundCommand *MkShCompoundCommand Separator MkShSeparator Simple *MkShSimpleCommand FuncDef *MkShFunctionDefinition For *MkShForClause If *MkShIfClause Case *MkShCaseClause CaseItem *MkShCaseItem Loop *MkShLoopClause Words []*ShToken Word *ShToken Redirections []*MkShRedirection Redirection *MkShRedirection } %type start program compound_list brace_group subshell term do_group %type and_or %type pipeline pipe_sequence %type command %type compound_command %type separator separator_op sequential_sep %type simple_command cmd_prefix cmd_suffix %type function_definition %type for_clause %type if_clause else_part %type case_clause case_list case_list_ns %type case_item case_item_ns %type while_clause until_clause %type wordlist case_selector pattern %type filename cmd_word here_end %type redirect_list %type io_redirect io_file io_here %% start : program { shyylex.(*ShellLexer).result = $$ } program : compound_list { $$ = $1 } program : /* empty */ { $$ = &MkShList{} } and_or : pipeline { $$ = NewMkShAndOr($1) } and_or : and_or tkAND linebreak pipeline { $$.Add("&&", $4) } and_or : and_or tkOR linebreak pipeline { $$.Add("||", $4) } pipeline : pipe_sequence { /* empty */ } pipeline : tkEXCLAM pipe_sequence { $$ = $2 $$.Negated = true } pipe_sequence : command { $$ = NewMkShPipeline(false, $1) } pipe_sequence : pipe_sequence tkPIPE linebreak command { $$.Add($4) } command : simple_command { $$ = &MkShCommand{Simple: $1} } command : compound_command { $$ = &MkShCommand{Compound: $1} } command : compound_command redirect_list { $$ = &MkShCommand{Compound: $1, Redirects: $2} } command : function_definition { $$ = &MkShCommand{FuncDef: $1} } command : function_definition redirect_list { $$ = &MkShCommand{FuncDef: $1, Redirects: $2} } compound_command : brace_group { $$ = &MkShCompoundCommand{Brace: $1} } compound_command : subshell { $$ = &MkShCompoundCommand{Subshell: $1} } compound_command : for_clause { $$ = &MkShCompoundCommand{For: $1} } compound_command : case_clause { $$ = &MkShCompoundCommand{Case: $1} } compound_command : if_clause { $$ = &MkShCompoundCommand{If: $1} } compound_command : while_clause { $$ = &MkShCompoundCommand{Loop: $1} } compound_command : until_clause { $$ = &MkShCompoundCommand{Loop: $1} } subshell : tkLPAREN compound_list tkRPAREN { $$ = $2 } compound_list : linebreak term { $$ = $2 } compound_list : linebreak term separator { $$ = $2 $$.AddSeparator($3) } term : and_or { $$ = NewMkShList() $$.AddAndOr($1) } term : term separator and_or { $$.AddSeparator($2) $$.AddAndOr($3) } for_clause : tkFOR tkWORD linebreak do_group { args := NewShToken("\"$$@\"", &ShAtom{shtWord, "\"",shqDquot, nil}, &ShAtom{shtWord, "$$@",shqDquot, nil}, &ShAtom{shtWord,"\"",shqPlain, nil}) $$ = &MkShForClause{$2.MkText, []*ShToken{args}, $4} } for_clause : tkFOR tkWORD linebreak tkIN sequential_sep do_group { $$ = &MkShForClause{$2.MkText, nil, $6} } for_clause : tkFOR tkWORD linebreak tkIN wordlist sequential_sep do_group { $$ = &MkShForClause{$2.MkText, $5, $7} } wordlist : tkWORD { $$ = append($$, $1) } wordlist : wordlist tkWORD { $$ = append($$, $2) } case_clause : tkCASE tkWORD linebreak tkIN linebreak case_list tkESAC { $$ = $6 $$.Word = $2 } case_clause : tkCASE tkWORD linebreak tkIN linebreak case_list_ns tkESAC { $$ = $6 $$.Word = $2 } case_clause : tkCASE tkWORD linebreak tkIN linebreak tkESAC { $$ = &MkShCaseClause{$2, nil} } case_list_ns : case_item_ns { $$ = &MkShCaseClause{nil, nil} $$.Cases = append($$.Cases, $1) } case_list_ns : case_list case_item_ns { $$.Cases = append($$.Cases, $2) } case_list : case_item { $$ = &MkShCaseClause{nil, nil} $$.Cases = append($$.Cases, $1) } case_list : case_list case_item { $$.Cases = append($$.Cases, $2) } case_selector : tkLPAREN pattern tkRPAREN { $$ = $2 } case_selector : pattern tkRPAREN { /* empty */ } case_item_ns : case_selector linebreak { $$ = &MkShCaseItem{$1, &MkShList{}, sepNone} } case_item_ns : case_selector linebreak term linebreak { $$ = &MkShCaseItem{$1, $3, sepNone} } case_item_ns : case_selector linebreak term separator_op linebreak { $$ = &MkShCaseItem{$1, $3, $4} } case_item : case_selector linebreak tkSEMISEMI linebreak { $$ = &MkShCaseItem{$1, &MkShList{}, sepNone} } case_item : case_selector compound_list tkSEMISEMI linebreak { $$ = &MkShCaseItem{$1, $2, sepNone} } pattern : tkWORD { $$ = nil $$ = append($$, $1) } pattern : pattern tkPIPE tkWORD { $$ = append($$, $3) } if_clause : tkIF compound_list tkTHEN compound_list else_part tkFI { $$ = $5 $$.Prepend($2, $4) } if_clause : tkIF compound_list tkTHEN compound_list tkFI { $$ = &MkShIfClause{} $$.Prepend($2, $4) } else_part : tkELIF compound_list tkTHEN compound_list { $$ = &MkShIfClause{} $$.Prepend($2, $4) } else_part : tkELIF compound_list tkTHEN compound_list else_part { $$ = $5 $$.Prepend($2, $4) } else_part : tkELSE compound_list { $$ = &MkShIfClause{nil, nil, $2} } while_clause : tkWHILE compound_list do_group { $$ = &MkShLoopClause{$2, $3, false} } until_clause : tkUNTIL compound_list do_group { $$ = &MkShLoopClause{$2, $3, true} } function_definition : tkWORD tkLPAREN tkRPAREN linebreak compound_command { /* Apply rule 9 */ $$ = &MkShFunctionDefinition{$1.MkText, $5} } brace_group : tkLBRACE compound_list tkRBRACE { $$ = $2 } do_group : tkDO compound_list tkDONE { $$ = $2 } simple_command : cmd_prefix cmd_word cmd_suffix { $$.Name = $2 $$.Args = append($$.Args, $3.Args...) $$.Redirections = append($$.Redirections, $3.Redirections...) } simple_command : cmd_prefix cmd_word { $$.Name = $2 } simple_command : cmd_prefix { /* empty */ } simple_command : tkWORD cmd_suffix { $$ = $2 $$.Name = $1 } simple_command : tkWORD { $$ = &MkShSimpleCommand{Name: $1} } cmd_word : tkWORD { /* Apply rule 7b */ /* empty */ } cmd_prefix : io_redirect { $$ = &MkShSimpleCommand{} $$.Redirections = append($$.Redirections, $1) } cmd_prefix : tkASSIGNMENT_WORD { $$ = &MkShSimpleCommand{} $$.Assignments = append($$.Assignments, $1) } cmd_prefix : cmd_prefix io_redirect { $$.Redirections = append($$.Redirections, $2) } cmd_prefix : cmd_prefix tkASSIGNMENT_WORD { $$.Assignments = append($$.Assignments, $2) } cmd_suffix : io_redirect { $$ = &MkShSimpleCommand{} $$.Redirections = append($$.Redirections, $1) } cmd_suffix : tkWORD { $$ = &MkShSimpleCommand{} $$.Args = append($$.Args, $1) } cmd_suffix : cmd_suffix io_redirect { $$.Redirections = append($$.Redirections, $2) } cmd_suffix : cmd_suffix tkWORD { $$.Args = append($$.Args, $2) } redirect_list : io_redirect { $$ = nil $$ = append($$, $1) } redirect_list : redirect_list io_redirect { $$ = append($$, $2) } io_redirect : io_file { /* empty */ } io_redirect : tkIO_NUMBER io_file { $$ = $2 $$.Fd = $1 } io_redirect : io_here { /* empty */ } io_redirect : tkIO_NUMBER io_here { $$ = $2 $$.Fd = $1 } io_file : tkLT filename { $$ = &MkShRedirection{-1, "<", $2} } io_file : tkLTAND filename { $$ = &MkShRedirection{-1, "<&", $2} } io_file : tkGT filename { $$ = &MkShRedirection{-1, ">", $2} } io_file : tkGTAND filename { $$ = &MkShRedirection{-1, ">&", $2} } io_file : tkGTGT filename { $$ = &MkShRedirection{-1, ">>", $2} } io_file : tkLTGT filename { $$ = &MkShRedirection{-1, "<>", $2} } io_file : tkGTPIPE filename { $$ = &MkShRedirection{-1, ">|", $2} } filename : tkWORD { /* Apply rule 2 */ /* empty */ } io_here : tkLTLT here_end { $$ = &MkShRedirection{-1, "<<", $2} } io_here : tkLTLTDASH here_end { $$ = &MkShRedirection{-1, "<<-", $2} } here_end : tkWORD { /* Apply rule 3 */ /* empty */ } newline_list : tkNEWLINE { /* empty */ } newline_list : newline_list tkNEWLINE { /* empty */ } linebreak : newline_list { /* empty */ } linebreak : /* empty */ { /* empty */ } separator_op : tkBACKGROUND { $$ = sepBackground } separator_op : tkSEMI { $$ = sepSemicolon } separator : separator_op linebreak { /* empty */ } separator : newline_list { $$ = sepNewline } sequential_sep : tkSEMI linebreak { $$ = sepSemicolon } sequential_sep : tkNEWLINE linebreak { $$ = sepNewline }