معرفی شرکت ها


clismo-0.1.4


Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر
Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر
Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر
Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر
Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر

توضیحات

Programing language designed for simulating and optimizing client-server like discret events models
ویژگی مقدار
سیستم عامل -
نام فایل clismo-0.1.4
نام clismo
نسخه کتابخانه 0.1.4
نگهدارنده []
ایمیل نگهدارنده []
نویسنده Jorge Morgado Vega
ایمیل نویسنده jorge.morgadov@gmail.com
آدرس صفحه اصلی -
آدرس اینترنتی https://pypi.org/project/clismo/
مجوز MIT
# Clismo ## Índice - [Objetivos](https://github.com/jmorgadov/clismo#objetivos) - [Instalación](https://github.com/jmorgadov/clismo#instalaci%C3%B3n) - [Lenguaje](https://github.com/jmorgadov/clismo#lenguaje) - [Características básicas](https://github.com/jmorgadov/clismo#caracter%C3%ADsticas-b%C3%A1sicas) - [Declaración y uso de variables](https://github.com/jmorgadov/clismo#declaraci%C3%B3n-y-uso-de-variables) - [Condicionales](https://github.com/jmorgadov/clismo#condicionales) - [Ciclos](https://github.com/jmorgadov/clismo#cliclos) - [Estructuras principales](https://github.com/jmorgadov/clismo#estructuras-principales) - [Atributos y funciones especiales](https://github.com/jmorgadov/clismo#atributos-y-funciones-especiales) - [Client](https://github.com/jmorgadov/clismo#client) - [Server](https://github.com/jmorgadov/clismo#server) - [Step](https://github.com/jmorgadov/clismo#step) - [Simulation](https://github.com/jmorgadov/clismo#simulation) - [General](https://github.com/jmorgadov/clismo#general) - [Funciones built-in](https://github.com/jmorgadov/clismo#funciones-built-in) - [Implementación](https://github.com/jmorgadov/clismo#implementaci%C3%B3n) - [Autómatas](https://github.com/jmorgadov/clismo#aut%C3%B3matas) - [Motor de expresiones regulares](https://github.com/jmorgadov/clismo#motor-de-expresiones-regulares) - [Tokenizador](https://github.com/jmorgadov/clismo#tokenizador) - [Gramáticas](https://github.com/jmorgadov/clismo#gram%C3%A1ticas) - [Árbol de Sintaxis Abstracta (AST)](https://github.com/jmorgadov/clismo#%C3%A1rbol-de-sintaxis-abstracta-ast) - [Parser](https://github.com/jmorgadov/clismo#parser) - [Visitors](https://github.com/jmorgadov/clismo#visitors) - [Ejecución](https://github.com/jmorgadov/clismo#ejecuci%C3%B3n) - [Reconocimiento de las estructuras declaradas](https://github.com/jmorgadov/clismo#reconocimiento-de-las-estructuras-declaradas) - [Chequeo semántico](https://github.com/jmorgadov/clismo#chequeo-sem%C3%A1ntico) - [Evaluación](https://github.com/jmorgadov/clismo#evaluaci%C3%B3n) - [Ejemplo](https://github.com/jmorgadov/clismo#ejemplo) ## Objetivos El objetivo de este proyecto es crear un lenguaje de programación (específicamente un DCL) que permita la creación, ejecución y optmización de modelos de simulación de eventos discretos de tipo cliente-servidor. De esta forma, los usuarios pueden enfocarse más en la estructura de los modelos y no en la implementación interna de los mismos. El lenguaje ofrece además la posibilidad de optimizar los modelos creados bajo criterios que el mismo usuario puede especificar. ## Instalación ```bash pip install clismo ``` Para más información sobre el uso del programa: ```bash clismo --help ``` ## Lenguaje **Clismo** es un lenguaje no orientado a objetos, y a pesar de ser un lenguaje de programación imperativo, el mismo tiene características funcionales. El tipado es estático, aunque no es necesario declarar los tipos en la sintaxis (son inferidos automáticamente). Al ser un lenguaje dedicado a la creación de modelos de simulación del tipo cliente-servidor, posee cuatro estructuras básicas: `client`, `server`, `step` y `simulation`. Las relaciones entre estas estructuras son: - Los servidores atienden a los clientes. - Un `step` contiene una serie de servidores (indica que están en paralelo). - Un `simulation` contiene una serie de steps (indica que están en secuencia). Cada una de estas estructuras puede contener atributos y/o funciones especiales que definen el comportamiento del sistema que serán analizados en futuras secciones. ### Características básicas A continuación se presentan ejemplos de código que muestran las características básicas del lenguaje. #### Declaración y uso de variables ```text var name = "John" var is_elder = False var count = 10 count = count + 3 ``` #### Condicionales ```text if count == 10: ... else: ... ``` > Con indentaciones similar a Python. #### Cliclos - Ciclo infinito ```text loop: ... ``` > Dentro de los loops se pueden usar las palabras claves `endloop` o `nextloop` > para terminar el loop o saltar a la siguiente iteración respectivamente. - Ciclo infinito con variable ```text loop i: ... ``` - Ciclo infinito con variable y comienzo ```text loop i from 5: ... ``` - Ciclo con comienzo y fin ```text loop i from 5 to 10: ... ``` - Ciclo con comienzo, fin y paso ```text loop i from 5 to 10 by 2: ... ``` ### Estructuras principales Las estructuras principales (`client`, `server`, `step` y `simulation`) se crean de la siguiente forma: ```text <struct_type> <name>: <attr_name> = <attr_value> ... <func_name>(<specifications>): <func_body> ... ``` > Los <specifications> no representan parámetros que se pasan a la función, > sino que son, como su nombre lo indica, especificaciones. Por ejemplo, para crear un servidor: ```text server server1: my_own_attr = 10 attend_client(): return norm() + 10 ``` > `attend_client` es una función especial que deben definir los servidores para > indicar el tiempo que tardará en atender a un cliente. ### Atributos y funciones especiales Algunas estructuras requieren de atributos y funciones especiales para ser poder definidas. (Como en el ejemplo anterior `attend_client`, no se puede crear un servidor si no se define esta función en el cuerpo del mismo). #### Client Los clientes no requieren de atributos especiales, pero si pueden definir opcionalmente una función `on_server_out` que será llamada cuando el cliente sale del servidor. Esta función no debe devolver ningun valor, se usa generalemente para indicar modificar attributos del cliente. > Más adelante se explicará como se pueden obtener y modificar los atributos > de una estructura. #### Server Los servidores requieren la implementación de la función `attend_client`. Esta función debe devolver el tiempo que tardará en atender a un cliente. Dentro de ella se puede acceder al cliente actual mediante la variable `current_client`. #### Step Los steps no requieren ninguna función especial pero si la declaración del atributo `servers`, una lista de servidores que se encargarán de atender a los clientes. ```text step step1: servers = [server1, server2] ``` #### Simulation Las simulaciones requieren la declaración de los siguientes atributos: - `steps`: una lista de steps que representan los servidores en serie. - `time_limit` o `client_limit`: una cantidad de tiempo o cantidad de clientes que se desea simular. Solo es necesario uno de los dos (aunque se pueden indicar ambas) - `arrive(<client_type>)`: una función que devuelve el tiempo que tardará en llegar un cliente de un tipo determinado. Ejemplo: ```text arrive(DefaultClient): return norm() + 10 ``` > `DefaultClient` es un tipo de cliente que se usa por defecto. Cada simulación > puede definir más funciones `arrive` con clientes de distintos tipos. También > se puede definir una misma función `arrive` para varios tipos de clientes, > por ejemplo `arrive(ClientType1, ClientType2)`. Opcionalemente, se puede definit el modo de ejecución de la simulación con el atributo `mode`. El mismo puede ser `"run"` (por defecto) o `"optimize"`. Estos indican si se quiere ejecutar la simulación u optimizar el modelo. En caso de que se quiera optimizar el modelo, se debe definir la función `minimize()` que devuelve el valor que se desea minimizar. Para la configuración del algoritmo genético utilizado para la optimización, se pueden especificar los atributos: - `pop_size`: cantidad de individuos en la población. Por defecto es 10. - `max_iter`: cantidad máxima de iteraciones. Por defecto es 5. - `mut_prob`: probabilidad de mutación. Por defecto es 0.1. - `best_sel`: cantidad de individuos que se seleccionan para la siguiente generación. Por defecto es 3. - `new_rand`: cantidad de nuevos individuos que se generan al final de cada iteración. Por defecto es 2. ### General Cada estructura puede definir una función `possible(<attr_name>)`. Esta función debe devolver un posible valor (aleatorio) para el atributo especificado. Estas funciones se utlizarán para crear el vector de posibles valores para los diferentes atributos. Con ello se pueden crear diferentes versiones de un mismo modelo y es lo que el algoritmo genético utilizará para optimizar el mismo. Adicionalemente, en cualquier función se puede acceder a las variables globales `time` y `clients` que representan el tiempo actual se la simulación y la cantidad de clientes que han salido del sistema. ### Funciones built-in Dentro de la implementación de las funciones especiales se pueden usar diversas funciones built-in. Dos de las funciones principales son: `get` y `set`. Con estas funciones se pueden obtener y modificar los atributos de una estructura. Por ejemplo: ```text server S1: mean = 10 total = 0 attend_client(): set(self, "total", get(self, total) + 1) return nomr() + get(self, mean) ``` > `self` es una referencia a la instancia de la estructura actual Como el lenguaje no es orintado a objetos, existen diversas funciones que permiten realizar todo tipo de acciones: - `get_at(<list>, <index>)`: devuelve el valor del elemento en la posición especificada de la lista. - `set_at(<list>, <index>, <value>)`: modifica el valor del elemento en la posición especificada de la lista. - `append(<list>, <value>)`: agrega un elemento al final de la lista. - `rand()`: devuelve un valor aleatorio entre 0 y 1. - `norm()`: devuelve un valor aleatorio entre 0 y 1 con distribución normal. - `randint(<min>, <max>)`: devuelve un valor aleatorio entre `min` y `max`. - `startswith(<string>, <substring>)`: devuelve `True` si `string` empieza con `substring`. - `lower(<string>)`: devuelve `string` en minúsculas. - `upper(<string>)`: devuelve `string` en mayúsculas. - `sqrt(<number>)`: devuelve la raíz cuadrada de `number`. - `abs(<number>)`: devuelve el valor absoluto de `number`. - `round(<number>, <decimals>)`: devuelve `number` redondeado a `decimals` decimales. Entre muchas otras similares a las funciones de python. ## Implementación **Clismo** es un lenguaje evaluado escrito en Python. A continuación se exponen las características principales de la implementación de cada etapa. ### Autómatas Para la creación de las algunas de las proximas funcionalidades, se realizaó una implementación de un tipo `Automata` que permite simular una máquina de estados de forma genérica. A los mismos se le pueden agregar estados así como transiciones entre los mismos. Cada autómata tiene un estado inicial y uno o varios estados finales. La ejecución de una máquina de estados realizada con un autómata es bastante simple. Dado una entrada iterable, se comienza en el estado inicial y se va ejecutando cada transición hasta llegar a un estado final. En caso de llegar a un estado en el que ninguna transición es válida, se termina la ejecución y la entrada no es válida. En caso de terminar de recorrer la entrada se clasifica la entrada como válida o inválida en dependencia de si se llegó a un estado final o no respectivamente. Los autómatas pueden tener transiciones **épsilon** entre estados, en este caso, la ejecución se bifurca y la maquina de estados se mueve por todos los estaos posibles al mismo timepo. Esto da la posibliadad de ejecutar autómatas no deterministas. Se implementó además, utilizando el algoritmo visto en clase (calculando los **goto** y **epsilon clausuras**) la opción de convertir un autómata no determinista (NFA) a un autómata determinista (DFA). ### Motor de expresiones regulares Las principales funcionalidades implementadas son: - Operador `*`: Matchea cero o más veces la expresión anterior. - Operador `|`: Mathcea la expresión anterior o la siguiente. - Operador `^`: Matchea cualquier expresion excepto la expresión que le prosigue. - Caracter `.`: Matchea cualquier caracter (ASCII). - Caracter `\`: Inicio de un caracter especial. - Caracter `\d`: Matchea un dígito. - Caracter `\a`: Matchea una letra minúscula. - Caracter `\A`: Matchea una letra mayúscula. - Parentesis `(` y `)`: Agrupan una expresión regular. > Cualquier operador o caracter especal puede ser escapado con `\`. Para la realización del motor de expresiones regulares se utilizó la clase `Automata`. Para cada expresión regular se construye un autómata finito no determinista (NFA) usando el algoritmo de Thompson y luego el mismo se convierte a un DFA utlizando el método `to_dfa` de la clase `Automata`. Se ofrecen además dos funciones para el matcheo de cadenas segun una expresión regular: `match` (la cual tiene un comportamiento similar a `re.match`) y `compile_patt` (la cual tiene un comportamiento similar a `re.compile`). La ventaja principal de usar `compile_patt` es que se no es necesario crear un autómata para cada vez que se desea matchear una cadena (ya que el autómata es construido una sola vez). ### Tokenizador Para la implementación del tokenizador se creó una clase `Tokenizer`. Esta clase se encarga de tomar un texto y dividirlo en diferentes tipos de tokens. Cada patrón que se agrega está definido por un nombre (tipo del token) y una expresión regular (se hace uso del motor de expresiones regulares implementado). ```python tknz = Tokenizer() tknz.add_pattern("NUMBER", r"\d\d*|\d\d*\.\d\d*") ``` Al tokenizar un texto, se revisan los patrones comenzando por el primero (en el mismo orden en el que fueron agregados) y el primero que matchee con un prefijo de la cadena se establece como un token nuevo (se toma como lexema la subcadena que matcheó con la expresión regular). Luego se vuelve a realizar esta operación con el resto de la cadena, así sucesivamente hasta terminar la misma. Si en algún punto no se encuentra un token que matchee con el inicio de la cadena, se considera que la cadena no se puede tokenizar (con los tipos de tokens establecidos). Cada vez que se agrega un patrón al tokenizador se puede establecer una función que se aplicará al lexema antes de guardar su valor en el token. Por ejemplo, para quitar las comillas al tokenizar un **string**: ```python tknz.add_pattern("STRING", r"'((^')|(\\'))*(^\\)'", lambda t: t[1:-1]) ``` Esta función tambien puede ser utilizada para indicar que se quiere ignorar los tokens de un tipo determinado. En tal caso basta con que la función devuelva `None`: ```python tknz.add_pattern("SPACE", r"( | \t)( |\t)*", lambda t: None) ``` Se ofrece también la opción de agregar `keywords` (palabras claves) para una mayor comodidad. Esto se hace mediante el método `add_keywords()` el cual recibe una lista de palabras. En el proceso de tokenización, si el prefijo matcheado conicide con alguna de las palabras clave, entonces el tipo del token se establece como `KEYWORD`. En caso de que se quiera aplicar una función para procesar todos los tokens obtenidos, se puede usar el decorador `process_tokens` de la clase `Tokenizer`. Este debe ser usado en una función que reciba un solo argumento (la lista de tokens) y devuelva una lista de tokens procesados. ```python @tknz.process_tokens def process_tokens(tokens): # ... return tokens ``` Finalmente, para obtener los tokens de un texto basta con usar la función `tokenize`: ```python tokens = tknz.tokenize("some text") ``` ### Gramáticas Se implementaron las clases `Grammar`, `NonTerminal`, `Terminal` y `Production` las cuales son usadas para la representación de una gramática general. Se implementó además un parser de gramáticas con el cual es posible crear gramáticas dado un formato, esto permite definir la gramática del lenguaje en un archivo y poder cambiarla fácilmente. Dado la sencillez del formato (el lenguaje de las gramáticas), se implementó un sencillo parser recursivo descendente para la creación de las mismas. El formato especificado es el siguiente: ``` expression: production_1 | production_2 | ... | production_n ``` De forma equivalente, para mayor legibilidad: ``` expression: | production_1 | production_2 | ... | production_n ``` Ejemplo: ``` ExprAB: | 'a' ExprAB 'b' | EPS ``` > EPS es un elemento especial en las gramáticas para representar *epsilon* Las gramáticas luego pueden ser cargadas como se muestra a continuación: ```python from grammar im port Grammar gm = Grammar.open("expr_ab.gm") ``` Las gramáticas están compuestas por una lista de expresiones (no terminales). Cada no terminal de la gramática, contiene una lista de producciones. Cada producción contiene una lista de elementos (terminales o no terminales). ### Árbol de Sintaxis Abstracta (AST) Para la creación de un AST se creó la clase abstracta `AST`. De esta clase heredan todos las clases que representan los nodos del árbol de sintaxis abstracta del lenguaje. En la clase se implementa también un método `dump` que permite mostrar el árbol de forma legible. Este método usa el atributo `__slots__` mediante el cual se definen los atributos que se quieren mostrar. Para definir cómo se construye cada nodo del AST se pueden asignar los constructores a cada producción de la gramática usando la función `assign_builders`. Esta función recibe un diccionario donde las llaves son la representación textual de la producción y los valores son funciones que reciben como argumentos los elementos de la producción. En caso de que el símbolo sea un terminal la función recibirá dicho terminal, en caso de ser un no terminal, la función recibirá el resultado de la ejecución algunas de las funciones constructoras de las producciones que tengan como cabeza a dicho no terminal. Por ejemplo, a continuación se muestran algunos de los constructores para la gramática de **Clismo**: ```python builders = { # ------------------------------------------------------------------------- "program -> obj_def program": lambda s, p: ast.Program([s] + p.stmts), "program -> NEWLINE program": lambda n, p: p, "program -> EPS": lambda: ast.Program([]), # ------------------------------------------------------------------------- "obj_def -> client_def": lambda c: c, "obj_def -> server_def": lambda s: s, "obj_def -> step_def": lambda s: s, "obj_def -> sim_def": lambda s: s, # ------------------------------------------------------------------------- # ... # ... ``` ### Parser Para la implementación del parser principal del lenguaje se creó la clase abstacta `Parser`. Usando esta clase como base se creó una clase `LR1Parser`, la cual implementa un parser LR(1). Para la realización del parser LR(1) fue necesario implementar las clases `LR1Item` y `LR1Table`. La primera de estas clases representa un item del parser, el cual contiene: la producción que lo genera, la posición del punto (dot) en la producción y el terminal que le debe proseguir (lookahead). La segunda clase (`LR1Table`) representa la tabla de transición del parser. Cada posición de la tabla puede contener tres tipos de elementos: un **string** `"OK"`, que indica que el estado de aceptación; un valór numérico entero, que indica cual es el siguiente estado; o un no terminal de la gramática, el cual representa que hay que realizar una reducción. Para no tener que recalcular la tabla cada vez que se va a parsear un texto, la misma puede ser serializada y luego cargada. La construcción de la tabla se realizó siguiendo el algoritmo visto en las conferencias de la asignatura (calculando los **goto** y las **clausuras** de los estados). En el proceso de parsing, al realizar una acción de reducción, es donde se utilizan las funciones constructoras vistas en la sección anterior. En dependencia de la producción que se está reduciendo, se llama a la función constructora correspondiente. Para una mayor comodidad se implementó también la clase `ParserManager`. Esta clase ofrece, dado una gramática, un tokenizador (opcional) y un parser (opcional, por defecto LR(1)), métodos como: `parse_file` (para parsear un archivo), `parse` (para parsear un texto) y `parse_tokens` (para parsear una lista de tokens directamete). Estas funciones devuelven el AST resultante del proceso de parsing. ### Visitors Una vez obtenido el AST de un programa es necesario realizar recorridos sobre él. Para ello se implmentó una clase `Visitor` la cual contiene dos decoradores `@visitor` y `@callback`. Por cada **visitor** que se quiera implementar para el AST, se debe implementar una nueva clase que tenga como atributo de clase una instancia de la clase `Visitor`. Luego, cada método de la clase que tenga el decorador `@visitor`, se establecerá como una sobrecarga. Es por ello que todos estos métodos deben tener sus argumentos tipados (esta es la forma en la que el **visitor** sabe cual de los métodos de la clase debe llamar). Por ejemplo: ```python from clismo.lang.visitor import Visitor class EvalVisitor: visitor_dec = Visitor().visitor @visitor_dec def eval(self, node: ast.Program): for stmt in node.stmts: stmt.eval(self) @visitor_dec def eval(self, node: ast.ClientDef): ... # ... ``` El decorador `@callback` se utiliza para definir funciones que se van a llamar cada vez que se llame a una función marcada como **visitor**. ### Ejecución El proceso de ejecución se divide en 3 partes: 1. Reconocimiento de las estructuras declaradas. 2. Chequeo semántico. 3. Ejecución. #### Reconocimiento de las estructuras declaradas En este paso se recorre el AST con un visitor el cual analiza las estructuras declaradas. Esta información es utilizada luego en el checqueo semántico. Esto permite usar dentro de las estructuras referencias a otras que por el orden del código no han sido definidas aún. #### Chequeo semántico En esta etapa se realiza una verificación a cada estructura declarado (igualmente con otro visitor). Se asegura que las estructuras declaradas tengan los atributos y funciones necesarias para su correcta ejecución. Además se realiza un chequeo de tipos de los atributos y cuerpos de las funciones. De esta forma se asegura que cada función devuelva un valor de tipo correcto. #### Evaluación Finalmente, un último vísitor se encarga de recorrer el AST configurando la simulación según las estructuras declaradas para luego ejecutar (u optimizar) el modelo definido. ## Ejemplo En el script [clismo_example.csm](./clismo_example.csm) se muestra un ejemplo de un programa. ```text client Normal: test_val = 5 possible(test_val): return randint(2, 10) server S1: total = 0 attend_client(): var t = 1 * get(current_client, test_val) set(self, total, get(self, total) + t) return t server S2: total = 0 attend_client(): var t = 3 * get(current_client, test_val) set(self, total, get(self, total) + t) return t step P1: servers = [S2, S2, S1] possible(servers): var s = [S1, S2] var count = len(get(self, servers)) var new_servers = list("server") loop _ from 0 to count: var r = randint(0, 1) new_servers = append(new_servers, get_at(s, r)) return new_servers simulation Test: mode = "run" steps = [P1] client_limit = 50 max_iter = 20 pop_size = 3 mut_prob = 0.4 new_rand = 10 arrive(Normal): return 5 minimize(): var servers = get(get_at(get(self, steps), 0), servers) var count = len(servers) var val = 0 loop i from 0 to count: val = val + get(get_at(servers, i), total) return val ```


نیازمندی

مقدار نام
>=0.4.0,<0.5.0 typer


زبان مورد نیاز

مقدار نام
>=3.8,<4.0 Python


نحوه نصب


نصب پکیج whl clismo-0.1.4:

    pip install clismo-0.1.4.whl


نصب پکیج tar.gz clismo-0.1.4:

    pip install clismo-0.1.4.tar.gz