455 lines
85 KiB
Plaintext
455 lines
85 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "9f97dd1e",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Libraries"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 1,
|
||
"id": "9e974df6",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"from typing import TypedDict, List, Annotated\n",
|
||
"from IPython.display import Image, display\n",
|
||
"\n",
|
||
"from langchain_core.documents import Document\n",
|
||
"from langchain_core.messages import SystemMessage\n",
|
||
"from langgraph.graph.message import add_messages\n",
|
||
"from langchain_elasticsearch import ElasticsearchStore\n",
|
||
"from langgraph.graph import StateGraph, END\n",
|
||
"\n",
|
||
"from src.llm_factory import create_chat_model\n",
|
||
"from src.emb_factory import create_embedding_model\n",
|
||
"from src.config import (\n",
|
||
" ELASTICSEARCH_LOCAL_URL,\n",
|
||
" ELASTICSEARCH_INDEX,\n",
|
||
" OLLAMA_LOCAL_URL,\n",
|
||
" OLLAMA_MODEL_NAME,\n",
|
||
" OLLAMA_EMB_MODEL_NAME\n",
|
||
")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "d8eb6506",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Create instances"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 2,
|
||
"id": "30edcecc",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"llm = create_chat_model(\n",
|
||
" provider=\"ollama\",\n",
|
||
" model=OLLAMA_MODEL_NAME,\n",
|
||
" base_url=OLLAMA_LOCAL_URL,\n",
|
||
" temperature=0,\n",
|
||
" validate_model_on_init=True,\n",
|
||
")\n",
|
||
"embeddings = create_embedding_model(\n",
|
||
" provider=\"ollama\",\n",
|
||
" model=OLLAMA_EMB_MODEL_NAME,\n",
|
||
" base_url=OLLAMA_LOCAL_URL,\n",
|
||
")\n",
|
||
"vector_store = ElasticsearchStore(\n",
|
||
" es_url=ELASTICSEARCH_LOCAL_URL,\n",
|
||
" index_name=ELASTICSEARCH_INDEX,\n",
|
||
" embedding=embeddings,\n",
|
||
" query_field=\"text\",\n",
|
||
" vector_query_field=\"vector\"\n",
|
||
")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 3,
|
||
"id": "50b71d45",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"LLM: qwen2.5:1.5b\n",
|
||
"Embeddings: qwen3-0.6B-emb:latest\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"print(\"LLM: \", llm.model)\n",
|
||
"print(\"Embeddings: \", embeddings.model)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "873ea2f6",
|
||
"metadata": {},
|
||
"source": [
|
||
"### State"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 4,
|
||
"id": "5f8c88cf",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"class AgentState(TypedDict):\n",
|
||
" messages: Annotated[list, add_messages]\n",
|
||
" reformulated_query: str\n",
|
||
" context: str"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "395966e2",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Prompts"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 5,
|
||
"id": "66ae23f0",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"REFORMULATE_PROMPT = SystemMessage(\n",
|
||
" content=(\n",
|
||
" \"You are a deterministic lexical query rewriter used for vector retrieval.\\n\"\n",
|
||
" \"Your task is to rewrite user questions into optimized keyword search queries.\\n\\n\"\n",
|
||
"\n",
|
||
" \"CRITICAL RULES (ABSOLUTE):\\n\"\n",
|
||
" \"1. NEVER answer the question.\\n\"\n",
|
||
" \"2. NEVER expand acronyms.\\n\"\n",
|
||
" \"3. NEVER introduce new terms not present in the original query.\\n\"\n",
|
||
" \"4. NEVER infer missing information.\\n\"\n",
|
||
" \"5. NEVER add explanations, definitions, or interpretations.\\n\"\n",
|
||
" \"6. Preserve all technical tokens exactly as written.\\n\"\n",
|
||
" \"7. Only remove filler words (e.g., what, does, is, explain, tell me, please).\\n\"\n",
|
||
" \"8. You may reorder terms for better retrieval.\\n\"\n",
|
||
" \"9. Output must be a single-line plain keyword query.\\n\"\n",
|
||
" \"10. If the query is already optimal, return it unchanged.\\n\\n\"\n",
|
||
" \"11. If you receive something that looks like code, do NOT attempt to rewrite it. Return it verbatim.\\n\\n\"\n",
|
||
"\n",
|
||
" \"ALLOWED OPERATIONS:\\n\"\n",
|
||
" \"- Remove interrogative phrasing.\\n\"\n",
|
||
" \"- Remove stopwords.\\n\"\n",
|
||
" \"- Reorder words.\\n\"\n",
|
||
" \"- Convert to noun phrase form.\\n\\n\"\n",
|
||
"\n",
|
||
" \"FORBIDDEN OPERATIONS:\\n\"\n",
|
||
" \"- Expanding abbreviations.\\n\"\n",
|
||
" \"- Paraphrasing into unseen vocabulary.\\n\"\n",
|
||
" \"- Adding definitions.\\n\"\n",
|
||
" \"- Answering implicitly.\\n\\n\"\n",
|
||
"\n",
|
||
" \"Examples:\\n\"\n",
|
||
" \"Input: What does AVAP stand for?\\n\"\n",
|
||
" \"Output: AVAP stand for\\n\"\n",
|
||
"\n",
|
||
" \"Input: Hey, I'm trying to understand how AVAP handels a ZeroDivisionError when doing divison or modulus operatoins. Can you explane what situatoins cause a ZeroDivisionError to be raised and how I can catch it in my AVAP scripts?\\n\"\n",
|
||
" \"Output: AVAP ZeroDivisionError division / modulus % catch try except\\n\"\n",
|
||
" \n",
|
||
" \"Input: What does AVAP stand for?\\n\"\n",
|
||
" \"Output: AVAP stand for\\n\"\n",
|
||
"\n",
|
||
" \"Input: Please explain how the import statement works in AVAP scripts.\\n\"\n",
|
||
" \"Output: AVAP import statement syntax behavior\\n\\n\"\n",
|
||
"\n",
|
||
" \"Return only the rewritten query.\"\n",
|
||
" )\n",
|
||
")\n",
|
||
"\n",
|
||
"GENERATE_PROMPT = SystemMessage(\n",
|
||
" content=(\n",
|
||
" \"\"\"You are a retrieval-grounded assistant.\n",
|
||
"\n",
|
||
" CRITICAL SCOPE RULES\n",
|
||
" - AVAP (Advanced Virtual API Programming) is a new/unknown programming language.\n",
|
||
" - If the user message is AVAP-related (explicitly mentions \"AVAP\" case-insensitive OR the provided context is AVAP code/docs), you MUST answer using ONLY <CONTEXT>.\n",
|
||
" - You MUST NOT use general programming knowledge to interpret AVAP.\n",
|
||
" - You MUST NOT assume AVAP behaves like any other language even if syntax looks similar.\n",
|
||
" - You MUST NOT infer missing details. Only state what is explicitly present in <CONTEXT>.\n",
|
||
"\n",
|
||
" GROUNDING REQUIREMENTS (AVAP-RELATED)\n",
|
||
" 1) Every non-trivial factual claim MUST be directly supported by an EXACT QUOTE from <CONTEXT>.\n",
|
||
" 2) If a claim is not supported by a quote, DO NOT include it.\n",
|
||
" 3) If <CONTEXT> does not contain enough information to answer, reply with EXACTLY:\n",
|
||
" \"I don't have enough information in the provided context to answer that.\"\n",
|
||
"\n",
|
||
" WORKFLOW (AVAP-RELATED) — FOLLOW IN ORDER\n",
|
||
" A) Identify the specific question(s) being asked.\n",
|
||
" B) Extract the minimum necessary quotes from <CONTEXT> that answer those question(s).\n",
|
||
" C) Write the answer using ONLY those quotes (paraphrase is allowed, but every statement must be backed by at least one quote).\n",
|
||
" D) Verify: for EACH sentence in your answer, confirm there is a supporting quote. If any sentence lacks a quote, delete it or refuse.\n",
|
||
"\n",
|
||
" OUTPUT FORMAT (AVAP-RELATED ONLY)\n",
|
||
" Answer:\n",
|
||
" <short, direct answer; no extra speculation; no unrelated tips>\n",
|
||
"\n",
|
||
" Evidence:\n",
|
||
" - \"<exact quote 1>\"\n",
|
||
" - \"<exact quote 2>\"\n",
|
||
" (Include only quotes you actually used. Prefer the smallest quotes that fully support the statements.)\n",
|
||
"\n",
|
||
" NON-AVAP QUESTIONS\n",
|
||
" - If the question is clearly not AVAP-related, answer normally using general knowledge.\n",
|
||
"\n",
|
||
" <CONTEXT>\n",
|
||
" {context}\n",
|
||
" </CONTEXT>\"\"\"\n",
|
||
" )\n",
|
||
")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "15780588",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Build graph"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 6,
|
||
"id": "100246d7",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"retrieve_kwargs = {\n",
|
||
" \"k\": 3\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 7,
|
||
"id": "36d0f54e",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"def format_context(docs: List[Document]) -> str:\n",
|
||
" chunks: List[str] = []\n",
|
||
" for i, doc in enumerate(docs, 1):\n",
|
||
" source = (doc.metadata or {}).get(\"source\", \"Untitled\")\n",
|
||
" source_id = (doc.metadata or {}).get(\"id\", f\"chunk-{i}\")\n",
|
||
" text = doc.page_content or \"\"\n",
|
||
" chunks.append(f\"[{i}] id={source_id} source={source}\\n{text}\")\n",
|
||
" return \"\\n\\n\".join(chunks)\n",
|
||
"\n",
|
||
"def reformulate(state: AgentState) -> AgentState:\n",
|
||
" \"\"\"Use the LLM to rewrite the user query for better retrieval.\"\"\"\n",
|
||
" user_msg = state[\"messages\"][-1]\n",
|
||
" resp = llm.invoke([REFORMULATE_PROMPT, user_msg])\n",
|
||
" reformulated = resp.content.strip()\n",
|
||
" print(f\"[reformulate] '{user_msg.content}' → '{reformulated}'\")\n",
|
||
" return {\"reformulated_query\": reformulated}\n",
|
||
"\n",
|
||
"\n",
|
||
"def retrieve(state: AgentState) -> AgentState:\n",
|
||
" \"\"\"Retrieve context using the reformulated query.\"\"\"\n",
|
||
" query = state[\"reformulated_query\"]\n",
|
||
" docs = vector_store.as_retriever(\n",
|
||
" search_type=\"similarity\",\n",
|
||
" search_kwargs=retrieve_kwargs,\n",
|
||
" ).invoke(query)\n",
|
||
" context = format_context(docs)\n",
|
||
" print(f\"[retrieve] {len(docs)} docs fetched\")\n",
|
||
" print(context)\n",
|
||
" return {\"context\": context}\n",
|
||
"\n",
|
||
"\n",
|
||
"def generate(state: AgentState) -> AgentState:\n",
|
||
" \"\"\"Generate the final answer using retrieved context.\"\"\"\n",
|
||
" prompt = SystemMessage(\n",
|
||
" content=GENERATE_PROMPT.content.format(context=state[\"context\"])\n",
|
||
" )\n",
|
||
" resp = llm.invoke([prompt] + state[\"messages\"])\n",
|
||
" return {\"messages\": [resp]}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 8,
|
||
"id": "fae46a58",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"graph_builder = StateGraph(AgentState)\n",
|
||
"\n",
|
||
"graph_builder.add_node(\"reformulate\", reformulate)\n",
|
||
"graph_builder.add_node(\"retrieve\", retrieve)\n",
|
||
"graph_builder.add_node(\"generate\", generate)\n",
|
||
"\n",
|
||
"graph_builder.set_entry_point(\"reformulate\")\n",
|
||
"graph_builder.add_edge(\"reformulate\", \"retrieve\")\n",
|
||
"graph_builder.add_edge(\"retrieve\", \"generate\")\n",
|
||
"graph_builder.add_edge(\"generate\", END)\n",
|
||
"\n",
|
||
"graph = graph_builder.compile()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 9,
|
||
"id": "7f57b543",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"image/png": "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",
|
||
"text/plain": [
|
||
"<IPython.core.display.Image object>"
|
||
]
|
||
},
|
||
"metadata": {},
|
||
"output_type": "display_data"
|
||
}
|
||
],
|
||
"source": [
|
||
"try:\n",
|
||
" display(Image(graph.get_graph().draw_mermaid_png()))\n",
|
||
"except Exception:\n",
|
||
" pass"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "1e9aff05",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Test"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 10,
|
||
"id": "8569cf39",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"def stream_graph_updates(user_input: str, graph: StateGraph):\n",
|
||
" for event in graph.stream(\n",
|
||
" {\"messages\": [{\"role\": \"user\", \"content\": user_input}]},\n",
|
||
" stream_mode=\"values\",\n",
|
||
" ):\n",
|
||
" event[\"messages\"][-1].pretty_print()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 13,
|
||
"id": "a1a1f3cf",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"user_input = \"\"\"How can you make loops in AVAP? Generate a code snippet\"\"\""
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 14,
|
||
"id": "53b89690",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||
"\n",
|
||
"How can you make loops in AVAP? Generate a code snippet\n",
|
||
"[reformulate] 'How can you make loops in AVAP? Generate a code snippet' → 'AVAP loop generate code snippet'\n",
|
||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||
"\n",
|
||
"How can you make loops in AVAP? Generate a code snippet\n",
|
||
"[retrieve] 3 docs fetched\n",
|
||
"[1] id=chunk-1 source=12_Loop_statement.txt\n",
|
||
"StartLoop() Statement The loop statement in AVAP™ allows you to execute a block of code repeatedly until a specific condition is met. Below is a detailed explanation of its syntax and functionality. 7.1 Syntax of the Loop Statement The full syntax of the loop statement in AVAP™ is as follows: startLoop(control, start, end) // Code block to repeat endLoop() This syntax consists of three main parts: control: This is the loop control variable used to track the progress of the loop. It is initialized with the starting value of the loop and is incremented with each iteration until it reaches the end value. start: This is the starting value of the loop. The loop begins at this value. end: This is the ending value of the loop. The loop terminates when the control variable reaches this value. 7.2 Functioning of the Loop Statement The loop statement in AVAP™ follows this execution process: The control variable control is initialized with the starting value specified in start. The loop condition is evaluated: while the value of control is less than or equal to the end value end, the code block within startLoop() is executed. If the value of control exceeds the end value, the loop terminates, and execution continues after endLoop(). In each iteration of the loop, the code block within startLoop() is executed, and the control variable control is automatically incremented by one. Once the control variable reaches or exceeds the end value end, the loop terminates, and execution continues after endLoop(). 7.3 Example of Use Below is an example of using the loop statement in AVAP™, along with a detailed explanation of each part of the code: // Loop Sample Use // Initialize the variable 'variable' with the value 5. addVar(variable,5) // Start the loop with the control variable 'control', ranging from 1 to 5. startLoop(control,1,5) // In each iteration of the loop, assign the current value of 'control' to the variable 'counter'. addVar(counter,$control) endLoop() // Add the final value of 'counter' to the API result. addResult(counter) 7.4 Result and Conclusions After executing the above code, the result returned by the API is as follows: { status , elapsed:0.01605510711669922, result: { counter:5 } } This result confirms that the execution was successful (status:true) and that the final value of counter is 5. In summary, the loop statement in AVAP™ provides an efficient way to execute a block of code repeatedly within a specified range. By automating tasks that require repetition, such as processing a list of items or generating sequential numbers, this statement becomes a fundamental tool for programming in AVAP™.\n",
|
||
"\n",
|
||
"[2] id=chunk-2 source=10_Execution_model_in_avap.txt\n",
|
||
"Execution Model in AVAP 4.1. Structure of a Program A program in AVAP is built from code blocks that execute linearly. A block is a section of the AVAP program text that executes as a unit. Code blocks in AVAP include: A script file. The body of a function. An import statement for additional files. Each line of code in AVAP is considered a block and executes sequentially. There is no interactive execution, deferred execution, or object classes. 4.2. Names and Bindings 4.2.1. Name Binding Names in AVAP refer to values and are introduced through name binding operations. The following constructs bind names: Formal parameters of functions. Function definitions. Assignment expressions. Name binding is performed using the addVar(value, variable) function, which assigns the value to the specified variable. There are no class declarations or complex targets in AVAP. Only functions and direct assignments to variables are valid code blocks. 4.2.2. Name Resolution A scope defines the visibility of a name in a code block. In AVAP, if a variable is defined in a code block, its scope includes that block. The scope of a variable within a function extends to the entire function block. When a name is used in a code block, it is resolved using the nearest enclosing scope. If the name is not found in the current scope, a NameError exception is raised. If a name binding operation occurs anywhere within a code block, all uses of the name within that block are treated as references to the current block. This means that variables must be defined before their use within the same block. In AVAP, there are no global or nonlocal declarations. All names are resolved within the scope in which they are defined. There is no dynamic code execution with eval or exec, so all bindings must be static and known at code writing time. 4.3. Importing Files In AVAP, it is possible to import the contents of other code files. The import file.avap statement inserts the contents of the specified file at the exact point where the import statement appears. This process is linear and sequential, meaning that the imported content is executed as if it were part of the original file. It is crucial that the necessary functions are defined before they are called. If a function is not defined before its call, a NameError exception will be raised. Example of import usage: avap // Content of the file main.avap addVar(10, x) import functions.avap myFunction(x) // Content of the file functions.avap def myFunction(y): addVar(y + 5, result) print(result) 4.4. Exceptions Exceptions in AVAP allow for the handling of errors or exceptional conditions. An exception is raised when an error is detected; it can be handled by the surrounding code block or by any code block that directly or indirectly invoked the block where the error occurred. The AVAP interpreter raises an exception when it detects a runtime error. An AVAP program can also explicitly raise an exception using the raise statement. Exception handlers are specified with the try ... except statement. Example of exception handling: try: addVar(10 / 0, result) except ZeroDivisionError: print(\"Cannot divide by zero.\") In this example, if a division by zero occurs, a ZeroDivisionError exception is raised and handled by the except block. This structure ensures that AVAP programs execute in a sequential and predictable manner, without advanced dynamic or deferred execution features, maintaining simplicity and clarity in name binding and import handling. 5. The Import System in AVAP AVAP code in one file gains access to code in another file through the import process. The import statement is the only way to invoke the import machinery in AVAP. The import statement inserts the contents of the specified file at the exact point where the import statement appears in the original file. There are no other ways to invoke the import system in AVAP. When an import statement is executed, the contents of the imported file are processed as if they were part of the original file, ensuring that all functions and variables from the imported file are available in the context of the original file. If the specified file is not found, a FileNotFoundError is raised. Example of using the import statement in AVAP: Content of file main.avap addVar(10, x) import functions.avap myFunction(x) Content of file functions.avap def myFunction(y): addVar(y + 5, result) print(result) In this example, the content of functions.avap is inserted into main.avap at the point of the import statement, ensuring that myFunction is defined before being called. 5.1. Import Rules Position of Import: The import statement must be placed at the exact location where the content of the imported file is to be included. The content of the imported file is executed linearly along with the original file. Import Error: If the file specified in the import statement is not found, a FileNotFoundError is raised. Scope of Imports: The functions and variables from the imported file are added to the local scope of the original file at the point of import. This means they can be accessed as if they were defined in the same file. 5.2. Limitations and Considerations No Packages: Unlike other languages, AVAP does not have a hierarchical package system. Each file is imported independently and treated as an autonomous unit. Sequential Execution: Execution in AVAP is sequential and does not allow lazy or deferred execution. Therefore, all functions and variables must be defined before use, and the content of imported files must be in the correct order. No Conditional Import: The import statement in AVAP does not support conditions. The specified file will always be imported at the point of the statement, regardless of any conditions. 5.3. Advanced Example Consider the following example where multiple files are imported: Content of the file main.avap addVar(5, a) import utilities.avap import operations.avap addVar(utilities.increment(a), b) addVar(operations.multiply(b, 2), c) print(c) Content of the file utilities.avap def increment(x): return x + 1 Content of the file operations.avap def multiply(x, y): return x * y In this example, utilities.avap and operations.avap are imported into main.avap at the specified points, allowing the increment and multiply functions to be used in main.avap. 6. Expressions in AVAP This chapter explains the meaning of expression elements in AVAP. 6.1. Arithmetic Conversions When describing an arithmetic operator in AVAP and using the phrase \"numeric arguments are converted to a common type,\" it means that the operator's implementation for built-in types works as follows: If either of the arguments is a complex number, the other is converted to complex. Otherwise, if either of the arguments is a floating-point number, the other is converted to floating-point. Otherwise, both must be integers, and no conversion is needed. Additional rules may apply for certain operators. 6.2. Atoms Atoms are the most basic elements of expressions in AVAP. The simplest atoms are identifiers or literals. Forms enclosed in parentheses, brackets, or braces are also syntactically categorized as atoms. The syntax for atoms is: atom ::= identifier | literal | enclosure enclosure ::= parenth_form | list_display | dict_display | set_display | generator_expression 6.2.1. Identifiers (Names) An identifier that appears as an atom is a name. When the name is bound to an object, evaluating the atom yields that object. When a name is not bound, an attempt to evaluate it raises a NameError exception. Private Name Mangling When an identifier that occurs literally in a class definition begins with two or more underscores and does not end with two or more underscores, it is considered a private name of that class. Private names are transformed into a longer form before code is generated for them. The transformation inserts the class name, with the initial underscores removed and a single underscore inserted, in front of the name. 6.2.2. Literals AVAP supports string and bytes literals, as well as various numeric literals: literal ::= stringliteral | bytesliteral | integer | floatnumber | imagnumber Evaluating a literal produces an object of the given type (string, bytes, integer, floating-point number, complex number) with the given value. All literals correspond to immutable data types. 6.2.3. Parenthesized Forms A parenthesized form is an optional list of expressions enclosed in parentheses: parenth_form ::= \"(\" [starred_expression] \")\" A parenthesized expression produces whatever the expression list produces: if the list contains at least one comma, it produces a tuple; otherwise, it produces the single expression that makes up the list of expressions. 6.2.4. Comprehensions for Lists, Sets and Dictionaries To construct a list, set, or dictionary, AVAP provides special syntax called \"comprehension,\" each in two flavors: The contents of the container are listed explicitly. They are computed using a set of loop and filtering instructions, called a \"comprehension.\" Common syntax elements for comprehensions are: comprehension ::= assignment_expression comp_for comp_for ::= \"for\" target_list \"in\" or_test [comp_iter] comp_iter ::= comp_for | comp_if comp_if ::= \"if\" or_test [comp_iter] A comprehension consists of a single expression followed by at least one for clause and zero or more for or if clauses. In this case, the elements of the new container are those produced by considering each for or if clause as a block, nested from left to right, and evaluating the expression to produce an element each time the innermost block is reached. 6.2.5. List Displays In AVAP, lists are generated and handled differently. To construct a list, the command variableToList(variable, list) is used, and an item from the list is retrieved with itemFromList(list, index, variable_to_store_item). To get the number of elements in the list, getListLen(list, var_to_store_list_length) is used. The syntax for list displays is: list_display ::= \"[\" [starred_list | comprehension] \"]\" A list display produces a new list object, whose content is specified by a list of expressions or a comprehension. When a list of expressions is provided, its elements are evaluated from left to right and placed in the list object in that order. 6.2.6. Set Displays A set display is denoted by curly braces and is distinguished from dictionary displays by the absence of colon characters separating keys and values: set_display ::= \"{\" (starred_list | comprehension) \"}\" A set display produces a new mutable set object, whose content is specified by a sequence of expressions or a comprehension. 6.2.7. Dictionary Displays In AVAP, objects are created and managed using specific commands. An object is created with AddvariableToJSON(key, value, object_variable), and a key from the object is retrieved with variableFromJSON(object_variable, key, var_to_store_key_value). The syntax for dictionary displays is: dict_display ::= \"{\" [dict_item_list | dict_comprehension] \"}\" dict_item_list ::= dict_item (\",\" dict_item)* [\",\"] dict_item ::= expression \":\" expression | \"**\" or_expr dict_comprehension ::= expression \":\" expression comp_for A dictionary display produces a new dictionary object. If a comma-separated sequence of dictionary items is provided, they are evaluated from left to right to define the dictionary entries. Slices A slice selects a range of elements in a sequence object (e.g., a string, tuple, or list). Slices can be used as expressions or as targets in assignments or statements. The syntax for a slice is as follows: slicing ::= primary \"[\" slice_list \"]\" slice_list ::= slice_item (\",\" slice_item)* [\",\"] slice_item ::= expression | proper_slice proper_slice ::= [lower_bound] \":\" [upper_bound] [ \":\" [stride] ] lower_bound ::= expression upper_bound ::= expression stride ::= expression There is ambiguity in the formal syntax here: anything that looks like a list expression also looks like a list slice, so any subscription might be interpreted as a slice. Instead of complicating the syntax further, this is disambiguated by defining that in this case, the interpretation as a subscription takes precedence over the interpretation as a slice (this is the case if the list slice does not contain a proper slice). The semantics for a slice are as follows. The primary is indexed (using the same __getitem__() method as in a normal subscription) with a key constructed from the slice list, as follows. If the slice list contains at least one comma, the key is a tuple that contains the conversion of the slice elements; otherwise, the conversion of the single slice element is the key. The conversion of a slice element that is an expression is that expression. The conversion of a proper slice is a slice object whose start, stop, and step attributes are the values of the expressions given as the lower bound, upper bound, and step, respectively, substituting None for missing expressions. Calls A call invokes a callable object (e.g., a function) with a possibly empty series of arguments: call ::= primary \"(\" [argument_list [\",\"] | comprehension] \")\" argument_list ::= positional_arguments [\",\" starred_and_keywords] [\",\" keywords_arguments] | starred_and_keywords [\",\" keywords_arguments] | keywords_arguments positional_arguments ::= positional_item (\",\" positional_item)* positional_item ::= assignment_expression | \"*\" expression starred_and_keywords ::= (\"*\" expression | keyword_item) (\",\" \"*\" expression | \",\" keyword_item)* keywords_arguments ::= (keyword_item | \"**\" expression) (\",\" keyword_item | \",\" \"**\" expression)* keyword_item ::= identifier \"=\" expression An optional trailing comma may be present after positional and keyword arguments but does not affect the semantics. The primary must evaluate to a callable object (user-defined functions, built-in functions, built-in object methods, class objects, class instance methods, and any object with a __call__() method are callable). All argument expressions are evaluated before attempting the call. Please refer to the Function Definitions section for the syntax of formal parameter lists. If keyword arguments are present, they are first converted into positional arguments as follows. First, a list of unfilled slots is created for the formal parameters. If there are N positional arguments, they are placed in the first N slots. Then, for each keyword argument, the identifier is used to determine the corresponding slot. If the slot is already filled, a TypeError exception is raised. Otherwise, the argument is placed in the slot, filling it (even if the expression is None, it fills the slot). When all arguments have been processed, any slots that are still empty are filled with the default value from the function definition. If there are unfilled slots for which no default value is specified, a TypeError exception is raised. Otherwise, the list of filled slots is used as the argument list for the call. Implementation Details in AVAP In AVAP, variables are stored as strings, and lists and objects are managed using specific commands: Lists: To generate a list, use variableToList(variable, list). To retrieve an item from the list, use itemFromList(list, index, variable_to_store_item). To get the number of items in the list, use getListLen(list, var_to_store_list_length). Objects (dictionaries): An object is created with AddvariableToJSON(key, value, object_variable). To retrieve a key from the object, use variableFromJSON(object_variable, key, var_to_store_key_value). Usage Example Creation and management of lists: // Creating a list variableToList(\"item1\", \"myList\") variableToList(\"item2\", \"myList\") variableToList(\"item3\", \"myList\") // Retrieving an item from the list itemFromList(\"myList\", 1, \"myVariable\") // Getting the length of the list getListLen(\"myList\", \"listLength\") Creation and management of objects (dictionaries): // Creating an object AddvariableToJSON(\"key1\", \"value1\", \"myObject\") AddvariableToJSON(\"key2\", \"value2\", \"myObject\") // Retrieving a value by key from the object variableFromJSON(\"myObject\", \"key1\", \"myVariable\") In this way, lists and objects in AVAP can be manipulated using the specific functions provided for working with variables stored as strings. Binary Arithmetic Operations Binary arithmetic operations have the conventional levels of precedence. Some of these operations also apply to certain non-numeric types. Aside from the exponentiation operator, there are two levels: one for multiplicative operators and another for additive ones: m_expr ::= u_expr | m_expr \"*\" u_expr | m_expr \"@\" m_expr | m_expr \"//\" u_expr | m_expr \"/\" u_expr | m_expr \"%\" u_expr a_expr ::= m_expr | a_expr \"+\" m_expr | a_expr \"-\" m_expr The * (multiplication) operator produces the product of its arguments. The arguments can both be numbers, or one argument must be an integer and the other a sequence. In the first case, the numbers are converted to a common type and then multiplied. In the second case, sequence repetition occurs; a negative repetition factor produces an empty sequence. The @ (matrix multiplication) operator is intended for matrix multiplication. No built-in type in Python implements this operator. The / (division) and // (floor division) operators produce the quotient of their arguments. Numeric arguments are converted to a common type. Division between integers produces a floating-point number, while floor division between integers results in an integer; the result is that of a mathematical division with the “floor” function applied to the result. Division by zero raises a ZeroDivisionError. The % (modulus) operator produces the remainder of the division of the first argument by the second. Numeric arguments are converted to a common type. A zero argument on the right raises a ZeroDivisionError. Arguments can be floating-point numbers, e.g., 3.14 % 0.7 is equal to 0.34 (since 3.14 is equal to 4 * 0.7 + 0.34). The modulus operator always produces a result with the same sign as its second operand (or zero); the absolute value of the result is strictly smaller than the absolute value of the second operand. The floor division and modulus operators are connected by the following identity: x == (x // y) * y + (x % y). Floor division and modulus are also connected by the built-in function divmod(): divmod(x, y) == (x // y, x % y). In addition to performing the modulus operation on numbers, the % operator is also overloaded by string objects for old-style string formatting (also known as interpolation). The syntax for string formatting is described in the Python Library Reference, section Old-Style String Formatting. The floor division operator, the modulus operator, and the divmod() function are not defined for complex numbers. Instead, convert to a floating-point number using the abs() function if appropriate. The + (addition) operator produces the sum of its arguments. The arguments must both be numbers or both be sequences of the same type. In the first case, the numbers are converted to a common type and then added. In the second case, the sequences are concatenated. The - (subtraction) operator produces the difference between its arguments. Numeric arguments are converted to a common type. Shift Operations Shift operations have lower precedence than arithmetic operations: shift_expr ::= a_expr | shift_expr (\"<<\" | \">>\") a_expr These operators accept integers as arguments. They shift the first argument left or right by the number of bits specified by the second argument. A right shift by n bits is defined as an integer floor division by pow(2, n). A left shift by n bits is defined as a multiplication by pow(2, n). Binary Bitwise Operations Each of the three binary bitwise operations has a different level of precedence: and_expr ::= shift_expr | and_expr \"&\" shift_expr xor_expr ::= and_expr | xor_expr \"^\" and_expr or_expr ::= xor_expr | or_expr \"|\" xor_expr * The & operator produces the bitwise AND of its arguments, which must be integers. * The ^ operator produces the bitwise XOR (exclusive OR) of its arguments, which must be integers. * The | operator produces the bitwise OR (inclusive OR) of its arguments, which must be integers. Comparisons Unlike C, all comparison operations in Python have the same priority, which is lower than any arithmetic, shift, or bitwise operation. Also, unlike C, expressions like a < b < c have the conventional mathematical interpretation: comparison ::= or_expr (comp_operator or_expr)* comp_operator ::= \"<\" | \">\" | \"==\" | \">=\" | \"<=\" | \"!=\" | \"is\" [\"not\"] | [\"not\"] \"in\" Comparisons produce boolean values: True or False. Comparisons can be arbitrarily chained, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once. Formally, if a, b, c, ..., y, z are expressions and op1, op2, ..., opN are comparison operators, then a op1 b op2 c ... y opN z is equivalent to a op1 b and b op2 c and ... y opN z, except that each expression is evaluated at most once. Note that a op1 b op2 c does not imply any comparison between a and c, so, for example, x < y > z is perfectly legal. Value Comparisons The operators <, >, ==, >=, <=, and != compare the values of two objects. The objects do not need to be of the same type. The chapter Objects, Values, and Types states that objects have a value (in addition to type and identity). The value of an object is a rather abstract notion in Python: For example, there is no canonical method to access the value of an object. Furthermore, there is no requirement that the value of an object must be constructed in a particular way, e.g., composed of all its data attributes. Comparison operators implement a particular notion of what an object's value is. The default behavior for equality comparison (== and !=) is based on object identity. Therefore, comparison of instances with the same identity results in equality, and comparison of equality of instances with different identities results in inequality. No default comparison order (<, >, <=, >=) is provided; an attempt generates a TypeError. The following list describes the comparison behavior of the most important built-in types: Numbers: Built-in numeric types (int, float, complex) and types from the standard library (fractions.Fraction and decimal.Decimal) can be compared with themselves and among their types, with the restriction that complex numbers do not support order comparisons. Within the limits of the involved types, they are compared mathematically correctly without loss of precision. None and NotImplemented: They are singletons. PEP 8 advises that comparisons for singletons should be done with is or is not, never with equality operators. Binary Sequences: Instances of bytes or bytearray compare lexicographically using the numeric values of their elements. Character Strings: Instances of str compare lexicographically using Unicode code points (the result of the built-in ord() function) or their characters. Sequences: Instances of tuple, list, or range can only be compared within their types, with the restriction that ranges do not support order comparisons. Equality comparisons between these types result in inequality, and order comparisons between these types generate TypeError. They compare lexicographically using comparison of their corresponding elements. Mappings: Instances of dict compare equal if and only if they have the same (key, value) pairs. Sets: Instances of set or frozenset can be compared with each other and among their types. They define order comparison operators with the intention of checking subsets and supersets. Other Built-in Types: Most other built-in types do not have comparison methods implemented, so they inherit the default comparison behavior. User-defined classes that customize their comparison behavior should follow some consistency rules, if possible: Equality comparison should be reflexive. Comparison should be symmetric. Comparison should be transitive. If any of these conditions are not met, the resulting behavior is undefined. Simple Statements In AVAP, a simple statement consists of a single logical line. Multiple simple statements can be placed on a single line, separated by semicolons. The syntax for simple statements is: simple_stmt ::= expression_stmt | assert_stmt | assignment_stmt | augmented_assignment_stmt | annotated_assignment_stmt | pass_stmt | del_stmt | return_stmt | yield_stmt | raise_stmt | break_stmt | continue_stmt | import_stmt | future_stmt | global_stmt | nonlocal_stmt | type_stmt Here’s a brief overview of each type of simple statement: Expression Statement (expression_stmt): Executes an expression, which can be used for operations or calling functions. Assert Statement (assert_stmt): Used for debugging purposes to test conditions. Assignment Statement (assignment_stmt): Assigns values to variables or data structures. Augmented Assignment Statement (augmented_assignment_stmt): Performs an operation on a variable and assigns the result back to the variable (e.g., x += 1). Annotated Assignment Statement (annotated_assignment_stmt): Used for assigning values with annotations (e.g., type hints). Pass Statement (pass_stmt): A placeholder that does nothing; used for syntactic requirements. Del Statement (del_stmt): Deletes variables, items, or attributes. Return Statement (return_stmt): Exits a function and optionally returns a value. Yield Statement (yield_stmt): Produces a value from a generator function. Raise Statement (raise_stmt): Raises exceptions for error handling. Break Statement (break_stmt): Exits the closest enclosing loop. Continue Statement (continue_stmt): Skips the current iteration of the closest enclosing loop. Import Statement (import_stmt): Imports modules or specific components from modules. Future Statement (future_stmt): Enables features from future versions of Python. Global Statement (global_stmt): Declares variables as global within a function. Nonlocal Statement (nonlocal_stmt): Declares variables as non-local, affecting scope in nested functions. Type Statement (type_stmt): Declares or checks types (e.g., type hints). Each simple statement performs a specific task and contributes to the overall functionality of the AVAP program. Expression Statements Expression statements are used (mostly interactively) to compute and write a value, or (usually) to call a method (a function that does not return a meaningful result; in Python, methods return the value None). Other uses of expression statements are allowed and occasionally useful. The syntax for an expression statement is: expression_stmt ::= starred_expression An expression statement evaluates the list of expressions (which can be a single expression). In interactive mode, if the value is not None, it is converted to a string using the built-in function repr(), and the resulting string is written to the standard output on a line by itself (except if the result is None, in which case the called procedure produces no output). Assignment Statements Assignment statements in AVAP are used to (re)assign names to values and to modify attributes or elements of mutable objects. Here is the syntax: assignment_stmt ::= (target_list \"=\")+ (starred_expression | yield_expression) target_list ::= target (\",\" target)* [\",\"] target ::= identifier | \"(\" [target_list] \")\" | \"[\" [target_list] \"]\" | attributeref | subscription | slicing | \"*\" target Here's a breakdown of how assignment statements work: Assignment Operation: An assignment statement evaluates the list of expressions and assigns the single resulting object to each of the target lists, from left to right. Recursive Definition: The assignment operation is defined recursively depending on the form of the target list. Target List: If the target list is a single object without ending in a comma, the object is assigned to that target. If the list contains a target prefixed with an asterisk, the object must be iterable with at least as many elements as targets, minus one. Elements before the starred target are assigned to the respective targets, and the remaining elements are assigned to the starred target. Single Target: If the target is an identifier (name), it is bound to the object in the current local namespace. For other targets, names are bound in the global or enclosing namespace, depending on `nonlocal`. Attribute Reference: If the target is an attribute reference, the primary expression is evaluated. It must produce an object with assignable attributes. Subscription: If the target is a subscription, the primary expression is evaluated to produce a mutable sequence or mapping object, which is then used to assign the value. Slice: If the target is a slice, the primary expression is evaluated, and the sequence object is requested to replace the slice with the assigned sequence elements. In summary, assignment statements in AVAP are crucial for assigning values to variables and modifying data structures effectively. Return Statement The return statement in AVAP is used to return the value of a desired variable from a function. Here is the syntax: return(variable_to_return): Here is an overview of how the return statement works: Function Context: The return statement can only occur within a function definition, not inside a nested class definition. Variable Evaluation: If a variable is provided, it is evaluated. If no variable is specified, None is used by default. Function Exit: The return statement exits the current function call and returns the specified value. Interaction with try-finally: When the return statement is executed within a try statement that has a finally clause, the finally clause is executed before the function exits. Generator Functions: In generator functions, the return statement indicates the end of the generator. It causes a StopIteration exception to be raised, with the returned value (if any) used to construct the StopIteration exception and set as the StopIteration.value attribute. The return statement is a fundamental part of functions and generators, allowing for the output of values and proper function termination. Raise Statement In AVAP, the raise statement is used to throw an exception. The syntax for the raise statement is as follows: raise [expression [\"from\" expression]] If no expressions are present, raise re-raises the currently handled exception, also known as the active exception. If there is no active exception, a RuntimeError is raised indicating that it is an error. Otherwise, raise evaluates the first expression as the exception object. It must be a subclass or an instance of BaseException. If it is a class, the exception instance is obtained when needed by creating an instance of the class without arguments. The type of the exception is the instance of the exception class, and the value is the instance itself. The from clause is used for exception chaining: if provided, the second expression must be another class or instance of exception. If the second expression is an exception instance, it will be attached to the raised exception as the __cause__ attribute (which is modifiable). If the expression is an exception class, the class will be instantiated and the resulting exception instance will be attached to the raised exception as the __cause__ attribute. If the raised exception is not handled, both exceptions will be printed. startLoop() try: print(1 / 0) except Exception as exc: raise RuntimeError(\"Something went wrong\") from exc endLoop() A mechanism works implicitly if a new exception is raised while an exception is already being handled. An exception may be handled by an except or finally clause, or a with statement. The previous exception is then attached as the new exception’s __context__ attribute: startLoop() try: print(1 / 0) except: raise RuntimeError(\"Something went wrong\") from None endLoop() Exception chaining can be explicitly suppressed by specifying None in the from clause: startLoop() try: print(1 / 0) except: raise RuntimeError(\"Something went wrong\") from None endLoop() Break Statement In AVAP, the break statement is used to terminate the closest enclosing loop. The syntax for the break statement is as follows: break When a break statement is encountered, it causes the loop to exit immediately, regardless of the loop's condition or any remaining iterations. This effectively transfers control to the statement following the loop. The break statement is typically used within for or while loops to provide a way to exit the loop prematurely based on a certain condition. for i in range(10): if i == 5: break print(i) print(\"Loop ended\") In this example, the loop will terminate when i equals 5, and \"Loop ended\" will be printed. The numbers 0 through 4 will be printed before the loop is exited. Break Statement The break statement in AVAP is used to terminate the closest enclosing loop. Here is an overview of its behavior: Usage Context: The break statement can only occur within a for or while loop. It cannot be nested within a function or class definition inside that loop. Loop Termination: It terminates the closest enclosing loop and skips the optional else clause if the loop has one. Loop Control Target: If a for loop is terminated by break, the loop control target retains its current value. Interaction with try-finally: When break is executed within a try statement with a finally clause, the finally clause is executed before actually exiting the loop. The break statement is essential for controlling loop execution, allowing for early exit from loops and proper handling of loop cleanup. Continue Statement In AVAP, the continue statement is used to proceed with the next iteration of the closest enclosing loop. The syntax for the continue statement is as follows: continue The continue statement can only syntactically occur nested within a for or while loop, but not within a function or class definition inside that loop. When continue is used within a loop that is also handling exceptions with a try statement containing a finally clause, the finally clause is executed before the next iteration of the loop begins. for i in range(10): try: if i % 2 == 0: continue print(i) finally: print(\"In finally clause\") print(\"Loop ended\") In this example, the continue statement will skip the current iteration when i is even, but before moving to the next iteration, the finally clause will print \"In finally clause.\" For odd numbers, the loop will print the number and then \"In finally clause.\" After the loop finishes, \"Loop ended\" will be printed. Import Statement In AVAP, the import statement is used to import an entire code file and define names in the local namespace. The syntax for the import statement is as follows: import file.avap The import statement in AVAP imports an entire code file and makes it available in the local namespace. No alias is assigned to the imported file; the file is simply referred to by its name. For example: # In the 'module.avap' file example_variable = 10 # In the main file import module.avap print(module.avap.example_variable) # Will print 10 In this example, the main file imports the module.avap file and can access the example_variable defined in that file using the module.avap syntax. Compound Statements In AVAP, compound statements contain (groups of) other statements; these affect or control the execution of those other statements in some way. In general, compound statements span multiple lines, though in simpler representations a complete compound statement might be contained within a single line. if statements implement traditional flow control constructs. match specifies matching patterns for variable values. Function and class definitions are also syntactically compound statements. A compound statement consists of one or more \"clauses.\" A clause consists of a header and a \"suite.\" The clause headers of a particular compound statement are all at the same level of indentation. Each clause header begins with a uniquely identifying keyword and ends with a colon. A suite is a group of statements controlled by a clause. A suite can be one or more simple statements separated by semicolons on the same line as the header, following the colon of the header, or it can be one or more statements indented on subsequent lines. Only the latter form of a suite can contain nested compound statements. Control Flow Structures in AVAP In AVAP, control flow structures include conditional statements and loops, which allow you to control the flow of execution based on conditions and iterate over a range of values. If Statements The syntax for an if statement in AVAP is: if (variable, variableValue, comparator, expression): code to execute This structure checks if the condition (variable compared to variableValue with the given comparator) is true, and if so, executes the block of code. Loops The syntax for a loop in AVAP is: startLoop(variable, from, to) code to execute endLoop() This structure initiates a loop where the variable iterates from the 'from' value to the 'to' value, executing the code block for each iteration. The if Statement The if statement in AVAP is used for conditional execution. The syntax is as follows: if (variable, variableValue, comparator, expression): code to execute This statement evaluates the condition specified by the variable, variableValue, comparator, and expression. It selects exactly one of the suites (blocks of code) by evaluating the expressions one by one until a true condition is found. The corresponding suite is then executed. If all conditions are false, no suites are executed. The try Statement The try statement in AVAP specifies exception handlers and/or cleanup code for a block of statements. The syntax is as follows: try(): code to execute except(): code to execute The try block contains code that might raise an exception. The except block contains code to handle exceptions raised by the try block. If an exception occurs, control is transferred to the except block. If no exception occurs, the except block is skipped. Additional information about exceptions can be found in the section Exceptions, and information about using the raise statement to throw exceptions can be found in the section The raise Statement. Patterns in AVAP In AVAP, patterns provide a powerful way to match and destructure values. Patterns can be used in match statements to perform complex value comparisons and deconstructions. Here is a description of the available patterns and how they are used: Literal Patterns: Match specific literal values such as numbers, strings, or booleans. For example: match value: case 10: # Code to execute if value is 10 case \"hello\": # Code to execute if value is \"hello\" Variable Patterns: Capture the value of a variable. This allows you to use the matched value in the corresponding case block: match value: case x: # Code to execute, x will be assigned the value Sequence Patterns: Match sequences like lists or tuples. You can also use the * operator to capture remaining elements: match value: case [1, 2, *rest]: # Code to execute, rest will capture any additional elements Mapping Patterns: Match dictionaries or similar mappings by specifying keys and their corresponding patterns: match value: case \"key\": 42: # Code to execute if the dictionary has \"key\" with value 42 Class Patterns: Match instances of classes. You can also match specific attributes within the instance: match value: case MyClass(attr1=42): # Code to execute if value is an instance of MyClass with attr1 equal to 42 Patterns in AVAP offer a flexible approach for handling different kinds of data structures and values, making it easier to write expressive and maintainable code. OR Patterns An OR pattern in AVAP allows you to specify multiple patterns separated by vertical bars (|). The OR pattern attempts to match each of its subpatterns with the subject value in order. If any of the subpatterns match, the OR pattern is considered successful. If none of the subpatterns match, the OR pattern fails. or_pattern ::= \"|\".closed_pattern+ Here's how you can use OR patterns in practice: match value: case 1 | 2 | 3: # Code to execute if value is 1, 2, or 3 case \"hello\" | \"world\": # Code to execute if value is \"hello\" or \"world\" case _: # Code to execute if value does not match any of the above In this example: The first case will match if value is either 1, 2, or 3. The second case will match if value is either \"hello\" or \"world\". The last case is a catch-all pattern that will execute if none of the previous patterns match. OR patterns provide a concise way to handle multiple possible values or types, simplifying pattern matching and making your code more readable. AS Patterns An AS pattern in AVAP is used to bind an OR pattern to a name. This allows you to match a value with an OR pattern and simultaneously capture it under a specified name for further use. The syntax for an AS pattern is: as_pattern ::= or_pattern \"as\" capture_pattern When an AS pattern is used, if the OR pattern succeeds, the subject is bound to the name specified by the capture pattern, and the AS pattern itself succeeds. Here's an example of how to use AS patterns: match value: case 1 | 2 | 3 as x: print(f\"Matched a number: x\") case \"hello\" | \"world\" as greeting: print(f\"Matched a greeting: greeting\") case _: print(\"No match\") In this example: The first case matches if value is 1, 2, or 3. The matched value is bound to the name x, which is then used in the print statement. The second case matches if value is \"hello\" or \"world\". The matched value is bound to the name greeting, which is then used in the print statement. The last case is a catch-all pattern that executes if none of the previous patterns match. AS patterns are useful for capturing matched values under a name while using OR patterns, allowing for more flexible and readable pattern matching in your code. Literal Patterns In AVAP, literal patterns are used to match specific literal values, such as numbers, strings, or boolean values. The syntax for a literal pattern is: literal_pattern ::= signed_number | strings | \"None\" | \"True\" | \"False\" A literal pattern only succeeds if the value of the subject is equal to the specified literal value. Here are examples of literal patterns and their usage: match value: case 42: print(\"Matched the number 42\") case \"hello\": print(\"Matched the string 'hello'\") case None: print(\"Matched None\") case True: print(\"Matched True\") case False: print(\"Matched False\") case _: print(\"No match\") In this example: case 42: matches if value is exactly 42. case \"hello\": matches if value is the string \"hello\". case None: matches if value is None. case True: matches if value is True. case False: matches if value is False. case _: is a catch-all pattern that executes if none of the previous patterns match. Literal patterns are useful for matching specific, known values and are a fundamental part of pattern matching in AVAP. Capture Patterns In AVAP, capture patterns are used to bind the subject's value to a name. The syntax for a capture pattern is: capture_pattern ::= NAME Capture patterns always succeed and bind the value of the subject to the specified name. Here’s how you might use capture patterns in AVAP: match value: case x: print(f\"Captured value: x\") In this example: case x: captures whatever value is in value and binds it to the name x. The pattern always succeeds. Capture patterns are useful when you want to extract and use the value of the subject within your code, regardless of what that value is. Wildcard Patterns In AVAP, wildcard patterns are used to match any value without binding it to a name. The syntax for a wildcard pattern is: wildcard_pattern ::= '_' Wildcard patterns always succeed and do not create any bindings. They are useful when you want to ignore the value of the subject and only care about whether it matches a certain pattern. Here’s how you might use wildcard patterns in AVAP: match value: case _: print(\"Matched any value\") In this example: case _: matches any value and does not bind it to a name. The pattern always succeeds, and the code within this case will be executed regardless of the value. Wildcard patterns are particularly useful when you need to handle a broad range of possibilities and are only interested in whether a value fits a general condition, not in the value itself. Value Patterns In AVAP, value patterns are used to match specific values. The syntax for a value pattern is: value_pattern ::= attr Value patterns only succeed if the subject's value matches the specified value. They are useful when you want to perform actions based on an exact value. Here’s how you might use value patterns in AVAP: match value: case 42: print(\"Matched the value 42\") case \"hello\": print(\"Matched the string 'hello'\") case _: print(\"Matched something else\") In this example: case 42: matches the value 42 specifically. case \"hello\": matches the string \"hello\" specifically. case _: matches any other value not covered by the previous cases. Value patterns are ideal for scenarios where you need to check for specific values and respond accordingly. They provide precise control over the matching process. Group Patterns In AVAP, group patterns are used to group multiple patterns together. The syntax for a group pattern is: group_pattern ::= \"(\" pattern \")\" Group patterns are useful when you want to combine patterns or when patterns need to be evaluated together. They have the same effect as the pattern they contain but allow for more complex pattern structures. Here’s an example of how to use group patterns in AVAP: match value: case (42 | 43): print(\"Matched either 42 or 43\") case (name, age) if age > 18: print(f\" is an adult\") case _: print(\"Matched something else\") In this example: case (42 | 43): uses a group pattern to match either the value 42 or 43. case (name, age) if age > 18: uses a group pattern to match a tuple and includes an additional condition on the age. case _: matches any other value not covered by the previous cases. Group patterns are ideal for creating more complex matching scenarios where patterns need to be combined or grouped together. Sequence Patterns In AVAP, sequence patterns are used to match elements within sequences like lists or tuples. The syntax for sequence patterns is: sequence_pattern ::= \"[\" [maybe_sequence_pattern] \"]\" | \"(\" [open_sequence_pattern] \")\" Sequence patterns can match elements of sequences based on specific rules. Here’s how they work: List Patterns: Use square brackets [ ] to match lists. You can include patterns for the elements within the list. case [a, b, c]: print(\"Matched a list with three elements\") Tuple Patterns: Use parentheses ( ) to match tuples. Similarly, you can specify patterns for the tuple elements. case (x, y): print(\"Matched a tuple with two elements\") Sequence patterns allow for flexible and powerful matching of sequence types. They can match sequences of various lengths and structures by defining the pattern for each element. Here’s an example of using sequence patterns in a match statement: match value: case [1, 2, 3]: print(\"Matched a list with elements 1, 2, 3\") case (a, b, c) if a + b == c: print(\"Matched a tuple where a + b equals c\") case _: print(\"Matched something else\") In this example: case [1, 2, 3]: matches a list with exactly the elements 1, 2, and 3. case (a, b, c) if a + b == c: matches a tuple and includes a condition to check if a + b equals c. case _: matches any other value not covered by the previous cases. Mapping Patterns In AVAP, mapping patterns are used to match mapping elements, such as dictionaries. Here is the syntax and behavior of mapping patterns: mapping_pattern ::= { [items_pattern] } Mapping Patterns are designed to match elements within mappings, such as dictionaries. They use specific rules to determine if a pattern matches the given mapping. Syntax: Mapping patterns are enclosed in curly braces { ... }. The items_pattern specifies the pattern for the mapping items. Matching Rules: The rules for matching mapping patterns include checking for key-value pairs in the mapping and ensuring they align with the specified pattern. Usage: Mapping patterns are useful for destructuring dictionaries and other mapping types in a concise manner. Mapping patterns enhance pattern matching capabilities by allowing for specific and flexible matching of dictionary elements. Class Patterns In AVAP, class patterns are used to match instances of specific classes. Here is a detailed overview: class_pattern ::= name \"(\" [pattern_arguments \",\"?] \")\" Pattern Syntax: A class pattern specifies the class name followed by a parenthesized list of pattern_arguments. The pattern matches instances of the specified class. Matching Instances: The pattern will match if the subject is an instance of the specified class and the pattern_arguments (if any) match according to the rules defined for the pattern. Usage: Class patterns are useful for deconstructing objects based on their class and extracting values from them, enabling more precise pattern matching. These patterns provide a way to work with objects based on their class type and structure, facilitating more sophisticated pattern matching and value extraction.\n",
|
||
"\n",
|
||
"[3] id=chunk-3 source=19_Control_logic_decision_structures.txt\n",
|
||
"SECTION III: Control Logic and Decision Structures This section details how AVAP manages execution flow. The language uses explicitly closed block structures that enable clear sequential reading, simplifying the debugging of complex APIs. 3.1 The Conditional Block (if / else / end) The if structure in AVAP is a versatile tool that allows atomic comparisons or the evaluation of complex logical expressions processed by the dynamic evaluation engine. Standard Interface if(variable_A, value_B, operator) Available Operators Operator Description Example = Strict equality (or numeric equivalence). if(role, \"admin\", \"=\") != Inequality. if(status, 200, \"!=\") > / < Numeric magnitude comparison. if(age, 18, \">\") in Checks whether an element belongs to a list or string. if(user, blacklist, \"in\") Complex Expression Evaluation AVAP allows omission of comparison parameters to evaluate a complete logical expression directly in the third parameter. Example: if(None, None, \"age >= 18 and balance > 100\") Block Closure Structure An if block may include an optional else() block and must always terminate with the end() command. 3.2 Iterations and Loops (startLoop / endLoop) For collection processing (such as database rows or parameter lists), AVAP implements an index-controlled loop structure. Interface startLoop(counter, start, end) Execution Mechanics Initialization: The engine creates the counter variable with the start value. Increment: On each iteration, the counter automatically increases by 1. Exit Condition: The loop terminates when the counter exceeds the end value. Practical Example: List Processing // Retrieve the length of a list captured in Section II getListLen(received_items, total) startLoop(i, 0, total) current_item = received_items[i] // Processing logic for each item... endLoop() 3.3 Error Handling and Robustness (try / exception) AVAP is designed for production environments where external failures (database timeouts, third-party API outages) are expected realities. The try block allows capturing such events without stopping the server. Interface try ... exception(error_variable) ... end() Technical Operation try Block: The engine attempts to execute the contained instructions. If a critical failure occurs, execution of that block stops immediately. exception Block: If an error is detected, control passes to this block. The error_variable is automatically populated with a string describing the failure (simplified stack trace). end() Block: Closes the structure and allows the script to continue normal execution after error handling. Connector Safety Example try // Attempt a query to an external connector (Section V) result = db.query(\"SELECT * FROM payments\") exception(failure_detail) // If it fails, log the error and notify addVar(_status, 500) addVar(message, \"Persistence error: %s\" % failure_detail) addResult(message) end() 3.4 Early Exit Control (return) The return() command is a control instruction that immediately terminates execution of the current context (either a function or the main script). If used inside a function, it returns control (and optionally a value) to the caller. If used in the main flow, it terminates API execution and triggers automatic delivery of the JSON response constructed up to that point. Examples 1. Simple Comparison Code snippet addParam(\"lang\", l) if(l, \"es\", \"=\") addVar(msg, \"Hello\") end() addResult(msg) 2. Standard Else Code snippet if(balance, 0, \">\") allow = True else() allow = False end() addResult(allow) 3. Complex Expression (Dynamic Evaluation) Code snippet if(None, None, \"user_type == 'VIP' or purchases > 100\") addVar(discount, 0.20) end() addResult(discount) 4. Loop from 1 to 10 (ID Generation) Code snippet startLoop(i, 1, 10) AddvariableToJSON(\"item_${i}\", \"generated_value\", my_json) endLoop() addResult(my_json) 5. HTTP Request Try-Catch Code snippet try RequestGet(\"https://api.test.com/data\", 0, 0, response) exception(e) addVar(error_trace, \"Connection failure: %s\" % e) addResult(error_trace) end() 6. Proper Loop Exit (Using Control Variable) Code snippet found = False startLoop(i, 1, 10) if(i, 5, \"==\") found = True // In AVAP, to exit you can force the index beyond the limit i = 11 end() endLoop() addResult(found) 7. 'in' Validation (Membership Check) Code snippet addParam(\"role\", r) if(r, [\"admin\", \"editor\", \"root\"], \"in\") access = True end() addResult(access) 8. Loop Over Data Length Code snippet getListLen(records, total) startLoop(idx, 0, total) current = records[idx] // processing logic endLoop() 9. Inequality If Code snippet if(new_password, old_password, \"!=\") addVar(change, \"Password updated\") end() addResult(change) 10. Critical SQL Error Handling Code snippet try ormDirect(\"UPDATE nonexistent_table SET a=1\", res) exception(e) addVar(_status, 500) addResult(\"Database error\") end()\n",
|
||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||
"\n",
|
||
"How can you make loops in AVAP? Generate a code snippet\n",
|
||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||
"\n",
|
||
"In AVAP, loops are typically implemented using the `startLoop` and `endLoop` interface. Here's an example of how to use these interfaces:\n",
|
||
"\n",
|
||
"```python\n",
|
||
"# Example: Loop over a list of items\n",
|
||
"\n",
|
||
"items = [\"apple\", \"banana\", \"cherry\"]\n",
|
||
"\n",
|
||
"startLoop(i, 0, len(items) - 1)\n",
|
||
" current_item = items[i]\n",
|
||
" print(f\"Processing item {current_item}\")\n",
|
||
"endLoop()\n",
|
||
"\n",
|
||
"print(\"All items processed.\")\n",
|
||
"```\n",
|
||
"\n",
|
||
"In this example:\n",
|
||
"- The `startLoop` function is used to start the loop.\n",
|
||
"- The loop variable `i` is initialized with a starting value of 0 and incremented by 1 on each iteration until it reaches the length of the list minus one (`len(items) - 1`).\n",
|
||
"- Inside the loop, you can perform any processing logic for each item in the list.\n",
|
||
"- The `endLoop` function marks the end of the loop.\n",
|
||
"\n",
|
||
"This is a simple example to illustrate how loops work in AVAP. You can extend this concept by adding more complex conditions or additional functionality as needed.\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"stream_graph_updates(user_input, graph)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "3dda5f99",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": []
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": "assistance-engine",
|
||
"language": "python",
|
||
"name": "python3"
|
||
},
|
||
"language_info": {
|
||
"codemirror_mode": {
|
||
"name": "ipython",
|
||
"version": 3
|
||
},
|
||
"file_extension": ".py",
|
||
"mimetype": "text/x-python",
|
||
"name": "python",
|
||
"nbconvert_exporter": "python",
|
||
"pygments_lexer": "ipython3",
|
||
"version": "3.11.13"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 5
|
||
}
|