class Yadriggy::Checker

AST checker. It visits the AST nodes and does various checks like type checking.

Public Class Methods

all_rules() click to toggle source

@api private @return [Array<Hash>] all the rules defined so far.

# File lib/yadriggy/checker.rb, line 35
def self.all_rules
  [@rules, @rule_declarators]
end
check_init_class() click to toggle source

@api private Initializes this class if necessary.

# File lib/yadriggy/checker.rb, line 29
def self.check_init_class
  init_class if @rules.nil?
end
find_rule_entry(ast) click to toggle source

@api private internal-use only. Don't call this. @return [Pair<Proc,Class>] a rule and the class declaring it, or nil.

# File lib/yadriggy/checker.rb, line 56
def self.find_rule_entry(ast)
  find_rule_entry2(ast.class, ast.usertype)
end
new() click to toggle source

Initializes the object.

# File lib/yadriggy/checker.rb, line 80
def initialize
  self.class.check_init_class
  @nerrors = 0
  @messages = []
  @check_list = []
  @current_ast = nil
  @current_env = nil
  @rule_declarator = nil
end
rule(node_type, &proc) click to toggle source

Defines the rule for a node type.

@param [Class] node_type the type of the AST node. @yield The block is executed for the node with `node_type`. @return [void]

# File lib/yadriggy/checker.rb, line 21
def self.rule(node_type, &proc)
  init_class if @rules.nil?
  @rules[node_type] = proc
  @rule_declarators[node_type] = self
end

Private Class Methods

find_rule_entry2(ast_class, utype) click to toggle source

@api private

# File lib/yadriggy/checker.rb, line 61
                     def self.find_rule_entry2(ast_class, utype)
  unless utype.nil?
    rule = @rules[utype]
    return [rule, @rule_declarators[utype]] unless rule.nil?
  end

  rule = @rules[ast_class]
  if rule.nil?
    if ast_class.superclass.nil?
      nil
    else
      find_rule_entry2(ast_class.superclass, utype)
    end
  else
    [rule, @rule_declarators[ast_class]]
  end
end
init_class() click to toggle source

@api private

# File lib/yadriggy/checker.rb, line 40
                     def self.init_class
  @rules = {}
  @rule_declarators = {}
  unless self.superclass == Object
    all = self.superclass.all_rules
    unless all[0].nil?
      @rules = all[0].clone
      @rule_declarators = all[1].clone
    end
  end
end

Public Instance Methods

apply_typing_rule(rule, an_ast, ast_tenv) click to toggle source

@api private internal use only

# File lib/yadriggy/checker.rb, line 197
def apply_typing_rule(rule, an_ast, ast_tenv)
  if rule.nil?
    error_found!(an_ast, "no rule for #{an_ast.class}")
  else
    old_ast = @current_ast
    old_tenv = @current_env
    old_declarator = @rule_declarator
    @current_ast = an_ast
    @current_env = ast_tenv unless ast_tenv.nil?
    @rule_declarator = rule[1]
    t = instance_exec(&rule[0])
    @rule_declarator = old_declarator
    @current_env = old_tenv
    @current_ast = old_ast
    return t
  end
end
ast() click to toggle source

@return [ASTnode] the current abstract syntax tree.

# File lib/yadriggy/checker.rb, line 233
def ast
  @current_ast
end
ast_env() click to toggle source

@return [Object] the current environment.

# File lib/yadriggy/checker.rb, line 238
def ast_env
  @current_env
end
check(an_ast, ast_env=nil) click to toggle source

Applies rules to the given AST.

It assumes that ast is processed by Syntax and it has usertype method. An exception is thrown when the checking fails. ast may be nil.

The environment given to this method can be accessed in the rules through ast_env(). It is optional and can be any object. The initial one is made by make_base_env().

@return [Object]

# File lib/yadriggy/checker.rb, line 186
def check(an_ast, ast_env=nil)
  if an_ast.nil?
    nil
  else
    rule = self.class.find_rule_entry(an_ast)
    apply_typing_rule(rule, an_ast, ast_env)
  end
end
check_all(an_ast) click to toggle source

Applies rules to the given AST. It returns the result of the rule-application or throws a CheckError. This is the entry point of the checker. It may also check the other ASTs invoked in the given AST.

@param [ASTree|ASTnode|nil] an_ast the AST. @return [Object]

# File lib/yadriggy/checker.rb, line 154
def check_all(an_ast)
  return nil if an_ast.nil?
  an_ast = an_ast.tree if an_ast.is_a?(ASTree)
  t = check(an_ast, make_base_env(an_ast.get_context_class))
  until (checked = @check_list.pop).nil?
    @current_ast = checked[0]
    @current_env = checked[1]
    checked[2].call
  end
  t
end
check_later(&proc) click to toggle source

Later invokes the block, which performs checking. The method immediately returns. This is used for avoiding infinite regression during the checking. @yield The block is later executed for checking. @return [void]

# File lib/yadriggy/checker.rb, line 247
def check_later(&proc)
  cur_ast = @current_ast
  cur_env = @current_env
  @check_list << [cur_ast, cur_env, proc]
end
error!(an_ast, msg='') click to toggle source

Records an error message.

@param [ASTnode] an_ast the AST causing the error. @param [String] msg the error message.

# File lib/yadriggy/checker.rb, line 126
def error!(an_ast, msg='')
  loc  = if an_ast.is_a?(ASTnode)
           an_ast.source_location_string
         else
           ''
         end
  emsg = "#{loc} DSL #{error_group} error. #{msg}"
  @nerrors += 1
  @messages << emsg
  binding.pry if Yadriggy.debug > 1
  emsg
end
error_found!(an_ast, msg='') click to toggle source

Throws an error.

@param [ASTnode] an_ast the AST causing the error. @param [String] msg the error message. @return [void] always throws an exception.

# File lib/yadriggy/checker.rb, line 116
def error_found!(an_ast, msg='')
  emsg = error!(an_ast, msg)
  raise CheckError.new(emsg)
end
error_group() click to toggle source

@api private The returned value is inserted into an error messagge as the error category.

# File lib/yadriggy/checker.rb, line 142
def error_group
  ''
end
error_messages() click to toggle source

Gets an array of error messages. @return [Array<String>] error messages or `[]`.

# File lib/yadriggy/checker.rb, line 100
def error_messages
  @messages
end
errors?() click to toggle source

Tests whether a type error was found. @return [Boolean] true if an error message is recorded.

# File lib/yadriggy/checker.rb, line 93
def errors?
  @nerrors > 0
end
last_error() click to toggle source

The last error message.

@return [String|nil] the message.

# File lib/yadriggy/checker.rb, line 107
def last_error
  @messages.last
end
make_base_env(klass) click to toggle source

Makes a new base environment with the given context class. It is called by `check_all`. Override this method to customize `check_all`.

@param [Module] klass the context class.

# File lib/yadriggy/checker.rb, line 171
def make_base_env(klass)
  klass
end
proceed(an_ast, envi=nil) click to toggle source

Applies the rule supplied by the superclass. @param [ASTnode] an_ast an AST. @param [Object] envi an environment object. @return [Object] the result of the application.

# File lib/yadriggy/checker.rb, line 219
def proceed(an_ast, envi=nil)
  rule = if @rule_declarator&.superclass == Object
           nil
         else
           @rule_declarator&.superclass.find_rule_entry(an_ast)
         end
  if rule.nil?
    error_found!(an_ast, 'no more rule. we cannot proceed')
  else
    apply_typing_rule(rule, an_ast, envi)
  end
end