class Yadriggy::Checker
AST checker. It visits the AST nodes and does various checks like type checking.
Public Class Methods
@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
@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
@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
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
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
@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
@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
@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
@return [ASTnode] the current abstract syntax tree.
# File lib/yadriggy/checker.rb, line 233 def ast @current_ast end
@return [Object] the current environment.
# File lib/yadriggy/checker.rb, line 238 def ast_env @current_env end
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
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
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
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
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
@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
Gets an array of error messages. @return [Array<String>] error messages or `[]`.
# File lib/yadriggy/checker.rb, line 100 def error_messages @messages end
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
The last error message.
@return [String|nil] the message.
# File lib/yadriggy/checker.rb, line 107 def last_error @messages.last end
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
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