Class: Qrpm::Compiler
- Inherits:
-
Object
- Object
- Qrpm::Compiler
- Defined in:
- lib/qrpm/compiler.rb
Overview
The main result data are #defs and #deps. #keys and #values are the parsed result of the keys and values of #deps and is used to interpolate strings when values of dependent variables are known
#defs is also partitioned into QRPM variables and directories
Instance Attribute Summary collapse
-
#ast ⇒ Object
readonly
Root node.
-
#defaults ⇒ Object
readonly
Defaults.
-
#defs ⇒ Object
readonly
Variable definitions.
-
#deps ⇒ Object
readonly
Map from path to list of variables it depends on.
-
#dict ⇒ Object
readonly
Dictionary.
Instance Method Summary collapse
-
#analyze(check_undefined: true, check_mandatory: true, check_field_types: true, check_directory_types: true) ⇒ Object
Analyze and decorate the AST tree.
-
#compile(yaml) ⇒ Object
Compile YAML into a Qrpm object.
- #dump ⇒ Object
-
#initialize(dict, system_dirs: true, defaults: true, srcdir: true) ⇒ Compiler
constructor
If :srcdir is true, a default $srcdir variable is prefixed to all local paths.
-
#parse(yaml) ⇒ Object
Parse the YAML source to an AST of Node objects and assign it to #ast.
Constructor Details
#initialize(dict, system_dirs: true, defaults: true, srcdir: true) ⇒ Compiler
If :srcdir is true, a default $srcdir variable is prefixed to all local paths. This is the default. srcdir:false
is only used when testing
31 32 33 34 35 36 37 38 39 40 |
# File 'lib/qrpm/compiler.rb', line 31 def initialize(dict, system_dirs: true, defaults: true, srcdir: true) constrain dict, Hash @ast = nil @defs = {} @deps = {} @dict = system_dirs ? INSTALL_DIRS.merge(dict) : dict @use_system_dirs = system_dirs @use_defaults = defaults @use_srcdir = srcdir end |
Instance Attribute Details
#ast ⇒ Object (readonly)
Root node
11 12 13 |
# File 'lib/qrpm/compiler.rb', line 11 def ast @ast end |
#defaults ⇒ Object (readonly)
Defaults. Map from key to source expression
27 28 29 |
# File 'lib/qrpm/compiler.rb', line 27 def defaults @defaults end |
#defs ⇒ Object (readonly)
Variable definitions. Map from path to Node
14 15 16 |
# File 'lib/qrpm/compiler.rb', line 14 def defs @defs end |
#deps ⇒ Object (readonly)
Map from path to list of variables it depends on. Paths with no dependencies have an empty list as value
18 19 20 |
# File 'lib/qrpm/compiler.rb', line 18 def deps @deps end |
#dict ⇒ Object (readonly)
Dictionary. The dictionary object are compiled into the AST before it is evaluated so the dictionary elements can be refered to with the usual $var notation. #dict is automatically augmented with the default system directory definitions unless :system_dirs is false
24 25 26 |
# File 'lib/qrpm/compiler.rb', line 24 def dict @dict end |
Instance Method Details
#analyze(check_undefined: true, check_mandatory: true, check_field_types: true, check_directory_types: true) ⇒ Object
Analyze and decorate the AST tree. Returns the AST
The various check_*
arguments are only used while testing to allow illegal but short expressions by suppressing specified checks in #analyze. The default is to apply all checks
86 87 88 89 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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/qrpm/compiler.rb', line 86 def analyze( check_undefined: true, check_mandatory: true, check_field_types: true, check_directory_types: true) # Set package/system directory of standard directories depending on the # number of files in the directory dirs = @ast.values.select { |n| n.is_a? DirectoryNode } freq = dirs.map { |d| (d.key_variables & STANDARD_DIRS) * dirs.size }.flatten.tally freq.each { |name, count| node = @defs[name] if count == 1 node.setsys else node.setpck end } # Collect definitions and dependencies ast.values.each { |node| collect_variables(node) } # Detect undefined variables and references to hashes or arrays if check_undefined deps.each { |path, path_deps| path_deps.each { |dep| if !defs.key?(dep) error "Undefined variable '#{dep}' in definition of '#{path}'" elsif !defs[dep].is_a?(ValueNode) error "Can't reference non-variable '#{dep}' in definition of '#{path}'" end } } end # Check for mandatory variables if check_mandatory missing = MANDATORY_FIELDS.select { |f| @defs[f].to_s.empty? } missing.empty? or error "Missing mandatory fields '#{missing.join("', '")}'" end # Check types of built-in variables if check_field_types FIELDS.each { |f,ts| ast.key?(f) or next ts.any? { |t| ast[f].class <= t } or error "Illegal type of field '#{f}'" } end @ast end |
#compile(yaml) ⇒ Object
Compile YAML into a Qrpm object
139 140 141 142 143 |
# File 'lib/qrpm/compiler.rb', line 139 def compile(yaml) parse(yaml) analyze Qrpm.new(defs, deps) end |
#dump ⇒ Object
145 146 147 148 149 150 |
# File 'lib/qrpm/compiler.rb', line 145 def dump puts "Defs" indent { defs.each { |k,v| puts "#{k}: #{v.signature}" if v.interpolated? }} puts "Deps" indent { deps.each { |k,v| puts "#{k}: #{v.join(", ")}" if !v.empty? } } end |
#parse(yaml) ⇒ Object
Parse the YAML source to an AST of Node objects and assign it to #ast. Returns the AST
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/qrpm/compiler.rb', line 44 def parse(yaml) # Root node @ast = RootNode.new # Compile standard directories. Also enter them into @defs so that #analyze # can access them by name before @defs is built by #collect_variables STANDARD_DIRS.each { |name| @defs[name] = StandardDirNode.new(@ast, name) } if @use_system_dirs # Parse yaml node yaml.each { |key, value| builtin_array = FIELDS[key]&.include?(ArrayNode) || false case [key.to_s, value, builtin_array] in [/[\/\$]/, _, _]; parse_directory_node(@ast, key, value) in [/^#{PATH_RE}$/, String, true]; parse_node(@ast, key, [value]) in [/^#{PATH_RE}$/, Array, false]; parse_directory_node(@ast, key, value) in [/^#{PATH_RE}$/, _, _]; parse_node(@ast, key, value) else error "Illegal key: #{key.inspect}" end } # Compile and add dictionary to the AST. This allows command line # assignments to override spec file values dict.each { |k,v| ValueNode.new(ast, k.to_s, Fragment::Fragment.parse(v)) } # Add defaults DEFAULTS.each { |k,v| next if k == "srcdir" # Special handling of $srcdir below parse_node(@ast, k, v) if !@ast.key?(k) } if @use_defaults # Only add a default $srcdir node when :srcdir is true parse_node(@ast, "srcdir", DEFAULTS["srcdir"]) if @use_srcdir && !@ast.key?("srcdir") @ast end |