1use crate::DslResult;
7use crate::ast::{ConditionNode, ConditionValue, LegalDocument, StatuteNode, TemporalField};
8use std::fmt::Write;
9
10fn temporal_field_to_string(field: &TemporalField) -> String {
12 match field {
13 TemporalField::CurrentDate => "current_date".to_string(),
14 TemporalField::DateField(name) => name.clone(),
15 }
16}
17
18pub trait CodeGenerator {
20 fn generate(&self, doc: &LegalDocument) -> DslResult<String>;
22
23 fn target_language(&self) -> &str;
25
26 fn file_extension(&self) -> &str;
28}
29
30pub struct SqlGenerator {
32 pub use_check_constraints: bool,
34 pub generate_defaults: bool,
36}
37
38impl Default for SqlGenerator {
39 fn default() -> Self {
40 Self {
41 use_check_constraints: true,
42 generate_defaults: true,
43 }
44 }
45}
46
47impl SqlGenerator {
48 pub fn new() -> Self {
50 Self::default()
51 }
52
53 fn table_name(&self, id: &str) -> String {
55 id.replace('-', "_")
56 }
57
58 fn generate_condition(&self, condition: &ConditionNode) -> DslResult<String> {
60 match condition {
61 ConditionNode::Comparison {
62 field,
63 operator,
64 value,
65 } => {
66 let val = self.format_value(value)?;
67 Ok(format!("{} {} {}", field, operator, val))
68 }
69 ConditionNode::HasAttribute { key } => Ok(format!("{} IS NOT NULL", key)),
70 ConditionNode::Between { field, min, max } => {
71 let min_val = self.format_value(min)?;
72 let max_val = self.format_value(max)?;
73 Ok(format!("{} BETWEEN {} AND {}", field, min_val, max_val))
74 }
75 ConditionNode::In { field, values } => {
76 let vals: Result<Vec<_>, _> = values.iter().map(|v| self.format_value(v)).collect();
77 let vals = vals?;
78 Ok(format!("{} IN ({})", field, vals.join(", ")))
79 }
80 ConditionNode::Like { field, pattern } => Ok(format!("{} LIKE '{}'", field, pattern)),
81 ConditionNode::And(left, right) => {
82 let left_sql = self.generate_condition(left)?;
83 let right_sql = self.generate_condition(right)?;
84 Ok(format!("({} AND {})", left_sql, right_sql))
85 }
86 ConditionNode::Or(left, right) => {
87 let left_sql = self.generate_condition(left)?;
88 let right_sql = self.generate_condition(right)?;
89 Ok(format!("({} OR {})", left_sql, right_sql))
90 }
91 ConditionNode::Not(inner) => {
92 let inner_sql = self.generate_condition(inner)?;
93 Ok(format!("NOT ({})", inner_sql))
94 }
95 ConditionNode::InRange {
96 field,
97 min,
98 max,
99 inclusive_min,
100 inclusive_max,
101 } => {
102 let min_val = self.format_value(min)?;
103 let max_val = self.format_value(max)?;
104 let min_op = if *inclusive_min { ">=" } else { ">" };
105 let max_op = if *inclusive_max { "<=" } else { "<" };
106 Ok(format!(
107 "({} {} {} AND {} {} {})",
108 field, min_op, min_val, field, max_op, max_val
109 ))
110 }
111 ConditionNode::NotInRange {
112 field,
113 min,
114 max,
115 inclusive_min,
116 inclusive_max,
117 } => {
118 let min_val = self.format_value(min)?;
119 let max_val = self.format_value(max)?;
120 let min_op = if *inclusive_min { "<" } else { "<=" };
121 let max_op = if *inclusive_max { ">" } else { ">=" };
122 Ok(format!(
123 "({} {} {} OR {} {} {})",
124 field, min_op, min_val, field, max_op, max_val
125 ))
126 }
127 ConditionNode::Matches {
128 field,
129 regex_pattern: _,
130 } => {
131 Ok(format!("{} LIKE '%'", field))
133 }
134 ConditionNode::TemporalComparison {
135 field,
136 operator,
137 value,
138 } => {
139 let val = self.format_value(value)?;
140 Ok(format!("{:?} {} {}", field, operator, val))
141 }
142 }
143 }
144
145 fn format_value(&self, value: &ConditionValue) -> DslResult<String> {
147 match value {
148 ConditionValue::Number(n) => Ok(n.to_string()),
149 ConditionValue::String(s) => Ok(format!("'{}'", s.replace('\'', "''"))),
150 ConditionValue::Boolean(b) => Ok(if *b {
151 "TRUE".to_string()
152 } else {
153 "FALSE".to_string()
154 }),
155 ConditionValue::Date(d) => Ok(format!("'{}'", d)),
156 ConditionValue::SetExpr(_) => Ok("NULL".to_string()), }
158 }
159
160 fn generate_table(&self, statute: &StatuteNode) -> DslResult<String> {
162 let mut sql = String::new();
163 let table_name = self.table_name(&statute.id);
164
165 writeln!(&mut sql, "-- Statute: {}", statute.title).unwrap();
166 writeln!(&mut sql, "CREATE TABLE {} (", table_name).unwrap();
167 writeln!(&mut sql, " id SERIAL PRIMARY KEY,").unwrap();
168
169 let mut fields = std::collections::HashSet::new();
171 for condition in &statute.conditions {
172 self.extract_fields(condition, &mut fields);
173 }
174
175 for field in &fields {
176 writeln!(&mut sql, " {} VARCHAR(255),", field).unwrap();
177 }
178
179 writeln!(&mut sql, " applied BOOLEAN DEFAULT FALSE,").unwrap();
181 writeln!(&mut sql, " applied_at TIMESTAMP").unwrap();
182
183 if self.use_check_constraints && !statute.conditions.is_empty() {
185 write!(&mut sql, " CONSTRAINT check_{} CHECK (", table_name).unwrap();
186 let conditions: Result<Vec<_>, _> = statute
187 .conditions
188 .iter()
189 .map(|c| self.generate_condition(c))
190 .collect();
191 let conditions = conditions?;
192 write!(&mut sql, "{}", conditions.join(" AND ")).unwrap();
193 writeln!(&mut sql, ")").unwrap();
194 }
195
196 writeln!(&mut sql, ");").unwrap();
197 writeln!(&mut sql).unwrap();
198
199 Ok(sql)
200 }
201
202 #[allow(clippy::only_used_in_recursion)]
204 fn extract_fields(
205 &self,
206 condition: &ConditionNode,
207 fields: &mut std::collections::HashSet<String>,
208 ) {
209 match condition {
210 ConditionNode::Comparison { field, .. }
211 | ConditionNode::Between { field, .. }
212 | ConditionNode::In { field, .. }
213 | ConditionNode::Like { field, .. }
214 | ConditionNode::Matches { field, .. }
215 | ConditionNode::InRange { field, .. }
216 | ConditionNode::NotInRange { field, .. } => {
217 fields.insert(field.clone());
218 }
219 ConditionNode::HasAttribute { key } => {
220 fields.insert(key.clone());
221 }
222 ConditionNode::And(left, right) | ConditionNode::Or(left, right) => {
223 self.extract_fields(left, fields);
224 self.extract_fields(right, fields);
225 }
226 ConditionNode::Not(inner) => {
227 self.extract_fields(inner, fields);
228 }
229 ConditionNode::TemporalComparison { .. } => {
230 }
232 }
233 }
234}
235
236impl CodeGenerator for SqlGenerator {
237 fn generate(&self, doc: &LegalDocument) -> DslResult<String> {
238 let mut sql = String::new();
239
240 writeln!(&mut sql, "-- Generated SQL from Legal DSL").unwrap();
241 writeln!(&mut sql, "-- Total statutes: {}", doc.statutes.len()).unwrap();
242 writeln!(&mut sql).unwrap();
243
244 for statute in &doc.statutes {
245 let table_sql = self.generate_table(statute)?;
246 sql.push_str(&table_sql);
247 }
248
249 Ok(sql)
250 }
251
252 fn target_language(&self) -> &str {
253 "SQL"
254 }
255
256 fn file_extension(&self) -> &str {
257 "sql"
258 }
259}
260
261pub struct PythonGenerator {
263 pub use_type_hints: bool,
265 pub generate_docstrings: bool,
267}
268
269impl Default for PythonGenerator {
270 fn default() -> Self {
271 Self {
272 use_type_hints: true,
273 generate_docstrings: true,
274 }
275 }
276}
277
278impl PythonGenerator {
279 pub fn new() -> Self {
281 Self::default()
282 }
283
284 fn function_name(&self, id: &str) -> String {
286 id.replace('-', "_").to_lowercase()
287 }
288
289 #[allow(clippy::only_used_in_recursion)]
291 fn generate_condition(&self, condition: &ConditionNode, indent: usize) -> DslResult<String> {
292 match condition {
293 ConditionNode::Comparison {
294 field,
295 operator,
296 value,
297 } => {
298 let py_op = match operator.as_str() {
299 "=" | "==" => "==",
300 op => op,
301 };
302 let val = self.format_value(value)?;
303 Ok(format!("{}.{} {} {}", "obj", field, py_op, val))
304 }
305 ConditionNode::HasAttribute { key } => Ok(format!(
306 "hasattr(obj, '{}') and obj.{} is not None",
307 key, key
308 )),
309 ConditionNode::Between { field, min, max } => {
310 let min_val = self.format_value(min)?;
311 let max_val = self.format_value(max)?;
312 Ok(format!("{} <= obj.{} <= {}", min_val, field, max_val))
313 }
314 ConditionNode::In { field, values } => {
315 let vals: Result<Vec<_>, _> = values.iter().map(|v| self.format_value(v)).collect();
316 let vals = vals?;
317 Ok(format!("obj.{} in [{}]", field, vals.join(", ")))
318 }
319 ConditionNode::And(left, right) => {
320 let left_py = self.generate_condition(left, indent)?;
321 let right_py = self.generate_condition(right, indent)?;
322 Ok(format!("({} and {})", left_py, right_py))
323 }
324 ConditionNode::Or(left, right) => {
325 let left_py = self.generate_condition(left, indent)?;
326 let right_py = self.generate_condition(right, indent)?;
327 Ok(format!("({} or {})", left_py, right_py))
328 }
329 ConditionNode::Not(inner) => {
330 let inner_py = self.generate_condition(inner, indent)?;
331 Ok(format!("not ({})", inner_py))
332 }
333 _ => Ok("True".to_string()),
334 }
335 }
336
337 fn format_value(&self, value: &ConditionValue) -> DslResult<String> {
339 match value {
340 ConditionValue::Number(n) => Ok(n.to_string()),
341 ConditionValue::String(s) => Ok(format!("'{}'", s.replace('\'', "\\'"))),
342 ConditionValue::Boolean(b) => Ok(if *b {
343 "True".to_string()
344 } else {
345 "False".to_string()
346 }),
347 ConditionValue::Date(d) => Ok(format!("'{}'", d)),
348 ConditionValue::SetExpr(_) => Ok("None".to_string()), }
350 }
351
352 fn generate_function(&self, statute: &StatuteNode) -> DslResult<String> {
354 let mut py = String::new();
355 let func_name = self.function_name(&statute.id);
356
357 if self.use_type_hints {
359 writeln!(&mut py, "def {}(obj: Any) -> bool:", func_name).unwrap();
360 } else {
361 writeln!(&mut py, "def {}(obj):", func_name).unwrap();
362 }
363
364 if self.generate_docstrings {
366 writeln!(&mut py, " \"\"\"{}\"\"\"", statute.title).unwrap();
367 }
368
369 if !statute.conditions.is_empty() {
371 let conditions: Result<Vec<_>, _> = statute
372 .conditions
373 .iter()
374 .map(|c| self.generate_condition(c, 1))
375 .collect();
376 let conditions = conditions?;
377 writeln!(&mut py, " return {}", conditions.join(" and ")).unwrap();
378 } else {
379 writeln!(&mut py, " return True").unwrap();
380 }
381
382 writeln!(&mut py).unwrap();
383 Ok(py)
384 }
385}
386
387impl CodeGenerator for PythonGenerator {
388 fn generate(&self, doc: &LegalDocument) -> DslResult<String> {
389 let mut py = String::new();
390
391 writeln!(&mut py, "# Generated Python from Legal DSL").unwrap();
392 writeln!(&mut py, "# Total statutes: {}", doc.statutes.len()).unwrap();
393 writeln!(&mut py, "from typing import Any").unwrap();
394 writeln!(&mut py).unwrap();
395
396 for statute in &doc.statutes {
397 let func_py = self.generate_function(statute)?;
398 py.push_str(&func_py);
399 }
400
401 Ok(py)
402 }
403
404 fn target_language(&self) -> &str {
405 "Python"
406 }
407
408 fn file_extension(&self) -> &str {
409 "py"
410 }
411}
412
413pub struct PrologGenerator {
415 pub generate_module: bool,
417 pub use_dynamic: bool,
419}
420
421impl Default for PrologGenerator {
422 fn default() -> Self {
423 Self {
424 generate_module: true,
425 use_dynamic: false,
426 }
427 }
428}
429
430impl PrologGenerator {
431 pub fn new() -> Self {
433 Self::default()
434 }
435
436 fn predicate_name(&self, id: &str) -> String {
438 id.replace('-', "_").to_lowercase()
439 }
440
441 #[allow(clippy::only_used_in_recursion)]
443 fn generate_condition(&self, condition: &ConditionNode, var: &str) -> DslResult<String> {
444 match condition {
445 ConditionNode::Comparison {
446 field,
447 operator,
448 value,
449 } => {
450 let pl_op = match operator.as_str() {
451 "=" | "==" => "=",
452 "!=" => "\\=",
453 ">=" => ">=",
454 "<=" => "=<", ">" => ">",
456 "<" => "<",
457 _ => "=",
458 };
459 let val = self.format_value(value)?;
460 Ok(format!("{}_{} {} {}", var, field, pl_op, val))
461 }
462 ConditionNode::HasAttribute { key } => Ok(format!(
463 "nonvar({}_{}) , {}_{} \\= null",
464 var, key, var, key
465 )),
466 ConditionNode::Between { field, min, max } => {
467 let min_val = self.format_value(min)?;
468 let max_val = self.format_value(max)?;
469 Ok(format!(
470 "{}_{} >= {} , {}_{} =< {}",
471 var, field, min_val, var, field, max_val
472 ))
473 }
474 ConditionNode::In { field, values } => {
475 let vals: Result<Vec<_>, _> = values.iter().map(|v| self.format_value(v)).collect();
476 let vals = vals?;
477 Ok(format!("member({}_{}, [{}])", var, field, vals.join(", ")))
478 }
479 ConditionNode::Like { field, pattern } => {
480 Ok(format!(
482 "atom_string({}_{}, Str), sub_string(Str, _, _, _, \"{}\")",
483 var, field, pattern
484 ))
485 }
486 ConditionNode::And(left, right) => {
487 let left_pl = self.generate_condition(left, var)?;
488 let right_pl = self.generate_condition(right, var)?;
489 Ok(format!("({} , {})", left_pl, right_pl))
490 }
491 ConditionNode::Or(left, right) => {
492 let left_pl = self.generate_condition(left, var)?;
493 let right_pl = self.generate_condition(right, var)?;
494 Ok(format!("({} ; {})", left_pl, right_pl))
495 }
496 ConditionNode::Not(inner) => {
497 let inner_pl = self.generate_condition(inner, var)?;
498 Ok(format!("\\+ ({})", inner_pl))
499 }
500 ConditionNode::InRange {
501 field,
502 min,
503 max,
504 inclusive_min,
505 inclusive_max,
506 } => {
507 let min_val = self.format_value(min)?;
508 let max_val = self.format_value(max)?;
509 let min_op = if *inclusive_min { ">=" } else { ">" };
510 let max_op = if *inclusive_max { "=<" } else { "<" };
511 Ok(format!(
512 "{}_{} {} {} , {}_{} {} {}",
513 var, field, min_op, min_val, var, field, max_op, max_val
514 ))
515 }
516 ConditionNode::NotInRange {
517 field,
518 min,
519 max,
520 inclusive_min,
521 inclusive_max,
522 } => {
523 let min_val = self.format_value(min)?;
524 let max_val = self.format_value(max)?;
525 let min_op = if *inclusive_min { "<" } else { "=<" };
526 let max_op = if *inclusive_max { ">" } else { ">=" };
527 Ok(format!(
528 "({}_{} {} {} ; {}_{} {} {})",
529 var, field, min_op, min_val, var, field, max_op, max_val
530 ))
531 }
532 ConditionNode::Matches {
533 field,
534 regex_pattern,
535 } => {
536 Ok(format!(
538 "atom_string({}_{}, Str), re_match(\"{}\"^^_, Str)",
539 var, field, regex_pattern
540 ))
541 }
542 ConditionNode::TemporalComparison {
543 field,
544 operator,
545 value,
546 } => {
547 let val = self.format_value(value)?;
548 let pl_op = match operator.as_str() {
549 "=" | "==" => "=",
550 "!=" => "\\=",
551 ">=" => ">=",
552 "<=" => "=<",
553 ">" => ">",
554 "<" => "<",
555 _ => "=",
556 };
557 Ok(format!("{:?}_{} {} {}", field, var, pl_op, val))
558 }
559 }
560 }
561
562 fn format_value(&self, value: &ConditionValue) -> DslResult<String> {
564 match value {
565 ConditionValue::Number(n) => Ok(n.to_string()),
566 ConditionValue::String(s) => Ok(format!("'{}'", s.replace('\'', "\\'"))),
567 ConditionValue::Boolean(b) => Ok(if *b {
568 "true".to_string()
569 } else {
570 "false".to_string()
571 }),
572 ConditionValue::Date(d) => Ok(format!("'{}'", d)),
573 ConditionValue::SetExpr(_) => Ok("[]".to_string()), }
575 }
576
577 fn generate_predicate(&self, statute: &StatuteNode) -> DslResult<String> {
579 let mut pl = String::new();
580 let pred_name = self.predicate_name(&statute.id);
581
582 writeln!(&mut pl, "% {}", statute.title).unwrap();
584
585 if !statute.requires.is_empty() {
587 writeln!(&mut pl, "% Requires: {}", statute.requires.join(", ")).unwrap();
588 }
589
590 write!(&mut pl, "{}(Entity) :- ", pred_name).unwrap();
592
593 if !statute.conditions.is_empty() {
595 let conditions: Result<Vec<_>, _> = statute
596 .conditions
597 .iter()
598 .map(|c| self.generate_condition(c, "Entity"))
599 .collect();
600 let conditions = conditions?;
601
602 let body = conditions.join(" , ");
604 writeln!(&mut pl, "{}", body).unwrap();
605 } else {
606 writeln!(&mut pl, "true").unwrap();
607 }
608
609 for req in &statute.requires {
611 let req_pred = self.predicate_name(req);
612 writeln!(&mut pl, " , {}(Entity)", req_pred).unwrap();
613 }
614
615 writeln!(&mut pl, ".").unwrap();
616 writeln!(&mut pl).unwrap();
617
618 for (idx, effect) in statute.effects.iter().enumerate() {
620 writeln!(
621 &mut pl,
622 "% Effect {}: {} - {}",
623 idx + 1,
624 effect.effect_type,
625 effect.description
626 )
627 .unwrap();
628 writeln!(
629 &mut pl,
630 "{}_effect_{}(Entity, '{}') :- {}(Entity).",
631 pred_name,
632 idx + 1,
633 effect.description,
634 pred_name
635 )
636 .unwrap();
637 writeln!(&mut pl).unwrap();
638 }
639
640 Ok(pl)
641 }
642}
643
644impl CodeGenerator for PrologGenerator {
645 fn generate(&self, doc: &LegalDocument) -> DslResult<String> {
646 let mut pl = String::new();
647
648 writeln!(&mut pl, "% Generated Prolog from Legal DSL").unwrap();
649 writeln!(&mut pl, "% Total statutes: {}", doc.statutes.len()).unwrap();
650 writeln!(&mut pl).unwrap();
651
652 if self.generate_module {
653 writeln!(&mut pl, ":- module(legal_statutes, []).").unwrap();
654 writeln!(&mut pl).unwrap();
655 }
656
657 if self.use_dynamic {
658 for statute in &doc.statutes {
659 let pred_name = self.predicate_name(&statute.id);
660 writeln!(&mut pl, ":- dynamic {}/1.", pred_name).unwrap();
661 }
662 writeln!(&mut pl).unwrap();
663 }
664
665 for statute in &doc.statutes {
666 let pred_pl = self.generate_predicate(statute)?;
667 pl.push_str(&pred_pl);
668 }
669
670 Ok(pl)
671 }
672
673 fn target_language(&self) -> &str {
674 "Prolog"
675 }
676
677 fn file_extension(&self) -> &str {
678 "pl"
679 }
680}
681
682pub struct TypeScriptGenerator {
684 pub use_typescript: bool,
686 pub use_es6_modules: bool,
688}
689
690impl Default for TypeScriptGenerator {
691 fn default() -> Self {
692 Self {
693 use_typescript: true,
694 use_es6_modules: true,
695 }
696 }
697}
698
699impl TypeScriptGenerator {
700 pub fn new() -> Self {
702 Self::default()
703 }
704
705 fn function_name(&self, id: &str) -> String {
707 id.replace('-', "_")
708 }
709
710 fn generate_condition(&self, condition: &ConditionNode, entity_var: &str) -> DslResult<String> {
712 match condition {
713 ConditionNode::Comparison {
714 field,
715 operator,
716 value,
717 } => {
718 let val = self.format_value(value)?;
719 Ok(format!("{}.{} {} {}", entity_var, field, operator, val))
720 }
721 ConditionNode::HasAttribute { key } => Ok(format!(
722 "{}.{} !== undefined && {}.{} !== null",
723 entity_var, key, entity_var, key
724 )),
725 ConditionNode::Between { field, min, max } => {
726 let min_val = self.format_value(min)?;
727 let max_val = self.format_value(max)?;
728 Ok(format!(
729 "{}.{} >= {} && {}.{} <= {}",
730 entity_var, field, min_val, entity_var, field, max_val
731 ))
732 }
733 ConditionNode::In { field, values } => {
734 let vals: Result<Vec<_>, _> = values.iter().map(|v| self.format_value(v)).collect();
735 let vals = vals?;
736 Ok(format!(
737 "[{}].includes({}.{})",
738 vals.join(", "),
739 entity_var,
740 field
741 ))
742 }
743 ConditionNode::Like { field, pattern } => Ok(format!(
744 "/{}/i.test({}.{})",
745 pattern.replace('%', ".*"),
746 entity_var,
747 field
748 )),
749 ConditionNode::And(left, right) => {
750 let left_js = self.generate_condition(left, entity_var)?;
751 let right_js = self.generate_condition(right, entity_var)?;
752 Ok(format!("({} && {})", left_js, right_js))
753 }
754 ConditionNode::Or(left, right) => {
755 let left_js = self.generate_condition(left, entity_var)?;
756 let right_js = self.generate_condition(right, entity_var)?;
757 Ok(format!("({} || {})", left_js, right_js))
758 }
759 ConditionNode::Not(inner) => {
760 let inner_js = self.generate_condition(inner, entity_var)?;
761 Ok(format!("!({})", inner_js))
762 }
763 ConditionNode::InRange {
764 field,
765 min,
766 max,
767 inclusive_min,
768 inclusive_max,
769 } => {
770 let min_val = self.format_value(min)?;
771 let max_val = self.format_value(max)?;
772 let min_op = if *inclusive_min { ">=" } else { ">" };
773 let max_op = if *inclusive_max { "<=" } else { "<" };
774 Ok(format!(
775 "{}.{} {} {} && {}.{} {} {}",
776 entity_var, field, min_op, min_val, entity_var, field, max_op, max_val
777 ))
778 }
779 ConditionNode::NotInRange {
780 field,
781 min,
782 max,
783 inclusive_min,
784 inclusive_max,
785 } => {
786 let min_val = self.format_value(min)?;
787 let max_val = self.format_value(max)?;
788 let min_op = if *inclusive_min { "<" } else { "<=" };
789 let max_op = if *inclusive_max { ">" } else { ">=" };
790 Ok(format!(
791 "{}.{} {} {} || {}.{} {} {}",
792 entity_var, field, min_op, min_val, entity_var, field, max_op, max_val
793 ))
794 }
795 ConditionNode::Matches {
796 field,
797 regex_pattern,
798 } => Ok(format!(
799 "/{}/i.test({}.{})",
800 regex_pattern, entity_var, field
801 )),
802 ConditionNode::TemporalComparison {
803 field,
804 operator,
805 value,
806 } => {
807 let val = self.format_value(value)?;
808 let field_str = temporal_field_to_string(field);
809 Ok(format!(
810 "new Date({}.{}) {} new Date({})",
811 entity_var, field_str, operator, val
812 ))
813 }
814 }
815 }
816
817 fn format_value(&self, value: &ConditionValue) -> DslResult<String> {
819 match value {
820 ConditionValue::Number(n) => Ok(n.to_string()),
821 ConditionValue::String(s) => Ok(format!("\"{}\"", s.replace('"', "\\\""))),
822 ConditionValue::Boolean(b) => Ok(b.to_string()),
823 ConditionValue::Date(d) => Ok(format!("\"{}\"", d)),
824 ConditionValue::SetExpr(_) => Ok("[]".to_string()),
825 }
826 }
827
828 fn generate_function(&self, statute: &StatuteNode) -> DslResult<String> {
830 let mut code = String::new();
831 let fn_name = self.function_name(&statute.id);
832
833 writeln!(&mut code, "/**").unwrap();
835 writeln!(&mut code, " * {}", statute.title).unwrap();
836 if !statute.conditions.is_empty() {
837 writeln!(
838 &mut code,
839 " * @param {{any}} entity - The entity to validate"
840 )
841 .unwrap();
842 writeln!(
843 &mut code,
844 " * @returns {{boolean}} - Whether the statute applies"
845 )
846 .unwrap();
847 }
848 writeln!(&mut code, " */").unwrap();
849
850 if self.use_typescript {
852 write!(
853 &mut code,
854 "export function {}(entity: any): boolean ",
855 fn_name
856 )
857 .unwrap();
858 } else {
859 write!(&mut code, "export function {}(entity) ", fn_name).unwrap();
860 }
861
862 writeln!(&mut code, "{{").unwrap();
863
864 if statute.conditions.is_empty() {
865 writeln!(&mut code, " return true;").unwrap();
866 } else {
867 write!(&mut code, " return ").unwrap();
868 let conditions: Result<Vec<_>, _> = statute
869 .conditions
870 .iter()
871 .map(|c| self.generate_condition(c, "entity"))
872 .collect();
873 let conditions = conditions?;
874 writeln!(&mut code, "{};", conditions.join(" && ")).unwrap();
875 }
876
877 writeln!(&mut code, "}}").unwrap();
878 writeln!(&mut code).unwrap();
879
880 Ok(code)
881 }
882}
883
884impl CodeGenerator for TypeScriptGenerator {
885 fn generate(&self, doc: &LegalDocument) -> DslResult<String> {
886 let mut code = String::new();
887
888 writeln!(
889 &mut code,
890 "// Generated TypeScript/JavaScript from Legal DSL"
891 )
892 .unwrap();
893 writeln!(&mut code, "// Total statutes: {}", doc.statutes.len()).unwrap();
894 writeln!(&mut code).unwrap();
895
896 for statute in &doc.statutes {
897 let fn_code = self.generate_function(statute)?;
898 code.push_str(&fn_code);
899 }
900
901 Ok(code)
902 }
903
904 fn target_language(&self) -> &str {
905 if self.use_typescript {
906 "TypeScript"
907 } else {
908 "JavaScript"
909 }
910 }
911
912 fn file_extension(&self) -> &str {
913 if self.use_typescript { "ts" } else { "js" }
914 }
915}
916
917pub struct RustGenerator {
919 pub use_serde: bool,
921}
922
923impl Default for RustGenerator {
924 fn default() -> Self {
925 Self { use_serde: true }
926 }
927}
928
929impl RustGenerator {
930 pub fn new() -> Self {
932 Self::default()
933 }
934
935 fn function_name(&self, id: &str) -> String {
937 id.replace('-', "_")
938 }
939
940 fn generate_condition(&self, condition: &ConditionNode, entity_var: &str) -> DslResult<String> {
942 match condition {
943 ConditionNode::Comparison {
944 field,
945 operator,
946 value,
947 } => {
948 let val = self.format_value(value)?;
949 Ok(format!("{}.{} {} {}", entity_var, field, operator, val))
950 }
951 ConditionNode::HasAttribute { key } => Ok(format!("{}.{}.is_some()", entity_var, key)),
952 ConditionNode::Between { field, min, max } => {
953 let min_val = self.format_value(min)?;
954 let max_val = self.format_value(max)?;
955 Ok(format!(
956 "{}.{} >= {} && {}.{} <= {}",
957 entity_var, field, min_val, entity_var, field, max_val
958 ))
959 }
960 ConditionNode::In { field, values } => {
961 let vals: Result<Vec<_>, _> = values.iter().map(|v| self.format_value(v)).collect();
962 let vals = vals?;
963 Ok(format!(
964 "[{}].contains(&{}.{})",
965 vals.join(", "),
966 entity_var,
967 field
968 ))
969 }
970 ConditionNode::Like { field, pattern } => Ok(format!(
971 "{}.{}.contains(\"{}\")",
972 entity_var,
973 field,
974 pattern.replace('%', "")
975 )),
976 ConditionNode::And(left, right) => {
977 let left_rs = self.generate_condition(left, entity_var)?;
978 let right_rs = self.generate_condition(right, entity_var)?;
979 Ok(format!("({} && {})", left_rs, right_rs))
980 }
981 ConditionNode::Or(left, right) => {
982 let left_rs = self.generate_condition(left, entity_var)?;
983 let right_rs = self.generate_condition(right, entity_var)?;
984 Ok(format!("({} || {})", left_rs, right_rs))
985 }
986 ConditionNode::Not(inner) => {
987 let inner_rs = self.generate_condition(inner, entity_var)?;
988 Ok(format!("!({})", inner_rs))
989 }
990 ConditionNode::InRange {
991 field,
992 min,
993 max,
994 inclusive_min,
995 inclusive_max,
996 } => {
997 let min_val = self.format_value(min)?;
998 let max_val = self.format_value(max)?;
999 let min_op = if *inclusive_min { ">=" } else { ">" };
1000 let max_op = if *inclusive_max { "<=" } else { "<" };
1001 Ok(format!(
1002 "{}.{} {} {} && {}.{} {} {}",
1003 entity_var, field, min_op, min_val, entity_var, field, max_op, max_val
1004 ))
1005 }
1006 ConditionNode::NotInRange {
1007 field,
1008 min,
1009 max,
1010 inclusive_min,
1011 inclusive_max,
1012 } => {
1013 let min_val = self.format_value(min)?;
1014 let max_val = self.format_value(max)?;
1015 let min_op = if *inclusive_min { "<" } else { "<=" };
1016 let max_op = if *inclusive_max { ">" } else { ">=" };
1017 Ok(format!(
1018 "{}.{} {} {} || {}.{} {} {}",
1019 entity_var, field, min_op, min_val, entity_var, field, max_op, max_val
1020 ))
1021 }
1022 ConditionNode::Matches {
1023 field,
1024 regex_pattern,
1025 } => Ok(format!(
1026 "Regex::new(r\"{}\").unwrap().is_match(&{}.{})",
1027 regex_pattern, entity_var, field
1028 )),
1029 ConditionNode::TemporalComparison {
1030 field,
1031 operator,
1032 value,
1033 } => {
1034 let val = self.format_value(value)?;
1035 let field_str = temporal_field_to_string(field);
1036 Ok(format!("{}.{} {} {}", entity_var, field_str, operator, val))
1037 }
1038 }
1039 }
1040
1041 fn format_value(&self, value: &ConditionValue) -> DslResult<String> {
1043 match value {
1044 ConditionValue::Number(n) => Ok(n.to_string()),
1045 ConditionValue::String(s) => Ok(format!("\"{}\"", s.replace('"', "\\\""))),
1046 ConditionValue::Boolean(b) => Ok(b.to_string()),
1047 ConditionValue::Date(d) => Ok(format!("\"{}\"", d)),
1048 ConditionValue::SetExpr(_) => Ok("vec![]".to_string()),
1049 }
1050 }
1051
1052 fn generate_function(&self, statute: &StatuteNode) -> DslResult<String> {
1054 let mut code = String::new();
1055 let fn_name = self.function_name(&statute.id);
1056
1057 writeln!(&mut code, "/// {}", statute.title).unwrap();
1058 writeln!(&mut code, "#[allow(dead_code)]").unwrap();
1059 write!(&mut code, "pub fn {}<T>(entity: &T) -> bool ", fn_name).unwrap();
1060 writeln!(&mut code, "{{").unwrap();
1061
1062 if statute.conditions.is_empty() {
1063 writeln!(&mut code, " true").unwrap();
1064 } else {
1065 write!(&mut code, " ").unwrap();
1066 let conditions: Result<Vec<_>, _> = statute
1067 .conditions
1068 .iter()
1069 .map(|c| self.generate_condition(c, "entity"))
1070 .collect();
1071 let conditions = conditions?;
1072 writeln!(&mut code, "{}", conditions.join(" && ")).unwrap();
1073 }
1074
1075 writeln!(&mut code, "}}").unwrap();
1076 writeln!(&mut code).unwrap();
1077
1078 Ok(code)
1079 }
1080}
1081
1082impl CodeGenerator for RustGenerator {
1083 fn generate(&self, doc: &LegalDocument) -> DslResult<String> {
1084 let mut code = String::new();
1085
1086 writeln!(&mut code, "// Generated Rust code from Legal DSL").unwrap();
1087 writeln!(&mut code, "// Total statutes: {}", doc.statutes.len()).unwrap();
1088 writeln!(&mut code).unwrap();
1089
1090 if self.use_serde {
1091 writeln!(&mut code, "use serde::{{Serialize, Deserialize}};").unwrap();
1092 }
1093 writeln!(&mut code, "use regex::Regex;").unwrap();
1094 writeln!(&mut code).unwrap();
1095
1096 for statute in &doc.statutes {
1097 let fn_code = self.generate_function(statute)?;
1098 code.push_str(&fn_code);
1099 }
1100
1101 Ok(code)
1102 }
1103
1104 fn target_language(&self) -> &str {
1105 "Rust"
1106 }
1107
1108 fn file_extension(&self) -> &str {
1109 "rs"
1110 }
1111}
1112
1113pub struct GoGenerator {
1115 pub package_name: String,
1117}
1118
1119impl Default for GoGenerator {
1120 fn default() -> Self {
1121 Self {
1122 package_name: "statutes".to_string(),
1123 }
1124 }
1125}
1126
1127impl GoGenerator {
1128 pub fn new() -> Self {
1130 Self::default()
1131 }
1132
1133 fn function_name(&self, id: &str) -> String {
1135 let name = id.replace('-', "_");
1136 let mut chars = name.chars();
1137 match chars.next() {
1138 None => String::new(),
1139 Some(first) => first.to_uppercase().chain(chars).collect(),
1140 }
1141 }
1142
1143 fn generate_condition(&self, condition: &ConditionNode, entity_var: &str) -> DslResult<String> {
1145 match condition {
1146 ConditionNode::Comparison {
1147 field,
1148 operator,
1149 value,
1150 } => {
1151 let val = self.format_value(value)?;
1152 Ok(format!("{}.{} {} {}", entity_var, field, operator, val))
1153 }
1154 ConditionNode::HasAttribute { key } => Ok(format!("{}.{} != nil", entity_var, key)),
1155 ConditionNode::Between { field, min, max } => {
1156 let min_val = self.format_value(min)?;
1157 let max_val = self.format_value(max)?;
1158 Ok(format!(
1159 "{}.{} >= {} && {}.{} <= {}",
1160 entity_var, field, min_val, entity_var, field, max_val
1161 ))
1162 }
1163 ConditionNode::In { field, values } => {
1164 let vals: Result<Vec<_>, _> = values.iter().map(|v| self.format_value(v)).collect();
1165 let vals = vals?;
1166 Ok(format!(
1167 "contains([]interface{{{{{}}}}}, {}.{})",
1168 vals.join(", "),
1169 entity_var,
1170 field
1171 ))
1172 }
1173 ConditionNode::Like { field, pattern } => Ok(format!(
1174 "strings.Contains({}.{}, \"{}\")",
1175 entity_var,
1176 field,
1177 pattern.replace('%', "")
1178 )),
1179 ConditionNode::And(left, right) => {
1180 let left_go = self.generate_condition(left, entity_var)?;
1181 let right_go = self.generate_condition(right, entity_var)?;
1182 Ok(format!("({} && {})", left_go, right_go))
1183 }
1184 ConditionNode::Or(left, right) => {
1185 let left_go = self.generate_condition(left, entity_var)?;
1186 let right_go = self.generate_condition(right, entity_var)?;
1187 Ok(format!("({} || {})", left_go, right_go))
1188 }
1189 ConditionNode::Not(inner) => {
1190 let inner_go = self.generate_condition(inner, entity_var)?;
1191 Ok(format!("!({})", inner_go))
1192 }
1193 ConditionNode::InRange {
1194 field,
1195 min,
1196 max,
1197 inclusive_min,
1198 inclusive_max,
1199 } => {
1200 let min_val = self.format_value(min)?;
1201 let max_val = self.format_value(max)?;
1202 let min_op = if *inclusive_min { ">=" } else { ">" };
1203 let max_op = if *inclusive_max { "<=" } else { "<" };
1204 Ok(format!(
1205 "{}.{} {} {} && {}.{} {} {}",
1206 entity_var, field, min_op, min_val, entity_var, field, max_op, max_val
1207 ))
1208 }
1209 ConditionNode::NotInRange {
1210 field,
1211 min,
1212 max,
1213 inclusive_min,
1214 inclusive_max,
1215 } => {
1216 let min_val = self.format_value(min)?;
1217 let max_val = self.format_value(max)?;
1218 let min_op = if *inclusive_min { "<" } else { "<=" };
1219 let max_op = if *inclusive_max { ">" } else { ">=" };
1220 Ok(format!(
1221 "{}.{} {} {} || {}.{} {} {}",
1222 entity_var, field, min_op, min_val, entity_var, field, max_op, max_val
1223 ))
1224 }
1225 ConditionNode::Matches {
1226 field,
1227 regex_pattern,
1228 } => Ok(format!(
1229 "regexp.MustCompile(\"{}\").MatchString({}.{})",
1230 regex_pattern, entity_var, field
1231 )),
1232 ConditionNode::TemporalComparison {
1233 field,
1234 operator,
1235 value,
1236 } => {
1237 let val = self.format_value(value)?;
1238 let field_str = temporal_field_to_string(field);
1239 Ok(format!("{}.{} {} {}", entity_var, field_str, operator, val))
1240 }
1241 }
1242 }
1243
1244 fn format_value(&self, value: &ConditionValue) -> DslResult<String> {
1246 match value {
1247 ConditionValue::Number(n) => Ok(n.to_string()),
1248 ConditionValue::String(s) => Ok(format!("\"{}\"", s.replace('"', "\\\""))),
1249 ConditionValue::Boolean(b) => Ok(b.to_string()),
1250 ConditionValue::Date(d) => Ok(format!("\"{}\"", d)),
1251 ConditionValue::SetExpr(_) => Ok("[]interface{}{}".to_string()),
1252 }
1253 }
1254
1255 fn generate_function(&self, statute: &StatuteNode) -> DslResult<String> {
1257 let mut code = String::new();
1258 let fn_name = self.function_name(&statute.id);
1259
1260 writeln!(&mut code, "// {} - {}", fn_name, statute.title).unwrap();
1261 writeln!(&mut code, "func {}(entity interface{{}}) bool {{", fn_name).unwrap();
1262
1263 if statute.conditions.is_empty() {
1264 writeln!(&mut code, "\treturn true").unwrap();
1265 } else {
1266 write!(&mut code, "\treturn ").unwrap();
1267 let conditions: Result<Vec<_>, _> = statute
1268 .conditions
1269 .iter()
1270 .map(|c| self.generate_condition(c, "entity"))
1271 .collect();
1272 let conditions = conditions?;
1273 writeln!(&mut code, "{}", conditions.join(" && ")).unwrap();
1274 }
1275
1276 writeln!(&mut code, "}}").unwrap();
1277 writeln!(&mut code).unwrap();
1278
1279 Ok(code)
1280 }
1281}
1282
1283impl CodeGenerator for GoGenerator {
1284 fn generate(&self, doc: &LegalDocument) -> DslResult<String> {
1285 let mut code = String::new();
1286
1287 writeln!(&mut code, "// Generated Go code from Legal DSL").unwrap();
1288 writeln!(&mut code, "// Total statutes: {}", doc.statutes.len()).unwrap();
1289 writeln!(&mut code).unwrap();
1290
1291 writeln!(&mut code, "package {}", self.package_name).unwrap();
1292 writeln!(&mut code).unwrap();
1293 writeln!(&mut code, "import (").unwrap();
1294 writeln!(&mut code, "\t\"regexp\"").unwrap();
1295 writeln!(&mut code, "\t\"strings\"").unwrap();
1296 writeln!(&mut code, ")").unwrap();
1297 writeln!(&mut code).unwrap();
1298
1299 for statute in &doc.statutes {
1300 let fn_code = self.generate_function(statute)?;
1301 code.push_str(&fn_code);
1302 }
1303
1304 Ok(code)
1305 }
1306
1307 fn target_language(&self) -> &str {
1308 "Go"
1309 }
1310
1311 fn file_extension(&self) -> &str {
1312 "go"
1313 }
1314}
1315
1316pub struct JavaGenerator {
1318 pub package_name: String,
1320 pub class_name: String,
1322}
1323
1324impl Default for JavaGenerator {
1325 fn default() -> Self {
1326 Self {
1327 package_name: "com.legal.statutes".to_string(),
1328 class_name: "StatuteValidator".to_string(),
1329 }
1330 }
1331}
1332
1333impl JavaGenerator {
1334 pub fn new() -> Self {
1336 Self::default()
1337 }
1338
1339 fn method_name(&self, id: &str) -> String {
1341 let parts: Vec<&str> = id.split('-').collect();
1342 if parts.is_empty() {
1343 return String::new();
1344 }
1345
1346 let mut result = parts[0].to_string();
1347 for part in &parts[1..] {
1348 let mut chars = part.chars();
1349 if let Some(first) = chars.next() {
1350 result.push_str(&first.to_uppercase().chain(chars).collect::<String>());
1351 }
1352 }
1353 result
1354 }
1355
1356 fn generate_condition(&self, condition: &ConditionNode, entity_var: &str) -> DslResult<String> {
1358 match condition {
1359 ConditionNode::Comparison {
1360 field,
1361 operator,
1362 value,
1363 } => {
1364 let val = self.format_value(value)?;
1365 let getter = format!("get{}()", self.capitalize_first(field));
1366 Ok(format!("{}.{} {} {}", entity_var, getter, operator, val))
1367 }
1368 ConditionNode::HasAttribute { key } => {
1369 let getter = format!("get{}()", self.capitalize_first(key));
1370 Ok(format!("{}.{} != null", entity_var, getter))
1371 }
1372 ConditionNode::Between { field, min, max } => {
1373 let min_val = self.format_value(min)?;
1374 let max_val = self.format_value(max)?;
1375 let getter = format!("get{}()", self.capitalize_first(field));
1376 Ok(format!(
1377 "{}.{} >= {} && {}.{} <= {}",
1378 entity_var, getter, min_val, entity_var, getter, max_val
1379 ))
1380 }
1381 ConditionNode::In { field, values } => {
1382 let vals: Result<Vec<_>, _> = values.iter().map(|v| self.format_value(v)).collect();
1383 let vals = vals?;
1384 let getter = format!("get{}()", self.capitalize_first(field));
1385 Ok(format!(
1386 "Arrays.asList({}).contains({}.{})",
1387 vals.join(", "),
1388 entity_var,
1389 getter
1390 ))
1391 }
1392 ConditionNode::Like { field, pattern } => {
1393 let getter = format!("get{}()", self.capitalize_first(field));
1394 Ok(format!(
1395 "{}.{}.contains(\"{}\")",
1396 entity_var,
1397 getter,
1398 pattern.replace('%', "")
1399 ))
1400 }
1401 ConditionNode::And(left, right) => {
1402 let left_java = self.generate_condition(left, entity_var)?;
1403 let right_java = self.generate_condition(right, entity_var)?;
1404 Ok(format!("({} && {})", left_java, right_java))
1405 }
1406 ConditionNode::Or(left, right) => {
1407 let left_java = self.generate_condition(left, entity_var)?;
1408 let right_java = self.generate_condition(right, entity_var)?;
1409 Ok(format!("({} || {})", left_java, right_java))
1410 }
1411 ConditionNode::Not(inner) => {
1412 let inner_java = self.generate_condition(inner, entity_var)?;
1413 Ok(format!("!({})", inner_java))
1414 }
1415 ConditionNode::InRange {
1416 field,
1417 min,
1418 max,
1419 inclusive_min,
1420 inclusive_max,
1421 } => {
1422 let min_val = self.format_value(min)?;
1423 let max_val = self.format_value(max)?;
1424 let getter = format!("get{}()", self.capitalize_first(field));
1425 let min_op = if *inclusive_min { ">=" } else { ">" };
1426 let max_op = if *inclusive_max { "<=" } else { "<" };
1427 Ok(format!(
1428 "{}.{} {} {} && {}.{} {} {}",
1429 entity_var, getter, min_op, min_val, entity_var, getter, max_op, max_val
1430 ))
1431 }
1432 ConditionNode::NotInRange {
1433 field,
1434 min,
1435 max,
1436 inclusive_min,
1437 inclusive_max,
1438 } => {
1439 let min_val = self.format_value(min)?;
1440 let max_val = self.format_value(max)?;
1441 let getter = format!("get{}()", self.capitalize_first(field));
1442 let min_op = if *inclusive_min { "<" } else { "<=" };
1443 let max_op = if *inclusive_max { ">" } else { ">=" };
1444 Ok(format!(
1445 "{}.{} {} {} || {}.{} {} {}",
1446 entity_var, getter, min_op, min_val, entity_var, getter, max_op, max_val
1447 ))
1448 }
1449 ConditionNode::Matches {
1450 field,
1451 regex_pattern,
1452 } => {
1453 let getter = format!("get{}()", self.capitalize_first(field));
1454 Ok(format!(
1455 "Pattern.compile(\"{}\").matcher({}.{}).matches()",
1456 regex_pattern, entity_var, getter
1457 ))
1458 }
1459 ConditionNode::TemporalComparison {
1460 field,
1461 operator,
1462 value,
1463 } => {
1464 let val = self.format_value(value)?;
1465 let field_str = temporal_field_to_string(field);
1466 let getter = format!("get{}()", self.capitalize_first(&field_str));
1467 Ok(format!("{}.{} {} {}", entity_var, getter, operator, val))
1468 }
1469 }
1470 }
1471
1472 fn capitalize_first(&self, s: &str) -> String {
1474 let mut chars = s.chars();
1475 match chars.next() {
1476 None => String::new(),
1477 Some(first) => first.to_uppercase().chain(chars).collect(),
1478 }
1479 }
1480
1481 fn format_value(&self, value: &ConditionValue) -> DslResult<String> {
1483 match value {
1484 ConditionValue::Number(n) => Ok(n.to_string()),
1485 ConditionValue::String(s) => Ok(format!("\"{}\"", s.replace('"', "\\\""))),
1486 ConditionValue::Boolean(b) => Ok(b.to_string()),
1487 ConditionValue::Date(d) => Ok(format!("\"{}\"", d)),
1488 ConditionValue::SetExpr(_) => Ok("new ArrayList<>()".to_string()),
1489 }
1490 }
1491
1492 fn generate_method(&self, statute: &StatuteNode) -> DslResult<String> {
1494 let mut code = String::new();
1495 let method_name = self.method_name(&statute.id);
1496
1497 writeln!(&mut code, " /**").unwrap();
1498 writeln!(&mut code, " * {}", statute.title).unwrap();
1499 writeln!(&mut code, " * @param entity The entity to validate").unwrap();
1500 writeln!(&mut code, " * @return Whether the statute applies").unwrap();
1501 writeln!(&mut code, " */").unwrap();
1502 writeln!(
1503 &mut code,
1504 " public static boolean {}(Object entity) {{",
1505 method_name
1506 )
1507 .unwrap();
1508
1509 if statute.conditions.is_empty() {
1510 writeln!(&mut code, " return true;").unwrap();
1511 } else {
1512 write!(&mut code, " return ").unwrap();
1513 let conditions: Result<Vec<_>, _> = statute
1514 .conditions
1515 .iter()
1516 .map(|c| self.generate_condition(c, "entity"))
1517 .collect();
1518 let conditions = conditions?;
1519 writeln!(&mut code, "{};", conditions.join(" && ")).unwrap();
1520 }
1521
1522 writeln!(&mut code, " }}").unwrap();
1523 writeln!(&mut code).unwrap();
1524
1525 Ok(code)
1526 }
1527}
1528
1529impl CodeGenerator for JavaGenerator {
1530 fn generate(&self, doc: &LegalDocument) -> DslResult<String> {
1531 let mut code = String::new();
1532
1533 writeln!(&mut code, "// Generated Java code from Legal DSL").unwrap();
1534 writeln!(&mut code, "// Total statutes: {}", doc.statutes.len()).unwrap();
1535 writeln!(&mut code).unwrap();
1536
1537 writeln!(&mut code, "package {};", self.package_name).unwrap();
1538 writeln!(&mut code).unwrap();
1539 writeln!(&mut code, "import java.util.Arrays;").unwrap();
1540 writeln!(&mut code, "import java.util.ArrayList;").unwrap();
1541 writeln!(&mut code, "import java.util.regex.Pattern;").unwrap();
1542 writeln!(&mut code).unwrap();
1543
1544 writeln!(&mut code, "public class {} {{", self.class_name).unwrap();
1545 writeln!(&mut code).unwrap();
1546
1547 for statute in &doc.statutes {
1548 let method_code = self.generate_method(statute)?;
1549 code.push_str(&method_code);
1550 }
1551
1552 writeln!(&mut code, "}}").unwrap();
1553
1554 Ok(code)
1555 }
1556
1557 fn target_language(&self) -> &str {
1558 "Java"
1559 }
1560
1561 fn file_extension(&self) -> &str {
1562 "java"
1563 }
1564}
1565
1566pub struct CSharpGenerator {
1568 pub namespace: String,
1570 pub class_name: String,
1572}
1573
1574impl Default for CSharpGenerator {
1575 fn default() -> Self {
1576 Self {
1577 namespace: "Legal.Statutes".to_string(),
1578 class_name: "StatuteValidator".to_string(),
1579 }
1580 }
1581}
1582
1583impl CSharpGenerator {
1584 pub fn new() -> Self {
1586 Self::default()
1587 }
1588
1589 fn method_name(&self, id: &str) -> String {
1591 id.split('-')
1592 .map(|part| {
1593 let mut chars = part.chars();
1594 match chars.next() {
1595 None => String::new(),
1596 Some(first) => first.to_uppercase().chain(chars).collect(),
1597 }
1598 })
1599 .collect()
1600 }
1601
1602 fn generate_condition(&self, condition: &ConditionNode, entity_var: &str) -> DslResult<String> {
1604 match condition {
1605 ConditionNode::Comparison {
1606 field,
1607 operator,
1608 value,
1609 } => {
1610 let val = self.format_value(value)?;
1611 Ok(format!(
1612 "{}.{} {} {}",
1613 entity_var,
1614 self.capitalize_first(field),
1615 operator,
1616 val
1617 ))
1618 }
1619 ConditionNode::HasAttribute { key } => Ok(format!(
1620 "{}.{} != null",
1621 entity_var,
1622 self.capitalize_first(key)
1623 )),
1624 ConditionNode::Between { field, min, max } => {
1625 let min_val = self.format_value(min)?;
1626 let max_val = self.format_value(max)?;
1627 let prop = self.capitalize_first(field);
1628 Ok(format!(
1629 "{}.{} >= {} && {}.{} <= {}",
1630 entity_var, prop, min_val, entity_var, prop, max_val
1631 ))
1632 }
1633 ConditionNode::In { field, values } => {
1634 let vals: Result<Vec<_>, _> = values.iter().map(|v| self.format_value(v)).collect();
1635 let vals = vals?;
1636 let prop = self.capitalize_first(field);
1637 Ok(format!(
1638 "new[] {{ {} }}.Contains({}.{})",
1639 vals.join(", "),
1640 entity_var,
1641 prop
1642 ))
1643 }
1644 ConditionNode::Like { field, pattern } => {
1645 let prop = self.capitalize_first(field);
1646 Ok(format!(
1647 "{}.{}.Contains(\"{}\")",
1648 entity_var,
1649 prop,
1650 pattern.replace('%', "")
1651 ))
1652 }
1653 ConditionNode::And(left, right) => {
1654 let left_cs = self.generate_condition(left, entity_var)?;
1655 let right_cs = self.generate_condition(right, entity_var)?;
1656 Ok(format!("({} && {})", left_cs, right_cs))
1657 }
1658 ConditionNode::Or(left, right) => {
1659 let left_cs = self.generate_condition(left, entity_var)?;
1660 let right_cs = self.generate_condition(right, entity_var)?;
1661 Ok(format!("({} || {})", left_cs, right_cs))
1662 }
1663 ConditionNode::Not(inner) => {
1664 let inner_cs = self.generate_condition(inner, entity_var)?;
1665 Ok(format!("!({})", inner_cs))
1666 }
1667 ConditionNode::InRange {
1668 field,
1669 min,
1670 max,
1671 inclusive_min,
1672 inclusive_max,
1673 } => {
1674 let min_val = self.format_value(min)?;
1675 let max_val = self.format_value(max)?;
1676 let prop = self.capitalize_first(field);
1677 let min_op = if *inclusive_min { ">=" } else { ">" };
1678 let max_op = if *inclusive_max { "<=" } else { "<" };
1679 Ok(format!(
1680 "{}.{} {} {} && {}.{} {} {}",
1681 entity_var, prop, min_op, min_val, entity_var, prop, max_op, max_val
1682 ))
1683 }
1684 ConditionNode::NotInRange {
1685 field,
1686 min,
1687 max,
1688 inclusive_min,
1689 inclusive_max,
1690 } => {
1691 let min_val = self.format_value(min)?;
1692 let max_val = self.format_value(max)?;
1693 let prop = self.capitalize_first(field);
1694 let min_op = if *inclusive_min { "<" } else { "<=" };
1695 let max_op = if *inclusive_max { ">" } else { ">=" };
1696 Ok(format!(
1697 "{}.{} {} {} || {}.{} {} {}",
1698 entity_var, prop, min_op, min_val, entity_var, prop, max_op, max_val
1699 ))
1700 }
1701 ConditionNode::Matches {
1702 field,
1703 regex_pattern,
1704 } => {
1705 let prop = self.capitalize_first(field);
1706 Ok(format!(
1707 "Regex.IsMatch({}.{}, @\"{}\")",
1708 entity_var, prop, regex_pattern
1709 ))
1710 }
1711 ConditionNode::TemporalComparison {
1712 field,
1713 operator,
1714 value,
1715 } => {
1716 let val = self.format_value(value)?;
1717 let field_str = temporal_field_to_string(field);
1718 let prop = self.capitalize_first(&field_str);
1719 Ok(format!("{}.{} {} {}", entity_var, prop, operator, val))
1720 }
1721 }
1722 }
1723
1724 fn capitalize_first(&self, s: &str) -> String {
1726 let mut chars = s.chars();
1727 match chars.next() {
1728 None => String::new(),
1729 Some(first) => first.to_uppercase().chain(chars).collect(),
1730 }
1731 }
1732
1733 fn format_value(&self, value: &ConditionValue) -> DslResult<String> {
1735 match value {
1736 ConditionValue::Number(n) => Ok(n.to_string()),
1737 ConditionValue::String(s) => Ok(format!("\"{}\"", s.replace('"', "\\\""))),
1738 ConditionValue::Boolean(b) => Ok(if *b { "true" } else { "false" }.to_string()),
1739 ConditionValue::Date(d) => Ok(format!("\"{}\"", d)),
1740 ConditionValue::SetExpr(_) => Ok("new List<object>()".to_string()),
1741 }
1742 }
1743
1744 fn generate_method(&self, statute: &StatuteNode) -> DslResult<String> {
1746 let mut code = String::new();
1747 let method_name = self.method_name(&statute.id);
1748
1749 writeln!(&mut code, " /// <summary>").unwrap();
1750 writeln!(&mut code, " /// {}", statute.title).unwrap();
1751 writeln!(&mut code, " /// </summary>").unwrap();
1752 writeln!(
1753 &mut code,
1754 " /// <param name=\"entity\">The entity to validate</param>"
1755 )
1756 .unwrap();
1757 writeln!(
1758 &mut code,
1759 " /// <returns>Whether the statute applies</returns>"
1760 )
1761 .unwrap();
1762 writeln!(
1763 &mut code,
1764 " public static bool {}(object entity)",
1765 method_name
1766 )
1767 .unwrap();
1768 writeln!(&mut code, " {{").unwrap();
1769
1770 if statute.conditions.is_empty() {
1771 writeln!(&mut code, " return true;").unwrap();
1772 } else {
1773 write!(&mut code, " return ").unwrap();
1774 let conditions: Result<Vec<_>, _> = statute
1775 .conditions
1776 .iter()
1777 .map(|c| self.generate_condition(c, "entity"))
1778 .collect();
1779 let conditions = conditions?;
1780 writeln!(&mut code, "{};", conditions.join(" && ")).unwrap();
1781 }
1782
1783 writeln!(&mut code, " }}").unwrap();
1784 writeln!(&mut code).unwrap();
1785
1786 Ok(code)
1787 }
1788}
1789
1790impl CodeGenerator for CSharpGenerator {
1791 fn generate(&self, doc: &LegalDocument) -> DslResult<String> {
1792 let mut code = String::new();
1793
1794 writeln!(&mut code, "// Generated C# code from Legal DSL").unwrap();
1795 writeln!(&mut code, "// Total statutes: {}", doc.statutes.len()).unwrap();
1796 writeln!(&mut code).unwrap();
1797
1798 writeln!(&mut code, "using System;").unwrap();
1799 writeln!(&mut code, "using System.Linq;").unwrap();
1800 writeln!(&mut code, "using System.Collections.Generic;").unwrap();
1801 writeln!(&mut code, "using System.Text.RegularExpressions;").unwrap();
1802 writeln!(&mut code).unwrap();
1803
1804 writeln!(&mut code, "namespace {}", self.namespace).unwrap();
1805 writeln!(&mut code, "{{").unwrap();
1806 writeln!(&mut code, " public static class {}", self.class_name).unwrap();
1807 writeln!(&mut code, " {{").unwrap();
1808
1809 for statute in &doc.statutes {
1810 let method_code = self.generate_method(statute)?;
1811 code.push_str(&method_code);
1812 }
1813
1814 writeln!(&mut code, " }}").unwrap();
1815 writeln!(&mut code, "}}").unwrap();
1816
1817 Ok(code)
1818 }
1819
1820 fn target_language(&self) -> &str {
1821 "C#"
1822 }
1823
1824 fn file_extension(&self) -> &str {
1825 "cs"
1826 }
1827}
1828
1829#[cfg(test)]
1830mod tests {
1831 use super::*;
1832 use crate::ast::{ConditionValue, EffectNode};
1833
1834 fn sample_statute() -> StatuteNode {
1835 StatuteNode {
1836 id: "voting-rights".to_string(),
1837 visibility: crate::module_system::Visibility::Private,
1838 title: "Voting Rights Statute".to_string(),
1839 conditions: vec![
1840 ConditionNode::Comparison {
1841 field: "age".to_string(),
1842 operator: ">=".to_string(),
1843 value: ConditionValue::Number(18),
1844 },
1845 ConditionNode::HasAttribute {
1846 key: "citizen".to_string(),
1847 },
1848 ],
1849 effects: vec![EffectNode {
1850 effect_type: "grant".to_string(),
1851 description: "Right to vote".to_string(),
1852 parameters: vec![],
1853 }],
1854 discretion: None,
1855 exceptions: vec![],
1856 amendments: vec![],
1857 supersedes: vec![],
1858 defaults: vec![],
1859 requires: vec![],
1860 delegates: vec![],
1861 scope: None,
1862 constraints: vec![],
1863 priority: None,
1864 }
1865 }
1866
1867 #[test]
1868 fn test_sql_generation() {
1869 let doc = LegalDocument {
1870 namespace: None,
1871 exports: vec![],
1872 imports: vec![],
1873 statutes: vec![sample_statute()],
1874 };
1875
1876 let generator = SqlGenerator::new();
1877 let sql = generator.generate(&doc).unwrap();
1878
1879 assert!(sql.contains("CREATE TABLE voting_rights"));
1880 assert!(sql.contains("age"));
1881 assert!(sql.contains("citizen"));
1882 }
1883
1884 #[test]
1885 fn test_python_generation() {
1886 let doc = LegalDocument {
1887 namespace: None,
1888 exports: vec![],
1889 imports: vec![],
1890 statutes: vec![sample_statute()],
1891 };
1892
1893 let generator = PythonGenerator::new();
1894 let py = generator.generate(&doc).unwrap();
1895
1896 assert!(py.contains("def voting_rights"));
1897 assert!(py.contains("obj.age >= 18"));
1898 assert!(py.contains("hasattr(obj, 'citizen')"));
1899 }
1900
1901 #[test]
1902 fn test_sql_generator_metadata() {
1903 let generator = SqlGenerator::new();
1904 assert_eq!(generator.target_language(), "SQL");
1905 assert_eq!(generator.file_extension(), "sql");
1906 }
1907
1908 #[test]
1909 fn test_python_generator_metadata() {
1910 let generator = PythonGenerator::new();
1911 assert_eq!(generator.target_language(), "Python");
1912 assert_eq!(generator.file_extension(), "py");
1913 }
1914
1915 #[test]
1916 fn test_prolog_generation() {
1917 let doc = LegalDocument {
1918 namespace: None,
1919 exports: vec![],
1920 imports: vec![],
1921 statutes: vec![sample_statute()],
1922 };
1923
1924 let generator = PrologGenerator::new();
1925 let pl = generator.generate(&doc).unwrap();
1926
1927 assert!(pl.contains("voting_rights(Entity)"));
1928 assert!(pl.contains("Entity_age >= 18"));
1929 assert!(pl.contains("nonvar(Entity_citizen)"));
1930 }
1931
1932 #[test]
1933 fn test_prolog_generator_metadata() {
1934 let generator = PrologGenerator::new();
1935 assert_eq!(generator.target_language(), "Prolog");
1936 assert_eq!(generator.file_extension(), "pl");
1937 }
1938
1939 #[test]
1940 fn test_prolog_module_generation() {
1941 let doc = LegalDocument {
1942 namespace: None,
1943 exports: vec![],
1944 imports: vec![],
1945 statutes: vec![sample_statute()],
1946 };
1947
1948 let generator = PrologGenerator {
1949 generate_module: true,
1950 use_dynamic: true,
1951 };
1952 let pl = generator.generate(&doc).unwrap();
1953
1954 assert!(pl.contains(":- module(legal_statutes, [])"));
1955 assert!(pl.contains(":- dynamic voting_rights/1"));
1956 }
1957
1958 #[test]
1959 fn test_prolog_effect_generation() {
1960 let doc = LegalDocument {
1961 namespace: None,
1962 exports: vec![],
1963 imports: vec![],
1964 statutes: vec![sample_statute()],
1965 };
1966
1967 let generator = PrologGenerator::new();
1968 let pl = generator.generate(&doc).unwrap();
1969
1970 assert!(pl.contains("voting_rights_effect_1"));
1971 assert!(pl.contains("Right to vote"));
1972 }
1973
1974 #[test]
1975 fn test_sql_roundtrip_validation() {
1976 let doc = LegalDocument {
1977 namespace: None,
1978 exports: vec![],
1979 imports: vec![],
1980 statutes: vec![sample_statute()],
1981 };
1982
1983 let generator = SqlGenerator::new();
1984 let sql = generator.generate(&doc).unwrap();
1985
1986 assert!(sql.contains("CREATE TABLE"));
1988 assert!(sql.contains("PRIMARY KEY"));
1989 assert!(sql.contains("CHECK"));
1990
1991 assert!(!sql.contains(";;")); assert!(sql.matches('(').count() == sql.matches(')').count()); }
1995
1996 #[test]
1997 fn test_python_roundtrip_validation() {
1998 let doc = LegalDocument {
1999 namespace: None,
2000 exports: vec![],
2001 imports: vec![],
2002 statutes: vec![sample_statute()],
2003 };
2004
2005 let generator = PythonGenerator::new();
2006 let py = generator.generate(&doc).unwrap();
2007
2008 assert!(py.contains("def "));
2010 assert!(py.contains("return "));
2011 assert!(py.contains("from typing import Any"));
2012
2013 assert!(py.matches("def ").count() == py.matches("return ").count());
2015 }
2016
2017 #[test]
2018 fn test_prolog_roundtrip_validation() {
2019 let doc = LegalDocument {
2020 namespace: None,
2021 exports: vec![],
2022 imports: vec![],
2023 statutes: vec![sample_statute()],
2024 };
2025
2026 let generator = PrologGenerator::new();
2027 let pl = generator.generate(&doc).unwrap();
2028
2029 assert!(pl.contains("(Entity) :- "));
2031 assert!(pl.ends_with("\n") || pl.ends_with("."));
2032
2033 assert!(pl.matches(":-").count() <= pl.matches('.').count());
2035 }
2036
2037 #[test]
2038 fn test_complex_document_all_generators() {
2039 let complex_statute = StatuteNode {
2040 id: "complex-law".to_string(),
2041 visibility: crate::module_system::Visibility::Private,
2042 title: "Complex Law Test".to_string(),
2043 conditions: vec![ConditionNode::And(
2044 Box::new(ConditionNode::Comparison {
2045 field: "age".to_string(),
2046 operator: ">=".to_string(),
2047 value: ConditionValue::Number(18),
2048 }),
2049 Box::new(ConditionNode::In {
2050 field: "status".to_string(),
2051 values: vec![
2052 ConditionValue::String("citizen".to_string()),
2053 ConditionValue::String("resident".to_string()),
2054 ],
2055 }),
2056 )],
2057 effects: vec![
2058 EffectNode {
2059 effect_type: "GRANT".to_string(),
2060 description: "Voting rights".to_string(),
2061 parameters: vec![],
2062 },
2063 EffectNode {
2064 effect_type: "OBLIGATION".to_string(),
2065 description: "Register to vote".to_string(),
2066 parameters: vec![],
2067 },
2068 ],
2069 discretion: None,
2070 exceptions: vec![],
2071 amendments: vec![],
2072 supersedes: vec![],
2073 defaults: vec![],
2074 requires: vec![],
2075 delegates: vec![],
2076 scope: None,
2077 constraints: vec![],
2078 priority: None,
2079 };
2080
2081 let doc = LegalDocument {
2082 namespace: None,
2083 exports: vec![],
2084 imports: vec![],
2085 statutes: vec![complex_statute],
2086 };
2087
2088 let sql_gen = SqlGenerator::new();
2090 let sql = sql_gen.generate(&doc).unwrap();
2091 assert!(sql.len() > 100);
2092
2093 let py_gen = PythonGenerator::new();
2094 let py = py_gen.generate(&doc).unwrap();
2095 assert!(py.len() > 100);
2096
2097 let pl_gen = PrologGenerator::new();
2098 let pl = pl_gen.generate(&doc).unwrap();
2099 assert!(pl.len() > 100);
2100
2101 let ts_gen = TypeScriptGenerator::new();
2103 let ts = ts_gen.generate(&doc).unwrap();
2104 assert!(ts.len() > 100);
2105
2106 let rust_gen = RustGenerator::new();
2107 let rust = rust_gen.generate(&doc).unwrap();
2108 assert!(rust.len() > 100);
2109
2110 let go_gen = GoGenerator::new();
2111 let go = go_gen.generate(&doc).unwrap();
2112 assert!(go.len() > 100);
2113
2114 let java_gen = JavaGenerator::new();
2115 let java = java_gen.generate(&doc).unwrap();
2116 assert!(java.len() > 100);
2117
2118 let cs_gen = CSharpGenerator::new();
2119 let cs = cs_gen.generate(&doc).unwrap();
2120 assert!(cs.len() > 100);
2121 }
2122
2123 #[test]
2124 fn test_typescript_generation() {
2125 let doc = LegalDocument {
2126 namespace: None,
2127 exports: vec![],
2128 imports: vec![],
2129 statutes: vec![sample_statute()],
2130 };
2131
2132 let generator = TypeScriptGenerator::new();
2133 let ts = generator.generate(&doc).unwrap();
2134
2135 assert!(ts.contains("export function"));
2136 assert!(ts.contains("voting_rights"));
2137 assert!(ts.contains("entity"));
2138 assert!(ts.contains(": boolean"));
2139 }
2140
2141 #[test]
2142 fn test_rust_generation() {
2143 let doc = LegalDocument {
2144 namespace: None,
2145 exports: vec![],
2146 imports: vec![],
2147 statutes: vec![sample_statute()],
2148 };
2149
2150 let generator = RustGenerator::new();
2151 let rs = generator.generate(&doc).unwrap();
2152
2153 assert!(rs.contains("pub fn voting_rights"));
2154 assert!(rs.contains("-> bool"));
2155 assert!(rs.contains("use regex::Regex"));
2156 }
2157
2158 #[test]
2159 fn test_go_generation() {
2160 let doc = LegalDocument {
2161 namespace: None,
2162 exports: vec![],
2163 imports: vec![],
2164 statutes: vec![sample_statute()],
2165 };
2166
2167 let generator = GoGenerator::new();
2168 let go = generator.generate(&doc).unwrap();
2169
2170 assert!(go.contains("package statutes"));
2171 assert!(go.contains("func Voting_rights"));
2172 assert!(go.contains("bool"));
2173 assert!(go.contains("import ("));
2174 }
2175
2176 #[test]
2177 fn test_java_generation() {
2178 let doc = LegalDocument {
2179 namespace: None,
2180 exports: vec![],
2181 imports: vec![],
2182 statutes: vec![sample_statute()],
2183 };
2184
2185 let generator = JavaGenerator::new();
2186 let java = generator.generate(&doc).unwrap();
2187
2188 assert!(java.contains("public class StatuteValidator"));
2189 assert!(java.contains("public static boolean votingRights"));
2190 assert!(java.contains("package com.legal.statutes"));
2191 assert!(java.contains("import java.util"));
2192 }
2193
2194 #[test]
2195 fn test_typescript_javascript_mode() {
2196 let doc = LegalDocument {
2197 namespace: None,
2198 exports: vec![],
2199 imports: vec![],
2200 statutes: vec![sample_statute()],
2201 };
2202
2203 let mut generator = TypeScriptGenerator::new();
2204 generator.use_typescript = false;
2205
2206 let js = generator.generate(&doc).unwrap();
2207 assert!(js.contains("export function"));
2208 assert!(!js.contains(": boolean"));
2209 assert_eq!(generator.file_extension(), "js");
2210 assert_eq!(generator.target_language(), "JavaScript");
2211 }
2212
2213 #[test]
2214 fn test_csharp_generation() {
2215 let doc = LegalDocument {
2216 namespace: None,
2217 exports: vec![],
2218 imports: vec![],
2219 statutes: vec![sample_statute()],
2220 };
2221
2222 let generator = CSharpGenerator::new();
2223 let cs = generator.generate(&doc).unwrap();
2224
2225 assert!(cs.contains("namespace Legal.Statutes"));
2226 assert!(cs.contains("public static class StatuteValidator"));
2227 assert!(cs.contains("public static bool VotingRights"));
2228 assert!(cs.contains("using System"));
2229 }
2230
2231 #[test]
2232 fn test_all_generators_file_extensions() {
2233 assert_eq!(SqlGenerator::new().file_extension(), "sql");
2234 assert_eq!(PythonGenerator::new().file_extension(), "py");
2235 assert_eq!(PrologGenerator::new().file_extension(), "pl");
2236 assert_eq!(TypeScriptGenerator::new().file_extension(), "ts");
2237 assert_eq!(RustGenerator::new().file_extension(), "rs");
2238 assert_eq!(GoGenerator::new().file_extension(), "go");
2239 assert_eq!(JavaGenerator::new().file_extension(), "java");
2240 assert_eq!(CSharpGenerator::new().file_extension(), "cs");
2241 }
2242
2243 #[test]
2244 fn test_all_generators_target_languages() {
2245 assert_eq!(SqlGenerator::new().target_language(), "SQL");
2246 assert_eq!(PythonGenerator::new().target_language(), "Python");
2247 assert_eq!(PrologGenerator::new().target_language(), "Prolog");
2248 assert_eq!(TypeScriptGenerator::new().target_language(), "TypeScript");
2249 assert_eq!(RustGenerator::new().target_language(), "Rust");
2250 assert_eq!(GoGenerator::new().target_language(), "Go");
2251 assert_eq!(JavaGenerator::new().target_language(), "Java");
2252 assert_eq!(CSharpGenerator::new().target_language(), "C#");
2253 }
2254}