-
Notifications
You must be signed in to change notification settings - Fork 0
/
string_formatter.rb
124 lines (96 loc) · 2.65 KB
/
string_formatter.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
require_relative 'StatementLexer'
require_relative 'StatementParser'
$lexer_cache = {}
$expression_cache = {}
$debug = false
class StringFormatter
def initialize(format_string)
@format_string = format_string
end
def pre_compute
create_expressions
self
end
def evaluate(arguments = nil)
argument_mapper = ArgumentMap.new(arguments)
formatted_value = ''
@expressions.each do |expression|
formatted_value += expression.evaluate(argument_mapper)
end
formatted_value
end
class << self
def find_or_create(format_string, arguments)
StringFormatter.new(format_string).pre_compute.evaluate(arguments)
end
end
private
def create_expressions
if $expression_cache[@format_string]
@expressions = $expression_cache[@format_string]
else
@expressions = []
index = 0
str = @format_string
loop do
data = str.match(/{{(?<statement>([^}]||}(?!}))*)}}/)
if data
index += data.begin(:statement)
@expressions << Expression.new(data.pre_match, data[:statement], index)
str = data.post_match
else
@expressions << Expression.new(str, nil, index)
break
end
end
$expression_cache[@format_string] = @expressions
end
end
end
class ArgumentMap
def initialize(arguments)
@arguments = arguments
end
def apply(value)
return value unless value
return value unless @arguments
@arguments.each_with_index do |argument, index|
value = value.to_s.gsub(/\$#{index + 1}/, argument.to_s)
end
value
end
def get_value(argument)
index = argument[1..-1].to_i - 1
$stderr.puts "parse error #{argument} not found" if $debug and @arguments.size <= index
@arguments[index]
end
end
class Expression
def initialize(pre_match, statement, index)
@pre_match = pre_match
@statement = statement
@index = index
end
def evaluate(argument_mapper)
$argument_mapper = argument_mapper
parsed_statement = @statement
unless parsed_statement.nil? || parsed_statement.strip.nil?
$stderr.puts "parse #{@index}:#{@index + parsed_statement.size}" if $debug
lexer = create_or_find_lexer(parsed_statement)
parser = Statement::Parser.new(lexer)
parsed_statement = parser.program.value
end
"#{argument_mapper.apply(@pre_match)}#{argument_mapper.apply(parsed_statement)}"
end
private
def create_or_find_lexer(statement)
if $lexer_cache[statement]
lexer = $lexer_cache[statement]
else
lexer = Statement::Lexer.new(statement)
$lexer_cache[statement] = lexer
end
lexer.reset
lexer
end
end