Skip to content

Commit

Permalink
updating to include more advanced functions
Browse files Browse the repository at this point in the history
  • Loading branch information
infuzu-yidisprei committed Dec 25, 2023
1 parent 7a247a8 commit 6574028
Show file tree
Hide file tree
Showing 12 changed files with 1,040 additions and 24 deletions.
55 changes: 54 additions & 1 deletion oqs-specification.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# `OQS` (Open Quick Script) Language Guidelines
## Version: 0.4
## Version: 0.5
## Overview
This document establishes the comprehensive guidelines for the `OQS` (Open Quick Script) language. `OQS` aims to be a universally adoptable, streamlined, and system-neutral scripting language that integrates effortlessly into diverse platforms. `OQS` is not designed to be a feature complete programming language. Rather, it is designed to be a simple, yet powerful, expression engine. It is specifically crafted to process expressions encompassing fundamental types and operations, interpreting a solitary expression—optionally accompanied by a dictionary, map, or JSON containing variables—to yield a consistent and logical outcome.

Expand Down Expand Up @@ -319,6 +319,59 @@ Interactions between types are explicitly defined within `OQS` as follows:
- **Amount**: A minimum of two arguments up to an unlimited amount.
- **Types**: Alternating between conditions (any type evaluated for truthiness) and results (any type).
- **Outputs**: The result corresponding to the first true condition or the `else` result.
- `TYPE(argument)` - Determines the type of the given argument:
- **Inputs**:
- **Amount**: Exactly one input.
- **Types**: Any single type.
- **Outputs**: A `String` representing the type of the argument, such as "number", "integer", "decimal", "boolean", "list", "string", "function", "null", or "kvs".
- **Examples**:
- **Input**: `TYPE(5)` **Output**: `"integer"`
- **Input**: `TYPE([1, 2, 3])` **Output**: `"list"`
- `IS_TYPE(argument, type_string)` - Evaluates whether the argument's type matches the specified type string:
- **Inputs**:
- **Amount**: Exactly two inputs.
- **Types**: First input of any type, second input a `String`.
- **Case Insensitivity**: The `type_string` input is case-insensitive.
- **Outputs**: A `Boolean` indicating the type match.
- **Superiority Rules**: Types "integer" and "decimal" are considered subtypes of "number". An argument matching "integer" or "decimal" also returns `true` for "number".
- **Examples**:
- **Input**: `IS_TYPE(5, "number")` **Output**: `true`
- **Input**: `IS_TYPE("hello", "string")` **Output**: `true`
- `TRY(expression, error_type1, result1, ..., error_typeN, resultN)` - Attempts to evaluate an expression and handles specific errors with corresponding fallback expressions:
- **Inputs**:
- **Amount**: Minimum of three, odd number, no maximum.
- **Types**: The first input is an expression of any type, followed by alternating `String` error types and their corresponding expressions.
- **Case Insensitivity**: The `error_type` inputs are case-insensitive.
- **Superiority Rules**: Error types follow a hierarchy, with specific error types taking precedence over general ones.
- **Outputs**: The result of the first expression if no error occurs, or the result of the corresponding expression for the first true matching error.
- **Examples**:
- **Input**: `TRY(1/0, "Division By Zero Error", "Infinity", "Syntax Error", "Check expression")` **Output**: `"Infinity"`
- `RANGE(start, stop, step)` - Generates a list of integers starting from `start`, ending before `stop`, incrementing by `step`:
- **Inputs**:
- **Amount**: Between 1 and 3, all integers.
- **Defaults**: If only one argument is provided, it is considered as `stop` with `start` defaulting to 0 and `step` defaulting to 1.
- **Outputs**: A `List` of integers.
- **Examples**:
- **Input**: `RANGE(3)` **Output**: `[0, 1, 2]`
- **Input**: `RANGE(1, 3)` **Output**: `[1, 2]`
- **Input**: `RANGE(2, 10, 2)` **Output**: `[2, 4, 6, 8]`
- `FOR(list, variable_name, expression)` / `MAP(list, variable_name, expression)`- Iterates over each item in a list, executing an expression for each item:
- **Inputs**:
- **Amount**: Exactly three inputs.
- **Types**: First input must be a `List`, second a `String` for the variable name that will be set to the current item from the list, and third an expression.
- **Variable**: During each iteration, the variable with the name set in the second argument is set to the current item from the list.
- **Outputs**: A `List` of the results from evaluating the expression for each list item.
- **Examples**:
- **Input**: `FOR([1, 2, 3], FOR_LIST_ITEM * 2)` **Output**: `[2, 4, 6]`
- `RAISE(error_name, error_message)` - Triggers a specified error or creates a custom error:
- **Inputs**:
- **Amount**: Exactly two inputs, both `String`.
- **Types**: First input for the error name, second for the error message.
- **Custom Error Handling**: If `error_name` is not a predefined error, a custom error with that name is raised.
- **Outputs**: Raises the specified error.
- **Examples**:
- **Input**: `RAISE("Syntax Error", "Invalid syntax")` **Output**: Raises a Syntax Error with the message "Invalid syntax".
- **Input**: `RAISE("NewError", "Custom error occurred")` **Output**: Raises a custom error named "NewError" with the message "Custom error occurred".


