require 'erb' require_relative '../builder' require_relative './javascript_templates' module Shift module Generator class JavaScriptGenerator attr_accessor :blocks, :functions, :builder, :url_variables, :has_models, :defined_hashes def initialize() @blocks = [] @defined_hashes = [] @functions = [] @has_models = false @builder = Shift::ShiftBuilder.new end def get_data(builder) @builder = builder end def get_data_from_file(file_name) @builder.get_data_from_file file_name end def get_import_statements() Shift::Generator::JavaScriptTemplates::IMPORT_JQNODE_STATEMENT + Shift::Generator::JavaScriptTemplates::IMPORT_UTIL_STATEMENT + Shift::Generator::JavaScriptTemplates::IMPORT_MU_STATEMENT + (@has_models ? Shift::Generator::JavaScriptTemplates::IMPORT_DB_STATEMENT : "") + Shift::Generator::JavaScriptTemplates::UTILITY_FUNCTIONS + "\n" end def get_model_data() result = "" model_name_template = ERB.new(Shift::Generator::JavaScriptTemplates::MODEL_NAME_TEMPLATE) @builder.models.each do | model | model_name = model.name result += model_name_template.result(binding) + "\n" first = true # TODO: Test if comma syntax is valid model.attributes.each do | attribute | if (attribute[:type] == "string") if (first == true) first = false else result += "," end result += ("\t" + attribute[:name] + " : " + "String" + "\n") else if (attribute[:type] == "integer") if (first == true) first = false else result += "," end result += ("\t" + attribute[:name] + " : " + "Number" + "\n") end end end result += "});\n\n" end result end def url_transform(url) if(url.is_a?(Array)) result_url = "" url.each do | token | case token.keys[0] when :url_token result_url += "/" + token[:url_token].to_s when :url_variable result_url += "/:" + token[:url_variable].to_s end end result_url else url.to_s end end def expression_transform(statement, num_tabs) tabs = "" num_tabs.times { tabs += "\t" } key = statement.keys[0] case key when :assignment_statement extra = "" if(statement[:assignment_statement][:left].keys[0] == :hash_value) val = statement[:assignment_statement][:left][:hash_value].to_s hash_var = val[0..(val.index('[') - 1)] if !@defined_hashes.include?(hash_var) @defined_hashes.push hash_var extra += (tabs + "if (typeof " + hash_var + " == 'undefined')\n") extra += (tabs + "\t" + hash_var + " = {}\n") end end left = apply_transforms(statement[:assignment_statement][:left], 0) right = apply_transforms(statement[:assignment_statement][:right], 0) if(statement[:assignment_statement][:left].keys[0] == :object_value) value_name = statement[:assignment_statement][:left][:object_value].to_s value_name = value_name[(value_name.index('.') + 1)..(value_name.length - 1)] end if(statement[:assignment_statement][:right].is_a?(Hash) && statement[:assignment_statement][:right].keys[0] == :query) case statement[:assignment_statement][:right][:query][:operation] when "all" @functions.push (tabs + "});") tabs + right.to_s + left.to_s + ") {" when "new" tabs + "var " + left.to_s + " = " + right.to_s when "find" @functions.push (tabs + "});") tabs + right.to_s + left.to_s + ") {" when "find_by_id" @functions.push (tabs + "});") tabs + right.to_s + left.to_s + ") {" end else extra + tabs + left.to_s + " = " + right.to_s + ";" end else "" end end def token_transform(statement, num_tabs) tabs = "" num_tabs.times { tabs += "\t" } key = statement.keys[0] case key when :identifier result = statement[:identifier].to_s @url_variables.include?(result) ? "urlData['" + result + "']" : result when :object_value if (statement[:object_value].to_s.split(".")[1] == "save") tabs + statement[:object_value].to_s.split(".")[0] + ".save();" + "\n" else tabs + statement[:object_value].to_s end when :hash_value statement[:hash_value].to_s when :string statement[:string].to_s else "" end end def write_function_transform(statement, num_tabs) tabs = "" num_tabs.times { tabs += "\t" } write_data_template = ERB.new(Shift::Generator::JavaScriptTemplates::WRITE_DATA_TEMPLATE) write_data = apply_transforms(statement[:write_statement][:write_statement_param], 0) tabs + write_data_template.result(binding) end def write_file_function_transform(statement, num_tabs) tabs = "" num_tabs.times { tabs += "\t" } write_file_template = ERB.new(Shift::Generator::JavaScriptTemplates::WRITE_FILE_TEMPLATE) if(statement[:write_file_statement].is_a?(Array)) file_name = apply_transforms(statement[:write_file_statement][0][:file_name], 0) template_values = apply_transforms(statement[:write_file_statement][1][:template_values], 0) args = file_name + ', ' + template_values + ", response" else file_name = apply_transforms(statement[:write_file_statement][:file_name], 0) args = file_name + ', false' end tabs + write_file_template.result(binding) end def query_transform(statement, num_tabs) tabs = "" num_tabs.times { tabs += "\t" } statement = statement[:query] case statement[:operation].to_s when "all" statement[:model_name].to_s + ".all(function (err, " when "new" (statement[:model_name].to_s + "()") when "find" result = statement[:model_name].to_s + ".all({ where : " condition = statement[:condition] condition_attribute = apply_transforms(condition[:value], 0) condition_expression = "" case statement[:condition][:operator].to_s.gsub!(" ", "") when "<=" condition_expression = "{lte : " + condition_attribute + "}" else puts statement[:operator].to_s.gsub!(" ", "") end result += "{ " + condition[:attribute] + " : " + condition_expression + " }" result += " }, function (err, " when "find_by_id" result = statement[:model_name].to_s + ".find(" result += apply_transforms(statement[:item_id], 0) result += ", function(err, " when "update" @functions.push (tabs + "});") result = tabs + statement[:model_name].to_s + ".updateAttributes({}, function() {" when "delete" @functions.push (tabs + "});") result = tabs + statement[:model_name].to_s + ".destroy(function() {" end end def read_statement_transform(statement, num_tabs) param = apply_transforms(statement[:read_statement][:read_statement_param], 0) "data[" + param + "]" end def redirect_statement_transform(statement, num_tabs) tabs = "" num_tabs.times { tabs += "\t" } redirect_statement_template = ERB.new(Shift::Generator::JavaScriptTemplates::REDIRECT_STATEMENT_TEMPLATE) url = apply_transforms(statement[:redirect_statement][:redirect_statement_param], 0) "\n" + tabs + redirect_statement_template.result(binding) end def value_transform(statement, num_tabs) tabs = "" num_tabs.times { tabs += "\t" } key = statement.keys[0] statement[key].to_s end def control_statement_transform(statement, num_tabs) tabs = "" num_tabs.times { tabs += "\t" } key = statement.keys[0] result = "" @blocks.push (num_tabs) case key when :if_statement result = tabs + "if " + apply_transforms(statement[:if_statement][:condition], 0) + " {" when :while_statement result = tabs + "while " + apply_transforms(statement[:while_statement][:condition], 0) + " {" end result end def concatenated_string_expression_transform(statement, num_tabs) statement = statement.map do | token | apply_transforms(token, 0) end statement.join(" + ") end def integer_casted_expression_transform(statement, num_tabs) statement.delete(:int) "Number(" + apply_transforms(statement, 0) + ")" end def apply_transforms(statement, num_tabs) tabs = "" if (num_tabs > 0) num_tabs = num_tabs - 1 end result = "" if(@blocks.length > 0) if(@blocks[@blocks.length - 1] == num_tabs) t = "" @blocks.pop.times { t += "\t" } result += t + "}\n" end end num_tabs.times { tabs += "\t" } if(statement.is_a?(Hash)) key = statement.keys[0] case key when :assignment_statement result += expression_transform(statement, num_tabs) + "\n" when :write_statement result += write_function_transform(statement, num_tabs) + "\n" when :write_file_statement result += write_file_function_transform(statement, num_tabs) + "\n" when :read_statement result += read_statement_transform(statement, num_tabs) when :redirect_statement result += redirect_statement_transform(statement, num_tabs) + "\n" when :arithmetic_expression result += value_transform(statement, num_tabs) when :boolean_expression result += value_transform(statement, num_tabs) when :if_statement result += control_statement_transform(statement, num_tabs) + "\n" when :else_statement @blocks.push (num_tabs) result += tabs + value_transform(statement, num_tabs) + " {\n" when :for_statement statement = statement[:for_statement] tokens = statement.to_s.split(" ") @blocks.push (num_tabs) result += tabs + "for(var k in " + tokens[3] + ") {\n" result += tabs + "\t" + tokens[1] + " = " + tokens[3] + "[k];\n" when :while_statement result += control_statement_transform(statement, num_tabs) + "\n" when :query result += query_transform(statement, num_tabs) when :int integer_casted_expression_transform(statement, num_tabs) else result += token_transform(statement, num_tabs) end else if statement.is_a?(Array) concatenated_string_expression_transform(statement, num_tabs) else result += statement.to_s end end end def write_data() if(@builder.models.length != 0) @has_models = true end result = get_import_statements + get_model_data @builder.handlers.each do | handlers | url = url_transform(handlers.url) method = handlers.method @url_variables = [] current_url = handlers.url if(current_url.is_a?(Array)) current_url.each do | token | case token.keys[0] when :url_variable @url_variables.push token[:url_variable] end end end url_handler_template = ERB.new(Shift::Generator::JavaScriptTemplates::URL_HANDLER_DEFINITION) result += "\n" + url_handler_template.result(binding) + "\n" defined_hashes = [] handlers.handler.each do | statement | result += apply_transforms(statement[:statement], statement[:num_tabs] + functions.length) end i = @functions.length - 2 blocks.each do | t | tabs = "" t.times { tabs += "\t" } result += tabs + "}" + "\n" end functions.reverse.each do | closing_block | tabs = "" i.times { tabs += "\t" } result += tabs + closing_block + "\n" end @functions = [] @blocks = [] result += "});\n" end result += "\n\n" + Shift::Generator::JavaScriptTemplates::APP_START_STATEMENT end end end end