| Class | Liftoff::Parser |
| In: |
vendor/plugins/liftoff/lib/parser.rb
|
| Parent: | Object |
# File vendor/plugins/liftoff/lib/parser.rb, line 35
35: def initialize(calfile, modeldir)
36: @log = Logger.new(STDOUT);
37: @log.level = Logger::ERROR;
38: @log.debug "\n\nCAL file: " + File.expand_path(calfile)
39: @log.debug "Model path: " + File.expand_path(modeldir)
40: run(calfile, modeldir)
41: end
# File vendor/plugins/liftoff/lib/parser.rb, line 53
53: def parse_associations(calfile)
54: by_from = {};
55: by_to = {};
56: line_number = 0;
57: calfile.each_line do |line|
58: line_number += 1;
59:
60: # First, strip trailing newlines and comments.
61: line = line.chomp;
62: @log.debug "\nReading line " + line_number.to_s + ": " + line;
63:
64: arr = line.strip.split(%r{#+})
65:
66: if arr.length == 0
67: @log.debug "Ignoring blank line or empty comment..."
68: next
69: end
70:
71: # Practice for a future implementation that might interpret these comment fields.
72: if arr[0].length == 0
73: @log.debug("Ignoring line comment: " + "\"" + arr[1].strip + "\"" ) if arr[1] != nil && arr[1].length > 0
74: next
75: end
76: if arr[1] != nil && arr[1].length > 0
77: @log.debug "Ignoring side comment: " + "\"" + arr[1].strip + "\""
78: end
79:
80: # If we get this far, then the current line contains non-comment elements.
81: line = arr[0].rstrip
82: @log.debug "Found possible CAL chain: " + line
83: @log.debug("Tokenizing ...");
84:
85: # Each element is either a word (table) or an operator.
86: arr=line.scan(%r{\w+|<>|<=|=>|<|>});
87:
88: if arr.length < 3
89: @log.error "Syntax error at line " + line_number.to_s + ": line is too short or does not contain a valid operator.";
90: exit(1);
91: end
92:
93: @log.debug "token array = " + arr.inspect;
94:
95: # Parse the token array into a set of associations by visiting each operator
96: # in left to right order. Because operators correspond to odd-numbered elements,
97: # the index is initialized to 1 and incremented by 2 at the end of each cycle.
98: index = 1;
99: while index < arr.length
100: @log.debug "operator element = " + index.to_s;
101:
102: # This step is critical. The operator determines the type of
103: # the association while left and right specify the tables to which
104: # it applies.
105: left, op, right = arr[index-1 .. index+1];
106: if op == nil || right == nil
107: @log.error "Syntax error at line " + line_number.to_s + ": " + "missing token."
108: exit(1)
109: end
110:
111: sides = [left, right].map {|side| side.downcase.singularize}
112:
113:
114: # Construct the association corresponding to the operator. The first
115: # parameter to the constructor corresponds to the "from" side of the
116: # relationship, whereas the second parameter corresponds to the "to"
117: # side. Many-to-one and "right-has-one" associations are canonicalized
118: # as One-to-many and "left-has-one", respectively, by reversing the
119: # arguments to the constructor.
120: result = case op
121: when "<"
122: OneToMany.new(*sides)
123: when ">"
124: OneToMany.new(*sides.reverse)
125: when "=>"
126: LeftHasOne.new(*sides)
127: when "<="
128: LeftHasOne.new(*sides.reverse)
129: when "<>"
130: ManyToMany.new(*sides)
131: else
132: @log.error "Parse error at line #{line_number.to_s}: unrecognized operator: #{op}.";
133: exit(1);
134: end # case
135:
136: # Add each side of the relationship to the corresponding hash table.
137: # This will allow the model generator to compute all of the
138: # associations for a given model file by combining the by_from and
139: # by_to arrays of values that share the same model/table name as a
140: # common key.
141: @log.debug "Indexing association by 'from' side: " + result.from + "<=" + [result].to_s;
142: if ! by_from[result.from]
143: by_from[result.from] = [result];
144: else
145: by_from[result.from].push(result);
146: end
147: @log.debug "Indexing association by 'to' side: " + result.to + "<=" + [result].to_s;
148: if ! by_to[result.to]
149: by_to[result.to] = [result];
150: else
151: by_to[result.to].push(result);
152: end
153: index += 2;
154: end
155: end
156: return by_from, by_to;
157: end