drools-builder 0.1.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -375,6 +375,7 @@ function resolveRule(input) {
375
375
  var DroolsFileBuilder = class {
376
376
  constructor(name) {
377
377
  this._imports = [];
378
+ this._globals = [];
378
379
  this._rules = [];
379
380
  this._name = name;
380
381
  }
@@ -389,6 +390,17 @@ var DroolsFileBuilder = class {
389
390
  this._imports.push(className);
390
391
  return this;
391
392
  }
393
+ /**
394
+ * Declare a global variable available to all rules in this file.
395
+ * Emits `global type name;` in the DRL header.
396
+ *
397
+ * @example
398
+ * .global('com.example.AlertService', 'alertService')
399
+ */
400
+ global(type, name) {
401
+ this._globals.push({ type, name });
402
+ return this;
403
+ }
392
404
  /**
393
405
  * Add a rule to the file.
394
406
  * Accepts a plain Rule object or a RuleBuilder (auto-resolved via .build()).
@@ -405,6 +417,7 @@ var DroolsFileBuilder = class {
405
417
  return {
406
418
  name: this._name,
407
419
  imports: [...this._imports],
420
+ globals: [...this._globals],
408
421
  rules: [...this._rules]
409
422
  };
410
423
  }
@@ -469,6 +482,144 @@ function rawConsequence(code) {
469
482
  return { kind: "RawConsequence", code };
470
483
  }
471
484
 
485
+ // src/rule-builder/parser/MetaToDRLTransformer.ts
486
+ function generateConstraint(c) {
487
+ switch (c.kind) {
488
+ case "FieldConstraint":
489
+ return `${c.binding ? `${c.binding} : ` : ""}${c.field} ${c.operator} ${c.value}`;
490
+ case "BindingConstraint":
491
+ return `${c.binding} : ${c.field}`;
492
+ case "RawConstraint":
493
+ return c.expression;
494
+ }
495
+ }
496
+ function generateCondition(cond, indent = " ") {
497
+ switch (cond.kind) {
498
+ case "FactPattern": {
499
+ const binding = cond.binding ? `${cond.binding} : ` : "";
500
+ const constraints = cond.constraints.map(generateConstraint).join(", ");
501
+ return `${binding}${cond.factType}( ${constraints} )`;
502
+ }
503
+ case "UnboundPattern": {
504
+ const constraints = cond.constraints.map(generateConstraint).join(", ");
505
+ return `${cond.factType}( ${constraints} )`;
506
+ }
507
+ case "And": {
508
+ const parts = cond.conditions.map((c) => generateCondition(c, indent + " "));
509
+ return `( ${parts.join(`
510
+ ${indent} and `)} )`;
511
+ }
512
+ case "Or": {
513
+ const parts = cond.conditions.map((c) => generateCondition(c, indent + " "));
514
+ return `( ${parts.join(`
515
+ ${indent} or `)} )`;
516
+ }
517
+ case "Not":
518
+ return `not( ${generateCondition(cond.condition, indent)} )`;
519
+ case "Exists":
520
+ return `exists( ${generateCondition(cond.condition, indent)} )`;
521
+ case "Forall":
522
+ return `forall( ${generateCondition(cond.condition, indent)} )`;
523
+ case "Accumulate": {
524
+ const source = generateCondition(cond.source, indent + " ");
525
+ const fns = cond.functions.map((f) => `${f.binding} : ${f.function}( ${f.argument} )`).join(", ");
526
+ const result = cond.resultConstraint ? `;
527
+ ${indent} ${cond.resultConstraint}` : "";
528
+ return `accumulate(
529
+ ${indent} ${source};
530
+ ${indent} ${fns}${result}
531
+ ${indent})`;
532
+ }
533
+ case "From":
534
+ return `${generateCondition(cond.pattern, indent)} from ${cond.expression}`;
535
+ case "Eval":
536
+ return `eval( ${cond.expression} )`;
537
+ case "RawCondition":
538
+ return cond.drl;
539
+ }
540
+ }
541
+ function generateWhenBlock(conditions, indent = " ") {
542
+ const flat = conditions.length === 1 && conditions[0].kind === "And" ? conditions[0].conditions : conditions;
543
+ return flat.map((c) => `${indent}${generateCondition(c, indent)}`).join("\n");
544
+ }
545
+ function generateConsequence(cons, indent = " ") {
546
+ switch (cons.kind) {
547
+ case "ModifyConsequence": {
548
+ const mods = cons.modifications.map((m) => `${m.method}( ${m.args.join(", ")} )`).join(`,
549
+ ${indent} `);
550
+ return `modify( ${cons.binding} ) {
551
+ ${indent} ${mods}
552
+ ${indent}}`;
553
+ }
554
+ case "InsertConsequence":
555
+ return `insert( ${cons.objectExpression} );`;
556
+ case "RetractConsequence":
557
+ return `retract( ${cons.binding} );`;
558
+ case "SetGlobalConsequence":
559
+ return `${cons.expression};`;
560
+ case "RawConsequence":
561
+ return `${cons.code};`;
562
+ case "ReturnConsequence":
563
+ return cons.expression ? `return ${cons.expression};` : "return;";
564
+ case "IfConsequence": {
565
+ const thenLines = cons.then.map((c) => `${indent} ${generateConsequence(c, indent + " ")}`).join("\n");
566
+ if (cons.else && cons.else.length > 0) {
567
+ const elseLines = cons.else.map((c) => `${indent} ${generateConsequence(c, indent + " ")}`).join("\n");
568
+ return `if (${cons.condition}) {
569
+ ${thenLines}
570
+ ${indent}} else {
571
+ ${elseLines}
572
+ ${indent}}`;
573
+ }
574
+ return `if (${cons.condition}) {
575
+ ${thenLines}
576
+ ${indent}}`;
577
+ }
578
+ }
579
+ }
580
+ function generateDeclaration(decl) {
581
+ const attrs = decl.attributes.map((a) => ` ${a.name} : ${a.type}`).join("\n");
582
+ return `declare ${decl.className}${attrs ? "\n" + attrs + "\n" : "\n"}end`;
583
+ }
584
+ function generateFunction(fn, indent = " ") {
585
+ const body = fn.body.map((c) => `${indent}${generateConsequence(c, indent)}`).join("\n");
586
+ return `function ${fn.returnType} ${fn.name}(${fn.params}) {
587
+ ${body}
588
+ }`;
589
+ }
590
+ function generateRule(rule) {
591
+ const lines = [`rule "${rule.name}"`];
592
+ if (rule.salience !== void 0) lines.push(` salience ${rule.salience}`);
593
+ if (rule.agendaGroup !== void 0) lines.push(` agenda-group "${rule.agendaGroup}"`);
594
+ if (rule.ruleFlowGroup !== void 0) lines.push(` ruleflow-group "${rule.ruleFlowGroup}"`);
595
+ if (rule.noLoop) lines.push(" no-loop true");
596
+ if (rule.lockOnActive) lines.push(" lock-on-active true");
597
+ lines.push(" when");
598
+ lines.push(generateWhenBlock(rule.conditions));
599
+ lines.push(" then");
600
+ lines.push(rule.consequences.map((c) => ` ${generateConsequence(c)}`).join("\n"));
601
+ lines.push("end");
602
+ return lines.join("\n");
603
+ }
604
+ var MetaToDRLTransformer = {
605
+ generate(file) {
606
+ const sections = [];
607
+ if (file.imports.length > 0)
608
+ sections.push(file.imports.map((i) => `import ${i};`).join("\n"));
609
+ if (file.globals.length > 0)
610
+ sections.push(file.globals.map((g) => `global ${g.type} ${g.name};`).join("\n"));
611
+ if (file.declarations && file.declarations.length > 0)
612
+ sections.push(file.declarations.map((d) => generateDeclaration(d)).join("\n\n"));
613
+ if (file.functions && file.functions.length > 0)
614
+ sections.push(file.functions.map((fn) => generateFunction(fn)).join("\n\n"));
615
+ sections.push(file.rules.map(generateRule).join("\n\n"));
616
+ return sections.join("\n\n");
617
+ },
618
+ generateRule(rule) {
619
+ return generateRule(rule);
620
+ }
621
+ };
622
+
472
623
  // src/rule-builder/parser/DRLToMetaTransformer.ts
473
624
  function indexAtDepth0(text, needle) {
474
625
  let depth = 0;
@@ -569,26 +720,63 @@ function parseImports(drl) {
569
720
  while ((m = re.exec(drl)) !== null) imports.push(m[1].trim());
570
721
  return imports;
571
722
  }
723
+ function parseGlobals(drl) {
724
+ const globals = [];
725
+ const re = /^\s*global\s+(\S+)\s+(\S+?)\s*;?$/gm;
726
+ let m;
727
+ while ((m = re.exec(drl)) !== null) globals.push({ type: m[1], name: m[2] });
728
+ return globals;
729
+ }
572
730
  function extractRuleBlocks(drl) {
573
731
  const blocks = [];
574
- const re = /\brule\s+"[^"]*"[\s\S]*?\bend\b/g;
732
+ const re = /\brule\s+(?:"[^"]*"|'[^']*')[\s\S]*?\bend\b/g;
575
733
  let m;
576
734
  while ((m = re.exec(drl)) !== null) blocks.push(m[0]);
577
735
  return blocks;
578
736
  }
737
+ function parseFunctions(drl) {
738
+ const results = [];
739
+ const re = /\bfunction\s+([\w<>?,\s\[\]]+?)\s+(\w+)\s*\(([^)]*)\)\s*\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}/g;
740
+ let m;
741
+ while ((m = re.exec(drl)) !== null) {
742
+ results.push({
743
+ returnType: m[1].trim(),
744
+ name: m[2].trim(),
745
+ params: m[3].trim(),
746
+ body: parseConsequences(m[4].trim())
747
+ });
748
+ }
749
+ return results;
750
+ }
751
+ function parseDeclarations(drl) {
752
+ const results = [];
753
+ const re = /\bdeclare\s+(\w+)\s*([\s\S]*?)\bend\b/g;
754
+ let m;
755
+ while ((m = re.exec(drl)) !== null) {
756
+ const className = m[1].trim();
757
+ const attributes = [];
758
+ const attrRe = /^\s*(\w+)\s*:\s*(\S+)/gm;
759
+ let a;
760
+ while ((a = attrRe.exec(m[2])) !== null) {
761
+ attributes.push({ name: a[1].trim(), type: a[2].trim() });
762
+ }
763
+ results.push({ className, attributes });
764
+ }
765
+ return results;
766
+ }
579
767
  function parseRuleName(block) {
580
- const m = block.match(/\brule\s+"([^"]+)"/);
581
- return m ? m[1] : "unknown";
768
+ const m = block.match(/\brule\s+(?:"([^"]+)"|'([^']+)')/);
769
+ return m ? m[1] ?? m[2] : "unknown";
582
770
  }
583
771
  function parseRuleAttributes(block) {
584
772
  const attrs = {};
585
- const m = block.match(/\brule\s+"[^"]+"\s*([\s\S]*?)\bwhen\b/);
773
+ const m = block.match(/\brule\s+(?:"[^"]+"|'[^']+')\s*([\s\S]*?)\bwhen\b/);
586
774
  if (!m) return attrs;
587
775
  const attr = m[1];
588
776
  const salience = attr.match(/\bsalience\s+(-?\d+)/);
589
777
  if (salience) attrs.salience = parseInt(salience[1], 10);
590
- if (/\bno-loop\s+true\b/.test(attr)) attrs.noLoop = true;
591
- if (/\block-on-active\s+true\b/.test(attr)) attrs.lockOnActive = true;
778
+ if (!/\bno-loop\s+false\b/.test(attr) && /\bno-loop\b/.test(attr)) attrs.noLoop = true;
779
+ if (!/\block-on-active\s+false\b/.test(attr) && /\block-on-active\b/.test(attr)) attrs.lockOnActive = true;
592
780
  const ag = attr.match(/\bagenda-group\s+"([^"]+)"/);
593
781
  if (ag) attrs.agendaGroup = ag[1];
594
782
  const rfg = attr.match(/\bruleflow-group\s+"([^"]+)"/);
@@ -768,6 +956,23 @@ function parseConsequences(then) {
768
956
  function parseNextConsequence(text) {
769
957
  const t = text.trim();
770
958
  if (!t) return null;
959
+ if (/^if\s*\(/.test(t)) {
960
+ const condition = extractBalanced(t, "(", ")");
961
+ const afterCond = t.slice(t.indexOf("(") + condition.length + 2).trim();
962
+ const thenBlock = extractBalanced(afterCond, "{", "}");
963
+ const thenConsequences = parseConsequences(thenBlock);
964
+ let afterThen = afterCond.slice(afterCond.indexOf("{") + thenBlock.length + 2).trim();
965
+ let elseConsequences;
966
+ if (/^else\s*\{/.test(afterThen)) {
967
+ const elseBlock = extractBalanced(afterThen, "{", "}");
968
+ elseConsequences = parseConsequences(elseBlock);
969
+ afterThen = afterThen.slice(afterThen.indexOf("{") + elseBlock.length + 2).trim();
970
+ }
971
+ return {
972
+ consequence: { kind: "IfConsequence", condition: condition.trim(), then: thenConsequences, ...elseConsequences && { else: elseConsequences } },
973
+ rest: afterThen
974
+ };
975
+ }
771
976
  if (/^modify\s*\(/.test(t)) {
772
977
  const m = t.match(/^modify\s*\(\s*(\$\w+)\s*\)/);
773
978
  if (m) {
@@ -790,6 +995,14 @@ function parseNextConsequence(text) {
790
995
  const rest = t.slice(t.indexOf("(") + inner.length + 2).replace(/^\s*;/, "");
791
996
  return { consequence: { kind: "RetractConsequence", binding: inner.trim() }, rest };
792
997
  }
998
+ if (/^return\b/.test(t)) {
999
+ const semiIdx2 = indexAtDepth0(t, ";");
1000
+ const expression = t.slice("return".length, semiIdx2 !== -1 ? semiIdx2 : void 0).trim();
1001
+ return {
1002
+ consequence: { kind: "ReturnConsequence", expression },
1003
+ rest: semiIdx2 !== -1 ? t.slice(semiIdx2 + 1) : ""
1004
+ };
1005
+ }
793
1006
  const semiIdx = indexAtDepth0(t, ";");
794
1007
  if (semiIdx !== -1)
795
1008
  return { consequence: { kind: "RawConsequence", code: t.slice(0, semiIdx).trim() }, rest: t.slice(semiIdx + 1) };
@@ -808,7 +1021,10 @@ var DRLToMetaTransformer = {
808
1021
  const clean = stripComments(drl);
809
1022
  return {
810
1023
  name: "parsed",
811
- imports: parseImports(clean),
1024
+ imports: [...new Set(parseImports(clean))],
1025
+ globals: parseGlobals(clean),
1026
+ declarations: parseDeclarations(clean),
1027
+ functions: parseFunctions(clean),
812
1028
  rules: extractRuleBlocks(clean).map((block) => DRLToMetaTransformer.parseRule(block))
813
1029
  };
814
1030
  },
@@ -821,112 +1037,6 @@ var DRLToMetaTransformer = {
821
1037
  };
822
1038
  }
823
1039
  };
824
-
825
- // src/rule-builder/parser/MetaToDRLTransformer.ts
826
- function generateConstraint(c) {
827
- switch (c.kind) {
828
- case "FieldConstraint":
829
- return `${c.binding ? `${c.binding} : ` : ""}${c.field} ${c.operator} ${c.value}`;
830
- case "BindingConstraint":
831
- return `${c.binding} : ${c.field}`;
832
- case "RawConstraint":
833
- return c.expression;
834
- }
835
- }
836
- function generateCondition(cond, indent = " ") {
837
- switch (cond.kind) {
838
- case "FactPattern": {
839
- const binding = cond.binding ? `${cond.binding} : ` : "";
840
- const constraints = cond.constraints.map(generateConstraint).join(", ");
841
- return `${binding}${cond.factType}( ${constraints} )`;
842
- }
843
- case "UnboundPattern": {
844
- const constraints = cond.constraints.map(generateConstraint).join(", ");
845
- return `${cond.factType}( ${constraints} )`;
846
- }
847
- case "And": {
848
- const parts = cond.conditions.map((c) => generateCondition(c, indent + " "));
849
- return `( ${parts.join(`
850
- ${indent} and `)} )`;
851
- }
852
- case "Or": {
853
- const parts = cond.conditions.map((c) => generateCondition(c, indent + " "));
854
- return `( ${parts.join(`
855
- ${indent} or `)} )`;
856
- }
857
- case "Not":
858
- return `not( ${generateCondition(cond.condition, indent)} )`;
859
- case "Exists":
860
- return `exists( ${generateCondition(cond.condition, indent)} )`;
861
- case "Forall":
862
- return `forall( ${generateCondition(cond.condition, indent)} )`;
863
- case "Accumulate": {
864
- const source = generateCondition(cond.source, indent + " ");
865
- const fns = cond.functions.map((f) => `${f.binding} : ${f.function}( ${f.argument} )`).join(", ");
866
- const result = cond.resultConstraint ? `;
867
- ${indent} ${cond.resultConstraint}` : "";
868
- return `accumulate(
869
- ${indent} ${source};
870
- ${indent} ${fns}${result}
871
- ${indent})`;
872
- }
873
- case "From":
874
- return `${generateCondition(cond.pattern, indent)} from ${cond.expression}`;
875
- case "Eval":
876
- return `eval( ${cond.expression} )`;
877
- case "RawCondition":
878
- return cond.drl;
879
- }
880
- }
881
- function generateWhenBlock(conditions, indent = " ") {
882
- const flat = conditions.length === 1 && conditions[0].kind === "And" ? conditions[0].conditions : conditions;
883
- return flat.map((c) => `${indent}${generateCondition(c, indent)}`).join("\n");
884
- }
885
- function generateConsequence(cons, indent = " ") {
886
- switch (cons.kind) {
887
- case "ModifyConsequence": {
888
- const mods = cons.modifications.map((m) => `${m.method}( ${m.args.join(", ")} )`).join(`,
889
- ${indent} `);
890
- return `modify( ${cons.binding} ) {
891
- ${indent} ${mods}
892
- ${indent}}`;
893
- }
894
- case "InsertConsequence":
895
- return `insert( ${cons.objectExpression} );`;
896
- case "RetractConsequence":
897
- return `retract( ${cons.binding} );`;
898
- case "SetGlobalConsequence":
899
- return `${cons.expression};`;
900
- case "RawConsequence":
901
- return `${cons.code};`;
902
- }
903
- }
904
- function generateRule(rule) {
905
- const lines = [`rule "${rule.name}"`];
906
- if (rule.salience !== void 0) lines.push(` salience ${rule.salience}`);
907
- if (rule.agendaGroup !== void 0) lines.push(` agenda-group "${rule.agendaGroup}"`);
908
- if (rule.ruleFlowGroup !== void 0) lines.push(` ruleflow-group "${rule.ruleFlowGroup}"`);
909
- if (rule.noLoop) lines.push(" no-loop true");
910
- if (rule.lockOnActive) lines.push(" lock-on-active true");
911
- lines.push(" when");
912
- lines.push(generateWhenBlock(rule.conditions));
913
- lines.push(" then");
914
- lines.push(rule.consequences.map((c) => ` ${generateConsequence(c)}`).join("\n"));
915
- lines.push("end");
916
- return lines.join("\n");
917
- }
918
- var MetaToDRLTransformer = {
919
- generate(file) {
920
- const sections = [];
921
- if (file.imports.length > 0)
922
- sections.push(file.imports.map((i) => `import ${i};`).join("\n"));
923
- sections.push(file.rules.map(generateRule).join("\n\n"));
924
- return sections.join("\n\n");
925
- },
926
- generateRule(rule) {
927
- return generateRule(rule);
928
- }
929
- };
930
1040
  export {
931
1041
  AccumulateBuilder,
932
1042
  Aggregate,