Skip to content

Commit a5c8f65

Browse files
committed
feat: ✨ add _js_ keyword to run js code
1 parent 4ce2e3d commit a5c8f65

File tree

6 files changed

+40
-8
lines changed

6 files changed

+40
-8
lines changed

src/codegen/CompilerJS.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ export default class CompilerJS {
4646
case NodeTypes.CALL: {
4747
return this.jsCall(exp);
4848
}
49+
case NodeTypes._JS_: {
50+
return this.jsRaw(exp);
51+
}
4952
default:
5053
throw new Error(`Cannot compile to JS ${JSON.stringify(exp)} at ${exp.line}:${exp.col}`);
5154
}
@@ -121,4 +124,8 @@ export default class CompilerJS {
121124
jsCall(exp) {
122125
return `${this.js(exp.func)}(${exp.args.map(this.js.bind(this)).join(', ')})`;
123126
}
127+
128+
jsRaw(exp) {
129+
return `(${exp.code})`;
130+
}
124131
}

src/parser/NodeTypes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const NodeTypes = {
1212
LET: 'let',
1313
NOT: 'not',
1414
DEFINE: 'define',
15+
_JS_: '_js_',
1516
};
1617

1718
export default NodeTypes;

src/parser/Parser.js

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,7 @@ class Parser {
7272
if (hisPrec > myPrec) {
7373
this.tokens.next();
7474
const assigns = ['=', '*=', '**=', '+=', '-=', '/='];
75-
const type = assigns.includes(tok.value)
76-
? NodeTypes.ASSIGN
77-
: NodeTypes.BINARY;
75+
const type = assigns.includes(tok.value) ? NodeTypes.ASSIGN : NodeTypes.BINARY;
7876
return this.maybeBinary(
7977
{
8078
type,
@@ -103,7 +101,8 @@ class Parser {
103101
col: right.col,
104102
args: [this.maybePipe(left)],
105103
};
106-
} if (right.type === NodeTypes.CALL) {
104+
}
105+
if (right.type === NodeTypes.CALL) {
107106
return {
108107
type: NodeTypes.CALL,
109108
func: {
@@ -299,8 +298,10 @@ class Parser {
299298
if (left) {
300299
this.skipVar(left.value);
301300
} else {
302-
this.tokens.croak('Parse Error', `Expecting variable name after ${variant === 'immutable'
303-
? 'def' : 'def mut'}`);
301+
this.tokens.croak(
302+
'Parse Error',
303+
`Expecting variable name after ${variant === 'immutable' ? 'def' : 'def mut'}`
304+
);
304305
}
305306
let right = {
306307
type: NodeTypes.NUM,
@@ -320,6 +321,20 @@ class Parser {
320321
};
321322
}
322323

324+
parseRaw() {
325+
this.skipKeyword(Keywords._JS_);
326+
if (this.tokens.peek().type !== NodeTypes.STR) {
327+
this.tokens.croak(
328+
'Parse Error',
329+
'_JS_ raw code must be a plain string'
330+
);
331+
}
332+
return {
333+
type: NodeTypes._JS_,
334+
code: this.tokens.next().value,
335+
};
336+
}
337+
323338
parseAtom() {
324339
return this.maybeCall(() => {
325340
if (this.isPunc('(')) {
@@ -335,10 +350,13 @@ class Parser {
335350
if (this.isKeyword(Keywords.FN) || this.isKeyword(Keywords.FN_ARROW)) return this.parseFn();
336351
if (this.isKeyword(Keywords.DEF)) return this.parseDefine();
337352
if (this.isKeyword(Keywords.LET)) return this.parseLet();
353+
if (this.isKeyword(Keywords._JS_)) return this.parseRaw();
338354
const tok = this.tokens.next();
339-
if (tok.type === TokenTypes.VAR
355+
if (
356+
tok.type === TokenTypes.VAR
340357
|| tok.type === TokenTypes.NUM
341-
|| tok.type === TokenTypes.STR) {
358+
|| tok.type === TokenTypes.STR
359+
) {
342360
return tok;
343361
}
344362
return this.unexpected();

src/runtime/Evaluator.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ export default function evaluate(exp, env, callback) {
1010
callback(exp.value);
1111
return;
1212
}
13+
case NodeTypes._JS_: {
14+
callback(eval(exp.code));
15+
return;
16+
}
1317
case NodeTypes.VAR: {
1418
callback(env.get(exp.value), exp);
1519
return;

src/stdlib/interpreter/StdEnvYAS.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export default function stdEnv(ast) {
77
def car = fn(cell) -> cell(fn(a, b) -> a);
88
def cdr = fn(cell) -> cell(fn(a, b) -> b);
99
def NIL = fn(f) -> f(NIL, NIL);
10+
def not = fn(x) -> if x then false else true;
1011
1112
def foreach = fn(list, f) ->
1213
if list != NIL {

src/tokenizer/Keywords.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const Keywords = {
1111
MUT: 'mut',
1212
FN_ARROW: 'fn->',
1313
DEF: 'def',
14+
_JS_: '_js_',
1415
};
1516

1617
export default Keywords;

0 commit comments

Comments
 (0)