refining complex formulas
This commit is contained in:
parent
5fda938663
commit
e0c08c0e6a
@ -37,7 +37,7 @@ en:
|
|||||||
unparsable_formula: 'cannot be parsed'
|
unparsable_formula: 'cannot be parsed'
|
||||||
disallowed_token: 'includes disallowed token "%{token}" (of type: %{ttype})'
|
disallowed_token: 'includes disallowed token "%{token}" (of type: %{ttype})'
|
||||||
disallowed_keyword: 'includes disallowed keyword: "%{keyword}"'
|
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}'
|
unknown_quantity: 'contains undefined quantity: %{quantity}'
|
||||||
body_trackers:
|
body_trackers:
|
||||||
index:
|
index:
|
||||||
|
131
test/ripper.rb
131
test/ripper.rb
@ -1,24 +1,45 @@
|
|||||||
require 'ripper'
|
require 'ripper'
|
||||||
|
require 'set'
|
||||||
require 'pp'
|
require 'pp'
|
||||||
require 'byebug'
|
require 'byebug'
|
||||||
|
|
||||||
class DemoBuilder < Ripper::SexpBuilder
|
class DemoBuilder < Ripper::SexpBuilder
|
||||||
def initialize(*args)
|
def initialize(*args)
|
||||||
super(*args)
|
super(*args)
|
||||||
@errors = []
|
@disallowed = Hash.new { |h,k| h[k] = Set.new }
|
||||||
@identifiers = []
|
@identifiers = Set.new
|
||||||
|
@paramed_formula = nil
|
||||||
@arguments = []
|
@arguments = []
|
||||||
end
|
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}
|
events = private_instance_methods(false).grep(/\Aon_/) {$'.to_sym}
|
||||||
(PARSER_EVENTS - events).each do |event|
|
(PARSER_EVENTS - events).each do |event|
|
||||||
module_eval(<<-End, __FILE__, __LINE__ + 1)
|
module_eval(<<-End, __FILE__, __LINE__ + 1)
|
||||||
def on_#{event}(*args)
|
def on_#{event}(*args)
|
||||||
super.tap { |result| p result }
|
@disallowed[:token] << [args[1], event]
|
||||||
end
|
end
|
||||||
End
|
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
|
def on_args_new
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
@ -37,10 +58,10 @@ class DemoBuilder < Ripper::SexpBuilder
|
|||||||
args.map do |arg|
|
args.map do |arg|
|
||||||
ttype, token = arg
|
ttype, token = arg
|
||||||
case ttype
|
case ttype
|
||||||
when :quantity
|
when :bt_quantity
|
||||||
"params['#{token}']"
|
"params['#{token}']"
|
||||||
when :indexed_expr
|
when :bt_expression
|
||||||
# FIXME: token expression has to be evaluated in block with _index and
|
# FIXME: 'token' expression has to be evaluated in block with _index and
|
||||||
# result stored in @arguments
|
# result stored in @arguments
|
||||||
@arguments << token
|
@arguments << token
|
||||||
"args['#{@arguments.length - 1}']"
|
"args['#{@arguments.length - 1}']"
|
||||||
@ -51,48 +72,105 @@ class DemoBuilder < Ripper::SexpBuilder
|
|||||||
")"
|
")"
|
||||||
end
|
end
|
||||||
|
|
||||||
#def on_call(left, dot, right)
|
def on_stmts_new
|
||||||
# "#{left}#{dot}#{right}"
|
[]
|
||||||
#end
|
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)
|
def on_method_add_arg(method, paren)
|
||||||
# "#{method}#{arg}"
|
[
|
||||||
#end
|
:bt_method_call,
|
||||||
|
"#{method[1]}#{paren}"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
def on_binary(left, op, right)
|
def on_binary(left, op, right)
|
||||||
[
|
[
|
||||||
:indexed_expr,
|
:bt_expression,
|
||||||
[left, right].map do |side|
|
[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.join(op.to_s)
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_var_ref(var_ref)
|
def on_var_ref(var_ref)
|
||||||
ttype, name = var_ref
|
var_ref[0] == :bt_quantity ? var_ref : raise(NotImplementedError)
|
||||||
if ttype == :quantity_name
|
|
||||||
[:quantity, name]
|
|
||||||
else
|
|
||||||
raise NotImplementedError
|
|
||||||
end
|
|
||||||
end
|
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)
|
module_eval(<<-End, __FILE__, __LINE__ + 1)
|
||||||
def on_#{event}(tok)
|
def on_#{event}(token)
|
||||||
super.tap { |result| p result }
|
@disallowed[:token] << [token, event]
|
||||||
#super
|
|
||||||
end
|
end
|
||||||
End
|
End
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_const(token)
|
def on_const(token)
|
||||||
@identifiers << token
|
@identifiers << token
|
||||||
[:quantity_name, token]
|
[:bt_quantity, token]
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_ident(token)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -100,4 +178,5 @@ end
|
|||||||
src = "(Weight/Height.all(3*Dupa).lastBefore(TakenAt)^2) + 2*Other*Other"
|
src = "(Weight/Height.all(3*Dupa).lastBefore(TakenAt)^2) + 2*Other*Other"
|
||||||
#src = "a = 2; b = a"
|
#src = "a = 2; b = a"
|
||||||
pp DemoBuilder.new(src).parse
|
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)
|
pp Ripper.sexp_raw(src)
|
||||||
|
Reference in New Issue
Block a user