(require 'smie)
+;; Here's a simplified BNF grammar, for reference:
+;; http://www.cse.buffalo.edu/~regan/cse305/RubyBNF.pdf
(defconst ruby-smie-grammar
;; FIXME: Add support for Cucumber.
(smie-prec2->grammar
- (smie-bnf->prec2
- '((id)
- (insts (inst) (insts ";" insts))
- (inst (exp) (inst "iuwu-mod" exp))
- (exp (exp1) (exp "," exp) (exp "=" exp) (exp "-" exp) (exp "+" exp)
- (id " @ " exp))
- (exp1 (exp2) (exp2 "?" exp1 ":" exp1))
- (exp2 ("def" insts "end")
- ("begin" insts-rescue-insts "end")
- ("do" insts "end")
- ("class" insts "end") ("module" insts "end")
- ("for" for-body "end")
- ("[" expseq "]")
- ("{" hashvals "}")
- ("{" insts "}")
- ("while" insts "end")
- ("until" insts "end")
- ("unless" insts "end")
- ("if" if-body "end")
- ("case" cases "end"))
- (formal-params ("opening-|" exp "|"))
- (for-body (for-head ";" insts))
- (for-head (id "in" exp))
- (cases (exp "then" insts) ;; FIXME: Ruby also allows (exp ":" insts).
- (cases "when" cases) (insts "else" insts))
- (expseq (exp) );;(expseq "," expseq)
- (hashvals (id "=>" exp1) (hashvals "," hashvals))
- (insts-rescue-insts (insts)
- (insts-rescue-insts "rescue" insts-rescue-insts)
- (insts-rescue-insts "ensure" insts-rescue-insts))
- (itheni (insts) (exp "then" insts))
- (ielsei (itheni) (itheni "else" insts))
- (if-body (ielsei) (if-body "elsif" if-body)))
- '((nonassoc "in") (assoc ";") (right " @ ")
- (assoc ",") (right "=") (assoc "-" "+"))
- '((assoc "when"))
- '((assoc "elsif"))
- '((assoc "rescue" "ensure"))
- '((assoc ",")))))
+ (smie-merge-prec2s
+ (smie-bnf->prec2
+ '((id)
+ (insts (inst) (insts ";" insts))
+ (inst (exp) (inst "iuwu-mod" exp))
+ (exp (exp1) (exp "," exp) (exp "=" exp)
+ (id " @ " exp))
+ (exp1 (exp2) (exp2 "?" exp1 ":" exp1))
+ (exp2 ("def" insts "end")
+ ("begin" insts-rescue-insts "end")
+ ("do" insts "end")
+ ("class" insts "end") ("module" insts "end")
+ ("for" for-body "end")
+ ("[" expseq "]")
+ ("{" hashvals "}")
+ ("{" insts "}")
+ ("while" insts "end")
+ ("until" insts "end")
+ ("unless" insts "end")
+ ("if" if-body "end")
+ ("case" cases "end"))
+ (formal-params ("opening-|" exp "|"))
+ (for-body (for-head ";" insts))
+ (for-head (id "in" exp))
+ (cases (exp "then" insts) ;; FIXME: Ruby also allows (exp ":" insts).
+ (cases "when" cases) (insts "else" insts))
+ (expseq (exp) );;(expseq "," expseq)
+ (hashvals (id "=>" exp1) (hashvals "," hashvals))
+ (insts-rescue-insts (insts)
+ (insts-rescue-insts "rescue" insts-rescue-insts)
+ (insts-rescue-insts "ensure" insts-rescue-insts))
+ (itheni (insts) (exp "then" insts))
+ (ielsei (itheni) (itheni "else" insts))
+ (if-body (ielsei) (if-body "elsif" if-body)))
+ '((nonassoc "in") (assoc ";") (right " @ ")
+ (assoc ",") (right "="))
+ '((assoc "when"))
+ '((assoc "elsif"))
+ '((assoc "rescue" "ensure"))
+ '((assoc ",")))
+
+ (smie-precs->prec2
+ '((right "=")
+ (right "+=" "-=" "*=" "/=" "%=" "**=" "&=" "|=" "^="
+ "<<=" ">>=" "&&=" "||=")
+ (left ".." "...")
+ (left "+" "-")
+ (left "*" "/" "%" "**")
+ ;; (left "|") ; FIXME: Conflicts with | after block parameters.
+ (left "^" "&")
+ (nonassoc "<=>")
+ (nonassoc ">" ">=" "<" "<=")
+ (nonassoc "==" "===" "!=")
+ (nonassoc "=~" "!~")
+ (left "<<" ">>")
+ (left "&&" "||"))))))
(defun ruby-smie--bosp ()
(save-excursion (skip-chars-backward " \t")
(skip-chars-backward " \t")
(not (or (bolp)
(and (memq (char-before)
- '(?\; ?- ?+ ?* ?/ ?: ?. ?, ?\[ ?\( ?\{ ?\\))
+ '(?\; ?- ?+ ?* ?/ ?: ?. ?, ?\[ ?\( ?\{ ?\\ ?& ?> ?< ?% ?~))
;; Make sure it's not the end of a regexp.
(not (eq (car (syntax-after (1- (point)))) 7)))
(and (eq (char-before) ?\?)