### Case Sensitivity
Expand Down
55 changes: 54 additions & 1 deletion python_oqs_implementation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,59 @@ else:
- **Amount**: A minimum of two arguments up to an unlimited amount.
- **Types**: Alternating between conditions (any type evaluated for truthiness) and results (any type).
- **Outputs**: The result corresponding to the first true condition or the `else` result.
- `TYPE(argument)` - Determines the type of the given argument:
- **Inputs**:
- **Amount**: Exactly one input.
- **Types**: Any single type.
- **Outputs**: A `String` representing the type of the argument, such as "number", "integer", "decimal", "boolean", "list", "string", "function", "null", or "kvs".
- **Examples**:
- **Input**: `TYPE(5)` **Output**: `"integer"`
- **Input**: `TYPE([1, 2, 3])` **Output**: `"list"`
- `IS_TYPE(argument, type_string)` - Evaluates whether the argument's type matches the specified type string:
- **Inputs**:
- **Amount**: Exactly two inputs.
- **Types**: First input of any type, second input a `String`.
- **Case Insensitivity**: The `type_string` input is case-insensitive.
- **Outputs**: A `Boolean` indicating the type match.
- **Superiority Rules**: Types "integer" and "decimal" are considered subtypes of "number". An argument matching "integer" or "decimal" also returns `true` for "number".
- **Examples**:
- **Input**: `IS_TYPE(5, "number")` **Output**: `true`
- **Input**: `IS_TYPE("hello", "string")` **Output**: `true`
- `TRY(expression, error_type1, result1, ..., error_typeN, resultN)` - Attempts to evaluate an expression and handles specific errors with corresponding fallback expressions:
- **Inputs**:
- **Amount**: Minimum of three, odd number, no maximum.
- **Types**: The first input is an expression of any type, followed by alternating `String` error types and their corresponding expressions.
- **Case Insensitivity**: The `error_type` inputs are case-insensitive.
- **Superiority Rules**: Error types follow a hierarchy, with specific error types taking precedence over general ones.
- **Outputs**: The result of the first expression if no error occurs, or the result of the corresponding expression for the first true matching error.
- **Examples**:
- **Input**: `TRY(1/0, "Division By Zero Error", "Infinity", "Syntax Error", "Check expression")` **Output**: `"Infinity"`
- `RANGE(start, stop, step)` - Generates a list of integers starting from `start`, ending before `stop`, incrementing by `step`:
- **Inputs**:
- **Amount**: Between 1 and 3, all integers.
- **Defaults**: If only one argument is provided, it is considered as `stop` with `start` defaulting to 0 and `step` defaulting to 1.
- **Outputs**: A `List` of integers.
- **Examples**:
- **Input**: `RANGE(3)` **Output**: `[0, 1, 2]`
- **Input**: `RANGE(1, 3)` **Output**: `[1, 2]`
- **Input**: `RANGE(2, 10, 2)` **Output**: `[2, 4, 6, 8]`
- `FOR(list, variable_name, expression)` / `MAP(list, variable_name, expression)`- Iterates over each item in a list, executing an expression for each item:
- **Inputs**:
- **Amount**: Exactly three inputs.
- **Types**: First input must be a `List`, second a `String` for the variable name that will be set to the current item from the list, and third an expression.
- **Variable**: During each iteration, the variable with the name set in the second argument is set to the current item from the list.
- **Outputs**: A `List` of the results from evaluating the expression for each list item.
- **Examples**:
- **Input**: `FOR([1, 2, 3], FOR_LIST_ITEM * 2)` **Output**: `[2, 4, 6]`
- `RAISE(error_name, error_message)` - Triggers a specified error or creates a custom error:
- **Inputs**:
- **Amount**: Exactly two inputs, both `String`.
- **Types**: First input for the error name, second for the error message.
- **Custom Error Handling**: If `error_name` is not a predefined error, a custom error with that name is raised.
- **Outputs**: Raises the specified error.
- **Examples**:
- **Input**: `RAISE("Syntax Error", "Invalid syntax")` **Output**: Raises a Syntax Error with the message "Invalid syntax".
- **Input**: `RAISE("NewError", "Custom error occurred")` **Output**: Raises a custom error named "NewError" with the message "Custom error occurred".

