0

I'm pretty new to Nim.

I have this code:

type
  Environment* = object
    values*: Table[string, BaseType]
    error*: Error

proc newEnv*(errorObj: Error): Environment =
  return Environment(
    values: initTable[string, BaseType](),
    error: errorObj
  )

proc define*(env: var Environment, name: string, value: BaseType) =
  env.values[name] = value
  echo(env.values)

proc get*(env: var Environment, name: Token): BaseType =
  echo(env.values)
  if env.values.hasKey(name.value):
    return env.values[name.value]
  else:
    # do something...


type Interpreter* = object
  error*: Error
  env*: Environment

proc newInterpreter*(errorObj: Error): Interpreter =
  return Interpreter(
    error: errorObj,
    env: newEnv(errorObj)
  )

method eval*(self: var Interpreter, expre: Expr): BaseType {.base.} = discard
method eval*(self: var Interpreter, statement: Stmt) {.base.} = discard

method eval*(self: var Interpreter, expre: VariableExpr): BaseType = 
  return self.env.get(expre.name)

method eval*(self: var Interpreter, statement: VariableStmt) = 
  var value: BaseType
  if not statement.init.isNil: value = self.eval(statement.init)
  self.env.define(statement.name.value, value)

As you can see, when eval proc with VariableStmt is called, it calls define proc to bind the name with the value.

However, the two echo statements print this:

{"a": 123}
{:}

The first line is from define proc, and the second line is from get proc.

Even though it looks like "a" and 123 got saved from calling define proc, inside get proc, the values table is empty.

I am not sure why this is. Could you help me solve this?

I will provide more information if you need it.

Update

I put an echo statement just before return self.env.get(expre.name), and it prints out this:

(data: @[(hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil), (hcode: 0, key: "", val: nil)], counter: 0)

I'm not sure what this is. Is it a bug of Nim?

pooooky
  • 490
  • 2
  • 12
  • Regarding the update, that's just the internal representation of a table. I guess you somehow ended up with what `repr` outputs and not what `$` (the implicit to-string procedure in Nim) would give you. – PMunch Feb 19 '22 at 01:15

1 Answers1

1

I tried to boil this down to a minimal working sample (always a good strategy when trying to track down a bug or get help). However this works:

import tables

type
  Environment = object
    table: Table[string, string]
  Interpreter = object
    env: Environment

proc define(env: var Environment, name, key: string) =
  env.table[name] = key
  echo env.table

proc get(env: var Environment, name: string): string =
  echo env.table
  env.table.getOrDefault(name, "")

proc eval(self: var Interpreter, command, name: string, value = "") =
  if command == "ADD":
    self.env.define(name, value)
  elif command == "GET":
    echo self.env.get(name)

var interpreter: Interpreter

interpreter.eval("ADD", "hello", "world")
interpreter.eval("GET", "hello")

This leads me to believe that you somewhere manage to make a copy of your interpreter or the environment as you pass it around. An easy mistake to make, but hard to tell where you made it or if it even is that without a more complete code sample (preferably something which runs of course).

EDIT: By the way, you probably don't need methods. You might, but it's quite a common beginner mistake to use them when you don't.

PMunch
  • 1,525
  • 11
  • 20
  • I'll check if I make a copy of my interpreter or environment in my code. – pooooky Feb 19 '22 at 01:02
  • Hmm, I can't see anything that might copy my interpreter or environment. Do you have any idea – pooooky Feb 19 '22 at 01:31
  • Can you think of any other reasons that this happens? – pooooky Feb 19 '22 at 01:32
  • Not really, this really looks like it is a copy issue. They can sneak up on you if you're not careful. Hard to tell without being able to look at the source code, but you can try to make your Environment and Interpreter types as `ref object` instead which should give them reference semantics which might be what you're expecting if you don't see anything that would copy. – PMunch Feb 21 '22 at 15:50