1 //
2 // aegis - project change supervisor
3 // Copyright (C) 1998, 1999, 2001, 2002, 2004-2008, 2012 Peter Miller
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published
7 // by the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 //
18 
19 #include <common/ac/assert.h>
20 
21 #include <common/mem.h>
22 #include <common/regula_expre.h>
23 #include <common/trace.h>
24 #include <libaegis/aer/expr.h>
25 #include <libaegis/aer/func/substitute.h>
26 #include <libaegis/aer/value/error.h>
27 #include <libaegis/aer/value/integer.h>
28 #include <libaegis/aer/value/string.h>
29 #include <libaegis/sub.h>
30 
31 
~rpt_func_substitute()32 rpt_func_substitute::~rpt_func_substitute()
33 {
34 }
35 
36 
rpt_func_substitute()37 rpt_func_substitute::rpt_func_substitute()
38 {
39 }
40 
41 
42 rpt_func::pointer
create()43 rpt_func_substitute::create()
44 {
45     return pointer(new rpt_func_substitute());
46 }
47 
48 
49 const char *
name() const50 rpt_func_substitute::name()
51     const
52 {
53     return "subst";
54 }
55 
56 
57 bool
optimizable() const58 rpt_func_substitute::optimizable()
59     const
60 {
61     return true;
62 }
63 
64 
65 bool
verify(const rpt_expr::pointer & ep) const66 rpt_func_substitute::verify(const rpt_expr::pointer &ep)
67     const
68 {
69     return (ep->get_nchildren() == 3 || ep->get_nchildren() == 4);
70 }
71 
72 
73 rpt_value::pointer
run(const rpt_expr::pointer & ep,size_t argc,rpt_value::pointer * argv) const74 rpt_func_substitute::run(const rpt_expr::pointer &ep, size_t argc,
75     rpt_value::pointer *argv) const
76 {
77     trace(("rpt_func_substitute::run()\n"));
78     //
79     // Get the match pattern.
80     //
81     rpt_value::pointer arg = argv[0];
82     assert(!arg->is_an_error());
83     arg = rpt_value::stringize(arg);
84     rpt_value_string *s0p = dynamic_cast<rpt_value_string *>(arg.get());
85     if (!s0p)
86     {
87         sub_context_ty sc(__FILE__, __LINE__);
88         sc.var_set_charstar("Function", "subst");
89         sc.var_set_long("Number", 1);
90         sc.var_set_charstar("Name", argv[0]->name());
91         nstring s
92         (
93             sc.subst_intl
94             (
95                 i18n("$function: argument $number: string value required "
96                     "(was given $name)")
97             )
98         );
99         return rpt_value_error::create(ep->get_pos(), s);
100     }
101     nstring lhs(s0p->query());
102 
103     //
104     // Get the replacement pattern.
105     //
106     arg = argv[1];
107     assert(!arg->is_an_error());
108     arg = rpt_value::stringize(arg);
109     rpt_value_string *s1p = dynamic_cast<rpt_value_string *>(arg.get());
110     if (!s1p)
111     {
112         sub_context_ty sc(__FILE__, __LINE__);
113         sc.var_set_charstar("Function", "subst");
114         sc.var_set_long("Number", 2);
115         sc.var_set_charstar("Name", argv[1]->name());
116         nstring s
117         (
118             sc.subst_intl
119             (
120                 i18n("$function: argument $number: string value required "
121                     "(was given $name)")
122             )
123         );
124         return rpt_value_error::create(ep->get_pos(), s);
125     }
126     nstring rhs(s1p->query());
127 
128     //
129     // Get the string to be worked over.
130     //
131     arg = argv[2];
132     assert(!arg->is_an_error());
133     arg = rpt_value::stringize(arg);
134     rpt_value_string *s2p = dynamic_cast<rpt_value_string *>(arg.get());
135     if (!s2p)
136     {
137         sub_context_ty sc(__FILE__, __LINE__);
138         sc.var_set_charstar("Function", "subst");
139         sc.var_set_long("Number", 3);
140         sc.var_set_charstar("Name", argv[2]->name());
141         nstring s
142         (
143             sc.subst_intl
144             (
145                 i18n("$function: argument $number: string value required "
146                     "(was given $name)")
147             )
148         );
149         return rpt_value_error::create(ep->get_pos(), s);
150     }
151     nstring query_input(s2p->query());
152 
153     //
154     // Get the count of how many times to match.
155     //
156     long maximum_matches = 0;
157     if (argc >= 4)
158     {
159         arg = argv[3];
160         assert(!arg->is_an_error());
161         arg = rpt_value::integerize(arg);
162         rpt_value_integer *rip = dynamic_cast<rpt_value_integer *>(arg.get());
163         if (!rip)
164         {
165             sub_context_ty sc(__FILE__, __LINE__);
166             sc.var_set_charstar("Function", "subst");
167             sc.var_set_long("Number", 2);
168             sc.var_set_charstar("Name", argv[3]->name());
169             nstring s
170             (
171                 sc.subst_intl
172                 (
173                     i18n("$function: argument $number: integer value "
174                         "required (was given $name)")
175                 )
176             );
177             return rpt_value_error::create(ep->get_pos(), s);
178         }
179         maximum_matches = rip->query();
180     }
181 
182     //
183     // perform the substitution
184     //
185     regular_expression re(lhs);
186     nstring result;
187     if (!re.match_and_substitute(rhs, query_input, maximum_matches, result))
188     {
189         //
190         // Error... probably the LHS pattern was erroneous, but it could
191         // have been the RHS pattern.
192         //
193         // Return an error result.
194         //
195         sub_context_ty sc(__FILE__, __LINE__);
196         sc.var_set_charstar("Function", "subst");
197         sc.var_set_long("Number", 1);
198         sc.var_set_charstar("MeSsaGe", re.strerror());
199         nstring s(sc.subst_intl(i18n("$function: argument $number: $message")));
200         return rpt_value_error::create(ep->get_pos(), s);
201     }
202 
203     //
204     // build the result
205     //
206     trace_nstring(result);
207     return rpt_value_string::create(result);
208 }
209 
210 
211 // vim: set ts=8 sw=4 et :
212