Skip to content

Commit acd48c8

Browse files
committed
support objects with JavaScript function
1 parent 0e4a84f commit acd48c8

File tree

5 files changed

+274
-21
lines changed

5 files changed

+274
-21
lines changed

DNCL3.test.js

+21
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,24 @@ Deno.test("array default value", async () => {
3939
t.assertEquals(await run("a <- 1\nprint a[0][3][5]"), ["1"]); // 3D array
4040
t.assertEquals(await run("a <- 1\nprint a[0][3][5][6]"), ["1"]); // 3D array
4141
});
42+
Deno.test("import rnd", async () => {
43+
t.assertEquals(await run(`function rnd from "rnd.js"\nprint rnd(1)`), ["0"]);
44+
});
45+
Deno.test("import obj", async () => {
46+
t.assertEquals(await run(`function obj from "obj.js"\na <- obj(50)\nprint a.a`), ["100"]);
47+
t.assertEquals(await run(`
48+
function obj from "obj.js"
49+
a <- obj(50)
50+
a.b()
51+
`), []);
52+
//t.assertEquals(await run(`function obj from "obj.js"\na <- obj(50)\nprint a.b()`), ["[50]"]);
53+
});
54+
Deno.test("import obj2", async () => {
55+
t.assertEquals(await run(`function obj from "obj.js"\na <- obj(50)\nprint a.a`), ["100"]);
56+
t.assertEquals(await run(`
57+
function obj from "obj.js"
58+
a <- obj(50)
59+
print a.b()
60+
`), ["[50]"]);
61+
//t.assertEquals(await run(`function obj from "obj.js"\na <- obj(50)\nprint a.b()`), ["[50]"]);
62+
});

Parser.dncl.test.js

+205
Original file line numberDiff line numberDiff line change
@@ -309,3 +309,208 @@ Deno.test("for if", async () => {
309309
]
310310
});
311311
});
312+
313+
Deno.test("func simple", async () => {
314+
t.assertEquals(parse("a()"), {
315+
body: [
316+
{
317+
type: "ExpressionStatement",
318+
expression: {
319+
type: "CallExpression",
320+
arguments: [],
321+
callee: {
322+
name: "a",
323+
type: "Identifier",
324+
},
325+
},
326+
},
327+
],
328+
type: "Program",
329+
}
330+
);
331+
});
332+
Deno.test("func simple in expression", async () => {
333+
t.assertEquals(parse("print a()"), {
334+
body: [
335+
{
336+
type: "ExpressionStatement",
337+
expression: {
338+
type: "CallExpression",
339+
callee: {
340+
name: "print",
341+
type: "Identifier",
342+
},
343+
arguments: [
344+
{
345+
type: "CallExpression",
346+
callee: {
347+
name: "a",
348+
type: "Identifier",
349+
},
350+
arguments: [],
351+
},
352+
],
353+
},
354+
},
355+
],
356+
type: "Program",
357+
}
358+
);
359+
});
360+
Deno.test("func obj", async () => {
361+
t.assertEquals(parse("a.b()"), {
362+
body: [
363+
{
364+
type: "ExpressionStatement",
365+
expression: {
366+
type: "CallExpression",
367+
arguments: [],
368+
callee: {
369+
"type": "MemberExpression",
370+
"object": {
371+
"type": "Identifier",
372+
"name": "a"
373+
},
374+
"property": {
375+
"type": "Identifier",
376+
"name": "b"
377+
},
378+
computed: false,
379+
},
380+
},
381+
},
382+
],
383+
type: "Program",
384+
}
385+
);
386+
});
387+
Deno.test("func obj in expression", async () => {
388+
t.assertEquals(parse("print obj.func()"), {
389+
body: [
390+
{
391+
type: "ExpressionStatement",
392+
expression: {
393+
type: "CallExpression",
394+
callee: {
395+
name: "print",
396+
type: "Identifier",
397+
},
398+
arguments: [
399+
{
400+
type: "CallExpression",
401+
callee: {
402+
"type": "MemberExpression",
403+
"object": {
404+
"type": "Identifier",
405+
"name": "obj"
406+
},
407+
"property": {
408+
"type": "Identifier",
409+
"name": "func"
410+
},
411+
computed: false,
412+
},
413+
arguments: [],
414+
},
415+
],
416+
},
417+
},
418+
],
419+
type: "Program",
420+
}
421+
);
422+
});
423+
Deno.test("assign simple", async () => {
424+
t.assertEquals(parse("a <- 3"), {
425+
body: [
426+
{
427+
type: "ExpressionStatement",
428+
expression: {
429+
type: "AssignmentExpression",
430+
left: {
431+
name: "a",
432+
type: "Identifier",
433+
},
434+
operator: "=",
435+
right: {
436+
type: "Literal",
437+
value: 3,
438+
},
439+
},
440+
},
441+
],
442+
type: "Program",
443+
}
444+
);
445+
});
446+
Deno.test("assign multi", async () => {
447+
t.assertEquals(parse("a <- 3, b <- 3"), {
448+
body: [
449+
{
450+
type: "ExpressionStatement",
451+
expression: {
452+
type: "SequenceExpression",
453+
expressions: [
454+
{
455+
type: "AssignmentExpression",
456+
left: {
457+
name: "a",
458+
type: "Identifier",
459+
},
460+
operator: "=",
461+
right: {
462+
type: "Literal",
463+
value: 3,
464+
},
465+
},
466+
{
467+
type: "AssignmentExpression",
468+
left: {
469+
name: "b",
470+
type: "Identifier",
471+
},
472+
operator: "=",
473+
right: {
474+
type: "Literal",
475+
value: 3,
476+
},
477+
},
478+
],
479+
},
480+
},
481+
],
482+
type: "Program",
483+
}
484+
);
485+
});
486+
Deno.test("assign obj", async () => {
487+
t.assertEquals(parse("a.b <- 3"), {
488+
body: [
489+
{
490+
type: "ExpressionStatement",
491+
expression: {
492+
type: "AssignmentExpression",
493+
left: {
494+
"type": "MemberExpression",
495+
"object": {
496+
"type": "Identifier",
497+
"name": "a"
498+
},
499+
"property": {
500+
"type": "Identifier",
501+
"name": "b"
502+
},
503+
computed: false,
504+
},
505+
operator: "=",
506+
right: {
507+
type: "Literal",
508+
value: 3,
509+
},
510+
},
511+
},
512+
],
513+
type: "Program",
514+
}
515+
);
516+
});

