Skip to content

Commit

Permalink
Merge pull request #155 from angelcaru/varargs
Browse files Browse the repository at this point in the history
Explicit variable declaration.
  • Loading branch information
Almas-Ali authored Jun 1, 2024
2 parents 236dd90 + ceab54a commit add0f36
Show file tree
Hide file tree
Showing 33 changed files with 185 additions and 159 deletions.
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

0 comments on commit add0f36

Please sign in to comment.