From e0c08c0e6aed51560d9978ca3513e711c67d2fc5 Mon Sep 17 00:00:00 2001 From: cryptogopher Date: Tue, 14 Jan 2020 23:33:25 +0100 Subject: [PATCH] refining complex formulas --- config/locales/en.yml | 2 +- test/ripper.rb | 131 +++++++++++++++++++++++++++++++++--------- 2 files changed, 106 insertions(+), 27 deletions(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index 68b4938..4e8219a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -37,7 +37,7 @@ en: unparsable_formula: 'cannot be parsed' disallowed_token: 'includes disallowed token "%{token}" (of type: %{ttype})' disallowed_keyword: 'includes disallowed keyword: "%{keyword}"' - disallowed_function: 'includes disallowed function call: %{function}' + disallowed_method: 'includes disallowed method call: %{method}' unknown_quantity: 'contains undefined quantity: %{quantity}' body_trackers: index: diff --git a/test/ripper.rb b/test/ripper.rb index 44b643c..0bdf2c0 100644 --- a/test/ripper.rb +++ b/test/ripper.rb @@ -1,24 +1,45 @@ require 'ripper' +require 'set' require 'pp' require 'byebug' class DemoBuilder < Ripper::SexpBuilder def initialize(*args) super(*args) - @errors = [] - @identifiers = [] + @disallowed = Hash.new { |h,k| h[k] = Set.new } + @identifiers = Set.new + @paramed_formula = nil @arguments = [] end + def errors + @errors = [] + @disallowed[:token].each { |t, e| @errors << [:disallowed_token, {token: t, ttype: e}] } + @disallowed[:method].each { |f| @errors << [:disallowed_method, {method: f}] } + @disallowed[:keyword].each { |k| @errors << [:disallowed_keyword, {keyword: k}] } + @errors + end + + private + + METHODS = ['abs', 'nil?'] + events = private_instance_methods(false).grep(/\Aon_/) {$'.to_sym} (PARSER_EVENTS - events).each do |event| module_eval(<<-End, __FILE__, __LINE__ + 1) def on_#{event}(*args) - super.tap { |result| p result } + @disallowed[:token] << [args[1], event] end End end + def on_program(stmts) + puts @identifiers.inspect + @paramed_formula = join_stmts(stmts) + puts @paramed_formula + puts @arguments.inspect + end + def on_args_new [] end @@ -37,10 +58,10 @@ class DemoBuilder < Ripper::SexpBuilder args.map do |arg| ttype, token = arg case ttype - when :quantity + when :bt_quantity "params['#{token}']" - when :indexed_expr - # FIXME: token expression has to be evaluated in block with _index and + when :bt_expression + # FIXME: 'token' expression has to be evaluated in block with _index and # result stored in @arguments @arguments << token "args['#{@arguments.length - 1}']" @@ -51,48 +72,105 @@ class DemoBuilder < Ripper::SexpBuilder ")" end - #def on_call(left, dot, right) - # "#{left}#{dot}#{right}" - #end + def on_stmts_new + [] + end + + def on_stmts_add(stmts, new_stmt) + stmts << new_stmt + end + + def on_paren(stmts) + [ + :bt_expression, + "(" << join_stmts(stmts) << ")" + ] + end + + def on_call(left, dot, right) + [ + :bt_method_call, + case left[0] + when :bt_quantity + "params['#{left[1]}']" + when :bt_method_call + "#{left[1]}" + else + raise NotImplementedError + end << + dot.to_s << + case right[0] + when :bt_method + right[1] + else + raise NotImplementedError + end + ] + end + + def on_fcall(token) + @disallowed[:method] = token[1] + [:bt_method, token[1]] + end + + def on_vcall(token) + @identifiers << token[1] + [:bt_quantity, token[1]] + end - #def on_method_add_arg(method, arg) - # "#{method}#{arg}" - #end + def on_method_add_arg(method, paren) + [ + :bt_method_call, + "#{method[1]}#{paren}" + ] + end def on_binary(left, op, right) [ - :indexed_expr, + :bt_expression, [left, right].map do |side| - side[0] == :quantity ? "params['#{side[1]}'][_index]" : "#{side[1]}" + side[0] == :bt_quantity ? "params['#{side[1]}'][_index]" : "#{side[1]}" end.join(op.to_s) ] end def on_var_ref(var_ref) - ttype, name = var_ref - if ttype == :quantity_name - [:quantity, name] - else - raise NotImplementedError - end + var_ref[0] == :bt_quantity ? var_ref : raise(NotImplementedError) end - SCANNER_EVENTS.each do |event| + silenced_events = [:lparen, :rparen, :op, :period, :sp, :int, :float] + (SCANNER_EVENTS - silenced_events).each do |event| module_eval(<<-End, __FILE__, __LINE__ + 1) - def on_#{event}(tok) - super.tap { |result| p result } - #super + def on_#{event}(token) + @disallowed[:token] << [token, event] end End end def on_const(token) @identifiers << token - [:quantity_name, token] + [:bt_quantity, token] end def on_ident(token) - token + @disallowed[:method] << token unless METHODS.include?(token) + [:bt_method, token] + end + + def on_kw(token) + @disallowed[:keyword] << token unless token == 'nil' + end + + def join_stmts(stmts) + stmts.map do |stmt| + ttype, token = stmt + case ttype + when :bt_expression + token + else + raise NotImplementedError + end + end.join(';') end end @@ -100,4 +178,5 @@ end src = "(Weight/Height.all(3*Dupa).lastBefore(TakenAt)^2) + 2*Other*Other" #src = "a = 2; b = a" pp DemoBuilder.new(src).parse +puts "(params['Weight'][_index]/params['Height'].all(args['0']).lastBefore(params['TakenAt'])^2)+2*params['Other'][_index]*params['Other'][_index]" pp Ripper.sexp_raw(src)