Parser.js

+9-11
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ export class Parser {
252252
*/
253253
getValue() {
254254
const t1 = this.getToken();
255+
//console.log("getv", t1)
255256
if (t1.type == "(") {
256257
const res = this.getExpression();
257258
const t2 = this.getToken();
@@ -313,10 +314,11 @@ export class Parser {
313314
argument: this.getValue(),
314315
};
315316
} else if (t1.type == "var") {
317+
const varobj = this.parseVar(t1.name);
316318
const chk = this.getToken();
317319
if (chk.type != "(") {
318320
this.backToken(chk);
319-
return this.parseVar(t1.name);
321+
return varobj;
320322
}
321323
// function call
322324
const args = [];
@@ -332,10 +334,7 @@ export class Parser {
332334
}
333335
return {
334336
type: "CallExpression",
335-
callee: {
336-
type: "Identifier",
337-
name: t1.name,
338-
},
337+
callee: varobj,
339338
arguments: args,
340339
};
341340

@@ -554,6 +553,8 @@ export class Parser {
554553
// [var][idx] <= [expression]
555554
// [function]([params])
556555
// x [function] [params] // paramsで括弧付きのものがあると破天
556+
const var1 = this.parseVar(token.name);
557+
//console.log("var1", var1);
557558
const chk = this.getToken();
558559
if (chk.type == "(") { // function
559560
const params = [];
@@ -574,20 +575,16 @@ export class Parser {
574575
type: "ExpressionStatement",
575576
expression: {
576577
type: "CallExpression",
577-
callee: {
578-
type: "Identifier",
579-
name: token.name,
580-
},
578+
callee: var1,
581579
arguments: params,
582580
},
583581
});
584582
} else { // var
585583
this.backToken(chk);
586584
let token2 = token;
587585
const res = [];
586+
let left = var1;
588587
for (;;) {
589-
const left = this.parseVar(token2.name);
590-
591588
const c = this.getCharNotWhite();
592589
if (c == "=" || c == "<") {
593590
if (c == "<") {
@@ -629,6 +626,7 @@ export class Parser {
629626
//throw new Error("コンマ区切りで続けられるのは代入文のみです");
630627
throw new Error("only assign operation after comma");
631628
}
629+
left = this.parseVar(token2.name);
632630
}
633631
if (res.length == 1) {
634632
body.push({

Runtime.js

+33-10
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,29 @@ export class Runtime {
9797
}
9898
return prop;
9999
}
100+
getFunc(scope, cmd) {
101+
if (cmd.callee.type == "Identifier") {
102+
const name = cmd.callee.name;
103+
if (!scope.isDefined(name)) {
104+
throw new Error("定義されていない関数 " + name + " が使われました");
105+
}
106+
const func = scope.getVar(name);
107+
return { parent: null, name, func };
108+
} else if (cmd.callee.type == "MemberExpression") {
109+
if (cmd.callee.object.type != "Identifier") throw new Error("未対応です");
110+
if (cmd.callee.property.type != "Identifier") throw new Error("未対応です");
111+
if (cmd.callee.computed != false) throw new Error("未対応です");
112+
const name = cmd.callee.object.name;
113+
if (!scope.isDefined(name)) {
114+
throw new Error("定義されていない関数 " + name + " が使われました");
115+
}
116+
const parent = scope.getVar(name);
117+
const func = parent[cmd.callee.property.name];
118+
return { parent, name, func };
119+
} else {
120+
throw new Error("サポートしていないtypeです " + cmd.callee.type);
121+
}
122+
}
100123
async runBlock(ast, scope) {
101124
const body = ast.type == "BlockStatement" ||
102125
ast.type == "Program" ? ast.body :
@@ -175,14 +198,11 @@ export class Runtime {
175198
throw new Error("非対応の type です " + cmd.left.type);
176199
}
177200
} else if (cmd.type == "CallExpression") {
178-
const name = cmd.callee.name;
179-
if (!scope.isDefined(name)) {
180-
throw new Error("定義されていない関数 " + name + " が使われました");
181-
}
182-
const func = scope.getVar(name);
201+
const { parent, name, func } = this.getFunc(scope, cmd);
202+
// console.log(cmd.callee);
183203
if (typeof func == "object") {
184204
if (ast.arguments.length != func.params.length) {
185-
throw new Error("引数の数が合っていません");
205+
throw new Error("引数の数が合っていません 機能名: " + name);
186206
}
187207
const scope2 = new Scope(scope);
188208
for (let i = 0; i < ast.arguments.length; i++) {
@@ -204,7 +224,8 @@ export class Runtime {
204224
for (let i = 0; i < ast.arguments.length; i++) {
205225
params.push(await this.calcExpression(ast.arguments[i], scope));
206226
}
207-
await func(...params);
227+
//await func(...params);
228+
await func.apply(parent, params);
208229
} else {
209230
new Error("関数ではないものが関数として呼び出されました")
210231
}
@@ -458,7 +479,8 @@ export class Runtime {
458479
throw new Error("対応していない演算子が使われました");
459480
}
460481
} else if (ast.type == "CallExpression") {
461-
const name = ast.callee.name;
482+
const { parent, name, func } = this.getFunc(scope, ast);
483+
//const name = ast.callee.name;
462484
/*
463485
if (name == "input") {
464486
if (ast.arguments.length > 1) {
@@ -475,7 +497,7 @@ export class Runtime {
475497
if (!scope.isDefined(name)) {
476498
throw new Error("定義されていない関数 " + name + " が使われました");
477499
}
478-
const func = scope.getVar(name);
500+
//const func = scope.getVar(name);
479501
if (typeof func == "object") {
480502
if (ast.arguments.length != func.params.length) {
481503
throw new Error("引数の数が合っていません");
@@ -503,7 +525,8 @@ export class Runtime {
503525
for (let i = 0; i < ast.arguments.length; i++) {
504526
params.push(await this.calcExpression(ast.arguments[i], scope));
505527
}
506-
return await func(...params);
528+
//return await func(...params);
529+
return await func.apply(parent, params);
507530
} else {
508531
new Error("関数ではないものが関数として呼び出されました")
509532
}

0 commit comments

Comments
 (0)