Skip to content

Commit

Permalink
feat: Add random lib implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
FallenDeity committed Apr 29, 2023
1 parent 9256822 commit 6b933f9
Show file tree
Hide file tree
Showing 14 changed files with 276 additions and 33 deletions.
117 changes: 117 additions & 0 deletions src/headers/generics.lox
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,121 @@ class generics {
}
return buffer;
}

filter (callable, arr) {
var length_ = len(arr);
var buffer = array();
for (var i = 0; i < length_; i = i + 1) {
if (callable(arr.get(i))) {
buffer.append(arr.get(i));
}
}
return buffer;
}

reduce (callable, arr, initial) {
var length_ = len(arr);
var result = initial;
for (var i = 0; i < length_; i = i + 1) {
result = callable(result, arr.get(i));
}
return result;
}

sum (arr) {
var length_ = len(arr);
var result = 0;
for (var i = 0; i < length_; i = i + 1) {
result = result + arr.get(i);
}
return result;
}

max (arr) {
var length_ = len(arr);
var result = arr.get(0);
for (var i = 1; i < length_; i = i + 1) {
if (arr.get(i) > result) {
result = arr.get(i);
}
}
return result;
}

min (arr) {
var length_ = len(arr);
var result = arr.get(0);
for (var i = 1; i < length_; i = i + 1) {
if (arr.get(i) < result) {
result = arr.get(i);
}
}
return result;
}

all (arr) {
var length_ = len(arr);
for (var i = 0; i < length_; i = i + 1) {
if (!arr.get(i)) {
return false;
}
}
return true;
}

any (arr) {
var length_ = len(arr);
for (var i = 0; i < length_; i = i + 1) {
if (arr.get(i)) {
return true;
}
}
return false;
}

join (arr, sep) {
var length_ = len(arr);
var result = "";
for (var i = 0; i < length_; i = i + 1) {
if (i > 0) {
result = result + sep;
}
result = result + arr.get(i);
}
return result;
}

sort (arr) {
var length_ = len(arr);
for (var i = 0; i < length_; i = i + 1) {
for (var j = i + 1; j < length_; j = j + 1) {
if (arr.get(i) > arr.get(j)) {
var tmp = arr.get(i);
arr.set(i, arr.get(j));
arr.set(j, tmp);
}
}
}
}

sorted (arr) {
var result = arr.copy();
this.sort(result);
return result;
}

reverse (arr) {
var length_ = len(arr);
for (var i = 0; i < length_ / 2; i = i + 1) {
var tmp = arr.get(i);
arr.set(i, arr.get(length_ - i - 1));
arr.set(length_ - i - 1, tmp);
}
}

reversed (arr) {
var result = arr.copy();
this.reverse(result);
return result;
}
}
44 changes: 44 additions & 0 deletions src/headers/random.lox
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
class random {
init() {
this.seed = int(clock());
this.a = 594156893;
this.m = 2147483648;
this.c = 1597;
}

setSeed(seed) {
this.seed = seed;
}

nextInt() {
this.seed = (this.a * this.seed + this.c) % this.m;
return this.seed;
}

randint(min, max) {
return floor((max - min) * (this.nextInt() / (this.m - 1)) + min);
}

choice(arr) {
return this.randint(0, len(arr) - 1);
}

randrange(start, stop, step) {
var pick = this.randint(start, stop - 1);
var arr = array();
arr.append(pick - (pick % step));
arr.append(pick + (step - (pick % step)));
return arr.get(this.choice(arr));
}

shuffle(arr) {
var i = len(arr);
while (i > 0) {
var j = this.randint(0, i - 1);
var temp = arr.get(i - 1);
arr.set(i - 1, arr.get(j));
arr.set(j, temp);
i = i - 1;
}
}
}
34 changes: 34 additions & 0 deletions src/internals/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,35 @@ def __call__(self, interpreter: "Interpreter", arguments: list[t.Any], /) -> "Lo
raise PyLoxRuntimeError(interpreter.error(self.token, "Invalid slice."))


