lib/temple/static_analyzer.rb
# frozen_string_literal: true begin require 'ripper' rescue LoadError end module Temple module StaticAnalyzer STATIC_TOKENS = [ :on_tstring_beg, :on_tstring_end, :on_tstring_content, :on_embexpr_beg, :on_embexpr_end, :on_lbracket, :on_rbracket, :on_qwords_beg, :on_words_sep, :on_qwords_sep, :on_lparen, :on_rparen, :on_lbrace, :on_rbrace, :on_label, :on_int, :on_float, :on_imaginary, :on_comma, :on_sp, :on_ignored_nl, ].freeze DYNAMIC_TOKENS = [ :on_ident, :on_period, ].freeze STATIC_KEYWORDS = [ 'true', 'false', 'nil', ].freeze STATIC_OPERATORS = [ '=>', ].freeze class << self def available? defined?(Ripper) && Ripper.respond_to?(:lex) end def static?(code) return false if code.nil? || code.strip.empty? return false if syntax_error?(code) Ripper.lex(code).each do |_, token, str| case token when *STATIC_TOKENS # noop when :on_kw return false unless STATIC_KEYWORDS.include?(str) when :on_op return false unless STATIC_OPERATORS.include?(str) when *DYNAMIC_TOKENS return false else return false end end true end def syntax_error?(code) SyntaxChecker.new(code).parse false rescue SyntaxChecker::ParseError true end end if defined?(Ripper) class SyntaxChecker < Ripper class ParseError < StandardError; end private def on_parse_error(*) raise ParseError end end end end end