Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explicit variable declaration #155

Merged
merged 3 commits into from
Jun 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions core/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,11 @@ def generate_radiation(self) -> str:
ctx = self.context

while ctx:
fn = pos.fn if pos is not None else None
ln = pos.ln + 1 if pos is not None else None
name = ctx.display_name if ctx is not None else None
result = (
f" File {Log.light_info(pos.fn)}, line {Log.light_info(str(pos.ln + 1))}, in {Log.light_info(ctx.display_name)}\n"
+ result
f" File {Log.light_info(fn)}, line {Log.light_info(str(ln))}, in {Log.light_info(name)}\n" + result
)
pos = ctx.parent_entry_pos # type: ignore
ctx = ctx.parent # type: ignore
Expand Down
38 changes: 31 additions & 7 deletions core/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,35 +11,50 @@


class Interpreter:
def assign(self, *, var_name, value, context, extra_names=[], qualifier=None, pos_start, pos_end):
res = RTResult()
def assign(
self,
*,
var_name: str,
value: Value,
context: Context,
extra_names: list[Token] = [],
qualifier: Optional[Token] = None,
pos_start: Position,
pos_end: Position,
) -> RTResult[Value]:
res = RTResult[Value]()

if extra_names != []:
assert qualifier is None
nd = context.symbol_table.get(var_name)
prev = None

if not nd:
if nd is None:
return res.failure(RTError(pos_start, pos_end, f"'{var_name}' not defined", context))

for index, name_tok in enumerate(extra_names):
name = name_tok.value
assert isinstance(name, str)

if not isinstance(nd, Class) and not isinstance(nd, Instance):
return res.failure(RTError(pos_start, pos_end, "Value must be instance of class or class", context))

prev = nd
nd = nd.symbol_table.symbols[name] if name in nd.symbol_table.symbols else None

if not nd and index != len(extra_names) - 1:
if nd is None and index != len(extra_names) - 1:
return res.failure(RTError(pos_start, pos_end, f"'{name}' not defined", context))

assert prev is not None
assert isinstance(name, str)
res.register(prev.symbol_table.set(name, value))
if res.should_return():
return res
return res.success(value)

res.register(context.symbol_table.set(var_name, value, qualifier))
qualifier_str = None if qualifier is None else qualifier.value
assert qualifier_str is None or isinstance(qualifier_str, str)
res.register(context.symbol_table.set_var(var_name, value, qualifier_str))
if res.should_return():
return res
return res.success(value)
Expand Down Expand Up @@ -132,9 +147,11 @@ def visit_VarAccessNode(self, node: VarAccessNode, context: Context) -> RTResult
def visit_VarAssignNode(self, node: VarAssignNode, context: Context) -> RTResult[Value]:
res = RTResult[Value]()
var_name = node.var_name_tok.value
assert isinstance(var_name, str)
value = res.register(self.visit(node.value_node, context))
if res.should_return():
return res
assert value is not None

