yukigo 0.1.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/.mocharc.json +4 -0
- package/CHANGELOG.md +6 -0
- package/README.md +199 -0
- package/dist/analyzer/index.d.ts +71 -0
- package/dist/analyzer/index.js +110 -0
- package/dist/analyzer/inspections/functional.d.ts +46 -0
- package/dist/analyzer/inspections/functional.js +123 -0
- package/dist/analyzer/inspections/generic.d.ts +151 -0
- package/dist/analyzer/inspections/generic.js +427 -0
- package/dist/analyzer/inspections/imperative.d.ts +37 -0
- package/dist/analyzer/inspections/imperative.js +105 -0
- package/dist/analyzer/inspections/logic.d.ts +49 -0
- package/dist/analyzer/inspections/logic.js +140 -0
- package/dist/analyzer/inspections/object.d.ts +83 -0
- package/dist/analyzer/inspections/object.js +235 -0
- package/dist/analyzer/utils.d.ts +4 -0
- package/dist/analyzer/utils.js +16 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/interpreter/components/EnvBuilder.d.ts +16 -0
- package/dist/interpreter/components/EnvBuilder.js +78 -0
- package/dist/interpreter/components/FunctionRuntime.d.ts +8 -0
- package/dist/interpreter/components/FunctionRuntime.js +52 -0
- package/dist/interpreter/components/LazyRuntime.d.ts +7 -0
- package/dist/interpreter/components/LazyRuntime.js +75 -0
- package/dist/interpreter/components/LogicEngine.d.ts +21 -0
- package/dist/interpreter/components/LogicEngine.js +152 -0
- package/dist/interpreter/components/LogicResolver.d.ts +11 -0
- package/dist/interpreter/components/LogicResolver.js +87 -0
- package/dist/interpreter/components/Operations.d.ts +14 -0
- package/dist/interpreter/components/Operations.js +69 -0
- package/dist/interpreter/components/PatternMatcher.d.ts +41 -0
- package/dist/interpreter/components/PatternMatcher.js +206 -0
- package/dist/interpreter/components/Visitor.d.ts +64 -0
- package/dist/interpreter/components/Visitor.js +299 -0
- package/dist/interpreter/errors.d.ts +19 -0
- package/dist/interpreter/errors.js +36 -0
- package/dist/interpreter/index.d.ts +32 -0
- package/dist/interpreter/index.js +44 -0
- package/dist/interpreter/utils.d.ts +14 -0
- package/dist/interpreter/utils.js +57 -0
- package/dist/utils/helpers.d.ts +14 -0
- package/dist/utils/helpers.js +51 -0
- package/package.json +30 -0
- package/src/analyzer/index.ts +132 -0
- package/src/analyzer/inspections/functional.ts +159 -0
- package/src/analyzer/inspections/generic.ts +499 -0
- package/src/analyzer/inspections/imperative.ts +129 -0
- package/src/analyzer/inspections/logic.ts +166 -0
- package/src/analyzer/inspections/object.ts +282 -0
- package/src/analyzer/utils.ts +26 -0
- package/src/index.ts +3 -0
- package/src/interpreter/components/EnvBuilder.ts +97 -0
- package/src/interpreter/components/FunctionRuntime.ts +79 -0
- package/src/interpreter/components/LazyRuntime.ts +97 -0
- package/src/interpreter/components/LogicEngine.ts +227 -0
- package/src/interpreter/components/LogicResolver.ts +130 -0
- package/src/interpreter/components/Operations.ts +81 -0
- package/src/interpreter/components/PatternMatcher.ts +254 -0
- package/src/interpreter/components/Visitor.ts +493 -0
- package/src/interpreter/errors.ts +47 -0
- package/src/interpreter/index.ts +59 -0
- package/src/interpreter/utils.ts +79 -0
- package/src/utils/helpers.ts +73 -0
- package/tests/analyzer/functional.spec.ts +221 -0
- package/tests/analyzer/generic.spec.ts +100 -0
- package/tests/analyzer/helpers.spec.ts +83 -0
- package/tests/analyzer/logic.spec.ts +292 -0
- package/tests/analyzer/oop.spec.ts +338 -0
- package/tests/interpreter/EnvBuilder.spec.ts +178 -0
- package/tests/interpreter/FunctionRuntime.spec.ts +234 -0
- package/tests/interpreter/LazyRuntime.spec.ts +190 -0
- package/tests/interpreter/LogicEngine.spec.ts +194 -0
- package/tests/interpreter/Operations.spec.ts +220 -0
- package/tests/interpreter/PatternSystem.spec.ts +189 -0
- package/tests/interpreter/interpreter.spec.ts +937 -0
- package/tsconfig.build.json +7 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { isRuntimePredicate, TraverseVisitor, } from "@yukigo/ast";
|
|
2
|
+
import { define } from "../utils.js";
|
|
3
|
+
/**
|
|
4
|
+
* Builds the initial environment by collecting all top-level function declarations.
|
|
5
|
+
* Each function captures a closure of the environment at its definition time,
|
|
6
|
+
* allowing recursion by including itself in the closure.
|
|
7
|
+
*/
|
|
8
|
+
export class EnvBuilderVisitor extends TraverseVisitor {
|
|
9
|
+
env;
|
|
10
|
+
constructor(baseEnv) {
|
|
11
|
+
super();
|
|
12
|
+
this.env = baseEnv ?? [new Map()];
|
|
13
|
+
}
|
|
14
|
+
build(ast) {
|
|
15
|
+
for (const node of ast)
|
|
16
|
+
node.accept(this);
|
|
17
|
+
return this.env;
|
|
18
|
+
}
|
|
19
|
+
visitFunction(node) {
|
|
20
|
+
const name = node.identifier.value;
|
|
21
|
+
if (node.equations.length === 0)
|
|
22
|
+
throw new Error(`Function ${name} has no equations`);
|
|
23
|
+
const arity = node.equations[0].patterns.length;
|
|
24
|
+
for (const eq of node.equations) {
|
|
25
|
+
if (eq.patterns.length !== arity)
|
|
26
|
+
throw new Error(`All equations of ${name} must have the same arity`);
|
|
27
|
+
}
|
|
28
|
+
let placeholder;
|
|
29
|
+
define(this.env, name, placeholder);
|
|
30
|
+
const equations = node.equations.map((eq) => ({
|
|
31
|
+
patterns: eq.patterns,
|
|
32
|
+
body: eq.body,
|
|
33
|
+
}));
|
|
34
|
+
const runtimeFunc = {
|
|
35
|
+
identifier: name,
|
|
36
|
+
arity,
|
|
37
|
+
equations,
|
|
38
|
+
};
|
|
39
|
+
define(this.env, name, runtimeFunc);
|
|
40
|
+
}
|
|
41
|
+
visitFact(node) {
|
|
42
|
+
const identifier = node.identifier.value;
|
|
43
|
+
const runtimeValue = this.env[0].get(identifier);
|
|
44
|
+
if (isRuntimePredicate(runtimeValue) && runtimeValue.kind === "Fact") {
|
|
45
|
+
this.env[0].set(identifier, {
|
|
46
|
+
...runtimeValue,
|
|
47
|
+
equations: [...runtimeValue.equations, node],
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this.env[0].set(identifier, {
|
|
52
|
+
kind: "Fact",
|
|
53
|
+
identifier,
|
|
54
|
+
equations: [node],
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
visitRule(node) {
|
|
59
|
+
const identifier = node.identifier.value;
|
|
60
|
+
const runtimeValue = this.env[0].get(identifier);
|
|
61
|
+
if (isRuntimePredicate(runtimeValue) && runtimeValue.kind === "Rule") {
|
|
62
|
+
this.env[0].set(identifier, {
|
|
63
|
+
...runtimeValue,
|
|
64
|
+
equations: [...runtimeValue.equations, node],
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
this.env[0].set(identifier, {
|
|
69
|
+
kind: "Rule",
|
|
70
|
+
identifier,
|
|
71
|
+
equations: [node],
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
visit(node) {
|
|
76
|
+
return node.accept(this);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { EquationRuntime, PrimitiveValue } from "@yukigo/ast";
|
|
2
|
+
import { EnvStack } from "../index.js";
|
|
3
|
+
import { ExpressionEvaluator } from "../utils.js";
|
|
4
|
+
export declare class FunctionRuntime {
|
|
5
|
+
static apply(funcName: string, equations: EquationRuntime[], args: PrimitiveValue[], currentEnv: EnvStack, evaluatorFactory: (env: EnvStack) => ExpressionEvaluator): PrimitiveValue;
|
|
6
|
+
private static patternsMatch;
|
|
7
|
+
private static evaluateSequence;
|
|
8
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { UnguardedBody, Return, } from "@yukigo/ast";
|
|
2
|
+
import { PatternMatcher } from "./PatternMatcher.js";
|
|
3
|
+
import { InterpreterError } from "../errors.js";
|
|
4
|
+
class NonExhaustivePatterns extends InterpreterError {
|
|
5
|
+
constructor(funcName) {
|
|
6
|
+
super("PatternMatch", `Non-exhaustive patterns in '${funcName}'`);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export class FunctionRuntime {
|
|
10
|
+
static apply(funcName, equations, args, currentEnv, evaluatorFactory) {
|
|
11
|
+
for (const eq of equations) {
|
|
12
|
+
if (eq.patterns.length !== args.length)
|
|
13
|
+
continue;
|
|
14
|
+
const bindings = [];
|
|
15
|
+
if (!FunctionRuntime.patternsMatch(eq, args, bindings))
|
|
16
|
+
continue;
|
|
17
|
+
const localEnv = new Map(bindings);
|
|
18
|
+
const newStack = [localEnv, ...currentEnv];
|
|
19
|
+
const scopeEvaluator = evaluatorFactory(newStack);
|
|
20
|
+
// UnguardedBody
|
|
21
|
+
if (eq.body instanceof UnguardedBody)
|
|
22
|
+
return this.evaluateSequence(eq.body.sequence, scopeEvaluator);
|
|
23
|
+
// GuardedBody
|
|
24
|
+
for (const guard of eq.body) {
|
|
25
|
+
const cond = scopeEvaluator.evaluate(guard.condition);
|
|
26
|
+
if (cond === true)
|
|
27
|
+
return scopeEvaluator.evaluate(guard.body);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
throw new NonExhaustivePatterns(funcName);
|
|
31
|
+
}
|
|
32
|
+
static patternsMatch(eq, args, bindings) {
|
|
33
|
+
for (let i = 0; i < args.length; i++) {
|
|
34
|
+
const matcher = new PatternMatcher(args[i], bindings);
|
|
35
|
+
if (!eq.patterns[i].accept(matcher))
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
static evaluateSequence(seq, evaluator) {
|
|
41
|
+
let result = undefined;
|
|
42
|
+
for (const stmt of seq.statements) {
|
|
43
|
+
if (stmt instanceof Return) {
|
|
44
|
+
return evaluator.evaluate(stmt.body);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
result = evaluator.evaluate(stmt);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { PrimitiveValue, RangeExpression, ConsExpression } from "@yukigo/ast";
|
|
2
|
+
import { ExpressionEvaluator } from "../utils.js";
|
|
3
|
+
export declare class LazyRuntime {
|
|
4
|
+
static realizeList(val: PrimitiveValue): PrimitiveValue[];
|
|
5
|
+
static evaluateRange(node: RangeExpression, evaluator: ExpressionEvaluator, config: any): PrimitiveValue;
|
|
6
|
+
static evaluateCons(node: ConsExpression, evaluator: ExpressionEvaluator, lazy: boolean): PrimitiveValue;
|
|
7
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { isLazyList, } from "@yukigo/ast";
|
|
2
|
+
import { createStream } from "../utils.js";
|
|
3
|
+
export class LazyRuntime {
|
|
4
|
+
static realizeList(val) {
|
|
5
|
+
if (Array.isArray(val))
|
|
6
|
+
return val;
|
|
7
|
+
if (isLazyList(val)) {
|
|
8
|
+
const result = [];
|
|
9
|
+
const iter = val.generator();
|
|
10
|
+
let next = iter.next();
|
|
11
|
+
while (!next.done) {
|
|
12
|
+
if (next.value === undefined)
|
|
13
|
+
throw new Error("LazyList yielded undefined");
|
|
14
|
+
result.push(next.value);
|
|
15
|
+
next = iter.next();
|
|
16
|
+
}
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
throw new Error(`Expected List or LazyList, got ${typeof val}`);
|
|
20
|
+
}
|
|
21
|
+
static evaluateRange(node, evaluator, config) {
|
|
22
|
+
const startVal = evaluator.evaluate(node.start);
|
|
23
|
+
if (typeof startVal !== "number")
|
|
24
|
+
throw new Error("Range start must be a number");
|
|
25
|
+
const hasEnd = node.end != null;
|
|
26
|
+
let step = 1;
|
|
27
|
+
if (node.step) {
|
|
28
|
+
const secondVal = evaluator.evaluate(node.step);
|
|
29
|
+
if (typeof secondVal !== "number")
|
|
30
|
+
throw new Error("Range step must be a number");
|
|
31
|
+
step = secondVal - startVal;
|
|
32
|
+
if (step === 0)
|
|
33
|
+
throw new Error("Range step cannot be zero");
|
|
34
|
+
}
|
|
35
|
+
if (!hasEnd) {
|
|
36
|
+
return createStream(function* () {
|
|
37
|
+
let current = startVal;
|
|
38
|
+
while (true) {
|
|
39
|
+
yield current;
|
|
40
|
+
current += step;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const endVal = evaluator.evaluate(node.end);
|
|
45
|
+
if (typeof endVal !== "number")
|
|
46
|
+
throw new Error("Range end must be a number");
|
|
47
|
+
const result = [];
|
|
48
|
+
let current = startVal;
|
|
49
|
+
const cond = step > 0 ? () => current <= endVal : () => current >= endVal;
|
|
50
|
+
while (cond()) {
|
|
51
|
+
result.push(current);
|
|
52
|
+
current += step;
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
static evaluateCons(node, evaluator, lazy) {
|
|
57
|
+
const head = evaluator.evaluate(node.head);
|
|
58
|
+
if (lazy) {
|
|
59
|
+
const tail = evaluator.evaluate(node.tail);
|
|
60
|
+
if (Array.isArray(tail))
|
|
61
|
+
return [head, ...tail];
|
|
62
|
+
if (isLazyList(tail))
|
|
63
|
+
return createStream(function* () {
|
|
64
|
+
yield head;
|
|
65
|
+
yield* tail.generator();
|
|
66
|
+
});
|
|
67
|
+
throw new Error(`Invalid tail type for Cons: ${typeof tail}`);
|
|
68
|
+
}
|
|
69
|
+
// Eager behavior
|
|
70
|
+
const tail = evaluator.evaluate(node.tail);
|
|
71
|
+
if (isLazyList(tail) || !Array.isArray(tail))
|
|
72
|
+
throw new Error("Expected Array in eager Cons");
|
|
73
|
+
return [head, ...tail];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Exist, Findall, Goal, PrimitiveValue, Query } from "@yukigo/ast";
|
|
2
|
+
import { EnvStack, InterpreterConfig } from "../index.js";
|
|
3
|
+
import { ExpressionEvaluator } from "../utils.js";
|
|
4
|
+
export declare class LogicEngine {
|
|
5
|
+
private env;
|
|
6
|
+
private config;
|
|
7
|
+
private evaluator;
|
|
8
|
+
constructor(env: EnvStack, config: InterpreterConfig, evaluator: ExpressionEvaluator);
|
|
9
|
+
solveQuery(node: Query): PrimitiveValue;
|
|
10
|
+
solveGoal(node: Goal): PrimitiveValue;
|
|
11
|
+
solveFindall(node: Findall): PrimitiveValue;
|
|
12
|
+
solveExist(node: Exist): PrimitiveValue;
|
|
13
|
+
private solveConjunction;
|
|
14
|
+
private expressionToPattern;
|
|
15
|
+
private primitiveToPattern;
|
|
16
|
+
private instantiateExpressionAsPattern;
|
|
17
|
+
private substitutePattern;
|
|
18
|
+
private instantiateTemplate;
|
|
19
|
+
private handleOutputMode;
|
|
20
|
+
private formatLogicResult;
|
|
21
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { Exist, Goal, LiteralPattern, SymbolPrimitive, Variable, VariablePattern, } from "@yukigo/ast";
|
|
2
|
+
import { solveGoal, success, } from "./LogicResolver.js";
|
|
3
|
+
import { PatternResolver } from "./PatternMatcher.js";
|
|
4
|
+
import { createStream } from "../utils.js";
|
|
5
|
+
import { InterpreterError } from "../errors.js";
|
|
6
|
+
export class LogicEngine {
|
|
7
|
+
env;
|
|
8
|
+
config;
|
|
9
|
+
evaluator;
|
|
10
|
+
constructor(env, config, evaluator) {
|
|
11
|
+
this.env = env;
|
|
12
|
+
this.config = config;
|
|
13
|
+
this.evaluator = evaluator;
|
|
14
|
+
}
|
|
15
|
+
solveQuery(node) {
|
|
16
|
+
return this.evaluator.evaluate(node.expression);
|
|
17
|
+
}
|
|
18
|
+
solveGoal(node) {
|
|
19
|
+
const patterns = node.args;
|
|
20
|
+
const generator = solveGoal(this.env, node.identifier.value, patterns, (body, substs) => this.solveConjunction(body, substs));
|
|
21
|
+
return this.handleOutputMode(generator);
|
|
22
|
+
}
|
|
23
|
+
solveFindall(node) {
|
|
24
|
+
if (!(node.goal instanceof Goal))
|
|
25
|
+
throw new InterpreterError("solveFindall", "Findall expects a Goal");
|
|
26
|
+
const goalPatterns = node.goal.args.map((arg) => {
|
|
27
|
+
return this.expressionToPattern(arg);
|
|
28
|
+
});
|
|
29
|
+
const generator = solveGoal(this.env, node.goal.identifier.value, goalPatterns, (b, s) => this.solveConjunction(b, s));
|
|
30
|
+
const results = [];
|
|
31
|
+
for (const res of generator)
|
|
32
|
+
results.push(this.instantiateTemplate(node.template, res.substs));
|
|
33
|
+
return results;
|
|
34
|
+
}
|
|
35
|
+
solveExist(node) {
|
|
36
|
+
const generator = solveGoal(this.env, node.identifier.value, node.patterns, (body, substs) => this.solveConjunction(body, substs));
|
|
37
|
+
return this.handleOutputMode(generator);
|
|
38
|
+
}
|
|
39
|
+
*solveConjunction(expressions, substs) {
|
|
40
|
+
if (expressions.length === 0) {
|
|
41
|
+
yield success(substs);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const [head, ...tail] = expressions;
|
|
45
|
+
let headGen = null;
|
|
46
|
+
if (head instanceof Goal) {
|
|
47
|
+
const args = head.args.map((arg) => this.instantiateExpressionAsPattern(arg, substs));
|
|
48
|
+
headGen = solveGoal(this.env, head.identifier.value, args, (b, s) => this.solveConjunction(b, s));
|
|
49
|
+
}
|
|
50
|
+
else if (head instanceof Exist) {
|
|
51
|
+
const patterns = head.patterns.map((pat) => this.substitutePattern(pat, substs));
|
|
52
|
+
headGen = solveGoal(this.env, head.identifier.value, patterns, (b, s) => this.solveConjunction(b, s));
|
|
53
|
+
}
|
|
54
|
+
if (headGen) {
|
|
55
|
+
// backtracking
|
|
56
|
+
for (const headResult of headGen) {
|
|
57
|
+
const newSubsts = new Map([...substs, ...headResult.substs]);
|
|
58
|
+
yield* this.solveConjunction(tail, newSubsts);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// imperative
|
|
63
|
+
const result = this.evaluator.evaluate(head);
|
|
64
|
+
if (result)
|
|
65
|
+
yield* this.solveConjunction(tail, substs);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
expressionToPattern(expr) {
|
|
69
|
+
if (expr instanceof VariablePattern)
|
|
70
|
+
return expr;
|
|
71
|
+
if (expr instanceof Variable) {
|
|
72
|
+
const name = expr.identifier.value;
|
|
73
|
+
if (/^[A-Z_]/.test(name))
|
|
74
|
+
return new VariablePattern(expr.identifier);
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const val = this.evaluator.evaluate(expr);
|
|
78
|
+
return this.primitiveToPattern(val);
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
if (expr instanceof Variable)
|
|
82
|
+
return new VariablePattern(expr.identifier);
|
|
83
|
+
throw e;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
primitiveToPattern(val) {
|
|
87
|
+
if (typeof val === "number" ||
|
|
88
|
+
typeof val === "string" ||
|
|
89
|
+
typeof val === "boolean")
|
|
90
|
+
return new LiteralPattern(new SymbolPrimitive(String(val)));
|
|
91
|
+
// TODO: Manejar arrays/listas complejas si es necesario
|
|
92
|
+
throw new InterpreterError("primitiveToPattern", `Cannot convert value ${val} to Logic Pattern`);
|
|
93
|
+
}
|
|
94
|
+
instantiateExpressionAsPattern(expr, substs) {
|
|
95
|
+
const patternBase = this.expressionToPattern(expr);
|
|
96
|
+
return this.substitutePattern(patternBase, substs);
|
|
97
|
+
}
|
|
98
|
+
substitutePattern(pat, substs, visited = new Set()) {
|
|
99
|
+
if (pat instanceof VariablePattern) {
|
|
100
|
+
const name = pat.name.value;
|
|
101
|
+
if (visited.has(name))
|
|
102
|
+
return pat;
|
|
103
|
+
const val = substs.get(name);
|
|
104
|
+
if (val) {
|
|
105
|
+
const newVisited = new Set(visited);
|
|
106
|
+
newVisited.add(name);
|
|
107
|
+
return this.substitutePattern(val, substs, newVisited);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return pat;
|
|
111
|
+
}
|
|
112
|
+
instantiateTemplate(template, substs) {
|
|
113
|
+
const pat = this.instantiateExpressionAsPattern(template, substs);
|
|
114
|
+
if (pat instanceof LiteralPattern)
|
|
115
|
+
return this.evaluator.evaluate(pat.name);
|
|
116
|
+
throw new Error("Complex template instantiation not fully implemented");
|
|
117
|
+
}
|
|
118
|
+
handleOutputMode(gen) {
|
|
119
|
+
const mode = this.config.outputMode || "first";
|
|
120
|
+
switch (mode) {
|
|
121
|
+
case "stream": {
|
|
122
|
+
const self = this;
|
|
123
|
+
return createStream(function* () {
|
|
124
|
+
for (const res of gen)
|
|
125
|
+
yield self.formatLogicResult(res.substs);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
case "all": {
|
|
129
|
+
const res = [];
|
|
130
|
+
for (const r of gen)
|
|
131
|
+
res.push(this.formatLogicResult(r.substs));
|
|
132
|
+
return res;
|
|
133
|
+
}
|
|
134
|
+
case "first": {
|
|
135
|
+
const next = gen.next();
|
|
136
|
+
if (next.done)
|
|
137
|
+
return false;
|
|
138
|
+
return this.formatLogicResult(next.value.substs);
|
|
139
|
+
}
|
|
140
|
+
default:
|
|
141
|
+
throw new InterpreterError("handleOutputMode", `Unsupported mode: ${mode}. Supported modes: all | stream | first`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
formatLogicResult(substs) {
|
|
145
|
+
const solutions = new Map();
|
|
146
|
+
const resolver = new PatternResolver();
|
|
147
|
+
substs.forEach((pattern, key) => {
|
|
148
|
+
solutions.set(key, pattern.accept(resolver));
|
|
149
|
+
});
|
|
150
|
+
return { success: true, solutions };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Expression, Pattern } from "@yukigo/ast";
|
|
2
|
+
import { EnvStack } from "../index.js";
|
|
3
|
+
export type Substitution = Map<string, Pattern>;
|
|
4
|
+
export type InternalLogicResult = {
|
|
5
|
+
success: true;
|
|
6
|
+
substs: Substitution;
|
|
7
|
+
};
|
|
8
|
+
export type BodySolver = (expressions: Expression[], env: Substitution) => Generator<InternalLogicResult>;
|
|
9
|
+
export declare function success(substs: Substitution): InternalLogicResult;
|
|
10
|
+
export declare function solveGoal(envs: EnvStack, predicateName: string, args: Pattern[], solveBody: BodySolver): Generator<InternalLogicResult>;
|
|
11
|
+
export declare function unify(t1: Pattern, t2: Pattern, argEnv?: Substitution): Substitution | null;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Fact, FunctorPattern, ListPattern, LiteralPattern, Rule, VariablePattern, WildcardPattern, isRuntimePredicate, } from "@yukigo/ast";
|
|
2
|
+
import { lookup } from "../utils.js";
|
|
3
|
+
import { InterpreterError } from "../errors.js";
|
|
4
|
+
export function success(substs) {
|
|
5
|
+
return { success: true, substs };
|
|
6
|
+
}
|
|
7
|
+
function unifyParameters(patterns, args) {
|
|
8
|
+
const subst = new Map();
|
|
9
|
+
for (let i = 0; i < patterns.length; i++) {
|
|
10
|
+
const match = unify(patterns[i], args[i], subst);
|
|
11
|
+
if (!match)
|
|
12
|
+
return [, false];
|
|
13
|
+
match.forEach((v, k) => subst.set(k, v));
|
|
14
|
+
}
|
|
15
|
+
return [subst, true];
|
|
16
|
+
}
|
|
17
|
+
export function* solveGoal(envs, predicateName, args, solveBody) {
|
|
18
|
+
const pred = lookup(envs, predicateName);
|
|
19
|
+
if (!pred || !isRuntimePredicate(pred))
|
|
20
|
+
return;
|
|
21
|
+
for (const clause of pred.equations) {
|
|
22
|
+
if (clause.patterns.length !== args.length)
|
|
23
|
+
continue;
|
|
24
|
+
const [substs, matches] = unifyParameters(clause.patterns, args);
|
|
25
|
+
if (!matches)
|
|
26
|
+
continue;
|
|
27
|
+
if (clause instanceof Fact) {
|
|
28
|
+
yield success(substs);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (clause instanceof Rule) {
|
|
32
|
+
const bodyGenerator = solveBody(clause.expressions, substs);
|
|
33
|
+
for (const finalResult of bodyGenerator)
|
|
34
|
+
yield success(finalResult.substs);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
throw new InterpreterError("*solveGoal", `Unexpected node: ${JSON.stringify(clause)}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function resolve(node, env) {
|
|
41
|
+
if (node instanceof VariablePattern) {
|
|
42
|
+
const name = node.name.toString();
|
|
43
|
+
if (env.has(name))
|
|
44
|
+
return resolve(env.get(name), env);
|
|
45
|
+
}
|
|
46
|
+
return node;
|
|
47
|
+
}
|
|
48
|
+
export function unify(t1, t2, argEnv) {
|
|
49
|
+
const env = argEnv ?? new Map();
|
|
50
|
+
const r1 = resolve(t1, env);
|
|
51
|
+
const r2 = resolve(t2, env);
|
|
52
|
+
if (r1 === r2)
|
|
53
|
+
return env;
|
|
54
|
+
if (r1 instanceof WildcardPattern || r2 instanceof WildcardPattern)
|
|
55
|
+
return env;
|
|
56
|
+
if (r1 instanceof VariablePattern) {
|
|
57
|
+
env.set(r1.name.value, r2);
|
|
58
|
+
return env;
|
|
59
|
+
}
|
|
60
|
+
if (r2 instanceof VariablePattern) {
|
|
61
|
+
env.set(r2.name.value, r1);
|
|
62
|
+
return env;
|
|
63
|
+
}
|
|
64
|
+
if (r1 instanceof LiteralPattern && r2 instanceof LiteralPattern)
|
|
65
|
+
return r1.name.equals(r2.name) ? env : null;
|
|
66
|
+
if (r1 instanceof FunctorPattern && r2 instanceof FunctorPattern) {
|
|
67
|
+
if (r1.identifier.value !== r2.identifier.value)
|
|
68
|
+
return null;
|
|
69
|
+
if (r1.args.length !== r2.args.length)
|
|
70
|
+
return null;
|
|
71
|
+
for (let i = 0; i < r1.args.length; i++) {
|
|
72
|
+
if (!unify(r1.args[i], r2.args[i], env))
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
return env;
|
|
76
|
+
}
|
|
77
|
+
if (r1 instanceof ListPattern && r2 instanceof ListPattern) {
|
|
78
|
+
if (r1.elements.length !== r2.elements.length)
|
|
79
|
+
return null;
|
|
80
|
+
for (let i = 0; i < r1.elements.length; i++) {
|
|
81
|
+
if (!unify(r1.elements[i], r2.elements[i], env))
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
return env;
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { PrimitiveValue } from "@yukigo/ast";
|
|
2
|
+
export type UnaryOp<T, R = T> = (x: T) => R;
|
|
3
|
+
export type BinaryOp<T, R = T> = (x: T, y: T) => R;
|
|
4
|
+
export type OperatorTable<T> = Record<string, T>;
|
|
5
|
+
export declare const ArithmeticBinaryTable: OperatorTable<BinaryOp<number>>;
|
|
6
|
+
export declare const ComparisonOperationTable: OperatorTable<BinaryOp<boolean>>;
|
|
7
|
+
export declare const LogicalBinaryTable: OperatorTable<(l: boolean, r: () => boolean) => boolean>;
|
|
8
|
+
export declare const BitwiseBinaryTable: OperatorTable<BinaryOp<number>>;
|
|
9
|
+
export declare const StringOperationTable: OperatorTable<BinaryOp<string>>;
|
|
10
|
+
export declare const BitwiseUnaryTable: OperatorTable<UnaryOp<number>>;
|
|
11
|
+
export declare const LogicalUnaryTable: OperatorTable<UnaryOp<boolean>>;
|
|
12
|
+
export declare const ListBinaryTable: OperatorTable<BinaryOp<PrimitiveValue[]>>;
|
|
13
|
+
export declare const ListUnaryTable: OperatorTable<UnaryOp<PrimitiveValue[], PrimitiveValue>>;
|
|
14
|
+
export declare const ArithmeticUnaryTable: OperatorTable<UnaryOp<number>>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { isArrayOfNumbers } from "../utils.js";
|
|
2
|
+
export const ArithmeticBinaryTable = {
|
|
3
|
+
Plus: (a, b) => a + b,
|
|
4
|
+
Minus: (a, b) => a - b,
|
|
5
|
+
Multiply: (a, b) => a * b,
|
|
6
|
+
Divide: (a, b) => a / b,
|
|
7
|
+
Modulo: (a, b) => a % b,
|
|
8
|
+
Power: (a, b) => a ** b,
|
|
9
|
+
Min: (a, b) => Math.min(a, b),
|
|
10
|
+
Max: (a, b) => Math.max(a, b),
|
|
11
|
+
};
|
|
12
|
+
export const ComparisonOperationTable = {
|
|
13
|
+
Equal: (a, b) => a == b,
|
|
14
|
+
NotEqual: (a, b) => a != b,
|
|
15
|
+
Same: (a, b) => a === b,
|
|
16
|
+
NotSame: (a, b) => a !== b,
|
|
17
|
+
//Similar: (a, b) => ,
|
|
18
|
+
//NotSimilar: (a, b) => ,
|
|
19
|
+
GreaterOrEqualThan: (a, b) => a >= b,
|
|
20
|
+
GreaterThan: (a, b) => a > b,
|
|
21
|
+
LessOrEqualThan: (a, b) => a <= b,
|
|
22
|
+
LessThan: (a, b) => a < b,
|
|
23
|
+
};
|
|
24
|
+
export const LogicalBinaryTable = {
|
|
25
|
+
And: (left, rightThunk) => left && rightThunk(),
|
|
26
|
+
Or: (left, rightThunk) => left || rightThunk(),
|
|
27
|
+
};
|
|
28
|
+
export const BitwiseBinaryTable = {
|
|
29
|
+
BitwiseOr: (a, b) => a | b,
|
|
30
|
+
BitwiseAnd: (a, b) => a & b,
|
|
31
|
+
BitwiseLeftShift: (a, b) => a << b,
|
|
32
|
+
BitwiseRightShift: (a, b) => a >> b,
|
|
33
|
+
BitwiseUnsignedRightShift: (a, b) => a >>> b,
|
|
34
|
+
BitwiseXor: (a, b) => a ^ b,
|
|
35
|
+
};
|
|
36
|
+
export const StringOperationTable = {
|
|
37
|
+
Concat: (a, b) => `${a + b}`,
|
|
38
|
+
};
|
|
39
|
+
export const BitwiseUnaryTable = {
|
|
40
|
+
BitwiseNot: (a) => ~a,
|
|
41
|
+
};
|
|
42
|
+
export const LogicalUnaryTable = {
|
|
43
|
+
Negation: (a) => !a,
|
|
44
|
+
};
|
|
45
|
+
export const ListBinaryTable = {
|
|
46
|
+
Concat: (a, b) => a.concat(b),
|
|
47
|
+
};
|
|
48
|
+
export const ListUnaryTable = {
|
|
49
|
+
Size: (a) => a.length,
|
|
50
|
+
DetectMax: (a) => {
|
|
51
|
+
if (!isArrayOfNumbers(a))
|
|
52
|
+
throw new Error("Operand of DetectMax must be Array of numbers");
|
|
53
|
+
return Math.max(...a);
|
|
54
|
+
},
|
|
55
|
+
DetectMin: (a) => {
|
|
56
|
+
if (!isArrayOfNumbers(a))
|
|
57
|
+
throw new Error("Operand of DetectMin must be Array of numbers");
|
|
58
|
+
return Math.min(...a);
|
|
59
|
+
},
|
|
60
|
+
Flatten: (a) => a.flat(),
|
|
61
|
+
};
|
|
62
|
+
export const ArithmeticUnaryTable = {
|
|
63
|
+
Round: (a) => Math.round(a),
|
|
64
|
+
Absolute: (a) => Math.abs(a),
|
|
65
|
+
Ceil: (a) => Math.ceil(a),
|
|
66
|
+
Floor: (a) => Math.floor(a),
|
|
67
|
+
Negation: (a) => a * -1,
|
|
68
|
+
Sqrt: (a) => Math.sqrt(a),
|
|
69
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ApplicationPattern, AsPattern, ASTNode, ConsPattern, ConstructorPattern, FunctorPattern, ListPattern, LiteralPattern, PrimitiveValue, TuplePattern, UnionPattern, VariablePattern, Visitor, WildcardPattern } from "@yukigo/ast";
|
|
2
|
+
import { Bindings } from "../index.js";
|
|
3
|
+
export declare class PatternResolver implements Visitor<string> {
|
|
4
|
+
visitVariablePattern(node: VariablePattern): string;
|
|
5
|
+
visitWildcardPattern(node: WildcardPattern): string;
|
|
6
|
+
visitLiteralPattern(node: LiteralPattern): string;
|
|
7
|
+
visitTuplePattern(node: TuplePattern): string;
|
|
8
|
+
visitListPattern(node: ListPattern): string;
|
|
9
|
+
visitConsPattern(node: ConsPattern): string;
|
|
10
|
+
visitConstructorPattern(node: ConstructorPattern): string;
|
|
11
|
+
visitFunctorPattern(node: FunctorPattern): string;
|
|
12
|
+
visitApplicationPattern(node: ApplicationPattern): string;
|
|
13
|
+
visitAsPattern(node: AsPattern): string;
|
|
14
|
+
visit(node: ASTNode): string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Recursively matches a value against a pattern node.
|
|
18
|
+
* Updates `bindings` when variables are bound successfully.
|
|
19
|
+
* Returns true if the pattern matches, false otherwise.
|
|
20
|
+
*/
|
|
21
|
+
export declare class PatternMatcher implements Visitor<boolean> {
|
|
22
|
+
private value;
|
|
23
|
+
private bindings;
|
|
24
|
+
constructor(value: PrimitiveValue, bindings: Bindings);
|
|
25
|
+
visitVariablePattern(node: VariablePattern): boolean;
|
|
26
|
+
visitWildcardPattern(node: WildcardPattern): boolean;
|
|
27
|
+
visitLiteralPattern(node: LiteralPattern): boolean;
|
|
28
|
+
visitTuplePattern(node: TuplePattern): boolean;
|
|
29
|
+
visitListPattern(node: ListPattern): boolean;
|
|
30
|
+
private matchList;
|
|
31
|
+
visitConsPattern(node: ConsPattern): boolean;
|
|
32
|
+
private resolveCons;
|
|
33
|
+
visitConstructorPattern(node: ConstructorPattern): boolean;
|
|
34
|
+
visitFunctorPattern(node: FunctorPattern): boolean;
|
|
35
|
+
visitApplicationPattern(node: ApplicationPattern): boolean;
|
|
36
|
+
visitAsPattern(node: AsPattern): boolean;
|
|
37
|
+
visitUnionPattern(node: UnionPattern): boolean;
|
|
38
|
+
visit(node: ASTNode): boolean;
|
|
39
|
+
private deepEqual;
|
|
40
|
+
private realize;
|
|
41
|
+
}
|