Skip to content

Commit 877035d

Browse files
committed
Revamped the generator
1 parent af2fbff commit 877035d

File tree

8 files changed

+101
-83
lines changed

8 files changed

+101
-83
lines changed

examples/factorial.funk

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
`5..1` t1t
22

3-
factorial(x) =
4-
? x > 0 => x * factorial(x).step() :
5-
1
3+
factorial(x) = ? x > 1 => x * factorial(x).step() : 1
64

75
print(factorial(x):1)

examples/fizzbuzz copy.funk

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
`15..1` t1t
2+
3+
fizzbuzz(x) =
4+
? x % 15 == 0 => "fizzbuzz":
5+
? x % 3 == 0 => "fizz":
6+
? x % 5 == 0 => "buzz":
7+
x
8+
9+
fizzbuzz(x).step(15)
10+
print(x)

src/analyzer.js

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ export default function analyze(match) {
100100
const originalContext = context;
101101
context = context.newChildContext();
102102
context.add(param.sourceString, {
103-
kind: "Parameter",
103+
kind: "MutableRange",
104104
type: core.numberType,
105105
name: param.sourceString,
106106
});
@@ -122,6 +122,11 @@ export default function analyze(match) {
122122
type: core.functionType,
123123
name: functionId,
124124
});
125+
context.add(param.sourceString, {
126+
kind: "MutableRange",
127+
type: core.numberType,
128+
name: param.sourceString,
129+
});
125130
const func = core.funcDef(
126131
id.sourceString,
127132
param.sourceString,
@@ -282,7 +287,7 @@ export default function analyze(match) {
282287
// Syntax Sugar: Default step count is 1.
283288
return core.stepCall(
284289
expr.rep(),
285-
stepValue.rep().length ? stepValue.rep() : 1
290+
stepValue.rep().length ? stepValue.rep()[0].value : 1
286291
);
287292
},
288293

@@ -293,8 +298,8 @@ export default function analyze(match) {
293298
return core.inputStmt(prompt?.rep());
294299
},
295300

296-
TimeCall(funcCall, _colon, timeValue) {
297-
return core.timeCall(funcCall.rep(), timeValue.rep());
301+
TimeCall(id, _colon, timeValue) {
302+
return core.timeCall(id.rep(), timeValue.rep());
298303
},
299304

300305
GlobalRange(range, timestep) {
@@ -317,11 +322,10 @@ export default function analyze(match) {
317322
return core.timestep(value.rep());
318323
},
319324

320-
num(sign, value, period, decimal) {
325+
num(sign, value, _period, decimal) {
321326
const number = Number(
322327
sign.sourceString +
323328
value.sourceString +
324-
period.sourceString +
325329
decimal.sourceString
326330
);
327331
return core.num(number);
@@ -336,10 +340,9 @@ export default function analyze(match) {
336340
},
337341

338342
id(firstChar, name) {
339-
const entity = context.lookup(
340-
firstChar.sourceString + name?.sourceString
341-
);
342-
mustHaveBeenFound(entity, name.sourceString, { at: name });
343+
const idName = firstChar.sourceString + name?.sourceString;
344+
const entity = context.lookup(idName);
345+
mustHaveBeenFound(entity, idName, { at: name });
343346
return core.id(firstChar.sourceString + name?.sourceString);
344347
},
345348

src/core.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ export function inputStmt(prompt) {
7373
return { type: anyType, kind: "InputStmt", prompt };
7474
}
7575

76-
export function timeCall(funcCall, timeValue) {
77-
return { type: voidType, kind: "TimeCall", funcCall, timeValue };
76+
export function timeCall(id, timeValue) {
77+
return { type: voidType, kind: "TimeCall", id, timeValue };
7878
}
7979

8080
export function globalRange(range, timestep = null) {

src/funktion.ohm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Funktion {
3232
| StepCall
3333
| InputStmt
3434
PrintStmt = "print" "(" (TimeCall | CondExpr) ")"
35-
TimeCall = FuncCall ":" num
35+
TimeCall = id ":" num
3636
StepCall = (FuncCall | FunctionGroup) "." "step" "(" num? ")"
3737
InputStmt = "input" "(" stringliteral? ")"
3838

src/generator.js

Lines changed: 72 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -13,83 +13,91 @@ export default function generate(program) {
1313

1414
const gen = node => generators?.[node?.kind]?.(node) ?? node
1515

16-
// const gen = (node) => {
17-
// if (!node) return '';
18-
// const handler = generators[node.kind];
19-
// if (handler) {
20-
// return handler(node);
21-
// }
22-
// throw new Error(`No generator for node type: ${node.kind}`);
23-
// };
16+
const instantiatedMutableRanges = []
2417

25-
let currentFunction = { name: null, param: null };
18+
// let currentFunction = { name: null, param: null };
2619

2720
const generators = {
2821
Program(p) {
22+
/**
23+
* Standard generator code used in every file.
24+
*/
25+
const range = p.globalRange?.[0]?.range;
26+
const timestep = p.globalRange?.[0]?.timestep?.[0];
27+
const start = range ? gen(range.start) : 1;
28+
const end = range ? gen(range.end[0]) : 5;
29+
const step = timestep ? gen(timestep.value) :
30+
start <= end ? 1 : -1;
2931
output.push(`
30-
function generateRange(start, end, step) {
31-
const range = [];
32-
if (step === 0) step = 1;
33-
if (start <= end) {
34-
for (let i = start; i <= end; i += step) {
35-
range.push(i);
36-
}
37-
}
38-
else {
39-
for (let i = start; i >= end; i -= step) {
40-
range.push(i);
41-
}
42-
}
43-
return range;
32+
function generateRange(start = ${start}, end = ${end}, step = ${step}) {
33+
if (end < start) step *= -1;
34+
return {
35+
start,
36+
end,
37+
step
38+
};
39+
}
40+
41+
function initializeMutableRange(timestepRange = generateRange()) {
42+
return {
43+
timestepRange,
44+
values: [],
45+
index: -1,
46+
size: 0
47+
};
4448
}
4549
4650
function funktionPrint(value) {
4751
if (Array.isArray(value)) {
4852
console.log(value.join('\\n'));
53+
}
54+
else if (typeof value === "object") {
55+
console.log(value.values.join('\\n'));
4956
}
5057
else {
5158
console.log(value);
5259
}
5360
}
61+
62+
function applyFunction(gen, iterations, f) {
63+
let currentVal = gen.timestepRange.start + gen.timestepRange.step * (gen.index + 1);
64+
if (gen.size === 0) {
65+
gen.size++;
66+
gen.index++;
67+
gen.values.push(f(currentVal));
68+
currentVal += gen.timestepRange.step;
69+
}
70+
if (gen.timestepRange.step > 0) {
71+
while (currentVal <= gen.timestepRange.end && iterations > 0) {
72+
gen.size++;
73+
gen.index++;
74+
gen.values.push(f(currentVal));
75+
currentVal += gen.timestepRange.step;
76+
iterations--;
77+
}
78+
} else {
79+
while (currentVal >= gen.timestepRange.end && iterations > 0) {
80+
gen.size++;
81+
gen.index++;
82+
gen.values.push(f(currentVal));
83+
currentVal += gen.timestepRange.step;
84+
iterations--;
85+
}
86+
}
87+
}
5488
`);
55-
if (p.globalRange) {
56-
const start = gen(p.globalRange[0].range.start);
57-
const end = gen(p.globalRange[0].range.end[0]);
58-
const step = p.globalRange[0].timestep ? gen(p.globalRange[0].timestep[0].value) : (start <= end ? 1 : -1);
59-
output.push(`const globalRange = generateRange(${start}, ${end}, ${step});`);
60-
}
61-
else {
62-
output.push(`const globalRange = [];`);
63-
}
6489
p.statements.forEach(gen);
6590
},
6691

67-
// FuncDef(d) {
68-
// const funcName = targetName(d);
69-
// const param = d.param;
70-
// output.push(`const ${funcName} = [];`);
71-
// output.push(`let previous_${funcName} = 1;`);
72-
// output.push(`for (const ${param} of globalRange) {`);
73-
// const body = gen(d.body);
74-
// output.push(` ${funcName}.push(${body});`);
75-
// output.push(` previous_${funcName} = ${funcName}[${funcName}.length - 1];`);
76-
// output.push(`}`);
77-
// },
78-
7992
FuncDef(d) {
8093
const funcName = targetName(d.name);
81-
const param = d.param;
82-
currentFunction.name = funcName;
83-
currentFunction.param = param;
84-
output.push(`const ${funcName} = [];`);
85-
output.push(`let previous_${funcName} = 1;`);
86-
output.push(`for (const ${param} of globalRange) {`);
94+
const param = targetName(d.param);
8795
const body = gen(d.body);
88-
output.push(` ${funcName}.push(${body});`);
89-
output.push(` previous_${funcName} = ${funcName}[${funcName}.length - 1];`);
90-
output.push(`}`);
91-
currentFunction.name = null;
92-
currentFunction.param = null;
96+
const lastStmt = body.pop();
97+
output.push(`function ${funcName}(${param}) {\n${body}\nreturn ${lastStmt};\n} `);
98+
if (instantiatedMutableRanges.indexOf(param) === -1) {
99+
output.push(`let ${param} = initializeMutableRange();`);
100+
}
93101
},
94102

95103
PrintStmt(s) {
@@ -98,12 +106,9 @@ export default function generate(program) {
98106

99107
StepCall(s) {
100108
const expr = gen(s.expr);
101-
let stepValue = s.stepValue ? gen(s.stepValue) : 1;
102-
stepValue = Number(stepValue) || 1;
103-
if (currentFunction.name && expr === `${currentFunction.name}(${currentFunction.param})`) {
104-
return `previous_${currentFunction.name}`;
105-
}
106-
return `${expr}[${stepValue - 1}]`;
109+
const stepValue = gen(s.stepValue);
110+
const arg = targetName(s.expr.arg);
111+
output.push(`applyFunction(${arg}, ${stepValue}, ${expr});`);
107112
},
108113

109114
Expr(e) {
@@ -117,7 +122,9 @@ export default function generate(program) {
117122
CondExpr(e) {
118123
if (e.thenBranch) {
119124
const condleft = gen(e.leftCond);
120-
const op = e.op;
125+
const op = e.op === "==" ? "===" :
126+
e.op === "!=" ? "!==" :
127+
e.op;
121128
const condright = gen(e.rightCond);
122129
const thenBranch = gen(e.thenBranch);
123130
const elseBranch = gen(e.elseBranch);
@@ -178,7 +185,7 @@ export default function generate(program) {
178185
},
179186

180187
TimeCall(e) {
181-
return gen(e.funcCall);
188+
return `${gen(e.id)}.values.slice(0, ${gen(e.timeValue)})`;
182189
},
183190

184191
num(n) {
@@ -194,11 +201,11 @@ export default function generate(program) {
194201
},
195202

196203
id(i) {
197-
return i.name;
204+
return targetName(i.name);
198205
},
199206

200207
FuncCall(c) {
201-
return `${targetName(c.name)}(${c.arg})`;
208+
return `${targetName(c.name)}`;
202209
}
203210
};
204211

test/analyzer.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ const semanticChecks = [
4545
["function calls", "f(x)=1\nprint(f(x))"],
4646
["step call", "f(x)=x\nprint(f(x).step())"],
4747
["step call with specified number", "f(x)=x\nprint(f(x).step(4))"],
48-
["time call", "f(x)=x\nprint(f(x):5)"],
48+
["time call", "f(x)=x\nprint(x:5)"],
4949
["global range in increasing range", "`1..3`\n"],
5050
["global range in decreasing range", "`1..-4`\n"],
5151
["global range with positive integer time step", "`1..10` t2t\n"],

test/parser.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const syntaxChecks = [
1414
["function name containing a keyword", "printe(x)=2"],
1515
["function with 2x", "f(x)=2x"],
1616
["function call through step", "f(x).step(3)"],
17-
["function call through colon", "print(f(x):2)"],
17+
["variable with colon", "print(x:2)"],
1818
["function call with multiple steps", "{G(x) + f(x)}.step(2)"],
1919
["print statement", 'print("Hello, World!")'],
2020
["print statement with special characters", 'print("\\n\\t\\r\\b\\\\\\"")'],

0 commit comments

Comments
 (0)