@dataclasses.dataclass
class Extend(ArrayCallable):
parent: "LoxArray"
token: "Token"

@property
def arity(self) -> int:
return 1

def __call__(self, interpreter: "Interpreter", arguments: list[t.Any], /) -> None:
try:
self.parent.fields.extend(arguments[0])
except TypeError:
raise PyLoxRuntimeError(interpreter.error(self.token, "Invalid iterable."))


@dataclasses.dataclass
class Copy(ArrayCallable):
parent: "LoxArray"
token: "Token"

@property
def arity(self) -> int:
return 0

def __call__(self, interpreter: "Interpreter", arguments: list[t.Any], /) -> "LoxArray":
return LoxArray(self.parent.fields.copy())


class LoxArray(LoxContainer):
parent: t.Type["LoxArray"]
fields: list[t.Any]
Expand All @@ -191,12 +220,17 @@ class LoxArray(LoxContainer):
"sort": Sort,
"join": Join,
"slice": Slice,
"extend": Extend,
"copy": Copy,
}

def __init__(self, fields: t.Optional[list[t.Any]] = None) -> None:
self.parent = LoxArray
self.fields = fields or []

def __mul__(self, other: int, /) -> "LoxArray":
return LoxArray(self.fields * other)

def __str__(self) -> str:
return f"[{', '.join(map(str, self.fields))}]"

Expand Down
2 changes: 1 addition & 1 deletion src/internals/callables.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def find_method(self, name: str, /) -> t.Optional[LoxFunction]:
@dataclasses.dataclass
class LoxInstance:
parent: LoxClass
fields: t.Dict[str, t.Any] = dataclasses.field(default_factory=dict)
fields: t.Dict[t.Any, t.Any] = dataclasses.field(default_factory=dict)

def get(self, name: Token, /) -> t.Any:
if name.lexeme in self.fields:
Expand Down
15 changes: 2 additions & 13 deletions src/internals/string.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,23 +152,12 @@ def __int__(self) -> int:
def __float__(self) -> float:
return float(self.fields)

def __bool__(self) -> bool:
return bool(self.fields)

def __len__(self) -> int:
return len(self.fields)

def __getitem__(self, index: int, /) -> str:
return self.fields[index]
def __mul__(self, other: int) -> "LoxString":
return LoxString(self.fields * other)

def __hash__(self) -> int:
return hash(self.fields)

def __eq__(self, other: t.Any) -> bool:
if isinstance(other, LoxString):
return self.fields == other.fields
return False

def get(self, name: "Token", /) -> t.Any:
try:
return super().get(name)
Expand Down
48 changes: 47 additions & 1 deletion src/internals/types.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import dataclasses
import typing as t

from src.exceptions import PyLoxAttributeError, PyLoxIndexError
from src.exceptions import PyLoxAttributeError, PyLoxIndexError, PyLoxTypeError

from .callables import LoxCallable, LoxInstance

Expand Down Expand Up @@ -55,6 +55,52 @@ class LoxContainer(LoxInstance):
def __len__(self) -> int:
return len(self.fields)

def __getitem__(self, index: int, /) -> str:
return self.fields[index]

def __setitem__(self, index: int, value: t.Any, /) -> None:
self.fields[index] = value

def __gt__(self, other: t.Any) -> bool:
if isinstance(other, LoxContainer):
try:
return self.fields > other.fields # type: ignore
except TypeError:
raise PyLoxTypeError(f"Cannot compare {self.__class__.__name__} and {other.__class__.__name__}.")
return False

def __ge__(self, other: t.Any) -> bool:
if isinstance(other, LoxContainer):
try:
self.fields >= other.fields # type: ignore
except TypeError:
raise PyLoxTypeError(f"Cannot compare {self.__class__.__name__} and {other.__class__.__name__}.")
return False

def __lt__(self, other: t.Any) -> bool:
if isinstance(other, LoxContainer):
try:
self.fields < other.fields # type: ignore
except TypeError:
raise PyLoxTypeError(f"Cannot compare {self.__class__.__name__} and {other.__class__.__name__}.")
return False

