Skip to content

Commit

Permalink
lot of stuff
Browse files Browse the repository at this point in the history
- builtin modules
- importing "as" keyword
- error handling fixes
- vscode extension update
- bug fixes
  • Loading branch information
ImShyMike committed Dec 30, 2024
1 parent 1f44d74 commit b57de2e
Show file tree
Hide file tree
Showing 13 changed files with 187 additions and 108 deletions.
44 changes: 29 additions & 15 deletions docs/docs/language-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,14 @@ let null_val = null; # This is a null
Importing is done with the `import` keyword.
```C linenums="1"
import "math" # Imports a file name 'math.eryx'
from "math" import ["add", "pi"] # Imports the function 'add' and variable 'pi' from 'math.eryx'
import "test.erx" # Imports a file name 'test.eryx'
from "test.eryx" import ["add", "pi"] # Imports the function 'add' and variable 'pi' from 'test.eryx'
import "math" as "meth" # Imports the builtin 'math' module as 'meth'
```

!!! note "Builtins"
Builtin modules are imported without the ".eryx" (example: "math")

## Functions
Functions can be declared using the `func` keyword.

Expand All @@ -67,30 +71,40 @@ func add(x, y) {
print(add(1, 2)) # Output: 3
```
There are also many reserved builtin functions:
There are also many builtin functions:
!!! note "Values"
Values containing '?' are optional and '...' reffers to any amount of arguments.
* **print(** ... **)**: Print all values passed to it
* **time()**: Get the current time in seconds since the Epoch
* **getRequest(** url **)**: Send a get request to a url
* **postRequest(** url, data **)**: Send a post request with json data as a string to a url
* **input(** text?) **: Get user input as a string, optionally prompt with a message
* **readFile(** filename **)**: Read the contents of a file as a string
* **writeFile(** filename, text **)**: Write to a file
* **appendFile(** filename, text **)**: Append to the contents of a file
* **len(** item **)**: Get the length of a string, array, or object
* **exit(** code? **)**: Exit the program, optionally with a status code
* **str(** value? **)**: Convert a value to its string representation
* **int(** value? **)**: Convert a value to an integer
* **bool(** value? **)**: Convert a value to a boolean
* **array(** ... or string **)**: Create a new array from the given values or turn a string into an array
* **round(** number, n?**)**: Round a number to the n'th decimal place (default 0)
* **type(** value **)**: Get the type of the given value
* **sum(** array **)**: Get the sum of an array of numbers
* **min(** array **)**: Get the minimum value from an array of numbers
* **max(** array **)**: Get the maximum value from an array of numbers
!!! note "Values"
Values containing '?' are optional and '...' reffers to any amount of arguments.
There are also many builtin modules:
* **time**:
* **time()**: Get the current time in seconds since the Epoch
* **math**:
* **sum(** array **)**: Get the sum of an array of numbers
* **round(** number, n? **)**: Round a number to the n'th decimal place (default 0)
* **min(** array **)**: Get the minimum value from an array of numbers
* **max(** array **)**: Get the maximum value from an array of numbers
* **random()**: Get a random number between 0 and 1
* **pi**: The value for pi
* **file**:
* **read(** filename **)**: Read the contents of a file as a string
* **write(** filename, text **)**: Write to a file
* **append(** filename, text **)**: Append to the contents of a file
* **http**: (WIP)
* **get(** url **)**: Send a get request to a url
* **post(** url, data **)**: Send a post request with json data as a string to a url
## Operators
Currently, all supported operators are:
Expand Down
2 changes: 1 addition & 1 deletion eryx-syntax-highlighting/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "eryx-syntax-highlighting",
"displayName": "Eryx Syntax Highlighting",
"description": "Syntax highligh extension for the Eryx programming language",
"version": "0.0.1",
"version": "0.0.2",
"homepage": "https://github.com/ImShyMike/Eryx/tree/main/eryx-syntax-highlighting",
"repository": {
"type": "git",
Expand Down
6 changes: 3 additions & 3 deletions eryx-syntax-highlighting/syntaxes/eryx.tmLanguage.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"keywords": {
"patterns": [{
"name": "keyword.control.eryx",
"match": "\\b(if|else|return|func|let|const|true|false|null)\\b"
"match": "\\b(if|else|return|func|let|const|true|false|null|import|from|as)\\b"
}]
},
"strings": {
Expand All @@ -48,7 +48,7 @@
},
"operators": {
"name": "keyword.operator.eryx",
"match": "(\\+|-|\\*|\\/|%|=|!|<|>)"
"match": "(\\+|-|\\*|\\/|%|=|<|>|>=|<=|==|!=)"
},
"functions": {
"patterns": [
Expand All @@ -58,7 +58,7 @@
},
{
"name": "entity.name.function.builtin.eryx",
"match": "\\b(print|time|input|readFile|writeFile|appendFile|getRequest|postRequest|round|len|exit|str|int|bool|array|type|sum|min|max)\\b"
"match": "\\b(print|input|len|exit|str|int|bool|array|type)\\b"
}
]
},
Expand Down
Binary file not shown.
2 changes: 1 addition & 1 deletion eryx/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Version of the package."""

CURRENT_VERSION = "0.2.9"
CURRENT_VERSION = "0.2.10"
1 change: 1 addition & 0 deletions eryx/frontend/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,4 @@ class ImportStatement(Statement):

module: str
names: List[str] | None = None
alias: str | None = None
2 changes: 2 additions & 0 deletions eryx/frontend/lexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class TokenType(Enum):

IMPORT = auto()
FROM = auto()
AS = auto()

EQUALS = auto()

Expand Down Expand Up @@ -71,6 +72,7 @@ def __repr__(self) -> str:
"return": TokenType.RETURN,
"import": TokenType.IMPORT,
"from": TokenType.FROM,
"as": TokenType.AS
}


Expand Down
27 changes: 10 additions & 17 deletions eryx/frontend/parser.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
"""Parser module for the frontend of the compiler."""

import os

from eryx.frontend.ast import (
ArrayLiteral,
AssignmentExpression,
Expand Down Expand Up @@ -432,14 +430,14 @@ def parse_import_statement(self) -> Statement:
TokenType.STRING, "Expected a string after the import keyword."
)

if not os.path.exists(value.value + ".eryx"):
syntax_error(
self.source_code,
value.position,
f"Import file '{value.value}.eryx' does not exist.",
)
if self.at().type == TokenType.AS:
self.next() # Skip the as keyword

alias = self.assert_next(TokenType.STRING, "Expected a string for the import alias.")

return ImportStatement(value.value, None)
return ImportStatement(value.value, alias=alias.value)

return ImportStatement(value.value)

def parse_from_statement(self) -> Statement:
"""Parse a from statement."""
Expand Down Expand Up @@ -484,17 +482,12 @@ def parse_from_statement(self) -> Statement:
"Import properties must be strings.",
)

if not os.path.exists(from_value.value + ".eryx"):
syntax_error(
self.source_code,
from_value.position,
f"Import file '{from_value.value}.eryx' does not exist.",
)

return ImportStatement(
from_value.value,
[
item.value for item in import_value.elements if isinstance(item, StringLiteral)
item.value
for item in import_value.elements
if isinstance(item, StringLiteral)
],
)

Expand Down
70 changes: 57 additions & 13 deletions eryx/runtime/environment.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Environment class for storing variables (also called scope)."""

import math
import random
import sys
import time
from urllib.request import Request, urlopen
Expand All @@ -16,6 +18,8 @@
StringValue,
)

BUILTINS = {}


# pylint: disable=invalid-name
class Environment:
Expand All @@ -36,12 +40,17 @@ def __init__(
self.setup_scope()

def declare_variable(
self, variable_name: str, value: RuntimeValue, constant: bool = False
self,
variable_name: str,
value: RuntimeValue,
constant: bool = False,
overwrite: bool = False,
) -> RuntimeValue:
"""Declare a variable in the current scope."""
# Raise an exception if the variable is already declared
if variable_name in self.variables:
raise RuntimeError(f'Variable "{variable_name}" already declared')
if not overwrite:
raise RuntimeError(f'Variable "{variable_name}" already declared')

self.variables[variable_name] = value

Expand Down Expand Up @@ -84,26 +93,15 @@ def setup_scope(self) -> None:
self.declare_variable("null", NullValue(), True)

# Declare native methods
if not self.disable_file_io:
self.declare_variable("readFile", NativeFunctionValue(_readFile), True)
self.declare_variable("writeFile", NativeFunctionValue(_writeFile), True)
self.declare_variable("appendFile", NativeFunctionValue(_appendFile), True)
self.declare_variable("getRequest", NativeFunctionValue(_getRequest), True)
self.declare_variable("postRequest", NativeFunctionValue(_postRequest), True)
self.declare_variable("print", NativeFunctionValue(_print), True)
self.declare_variable("time", NativeFunctionValue(_time), True)
self.declare_variable("input", NativeFunctionValue(_input), True)
self.declare_variable("len", NativeFunctionValue(_len), True)
self.declare_variable("exit", NativeFunctionValue(_exit), True)
self.declare_variable("str", NativeFunctionValue(_str), True)
self.declare_variable("int", NativeFunctionValue(_int), True)
self.declare_variable("bool", NativeFunctionValue(_bool), True)
self.declare_variable("array", NativeFunctionValue(_array), True)
self.declare_variable("round", NativeFunctionValue(_round), True)
self.declare_variable("type", NativeFunctionValue(_type), True)
self.declare_variable("sum", NativeFunctionValue(_sum), True)
self.declare_variable("min", NativeFunctionValue(_min), True)
self.declare_variable("max", NativeFunctionValue(_max), True)


def get_value(value: RuntimeValue, inside_array: bool = False) -> str:
Expand Down Expand Up @@ -162,6 +160,18 @@ def _print(args: list[RuntimeValue], _: Environment) -> RuntimeValue:
return NullValue()


def _sqrt(args: list[RuntimeValue], _: Environment):
if not args:
raise RuntimeError("Missing number value")
if not isinstance(args[0], NumberValue):
raise RuntimeError("Input type must be a number")
return NumberValue(args[0].value ** 0.5)


def _random(_: list[RuntimeValue], __: Environment):
return NumberValue(random.random())


def _time(_: list[RuntimeValue], __: Environment) -> RuntimeValue:
return NumberValue(time.time())

Expand Down Expand Up @@ -350,3 +360,37 @@ def _max(args: list[RuntimeValue], _: Environment) -> RuntimeValue:
if all(isinstance(i, NumberValue) for i in args[0].elements):
return NumberValue(max(i.value for i in args[0].elements)) # type: ignore
raise RuntimeError(f"Cannot get max for {args[0]}")


# Declare builtin modules
BUILTINS["file"] = ObjectValue(
{
"read": NativeFunctionValue(_readFile),
"write": NativeFunctionValue(_writeFile),
"append": NativeFunctionValue(_appendFile),
},
immutable=True,
)

BUILTINS["http"] = ObjectValue(
{
"get": NativeFunctionValue(_getRequest),
"post": NativeFunctionValue(_postRequest),
},
immutable=True,
)

BUILTINS["math"] = ObjectValue(
{
"sum": NativeFunctionValue(_sum),
"min": NativeFunctionValue(_min),
"max": NativeFunctionValue(_max),
"round": NativeFunctionValue(_round),
"pi": NumberValue(math.pi),
"sqrt": NativeFunctionValue(_sqrt),
"random": NativeFunctionValue(_random),
},
immutable=True,
)

BUILTINS["time"] = ObjectValue({"time": NativeFunctionValue(_time)}, immutable=True)
Loading

0 comments on commit b57de2e

Please sign in to comment.