1
0

refining complex formulas

This commit is contained in:
cryptogopher 2020-01-14 23:33:25 +01:00
parent 5fda938663
commit e0c08c0e6a
2 changed files with 106 additions and 27 deletions

View File

@ -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:

View File

@ -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)