Skip to content

Commit 118edc9

Browse files
committed
Saving incoming
2 parents eb58dc1 + 4a42807 commit 118edc9

File tree

11 files changed

+1642
-722
lines changed

11 files changed

+1642
-722
lines changed

README.md

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -163,17 +163,6 @@ If a local scope exceeds the bounds of a given global scope, the program will le
163163
- `print(f(x):2)` - Prints the output of the function until 2.
164164

165165
In Funktion, any time a function is called with `.step()`, the function will generate the next output over the given time step, moving forward from the first step to also generate the next step when initially called. By default, `.step()` will step one defined time step. If the time step is defined as 2, it will step through the input value by 2. If a function with a time step of 2 calls itself with `.step(2)`, it will step twice, generating twice, stepping through the input value by 4. The local defined time step takes precedence over the global time step for all functions in the given program, which is 1 by default if not defined at the start of the program. The step value need not align with the global or even local time step, but the function will end generation once the end value is either reached or passed.
166-
167-
A function's generated outputs are influenced by any additional operations or mutations applied to it when stepped through. This mutation is shared between the functions involved for a given number of steps.
168-
169-
For example, consider the expression:
170-
171-
```funktion
172-
{G(x) + f(x)}.step(2)
173-
```
174-
175-
This will step through both `G` and `f` by `x`'s step value, while also making their output for that iteration the sum of their results for two generations.
176-
177166
---
178167

179168
### Chain of Questioning

src/analyzer.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -144,17 +144,21 @@ export default function analyze(match) {
144144
return core.funcCall(id.sourceString, arg.rep());
145145
},
146146

147-
// FunctionGroup(_open, expr, _close) {
148-
// return core.functionGroup(expr.rep());
149-
// },
150-
151147
Expr(condExpr, _sep, _newLine, rest) {
152148
return core.expr(
153149
condExpr.rep(),
154150
rest.children.map((r) => r.rep())
155151
);
156152
},
157153

