Giter VIP home page Giter VIP logo

jsonlang-chevrotain's Introduction

Compiladores - TP final 2c 2022

Instalación

Ejecutar el siguiente comando.

make setup

Ejecución de tests

Pueden ejecutarse los tests desde la herramienta de testing de Visual Studio Code, o corriendo este comando:

./deno test

¿Cómo agregaste soporte para strings?

El soporte para strings fue bastante sencillo de agregar en la grámatica. Consistió unicamente en agregar un token StringLiteral al lexer que detecte palabras entre comillas dobles:

export const StringLiteral = createToken({
  name: "StringLiteral",
  pattern: /"(?:""|[^"])*"/,
});

Luego se agrego la posibilidad de parsear dichos tokens en la gramatica, como una de las opciones de simpleExpression:

  simpleExpression = this.RULE("simpleExpression", () => {
    this.OR([
      ...
      { ALT: () => this.CONSUME(StringLiteral) },
      { ALT: () => this.CONSUME(Identifier) },
      ...
    ]);
  });

Por último, para mostrarlo en jsonlang, se modifico el visitor de simpleExpression:

  simpleExpression(ctx: SimpleExpressionCstChildren) {
    ...
    if (ctx.Identifier) return ctx.Identifier[0].image;
    if (ctx.StringLiteral)
      return ctx.StringLiteral[0].image.replace(/^"(.*)"$/, "$1");
    ...
  }

Resulta entonces la siguiente traduccion de mi lenguaje a jsonlang:

<x> = "test";  -->  { set: "x", value: "test" }

También, como agregue soporte para TDA y verificación de tipos, restringí algunas operaciones cuando las expresiones contienen strings. Ejemplo: 1 + "test" no está permitido.

¿Cómo agregaste soporte para arrays?

Agregue soporte para arrays de forma similar a los strings. Así resulta el parser tras agregar arrays a la gramatica:

  simpleExpression = this.RULE("simpleExpression", () => {
    this.OR([
      ...
      { ALT: () => this.SUBRULE(this.array) },
      ...
    ]);
  });

  array = this.RULE("array", () => {
    this.CONSUME(LBracket);
    this.MANY_SEP({
      SEP: Comma,
      DEF: () => this.SUBRULE(this.expression),
    });
    this.CONSUME(RBracket);
  });

Y asi resulto el visitor:

  simpleExpression(ctx: SimpleExpressionCstChildren) {
    ...
    if (ctx.array) return this.visit(ctx.array);
    ...
  }

  array(ctx: ArrayCstChildren) {
    if (!ctx.expression) return [];
    return ctx.expression.map((expr) => this.visit(expr));
  }

Resulta entonces la siguiente traduccion de mi lenguaje a jsonlang:

<x> = [1, 2, 3];  -->  { set: "x", value: [1, 2, 3] }

¿Cómo agregaste soporte para diccionarios?

Agregue soporte para diccionarios de forma similar a los arrays. Así resulta el parser tras agregarlos a la gramatica:

  simpleExpression = this.RULE("simpleExpression", () => {
    this.OR([
      ...
      { ALT: () => this.SUBRULE(this.dictionary) },
      ...
    ]);
  });

  dictionary = this.RULE("dictionary", () => {
    this.CONSUME(LCurly);
    this.MANY_SEP({
      SEP: Comma,
      DEF: () => {
        this.CONSUME(Identifier);
        this.CONSUME(Colon);
        this.SUBRULE(this.expression);
      },
    });
    this.CONSUME(RCurly);
  });

Y asi resulto el visitor:

  simpleExpression(ctx: SimpleExpressionCstChildren) {
    ...
    if (ctx.dictionary) return this.visit(ctx.dictionary);
    ...
  }

  dictionary(ctx: DictionaryCstChildren) {
    const retVal: any = { dict: [] };
    if (!ctx.Identifier || !ctx.expression) return retVal;
    for (let i = 0; i < ctx.Identifier.length; i++) {
      const key = ctx.Identifier[i].image;
      const value = this.visit(ctx.expression[i]);
      retVal.dict.push({ [key]: value });
    }
    return retVal;
  }

Resulta entonces la siguiente traduccion de mi lenguaje a jsonlang:

