Module: LangScan::EmacsLisp

Defined in:
lib/langscan/elisp.rb,
ext/langscan/elisp/elisp/elisp.c

Defined Under Namespace

Classes: Tokenizer

Constant Summary collapse

Keywords =
%w(
  defun defvar defmacro defgroup defcustom defconst and or
  condition-case function interactive require quote backquote
  lambda prog1 prog2 progn let let* if when unless cond catch throw
  save-current-buffer save-excursion save-restriction save-window-excurtion
  setq setq-default track-mouse unwind-protect while
  with-output-to-temp-buffer t nil
)
KeywordsHash =
{}
NotFuncallWordsHash =
{"lambda" => "lambda"}
NotFuncall2ndOuterWordsHash =
{"let" => "let", "let*" => "let*"}

Class Method Summary collapse

Class Method Details

.abbrevObject



24
25
26
# File 'lib/langscan/elisp.rb', line 24

def abbrev
  "elisp"
end

.extnamesObject



28
29
30
# File 'lib/langscan/elisp.rb', line 28

def extnames
  [".el"]
end

.funcall_list?(list) ⇒ Boolean

Returns:

  • (Boolean)


63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/langscan/elisp.rb', line 63

def funcall_list?(list)
  if list.before_open_length == 0
    return true
  end
  if list.before_open_length >= 2 && list.around_open(-2).text == "defun"
    return false
  end
  if NotFuncallWordsHash[list.around_open(-1).text]
    return false
  end
  if quote_list?(list)
    return false
  end
  outer = list.outer
  second_outer = outer.outer unless outer == nil
  if second_outer and NotFuncall2ndOuterWordsHash[second_outer.around_open(1).text]
    if NotFuncall2ndOuterWordsHash[outer.around_open(-1).text]
      return false
    end
  end
  return true
end

.nameObject



20
21
22
# File 'lib/langscan/elisp.rb', line 20

def name
  "Emacs Lisp"
end

.quote_list?(list) ⇒ Boolean

Returns:

  • (Boolean)


90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/langscan/elisp.rb', line 90

def quote_list?(list)
  l = list
  nest = 0
  while l
    if l.before_open_length >= 1
      before = l.around_open(-1)
      if before.type == :quote_chars
        return true if before.text.include?("'")
        nest = nest + quote_nestlevel(before.text)
      end
    end
    if l.after_open_length >= 1
      after = l.around_open(1)
      if after.text == 'quote'
        return true
      elsif after.text == 'backquote'
        nest = nest + 1
      elsif after.text == 'unquote'
        nest = nest - 1
      end
    end
    l = l.outer
  end
  return nest > 0
end

.quote_nestlevel(str) ⇒ Object



86
87
88
# File 'lib/langscan/elisp.rb', line 86

def quote_nestlevel(str)
  str.count("`") - str.count(",")
end

.scan(input, &block) ⇒ Object

LangScan::EmacsLisp.scan iterates over Emacs Lisp program. It yields for each Fragment.



34
35
36
37
# File 'lib/langscan/elisp.rb', line 34

def scan(input, &block)
  sorter = PairMatcher.fragmentsorter(block)
  scan_unsorted(input, &sorter)
end

.scan_unsorted(input, &block) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/langscan/elisp.rb', line 39

def scan_unsorted(input, &block)
  pm = LangScan::PairMatcher.new(2,2,2,2)
  pm.define_intertoken_fragment :space, nil
  pm.define_intertoken_fragment :comment, nil
  pm.define_pair :paren, :punct, "(", :punct, ")"
  reporter = lambda {|f|
    if (f.type == :ident || f.type == :funcall) && KeywordsHash[f.text]
      f.type = :keyword
    end
    if f.type == :quote_chars
      f.type = :punct
    end
    yield f
  }
  pm.parse(LangScan::EmacsLisp::Tokenizer.new(input), reporter) {|list|
    if (list.around_open(1).text == "defun" || list.around_open(1).text == "defmacro") && list.around_open(2).type == :ident
      list.around_open(2).type = :fundef
    end
    if list.around_open(1).type == :ident && funcall_list?(list)
      list.around_open(1).type = :funcall
    end
  }
end