return self.assign(
var_name=var_name,
Expand Down Expand Up @@ -247,7 +264,14 @@ def visit_ImportNode(self, node: ImportNode, context: Context) -> RTResult[Value

res = RTResult[Value]()
res.register(
self.assign(var_name=name, value=module, context=context, pos_start=node.pos_start, pos_end=node.pos_end)
self.assign(
var_name=name,
value=module,
context=context,
pos_start=node.pos_start,
pos_end=node.pos_end,
qualifier=Token(TT_KEYWORD, "const", pos_start=node.pos_start),
)
)
if res.should_return():
return res
Expand Down Expand Up @@ -502,7 +526,7 @@ def visit_FuncDefNode(self, node: FuncDefNode, context: Context) -> RTResult[Val
assert context.symbol_table is not None
assert isinstance(func_name, str), "this could be a bug in the parser"
if node.static:
context.symbol_table.set_static(func_name, func_value)
context.symbol_table.set_static(func_name, func_value, "var")
else:
context.symbol_table.set(func_name, func_value)

Expand Down
40 changes: 21 additions & 19 deletions core/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ def expr(self) -> ParseResult[Node]:
var_assign_node = res.try_register(self.assign_expr())
if var_assign_node is not None:
return res.success(var_assign_node)
elif res.error:
elif res.error is not None:
return res
else:
self.reverse(res.to_reverse_count)
Expand All @@ -316,7 +316,7 @@ def assign_expr(self) -> ParseResult[Node]:
self.advance(res)

qualifier = None
if self.current_tok.type == TT_KEYWORD and self.current_tok.value in ("global", "nonlocal", "const"):
if self.current_tok.type == TT_KEYWORD and self.current_tok.value in ("var", "const"):
qualifier = self.current_tok
self.advance(res)

Expand Down Expand Up @@ -1682,22 +1682,25 @@ def get(self, name: str) -> Optional[Value]:
return self.parent.get(name)
return value

def set(self, name: str, value: Value, qualifier: Optional[Token] = None) -> RTResult[None]:
class dummy:
TT_KW = TT_KEYWORD
def set(self, name: str, value: Value) -> RTResult[None]:
if name in self.consts:
return RTResult[None]().failure(
RTError(value.pos_start, value.pos_end, f"Cannot reassign to constant {name}", value.context)
)
self.symbols[name] = value
return RTResult[None]().success(None)

def set_var(self, name: str, value: Value, qualifier: Optional[str] = None) -> RTResult[None]:
if name in self.consts:
return RTResult[None]().failure(
RTError(value.pos_start, value.pos_end, f"Cannot reassign to constant {name}", value.context)
)
match qualifier:
case None:
self.symbols[name] = value
case Token(dummy.TT_KW, "nonlocal"):
if name in self.symbols:
self.symbols[name] = value
elif self.parent:
self.parent.set(name, value, qualifier)
elif self.parent is not None:
self.parent.set_var(name, value, qualifier)
else:
return RTResult[None]().failure(
RTError(
Expand All @@ -1707,21 +1710,20 @@ class dummy:
value.context,
)
)
case Token(dummy.TT_KW, "global"):
if self.parent is None:
self.symbols[name] = value
else:
self.parent.set(name, value, qualifier)
case Token(dummy.TT_KW, "const"):
case "var":
if name in self.symbols:
return RTResult[None]().failure(
RTError(value.pos_start, value.pos_end, f"Cannot re-declare variable {name}", value.context)
)
self.symbols[name] = value
case "const":
self.symbols[name] = value
self.consts.add(name)
case _:
assert False, "invalid qualifier"
return RTResult[None]().success(None)

def set_static(self, name: str, value: Value, qualifier: Optional[Token] = None) -> RTResult[None]:
def set_static(self, name: str, value: Value, qualifier: Optional[str] = None) -> RTResult[None]:
res = RTResult[None]()
res.register(self.set(name, value, qualifier))
res.register(self.set_var(name, value, qualifier))
if res.should_return():
return res
self.statics.add(name)
Expand Down
3 changes: 1 addition & 2 deletions core/tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,6 @@ def copy(self) -> Position:
"catch",
"as",
"in",
"nonlocal",
"global",
"const",
"static",
"assert",
Expand All @@ -135,6 +133,7 @@ def copy(self) -> Position:
"fallthrough",
"raise",
"fallout",
"var",
]

TokenValue: TypeAlias = Optional[str | int | float]
Expand Down
48 changes: 24 additions & 24 deletions stdlib/argparser.rn
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@

_i = 0
FULLNAME_INVALID = _i++
FULLNAME_FLAG = _i++
FULLNAME_NAMED = _i++
var _i = 0
const FULLNAME_INVALID = _i++
const FULLNAME_FLAG = _i++
const FULLNAME_NAMED = _i++
_i = 0

fun str_starts_with(s, prefix) {
Expand Down Expand Up @@ -30,7 +30,7 @@ class Argparser {
assert is_null(default_), "Required positional arguments cannot have a default value"
}
if is_null(conversor) {
nonlocal conversor = fun(x) -> x
conversor = fun(x) -> x
}
arr_append(this.pos_opts, {"name": name, "desc": desc, "default": default_, "required": required, "conversor": conversor})
return this
Expand All @@ -47,7 +47,7 @@ class Argparser {
fun add_named(name, desc, default_=null, conversor=null) {
assert str_starts_with(name, "--"), "Named arguments must start with '--'"
if is_null(conversor) {
nonlocal conversor = fun(x) -> x
conversor = fun(x) -> x
}
arr_append(this.named, {"name": name, "desc": desc, "default": default_, "conversor": conversor})
return this
Expand All @@ -63,17 +63,17 @@ class Argparser {
}

fun usage(program_name) {
ret = "Usage: "+program_name+" <flags> <options>\n"
var ret = "Usage: "+program_name+" <flags> <options>\n"
ret += "OPTIONS:\n"
for opt in this.pos_opts {
nonlocal ret += " " + opt["name"] + ": " + opt["desc"] + "\n"
ret += " " + opt["name"] + ": " + opt["desc"] + "\n"
}
ret += "FLAGS:\n"
for flag in this.flags {
nonlocal ret += " " + flag["fullname"] + ", " + flag["shortname"] + ": " + flag["desc"] + "\n"
ret += " " + flag["fullname"] + ", " + flag["shortname"] + ": " + flag["desc"] + "\n"
}
for named in this.named {
nonlocal ret += " " + named["name"] + " <value>: " + named["desc"] + "\n"
ret += " " + named["name"] + " <value>: " + named["desc"] + "\n"
}
return ret
}
Expand Down Expand Up @@ -109,14 +109,14 @@ class Argparser {

fun parse(args=null) {
if is_null(args) {
nonlocal args = argv[:]
args = argv[:]
}
pos_opts = this.pos_opts
flags = this.flags
var pos_opts = this.pos_opts
var flags = this.flags

program_name = arr_pop(args, 0)
parsed = {}
required_opts = {}
var program_name = arr_pop(args, 0)
var parsed = {}
var required_opts = {}
for pos_opt in pos_opts {
parsed[pos_opt["name"]] = pos_opt["default"]
if pos_opt["required"] {
Expand All @@ -129,9 +129,9 @@ class Argparser {
for named in this.named {
parsed[named["name"]] = named["default"]
}
pos_opts_idx = 0
var pos_opts_idx = 0
while arr_len(args) > 0 {
arg = arr_pop(args, 0)
var arg = arr_pop(args, 0)
if str_starts_with(arg, "-") {
if str_starts_with(arg, "--") {
switch this.fullname_type(arg) {
Expand All @@ -141,8 +141,8 @@ class Argparser {
if arr_len(args) == 0 {
this.report_error(program_name, "missing value for flag: '"+arg+"'")
}
value = arr_pop(args, 0)
conversor = this.get_named_conversor(arg)
const value = arr_pop(args, 0)
const conversor = this.get_named_conversor(arg)
try {
parsed[arg] = conversor(value)
} catch as e {
Expand All @@ -152,7 +152,7 @@ class Argparser {
}
} else {
for letter in arg[1:] {
flag = this.flag_by_shortname(letter)
const flag = this.flag_by_shortname(letter)
if is_null(flag) {
this.report_error(program_name, "unknown flag: '-"+letter+"'")
} else {
Expand All @@ -164,15 +164,15 @@ class Argparser {
if pos_opts_idx >= arr_len(pos_opts) {
this.report_error(program_name, "unexpected positional argument: '" + arg + "'")
}
arg_name = pos_opts[pos_opts_idx]["name"]
conversor = pos_opts[pos_opts_idx]["conversor"]
const arg_name = pos_opts[pos_opts_idx]["name"]
const conversor = pos_opts[pos_opts_idx]["conversor"]
try {
parsed[arg_name] = conversor(arg)
} catch as e {
this.report_error(program_name, "invalid value for argument: '" + arg_name + "': " + str(e))
}
required_opts[arg_name] = false
nonlocal pos_opts_idx++
pos_opts_idx++
}
}
for required in required_opts {
Expand Down
11 changes: 4 additions & 7 deletions stdlib/array.rn
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ class Array {
}

fun map(func) {
new_elements = []
const new_elements = []

for i = 0 to this.__len__() {
arr_append(new_elements, func(arr_get(this.list, i)))
for elt in this.list {
arr_append(new_elements, func(elt))
}

return new_elements
Expand All @@ -17,10 +17,7 @@ class Array {
fun pop(index) -> arr_pop(this.list, index)
fun extend(list) -> arr_extend(this.list, list)
fun find(index) -> arr_find(this.list, index)
fun slice(start, end) {
# These parentheses are dumb. We need to find another way.
return (this.list)[start:end]
}
fun slice(start, end) -> ((this.list)[start:end])

fun __len__() -> arr_len(this.list)
fun is_empty() -> this.list == []
Expand Down
Loading