<x> = { name: "Test", age: 30 }; --> { set: "x", value: { dict: [{ name: "Test" }, { age: 30]} }

Ademas, como agregue soporte para TDA y verificación de tipos, agregue funcionalidades para poder determinar si un diccionario es compatible con los campos que espera un TDA (se muestra el código en las próximas secciones).

¿Cómo agregaste Verificación de tipos estáticos?

Para agregar verificación de tipos estáticos, aproveche la facilidad de la herramienta para crear visitors y cree otro dedicado a la verificación de tipos.

Este se compone de dos partes. El visitor en si mismo, que visita los nodos del arbol, y un verificador de tipos con las siguientes responsabilidades:

  • Almacenar las variables y sus tipos
  • Determinar compatibilidades entre tipos

Este es, por ejemplo, el método para determinar si son compatibles dos tipos:

  areCompatible(expressionVarType: DefinedType, explicitVarType: DefinedType) {
    const explicitVar = this.definedTypes.find(
      (t) => t.typename === explicitVarType.typename
    );
    if (!explicitVar) throw new UndefinedTypeError(explicitVarType.typename);
    if (expressionVarType.typename === explicitVarType.typename) return true;
    if ([explicitVarType.typename, expressionVarType.typename].includes("any"))
      return true;
    if (this.canBeCompared(expressionVarType, explicitVar)) {
      for (const child of explicitVar.children!) {
        const prop = expressionVarType.children?.find(
          (c) => c.name === child.name
        );
        if (!prop) return false;
        if (prop.typename !== child.typename) return false;
      }
      return true;
    }
    return false;
  }

  canBeCompared(expressionVarType: DefinedType, explicitVarType: DefinedType) {
    return (
      explicitVarType.children &&
      explicitVarType.children &&
      ["object", "any"].includes(expressionVarType.typename)
    );
  }

Volviendo al visitor, este asignará tipos principalmente en las expresiones. Este es el metodo visit de simpleExpression:

  simpleExpression(ctx: SimpleExpressionCstChildren): DefinedType {
    if (ctx.Integer) return { typename: "number" };
    if (ctx.False || ctx.True) return { typename: "boolean" };
    if (ctx.Identifier)
      return this.typedVariables.getTypeForVariable(ctx.Identifier[0].image);
    if (ctx.StringLiteral) return { typename: "string" };
    if (ctx.array) return { typename: "array" };
    if (ctx.dictionary) return this.visit(ctx.dictionary);
    if (ctx.callExpression) return this.visit(ctx.callExpression);
    throw new Error();
  }

¿Cómo agregaste TDA?

Mi idea fue que esta sea la sintaxis para definir un nuevo tipo:

<<Person>> = {
  name: string;
  age: number;
}

Entonces, necesité ampliar el parser para permitirlo:

  typeStatement = this.RULE("typeStatement", () => {
    this.CONSUME(LAngleBracket);
    this.CONSUME2(LAngleBracket);
    this.CONSUME(Identifier);
    this.CONSUME(RAngleBracket);
    this.CONSUME2(RAngleBracket);
    this.CONSUME(LCurly);
    this.AT_LEAST_ONE(() => {
      this.CONSUME2(Identifier);
      this.CONSUME(Colon);
      this.CONSUME3(Identifier);
      this.CONSUME(SemiColon);
    });
    this.CONSUME(RCurly);
  });

Por último, solo resta poder saber si el tipo definido y una expresión de tipo objeto/diccionario que se le asigne son compatibles, para poder permitir lo siguiente:

<<Person>> {
  name: string;
  age: number;
}
<w: Person> = {
  name: "test",
  age: 30
};

Esto esta logrado en el metodo areCompatible que ya vimos:

  areCompatible(expressionVarType: DefinedType, explicitVarType: DefinedType) {
    ...
    if (this.canBeCompared(expressionVarType, explicitVar)) {
      for (const child of explicitVar.children!) {
        const prop = expressionVarType.children?.find(
          (c) => c.name === child.name
        );
        if (!prop) return false;
        if (prop.typename !== child.typename) return false;
      }
      return true;
    }
    return false;
  }

Herramienta de Parsing: Chevrotain

Link a la web de la herramienta

¿Cuán facil fue aprender esta herramienta de parsing? ¿Por qué?

La herramienta me resultó muy sencilla de entender ya que la documentación es muy clara y completa. Pocas veces necesite resolver dudas por fuera de esta documentación, y en estos casos, los ejemplos y las discusiones en el GitHub de la herramienta resultaron muy útiles.

Además, facilita mucho el armado de las diferentes partes del parser.

Por último, que sea una herramienta para Typescript me resulto muy util y me facilito mucho trabajo, dado que este me es muy familiar.

¿Recomendarías esta herramienta de parsing a futuros estudiantes de la materia? ¿Por qué?

La recomendaría totalmente a futuros estudiantes de la materia si se sienten comodos utilizando JS/TS.

El tutorial es clarisimo, y si se juega un poco con los features que ofrece, se pueden lograr de forma muy facil el desarrollo deseado.

Liste ventajas y desventajas del uso de esta herramienta de parsing.

Ventajas:

  • Documentacion muy clara.
  • Facil creacion del Lexer, permitiendo categorias de tokens, facil tratado de whitespaces, y otros features útiles.
  • La gramática se escribe con la misma herramienta. No se necesitan dependencias adicionales ni herramientas de generacion de codigo.
  • La gramática, al ser escrita en Typescript, es debuggeable.
  • El visitor con el que luego se podria implementar semantica (o cualquier otra funcionalidad una vez que se tiene el arbol de sintaxis) puede ser desarrollado por separado, o embebido en el mismo parser.
  • Generación automática de diagramas de sintaxis.
  • Generación automatica de tipos para el Visitor.

Desventajas:

  • No hay muchos mas recursos que la documentación y el GitHub de la herramienta, por lo que si se tiene algun problema cuya solución no se encuentre ahí, se complicará su resolución.
  • Los ejemplos estan en Javascript. Si bien soporta Typescript, hay que traducirlo y pueden suceder algunas confusiones.
  • La gramática puede quedar mas verbosa que si fuera escrita en, por ejemplo, BNF.

jsonlang-chevrotain's People

Contributors

sberoch avatar

Watchers

José Ignacio Sbruzzi avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.