Function calls are completed by putting the function name first and following it by putting an open parentheses `(` followed by any number of arguments separated by commas `,` and then followed by a closing parentheses `)`.

Expand Down Expand Up @@ -256,7 +309,7 @@ from oqs.utils.shortcuts import get_oqs_type


def custom_multiply(interpreter: OQSInterpreter, node: FunctionNode) -> int | float:
if 2 < len(node.args) < 2:
if not (2 < len(node.args) < 2):
raise OQSInvalidArgumentQuantityError(
function_name=node.name, expected_min=2, expected_max=2, actual=len(node.args)
)
Expand Down
129 changes: 126 additions & 3 deletions python_oqs_implementation/oqs/built_in_functions.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import json
from .constants.values import MAX_ARGS
from .errors import (OQSInvalidArgumentQuantityError, OQSDivisionByZeroError, OQSTypeError)
from .nodes import FunctionNode
from .utils.shortcuts import get_oqs_type
from .errors import (
OQSInvalidArgumentQuantityError,
OQSDivisionByZeroError,
OQSTypeError,
OQSFunctionEvaluationError,
OQSBaseError,
OQSCustomErrorParent,
ERROR_NAME_MAPPING
)
from .nodes import (FunctionNode, ASTNode)
from .utils.shortcuts import (get_oqs_type, is_oqs_instance)


def bif_add(interpreter: 'OQSInterpreter', node: FunctionNode) -> int | float | list | str | dict:
Expand Down Expand Up @@ -374,3 +382,118 @@ def bif_if(interpreter: 'OQSInterpreter', node: FunctionNode) -> any:
if len(node.args) % 2 != 0:
return interpreter.evaluate(node.args[-1])
return None


def bif_type(interpreter: 'OQSInterpreter', node: FunctionNode) -> str:
if len(node.args) != 1:
raise OQSInvalidArgumentQuantityError(
function_name=node.name, expected_min=1, expected_max=1, actual=len(node.args)
)
argument: any = interpreter.evaluate(node.args[0])
return get_oqs_type(argument)


def bif_is_type(interpreter: 'OQSInterpreter', node: FunctionNode) -> bool:
if len(node.args) != 2:
raise OQSInvalidArgumentQuantityError(
function_name=node.name, expected_min=2, expected_max=2, actual=len(node.args)
)
value, expected_type = [interpreter.evaluate(arg) for arg in node.args]
if not isinstance(expected_type, str):
raise OQSTypeError(message=f"Second argument must be a String. Instead got '{get_oqs_type(expected_type)}'.")
return is_oqs_instance(value, expected_type)


def bif_try(interpreter: 'OQSInterpreter', node: FunctionNode) -> any:
if len(node.args) < 3:
raise OQSInvalidArgumentQuantityError(
function_name=node.name, expected_min=3, expected_max=MAX_ARGS, actual=len(node.args)
)
if len(node.args) % 2 == 0:
raise OQSFunctionEvaluationError(
function_name=node.name, message=f"Expected an odd amount of input arguments. Instead got {len(node.args)}."
)
arguments: list[ASTNode] = node.args.copy()
primary_expression: ASTNode = arguments.pop(0)

try:
return interpreter.evaluate(primary_expression)
except OQSBaseError as error:
for i in range(0, len(arguments) - 1, 2):
exception: any = interpreter.evaluate(arguments[i])
if not isinstance(exception, str):
raise OQSTypeError(
message=f"Even argument must be a String. Instead got '{get_oqs_type(expected_type)}'."
)
if exception in error.error_hierarchy:
return interpreter.evaluate(arguments[i + 1])
raise


