func makeSynthRules(l lexer.Lexer, synthSymbols map[string]bool, userFn UserFn) ([]Rule, error) { var rules []Rule if userFn == nil { userFn = DefaultUserFn } for tok := range synthSymbols { if !isSynthName(tok) { tlog.Panic("should be a synth name but it's not: ", tok) } lsid, err := l.GetSymbolSet().GetByName(tok) if err != nil { return rules, err } end := tok[len(tok)-1:] tok := tok[:len(tok)-1] rsid, err := rhsCase(l, tok) if err != nil { return rules, err } switch end { case "?": rules = append(rules, Rule{lsid, []syms.SymbolID{syms.EMPTY}, userFn}) rules = append(rules, Rule{lsid, []syms.SymbolID{rsid}, userFn}) case "*": rules = append(rules, Rule{lsid, []syms.SymbolID{syms.EMPTY}, userFn}) rules = append(rules, Rule{lsid, []syms.SymbolID{lsid, rsid}, userFn}) case "+": rules = append(rules, Rule{lsid, []syms.SymbolID{rsid}, userFn}) rules = append(rules, Rule{lsid, []syms.SymbolID{lsid, rsid}, userFn}) } } return rules, nil }
func lhsCase(l lexer.Lexer, lhs string) (syms.SymbolID, error) { symbols := l.GetSymbolSet() sid, err := symbols.GetByName(lhs) if err == nil { return sid, nil } return symbols.Add(lhs, false) }
func rhsCase(l lexer.Lexer, tok string) (syms.SymbolID, error) { if tok == "" { return syms.ERROR, errors.New("empty rhs identifier") } symbols := l.GetSymbolSet() sid, err := symbols.GetByName(tok) if err == nil { return sid, nil } lt := len(tok) if isIdentName(tok) { return l.Ident(tok[1 : lt-1]), nil } else if isOperatorName(tok) { return l.Operator(tok[1 : lt-1]), nil } if tok == "" || !tokenRe.MatchString(tok) { return syms.ERROR, fmt.Errorf("%s is not a valid rhs identifier", tok) } sid, err = symbols.Add(tok, false) return sid, err }
func NewGrammarBlock(l lexer.Lexer, ruleBlock string, funcMap map[string]UserFn) (Grammar, error) { // defer tlog.FuncLog(tlog.Func("NewGrammarBlock")) var rules []Rule arrowSep := "→" defineSep := "::=" subruleSep := "|" ruleHandlerPrefix := "@" ruleList := strings.Split(ruleBlock, "\n") synthSymbols := make(map[string]bool) var lhsID syms.SymbolID var err error for lineno, r := range ruleList { r = strings.TrimSpace(r) if r == "" || strings.HasPrefix(r, "//") { continue } var rhs string if lhsID != syms.ERROR && strings.HasPrefix(r, subruleSep) { // new subrule, lhs stays the same rhs = r[len(subruleSep):] } else { // new rule idxArrow := strings.Index(r, arrowSep) idxDefine := strings.Index(r, defineSep) var lhs string if idxArrow < 0 && idxDefine < 0 { return Grammar{}, fmt.Errorf("rule on line %d has no '→' or '::=' symbol", lineno+1) } else if idxArrow >= 0 && (idxDefine < 0 || idxArrow < idxDefine) { lhs = r[:idxArrow] rhs = r[idxArrow+len(arrowSep):] } else { lhs = r[:idxDefine] rhs = r[idxDefine+len(defineSep):] } lhsTrim := strings.TrimSpace(lhs) if lhsTrim == "" || !identRe.MatchString(lhsTrim) { return Grammar{}, fmt.Errorf("line %d: '%s' is not a valid lhs identifier\n%s", lineno+1, lhs, r) } lhsID, err = lhsCase(l, lhsTrim) if err != nil { return Grammar{}, err } } var rhsa []syms.SymbolID var funcDecl string for _, tok := range strings.Fields(rhs) { if tok == subruleSep { if len(rhsa) == 0 { return Grammar{}, fmt.Errorf("line %d: empty partial rule\n%s", lineno+1, r) } rules = append(rules, Rule{lhsID, rhsa, funcMap[funcDecl]}) rhsa = nil funcDecl = "" } else if strings.HasPrefix(tok, ruleHandlerPrefix) { if funcDecl != "" { return Grammar{}, fmt.Errorf("line %d: multiple function decls\n%s", lineno+1, r) } funcDecl = tok[len(ruleHandlerPrefix):] } else { sid, err := rhsCase(l, tok) if err != nil { return Grammar{}, fmt.Errorf("line %d: %s\n%s", lineno+1, err, r) } rhsa = append(rhsa, sid) if isSynthName(tok) { synthSymbols[tok] = true } } } if len(rhsa) == 0 { return Grammar{}, fmt.Errorf("line %d: empty rhs\n%s", lineno+1, r) } rules = append(rules, Rule{lhsID, rhsa, funcMap[funcDecl]}) } newRules, err := makeSynthRules(l, synthSymbols, funcMap["synth"]) if err != nil { return Grammar{}, nil } rules = append(rules, newRules...) return NewGrammar(l.GetSymbolSet(), rules) }