154+
SliceExpr(expr, _backslash, rest) {
155+
const expressions = [expr.rep()];
156+
if (rest.children.length > 0) {
157+
expressions.push(...rest.children.map(child => child.rep()));
158+
}
159+
return core.sliceExpr(expressions);
160+
},
161+
158162
CondExpr_ternary(
159163
_question,
160164
condLeft,
@@ -303,10 +307,6 @@ export default function analyze(match) {
303307
return core.globalRange(range.rep(), timestep?.rep());
304308
},
305309

306-
// LocalRange(_open, id, _close, range, timestep) {
307-
// return core.localRange(id.sourceString, range.rep(), timestep?.rep());
308-
// },
309-
310310
numrange(_open, start, _dots, end, _close) {
311311
return core.numRange(start?.rep(), end?.rep());
312312
},

src/core.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ export function funcCall(name, arg) {
1717
return { type: anyType, kind: "FuncCall", name, arg };
1818
}
1919

20-
// export function functionGroup(expr) {
21-
// return { type: expr.type, kind: "FunctionGroup", expr };
22-
// }
23-
2420
export function expr(condExpr, rest = []) {
2521
return { type: condExpr.type, kind: "Expr", condExpr, rest };
2622
}
2723

24+
export function sliceExpr(expressions) {
25+
return { type: expressions[0].type, kind: "SliceExpr", expressions };
26+
}
27+
2828
export function condExpr(leftCond, op, rightCond, thenBranch, elseBranch) {
2929
return {
3030
type: thenBranch?.type ?? anyType,
@@ -81,10 +81,6 @@ export function globalRange(range, timestep = null) {
8181
return { type: voidType, kind: "GlobalRange", range, timestep };
8282
}
8383

84-
// export function localRange(id, range, timestep = null) {
85-
// return { type: voidType, kind: "LocalRange", id, range, timestep };
86-
// }
87-
8884
export function numRange(start, end) {
8985
return { type: voidType, kind: "numRange", start, end };
9086
}

src/funktion.ohm

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
Funktion {
22
Program = (GlobalRange "\n")? "\n"* ListOf<(Statement | FuncDef | ""), ("\n"+ | space+)>
33

4-
FuncDef = id "(" id ")" "=" "\n"? Expr (";" Expr)*
4+
FuncDef = id "(" id ")" "=" "\n"? SliceExpr (";" (FuncDef | SliceExpr))*
55
FuncCall = id "(" Expr ")"
66
Expr = CondExpr ("," "\n"? CondExpr)*
7+
SliceExpr = Expr ("\\" Expr)*
78
CondExpr = "?" CondExpr ("==" | "!=" | "<=" | "<" | ">=" | ">") CondExpr "=>" BitwiseExpr ":" "\n"? CondExpr --ternary
89
| BitwiseExpr
910
BitwiseExpr = BitwiseExpr ("&" | "|" | "^") ShiftExpr --binary

src/generator.js

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { voidType, numberType, stringType, functionType, standardLibrary } from "./core.js";
2-
31
export default function generate(program) {
42
const inputCode = [];
53
let inputIndex = 0;
@@ -23,12 +21,17 @@ export default function generate(program) {
2321
/**
2422
* Standard generator code used in every file.
2523
*/
24+
this.functions = new Map();
25+
p.statements.forEach(s => {
26+
if (s.kind === "FuncDef") {
27+
this.functions.set(s.name, s);
28+
}
29+
});
2630
const range = p.globalRange?.[0]?.range;
2731
const timestep = p.globalRange?.[0]?.timestep?.[0];
2832
const start = range ? gen(range.start) : 1;
2933
const end = range ? gen(range.end[0]) : 5;
30-
const step = timestep ? gen(timestep.value) :
31-
start <= end ? 1 : -1;
34+
const step = timestep ? gen(timestep.value) : 1;
3235
inputCode.push(`
3336
import { createInterface } from "node:readline/promises";
3437
import { stdin as input, stdout as output } from "node:process";
@@ -70,22 +73,25 @@ export default function generate(program) {
7073
if (gen.size === 0) {
7174
gen.size++;
7275
gen.index++;
73-
gen.values.push(f(currentVal));
76+
const result = f(currentVal);
77+
gen.values.push(Array.isArray(result) ? result.join(' ') : result);
7478
currentVal += gen.timestepRange.step;
7579
}
7680
if (gen.timestepRange.step > 0) {
7781
while (currentVal <= gen.timestepRange.end && iterations > 0) {
7882
gen.size++;
7983
gen.index++;
80-
gen.values.push(f(currentVal));
84+
const result = f(currentVal);
85+
gen.values.push(Array.isArray(result) ? result.join(' ') : result);
8186
currentVal += gen.timestepRange.step;
8287
iterations--;
8388
}
8489
} else {
8590
while (currentVal >= gen.timestepRange.end && iterations > 0) {
8691
gen.size++;
8792
gen.index++;
88-
gen.values.push(f(currentVal));
93+
const result = f(currentVal);
94+
gen.values.push(Array.isArray(result) ? result.join(' ') : result);
8995
currentVal += gen.timestepRange.step;
9096
iterations--;
9197
}
@@ -101,8 +107,12 @@ export default function generate(program) {
101107
const funcName = targetName(d.name);
102108
const param = targetName(d.param);
103109
const body = gen(d.body);
104-
const lastStmt = body.pop();
105-
output.push(`function ${funcName}(${param}) {\n${body}\nreturn ${lastStmt};\n} `);
110+
111+
// check for slices in the body of the function
112+
if (d.body.kind === "SliceExpr") {
113+
const sliceExpressions = d.body.expressions.map(expr => gen(expr)).join(", ");
114+
output.push(`function ${funcName}(${param}) { return [${sliceExpressions}]; }`);
115+
}
106116
if (!instantiatedMutableRanges.has(param)) {
107117
output.push(`let ${param} = initializeMutableRange();`);
108118
instantiatedMutableRanges.add(param)
@@ -118,6 +128,7 @@ export default function generate(program) {
118128
const stepValue = gen(s.stepValue);
119129
const arg = targetName(s.expr.arg);
120130
output.push(`applyFunction(${arg}, ${stepValue}, ${expr});`);
131+
return `${arg}.values[${arg}.index]`;
121132
},
122133

123134
Expr(e) {
@@ -128,6 +139,13 @@ export default function generate(program) {
128139
return exprs;
129140
},
130141

142+
//anywhere you see SliceExpr in the code, I added it in ~MS
143+
SliceExpr(e) {
144+
const slices = e.expressions.map(gen);
145+
// join slices with spaces in output
146+
return `[${slices.join(', ')}]`;
147+
},
148+
131149
CondExpr(e) {
132150
if (e.thenBranch) {
133151
const condleft = gen(e.leftCond);
@@ -173,7 +191,7 @@ export default function generate(program) {
173191
if (e.right) {
174192
const left = gen(e.left);
175193
const right = gen(e.right);
176-
return `(${left} ${e.op || "*"} ${right})`;
194+
return `(${left} ${e.op} ${right})`;
177195
}
178196
return gen(e.left);
179197
},
@@ -197,13 +215,13 @@ export default function generate(program) {
197215
return `${gen(e.id)}.values.slice(0, ${gen(e.timeValue)})`;
198216
},
199217

200-
InputStmt(e) {
201-
inputCode.push(`
202-
console.log(${gen(e.prompt[0])});
203-
const inputVar__${inputIndex} = await rl.question("Input: ");
204-
`);
205-
return `inputVar__${inputIndex++}`
206-
},
218+
// InputStmt(e) {
219+
// inputCode.push(`
220+
// console.log(${gen(e.prompt[0])});
221+
// const inputVar__${inputIndex} = await rl.question("Input: ");
222+
// `);
223+
// return `inputVar__${inputIndex++}`
224+
// },
207225

208226
num(n) {
209227
return n.value;

src/optimizer.js

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,60 @@ const optimizers = {
5050
return e;
5151
},
5252

53+
BitwiseExpr(e) {
54+
e.left = optimize(e.left);
55+
e.right = optimize(e.right);
56+
if (e.left.kind === 'num' && e.right.kind === 'num') {
57+
let result;
58+
switch (e.op) {
59+
case '&': result = e.left.value & e.right.value; break;
60+
case '|': result = e.left.value | e.right.value; break;
61+
case '^': result = e.left.value ^ e.right.value; break;
62+
}
63+
return core.num(result);
64+
}
65+
if (e.op === '&') {
66+
if ((e.left.kind === 'num' && e.left.value === 0) ||
67+
(e.right.kind === 'num' && e.right.value === 0)) {
68+
return core.num(0);
69+
}
70+
}
71+
if (e.op === '|') {
72+
if (e.left.kind === 'num' && e.left.value === 0) return e.right;
73+
if (e.right.kind === 'num' && e.right.value === 0) return e.left;
74+
}
75+
if (e.op === '^' && e.right.kind === 'num' && e.right.value === 0) {
76+
return e.left;
77+
}
78+
},
79+
80+
ShiftExpr(e) {
81+
if (e.op) {
82+
e.left = optimize(e.left);
83+
e.right = optimize(e.right);
84+
if (e.left.kind === 'num' && e.right.kind === 'num') {
85+
let result;
86+
switch (e.op) {
87+
case '<<': result = e.left.value << e.right.value; break;
88+
case '>>': result = e.left.value >> e.right.value; break;
89+
}
90+
return core.num(result);
91+
}
92+
if (e.right.kind === 'num' && e.right.value === 0) {
93+
return e.left;
94+
}
95+
return e;
96+
}
97+
},
98+
5399
AddExpr(e) {
54100
e.left = optimize(e.left);
55101
e.right = optimize(e.right);
56102
if (e.left.kind === 'num' && e.right.kind === 'num') {
57103
const result = e.op === '+'
58-
? e.left.value + e.right.value
59-
: e.left.value - e.right.value;
60-
return core.num(result);
104+
? e.left.value + e.right.value
105+
: e.left.value - e.right.value;
106+
return core.num(result);
61107
}
62108
if (e.op === '+') {
63109
if (e.left.kind === 'num' && e.left.value === 0) return e.right;
@@ -114,7 +160,7 @@ const optimizers = {
114160
},
115161

116162
TimeCall(t) {
117-
t.funcCall = optimize(t.funcCall);
163+
t.id = optimize(t.id);
118164
t.timeValue = optimize(t.timeValue);
119165
return t;
120166
},

test/analyzer.test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,15 @@ const semanticChecks = [
5252
["global range with negative integer time step", "`1..10` t-2t\n"],
5353
["global range with float time step", "`1..10` t2.5t\n"],
5454
["global range with character step", "`'a'..'z'`\n"],
55-
// ["function group", "f(x)=x\ng(x)=x\n{f(x)+g(x)}.step()"],
5655
["input statement with prompt", 'f(x)=input("Hello")'],
5756
["using the function parameter", "f(x)=x*3+x"],
5857
["identifiers", "f(x)=x"],
5958
["character literals", "f(x)='2'"],
6059
["parentheses around a primary", "f(x)=(3)"],
6160
["complicated equation", "f(x) = 1 + (2 + -3) << -4 >> (19 * 39) % x ^ 2 << 38 & 18"],
61+
["function with slices", "f(x) = x \\ x + 1 \\ x + 2"],
62+
["function with slices of different types", "f(x) = x \\ 'a' \\ x + 2"],
63+
["printing sliced function result", "f(x) = x \\ x + 1 \\ x + 2\nprint(f(x))"],
6264
];
6365

6466
// Programs that are syntactically correct but have semantic errors

test/compiler.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ describe("The compiler", () => {
2525
})
2626
it("generates js code when given the js option", () => {
2727
const compiled = compile(sampleProgram, "js")
28-
assert(compiled.startsWith("console.log(0)"))
28+
assert(compiled.includes("funktionPrint(0);"))
2929
})
3030
})

0 commit comments

Comments
 (0)