Skip to content

Commit 5066fa2

Browse files
authored
Merge pull request #29 from lucifer1004/string-macro
feat: support string interpolation in string macros (#28)
2 parents d450a87 + 5a33b56 commit 5066fa2

12 files changed

+109
-59
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# CHANGELOG
22

3+
## [v0.2.1] - Unreleased
4+
5+
### Changed
6+
7+
- String macros `@monkey_eval_str`, `@monkey_vm_str` and `@monkey_julia_str` now support string interpolations.
8+
39
## [v0.2.0] - 2022-02-04
410

511
### Added

Project.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "MonkeyLang"
22
uuid = "2e3b8ffb-b112-4a86-975c-b56a8cdf8971"
33
authors = ["Gabriel Wu <wuzihua@pku.edu.cn> and contributors"]
4-
version = "0.2.0"
4+
version = "0.2.1"
55

66
[deps]
77
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"

README.md

+25
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
**Table of Contents**
1010

1111
- [MonkeyLang](#monkeylang)
12+
- [Using Monkey in Julia](#using-monkey-in-julia)
1213
- [Compile MonkeyLang.jl to a standalone executable](#compile-monkeylangjl-to-a-standalone-executable)
1314
- [Start the REPL](#start-the-repl)
1415
- [Documentation](#documentation)
@@ -49,6 +50,30 @@
4950
- [A custom `reduce` function](#a-custom-reduce-function)
5051
- [Macro System](#macro-system)
5152

53+
## Using Monkey in Julia
54+
55+
You can start the REPL within Julia:
56+
57+
```julia
58+
using MonkeyLang
59+
60+
start_repl()
61+
```
62+
63+
Or you can evaluate Monkey programs using string macros:
64+
65+
```julia
66+
using MonkeyLang
67+
68+
a = 2
69+
70+
monkey_eval"let b = $a; puts(b)"
71+
72+
monkey_vm"let c = [$a, $a]; puts(c)"
73+
74+
monkey_julia"let d = {$a: $a}; puts(d)"
75+
```
76+
5277
## Compile MonkeyLang.jl to a standalone executable
5378

5479
Clone the repo, and run `make build` in the root directory.

benchmark/Manifest.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ uuid = "a63ad114-7e13-5084-954f-fe012c677804"
4343
deps = ["BenchmarkTools", "Printf"]
4444
path = ".."
4545
uuid = "2e3b8ffb-b112-4a86-975c-b56a8cdf8971"
46-
version = "0.2.0"
46+
version = "0.2.1"
4747

4848
[[deps.OpenBLAS_jll]]
4949
deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"]

src/MonkeyLang.jl

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
module MonkeyLang
22

3+
export start_repl, @monkey_eval_str, @monkey_vm_str
4+
35
using Printf
46

5-
const MONKEY_VERSION = v"0.2.0"
7+
const MONKEY_VERSION = v"0.2.1"
68
const MONKEY_AUTHOR = "Gabriel Wu"
79

810
# Tokens
@@ -50,6 +52,9 @@ include("repl.jl")
5052
# Transpilers
5153
include("transpilers/transpilers.jl")
5254

55+
using .Transpilers.JuliaTranspiler: @monkey_julia_str
56+
export @monkey_julia_str
57+
5358
# CLI
5459
include("cli.jl")
5560

src/analyzer.jl

+11-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ function analyze(code::String; input::IO = stdin, output::IO = stdout)
88
end
99
end
1010

11-
function analyze(program::Program; existing_symbol_table::Union{SymbolTable,Nothing} = nothing, exisiting_env::Union{Environment,Nothing} = nothing)
11+
function analyze(
12+
program::Program;
13+
existing_symbol_table::Union{SymbolTable,Nothing} = nothing,
14+
exisiting_env::Union{Environment,Nothing} = nothing,
15+
)
1216
if !isnothing(existing_symbol_table)
1317
symbol_table = SymbolTable(existing_symbol_table)
1418
else
@@ -53,13 +57,17 @@ function analyze(ls::LetStatement, symbol_table::SymbolTable)
5357
return ErrorObj("identifier not found: $(ls.name.value)")
5458
end
5559

56-
if sym.scope == FunctionScope || (sym.scope == OuterScope && sym.ptr.scope == FunctionScope)
60+
if sym.scope == FunctionScope ||
61+
(sym.scope == OuterScope && sym.ptr.scope == FunctionScope)
5762
return ErrorObj(
5863
"cannot reassign the current function being defined: $(ls.name.value)",
5964
)
6065
end
6166
else
62-
if !isnothing(sym) && (sym.scope == LocalScope || (is_global(symbol_table) && sym.scope == GlobalScope))
67+
if !isnothing(sym) && (
68+
sym.scope == LocalScope ||
69+
(is_global(symbol_table) && sym.scope == GlobalScope)
70+
)
6371
return ErrorObj("$(ls.name.value) is already defined")
6472
end
6573

src/evaluator.jl

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
macro monkey_eval_str(code::String)
2-
:(evaluate($code))
2+
quote
3+
run($(esc(Meta.parse("\"$(escape_string(code))\""))))
4+
end
35
end
46

57
evaluate(code::String; input = stdin, output = stdout) = begin

src/repl.jl

+2-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ function start_repl(; input::IO = stdin, output::IO = stdout, use_vm::Bool = fal
6262
expanded = expand_macros(program, macro_env)
6363

6464
if use_vm
65-
syntax_check_result = analyze(expanded; existing_symbol_table = symbol_table)
65+
syntax_check_result =
66+
analyze(expanded; existing_symbol_table = symbol_table)
6667
if isa(syntax_check_result, ErrorObj)
6768
println(output, syntax_check_result)
6869
continue

src/symbol_table.jl

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ mutable struct SymbolTable
2323
SymbolTable(; outer::Union{SymbolTable,Nothing} = nothing, within_loop::Bool = false) =
2424
new(Dict(), 0, outer, within_loop, [])
2525

26-
SymbolTable(s::SymbolTable) = new(copy(s.store), s.definition_count, s.outer, s.within_loop, s.free_symbols)
26+
SymbolTable(s::SymbolTable) =
27+
new(copy(s.store), s.definition_count, s.outer, s.within_loop, s.free_symbols)
2728
end
2829

2930
is_global(s::SymbolTable) = isnothing(s.outer)

src/transpilers/julia/julia_transpiler.jl

+3-1
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,9 @@ run(code::String; input::IO = stdin, output::IO = stdout) = begin
259259
end
260260

261261
macro monkey_julia_str(code::String)
262-
:(run($code))
262+
quote
263+
run($(esc(Meta.parse("\"$(escape_string(code))\""))))
264+
end
263265
end
264266

265267
end

src/vm.jl

+3-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ current_frame(vm::VM) = vm.frames[end]
6666
instructions(vm::VM) = instructions(current_frame(vm))
6767

6868
macro monkey_vm_str(code::String)
69-
:(run($code))
69+
quote
70+
run($(esc(Meta.parse("\"$(escape_string(code))\""))))
71+
end
7072
end
7173

7274
run(code::String; input = stdin, output = stdout) = begin

test/backend_test.jl

+46-48
Original file line numberDiff line numberDiff line change
@@ -361,24 +361,22 @@ function test_backend(run::Function, name::String; check_object::Bool = true)
361361
end
362362

363363
@testset "Redefine" begin
364-
for (code, expected) in [
365-
(
366-
"""
367-
let x = 5;
368-
let y = 1;
369-
let z = 0;
370-
while (y > 0) {
371-
z = z + x;
372-
let x = 9;
373-
z = z + x;
374-
y = y - 1;
375-
}
364+
for (code, expected) in [(
365+
"""
366+
let x = 5;
367+
let y = 1;
368+
let z = 0;
369+
while (y > 0) {
376370
z = z + x;
377-
z;
378-
""",
379-
19,
380-
),
381-
]
371+
let x = 9;
372+
z = z + x;
373+
y = y - 1;
374+
}
375+
z = z + x;
376+
z;
377+
""",
378+
19,
379+
)]
382380
if name == "julia"
383381
@test_broken run(code) == expected
384382
else
@@ -415,43 +413,43 @@ function test_backend(run::Function, name::String; check_object::Bool = true)
415413

416414
@testset "Closure" begin
417415
for (code, expected) in [(
418-
"""
419-
let newAdder = fn(x) {
420-
fn(y) { x + y };
421-
}
422-
423-
let addTwo = newAdder(2);
424-
addTwo(2);
425-
""",
426-
4,
427-
), (
428-
"""
429-
let a = 2;
430-
let ans = [];
431-
432-
let g = fn() {
433-
let b = 2;
434-
let f = fn() {
435-
b = b - 1;
436-
return b;
437-
}
438-
return f;
416+
"""
417+
let newAdder = fn(x) {
418+
fn(y) { x + y };
419+
}
420+
421+
let addTwo = newAdder(2);
422+
addTwo(2);
423+
""",
424+
4,
425+
), (
426+
"""
427+
let a = 2;
428+
let ans = [];
429+
430+
let g = fn() {
431+
let b = 2;
432+
let f = fn() {
433+
b = b - 1;
434+
return b;
439435
}
436+
return f;
437+
}
440438
441-
let f = g();
439+
let f = g();
442440
443-
ans = push(ans, f());
444-
ans = push(ans, f());
441+
ans = push(ans, f());
442+
ans = push(ans, f());
445443
446-
let ff = g();
444+
let ff = g();
447445
448-
ans = push(ans, ff());
449-
ans = push(ans, ff());
446+
ans = push(ans, ff());
447+
ans = push(ans, ff());
450448
451-
ans;
452-
""",
453-
[1, 0, 1, 0],
454-
)]
449+
ans;
450+
""",
451+
[1, 0, 1, 0],
452+
)]
455453
check(code, expected)
456454
end
457455
end

0 commit comments

Comments
 (0)