How to limit scope of evaluated variable assignment?

As described in this early draft essay, I’m developing a somewhat terse text specification of 3D meshes as an attempt to make 3D modeling more accessible to the blind. The code is probably working good enough for now but I noticed that all the evaluated variables in that text specification are showing up in the varinfo() list. Any tips on how to prevent that?

The following is the terse text file that specifies the mesh of a hollow cylinder. (The lines that start with ‘E’ denote evaluations of variables, and the lines that start with ‘P’ denote the initial coordinates of a rotating polygon that “extrudes” triangle faces.)

; file: _cyl.gcd (model of hollow cylinder)
E ri = 1.85
E ro = 2.6
E h = 2*ri
P 0 0 0
P 0 0 h
P ri 0 h
P ro 0 h
P ro 0 0
P ri 0 0

And the following is the Julia code I am currently using to read that terse text file. After running it, the evaluated variables ri (radius inner), ro (radius outer), and h (cylinder height) appear in the varinfo() list.

macro approw(PC, V) # append row
	PC = esc(PC)
	V = esc(V)
	return quote
		vcat($PC, $V)
	end
end

function gcd_run(fn::String)
	# define structure for polygon coordinates
	PC = Array{Float32}(undef, 0, 3)
	
	# read command file
	open(fn, "r") do io
		while !eof(io)
			strLine = readline(io)
			if strLine[1] == ';'
				continue
			elseif strLine[1] == 'E'
				eval(Meta.parse(strLine[2:end]))
			elseif strLine[1] == 'P'
				R = eval(Meta.parse(
					"[" * strLine[2:end] * "]"))
				PC = @approw PC R
			else
				println(strLine)
			end
		end
	end
	display(PC)
end

eval runs the expression in the global scope, and since the line after the leading E is an assignment expression, you end up assigning global variables. There’s no eval inside local scopes, its data and code are set in stone upon compilation, you can’t insert arbitrary variables and code at that point.

I’m not really sure what that line is supposed to accomplish instead because the result is not assigned to a local variable and the global variables aren’t used afterward. But if you need something holding arbitrary symbols, you can use Dict{Symbol, T}. The 2*ri part does seem like it requires eval, though you’d have to parse ri to an expression indexing that Dict.

1 Like

Thanks for the tip. I’ll look into using Dict.

Yes, currently the results aren’t used afterward because I am just at the point of testing the text parsing. Soon, gcd_run() will generate mesh vertices and triangle faces and write them to a 3D object file (.obj). Previously, I generated those vertices and triangle faces based upon the 3D object specifications coming from Julia code instead of an application specific command file. My hope is that the very short text file will be easier to edit (given the very limited number of characters that can be displayed on refreshable braille displays) than Julia code.

tip

“How to limit scope of evaluated variable assignment?”

To answer this directly, eval is always global to the module that it’s run in, but every module has its own eval. So you can contain the variables within a module.

Example:

julia> module MyMod end
Main.MyMod

julia> MyMod.eval(:( x=5; y=3 ))
3

julia> MyMod.x, MyMod.y
(5, 3)

julia> varinfo()
  name                    size summary
  –––––––––––––––– ––––––––––– –––––––––––––––––––
  Base                         Module
  Core                         Module
  InteractiveUtils 517.004 KiB Module
  Main                         Module
  MyMod              3.232 KiB Module
  ans                 16 bytes Tuple{Int64, Int64}
2 Likes