def bif_range(interpreter: 'OQSInterpreter', node: FunctionNode) -> list[int]:
if not (1 < len(node.args) < 3):
raise OQSInvalidArgumentQuantityError(
function_name=node.name, expected_min=1, expected_max=3, actual=len(node.args)
)
start: int = 0
step: int = 1
stop: int = 1
if len(node.args) == 1:
stop: any = interpreter.evaluate(node.args[0])
elif len(node.args) == 2:
start, stop = [interpreter.evaluate(arg) for arg in node.args]
elif len(node.args) == 3:
start, stop, step = [interpreter.evaluate(arg) for arg in node.args]
if not isinstance(start, int):
raise OQSTypeError(message=f"start argument must be an Integer. Instead got '{get_oqs_type(expected_type)}'.")
elif not isinstance(stop, int):
raise OQSTypeError(message=f"stop argument must be an Integer. Instead got '{get_oqs_type(expected_type)}'.")
elif not isinstance(step, int):
raise OQSTypeError(message=f"step argument must be an Integer. Instead got '{get_oqs_type(expected_type)}'.")
return list(range(start, stop, step))


def bif_for_or_map(interpreter: 'OQSInterpreter', node: FunctionNode) -> list[any]:
if len(node.args) != 3:
raise OQSInvalidArgumentQuantityError(
function_name=node.name, expected_min=3, expected_max=3, actual=len(node.args)
)
looping_list: any = interpreter.evaluate(node.args[0])
variable_name: any = interpreter.evaluate(node.args[1])
expression: ASTNode = node.args[3]
if not isinstance(looping_list, list):
raise OQSTypeError(message=f"list argument must be a List. Instead got '{get_oqs_type(expected_type)}'.")
elif not isinstance(variable_name, str):
raise OQSTypeError(
message=f"variable_name argument must be a String. Instead got '{get_oqs_type(expected_type)}'."
)
resulting_list: list[any] = []
for item in looping_list:
interpreter.variables[variable_name] = item
resulting_list.append(interpreter.evaluate(expression))
return resulting_list


def bif_raise(interpreter: 'OQSInterpreter', node: FunctionNode) -> any:
if len(node.args) != 2:
raise OQSInvalidArgumentQuantityError(
function_name=node.name, expected_min=2, expected_max=2, actual=len(node.args)
)
error_name, error_message = [interpreter.evaluate(arg) for arg in node.args]
if not isinstance(error_name, str):
raise OQSTypeError(
message=f"error_name argument must be a String. Instead got '{get_oqs_type(expected_type)}'."
)
elif not isinstance(error_message, str):
raise OQSTypeError(
message=f"error_message argument must be a String. Instead got '{get_oqs_type(expected_type)}'."
)
if error_name in ERROR_NAME_MAPPING:
raise ERROR_NAME_MAPPING[error_name](message=error_message)
else:
class CustomError(OQSCustomErrorParent):
READABLE_NAME: str = error_name

def __init__(self):
super().__init__(message=error_message)
raise CustomError()
2 changes: 2 additions & 0 deletions python_oqs_implementation/oqs/constants/types.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class ValueTypeStrings:
INTEGER: str = "Integer"
DECIMAL: str = "Decimal"
NUMBER: str = "Number"
STRING: str = "String"
LIST: str = "List"
KVS: str = "KVS"
Expand All @@ -20,3 +21,4 @@ class ErrorTypeStrings:
DIVISION_BY_ZERO: str = "Division By Zero Error"
UNEXPECTED_CHARACTER: str = "Unexpected Character Error"
MISSING_EXPECTED_CHARACTER: str = "Missing Expected Character Error"
CUSTOM: str = "Custom Error"
9 changes: 9 additions & 0 deletions python_oqs_implementation/oqs/constants/values.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@
bool: VTS.BOOLEAN,
type(None): VTS.NULL
}
OPS_TYPE_HIERARCHY_MAPPING: dict[type, list[str]] = {
int: [VTS.INTEGER, VTS.NUMBER],
float: [VTS.DECIMAL, VTS.NUMBER],
str: [VTS.STRING],
list: [VTS.LIST],
dict: [VTS.KVS],
bool: [VTS.BOOLEAN],
type(None): [VTS.NULL]
}


MAX_ARGS: int = 999_999_999_999
Loading

0 comments on commit 6574028

Please sign in to comment.