def __le__(self, other: t.Any) -> bool:
if isinstance(other, LoxContainer):
try:
self.fields <= other.fields # type: ignore
except TypeError:
raise PyLoxTypeError(f"Cannot compare {self.__class__.__name__} and {other.__class__.__name__}.")
return False

def __bool__(self) -> bool:
return bool(self.fields)

def __eq__(self, other: t.Any) -> bool:
if isinstance(other, LoxContainer):
return self.fields == other.fields
return False

def __str__(self) -> str:
return f"{self.__class__.__name__}({self.fields})"

Expand Down
16 changes: 11 additions & 5 deletions src/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,7 @@ def stringify(obj: t.Any) -> str:
@staticmethod
def is_truthy(obj: t.Any) -> bool:
"""Check if an object is truthy."""
if obj is None:
return False
if isinstance(obj, bool):
return obj
return True
return bool(obj)

def _numeric_validation(self, operator: "Token", *operands: t.Any) -> None:
"""Validate numeric operands."""
Expand Down Expand Up @@ -336,6 +332,8 @@ def visit_binary_expr(self, expr: "Binary") -> t.Any:
return left + right
if isinstance(left, LoxString) and isinstance(right, LoxString):
return LoxString(str(left) + str(right))
if isinstance(left, LoxArray) and isinstance(right, LoxArray):
return LoxArray(left.fields + right.fields)
raise PyLoxRuntimeError(self.error(expr.operator, "Operands must be two numbers or two strings."))
case SimpleTokenType.SLASH:
self._numeric_validation(expr.operator, left, right)
Expand All @@ -350,6 +348,14 @@ def visit_binary_expr(self, expr: "Binary") -> t.Any:
except ZeroDivisionError:
raise PyLoxRuntimeError(self.error(expr.operator, "Division by zero."))
case SimpleTokenType.STAR:
if isinstance(left, LoxString) and isinstance(right, int):
return LoxString(str(left) * right)
if isinstance(left, int) and isinstance(right, LoxString):
return LoxString(str(right) * left)
if isinstance(left, LoxArray) and isinstance(right, int):
return LoxArray(left.fields * right)
if isinstance(left, int) and isinstance(right, LoxArray):
return LoxArray(left * right.fields)
self._numeric_validation(expr.operator, left, right)
return left * right
case SimpleTokenType.MODULO:
Expand Down
3 changes: 2 additions & 1 deletion src/interpreter/lox.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ def __init__(self, source: str | pathlib.Path = "") -> None:
self.logger = Logger(name="PyLox")
self.interpreter = Interpreter(self, self.logger)
self._source = self._read_file(self._file_path) if self._file_path else ""
self._source = PreProcessor(self._source).source
process = PreProcessor(self._source)
self._source = process.source
self.lexer = Lexer(self._source, self.logger)

@staticmethod
Expand Down
4 changes: 2 additions & 2 deletions src/lexer/lexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,10 @@ def _scan_token(self) -> None:
return
elif char == str(SimpleTokenType.SLASH):
self._read_comment()
elif char in SimpleTokenType.as_dict().values():
self._add_token(SimpleTokenType(char))
elif char in ComplexTokenType.as_dict().values():
self._read_complex(char)
elif char in SimpleTokenType.as_dict().values():
self._add_token(SimpleTokenType(char))
elif char == "\n":
self._cursor.bump_line()
elif char in (str(LiteralTokenType.SINGLE_QUOTE), str(LiteralTokenType.DOUBLE_QUOTE)):
Expand Down
1 change: 1 addition & 0 deletions src/lexer/tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class ComplexTokenType(TokenType):
LESS = "<"
LESS_EQUAL = "<="
BACKSLASH = "\\"
BANG = "!"


class LiteralTokenType(TokenType):
Expand Down
Loading

0 comments on commit 6b933f9

Please sign in to comment.