Ver código fonte

Parsing function rework for program, types and ID. Initial function body parser

Lucas de Souza 6 anos atrás
pai
commit
889ec70bca
10 arquivos alterados com 2126 adições e 109 exclusões
  1. 2 1
      grammar/index.js
  2. 1 1
      js/asa/SyntaxError.js
  3. 103 38
      js/asa/analisadorSintatico.js
  4. 1 9
      js/main.js
  5. 88 0
      karma.conf.js
  6. 1864 36
      package-lock.json
  7. 10 0
      package.json
  8. 46 0
      tests/syntax.spec.js
  9. 0 13
      tests/tokensType.spec.js
  10. 11 11
      webpack.config.js

+ 2 - 1
grammar/index.js

@@ -1,3 +1,4 @@
 import * as ptBR from './pt-br/ivprog.g4';
 
-exports.pt_br = ptBR.ivprog;
+exports.pt_br = ptBR.ivprog;
+exports.default = ptBR.ivprog;

+ 1 - 1
js/asa/SyntaxError.js

@@ -11,6 +11,6 @@ export class SyntaxError extends Error {
 
   constructor (...msg) {
     super(...msg);
-    Error.captureStackTrace(this, SyntaxError);
+    //Error.captureStackTrace(this, SyntaxError);
   }
 }

+ 103 - 38
js/asa/analisadorSintatico.js

@@ -14,6 +14,7 @@ export class AnalisadorSintatico {
       this.lexerClass.PR_LOGICO,
       this.lexerClass.PR_CADEIA
     ];
+    this.functionTypes = this.variableTypes.push(this.lexerClass.PR_VAZIO);
   }
 
   parseTree () {
@@ -33,16 +34,25 @@ export class AnalisadorSintatico {
 
   parseProgram () {
     const token = this.getToken();
+    let globalVars = [];
+    let functions = [];
 
     if(this.lexerClass.PR_PROGRAMA === token.type) {
       this.pos++;
       this.consumeNewLines();
       this.checkOpenCurly();
       this.pos++;
-      this.consumeNewLines();
-      const globalVars = this.parseGlobalVariables();
-      this.consumeNewLines();
-      const functions = []; // this.parseFunctions();
+      while(true) {
+        this.consumeNewLines();
+        const token = this.getToken();
+        if (token.type === this.lexerClass.PR_CONST || token.type === this.lexerClass.ID) {
+          globalVars = globalVars.concat(this.parseGlobalVariables());
+        } else if (token.type === this.lexerClass.PR_FUNCAO) {
+          functions = functions.concat([]);
+        } else {
+          break;
+        }
+      }
       this.consumeNewLines();
       this.checkCloseCurly();
       this.pos++;
@@ -135,7 +145,7 @@ export class AnalisadorSintatico {
         throw SyntaxError.createError('new line or \';\'', eosToken);
       }
 
-      if (decl === null){
+      if (decl === null) {
         break;
       } else {
         vars = vars.concat(decl);
@@ -154,12 +164,8 @@ export class AnalisadorSintatico {
     const constToken = this.getToken();
     if(constToken.type === this.lexerClass.PR_CONST) {
       this.pos++;
-      const typeToken = this.getToken();
-      if(!this.isVariableType(typeToken)) {
-        throw SyntaxError.createError(this.getTypesAsString(), typeToken);
-      }
-      this.pos++;;
-      return this.parseDeclararion(typeToken, true);
+      const typeString = this.parseType();
+      return this.parseDeclararion(typeString, true);
     } else if(this.isVariableType(constToken)) {
       this.pos++;
       return this.parseDeclararion(constToken);
@@ -171,17 +177,13 @@ export class AnalisadorSintatico {
 
   /*
   * Parses a declarion of the form: type --- id --- (= --- EAnd)?
-  * @returns Declararion(const, type, id, initVal?)
+  * @returns a list of Declararion(const, type, id, initVal?)
   **/
-  parseDeclararion (typeToken, isConst = false) {
+  parseDeclararion (typeString, isConst = false) {
     let initial = null;
     let dim1 = null;
     let dim2 = null;
-    const idToken = this.getToken();
-    if(idToken.type !== this.lexerClass.ID) {
-      throw SyntaxError.createError('ID', idToken);
-    }
-    this.pos++;
+    const idString = this.parseID();
     // Check for array or vector
     // ID[int/IDi][int/IDj]
     if (this.checkOpenBrace(true)) {
@@ -209,18 +211,18 @@ export class AnalisadorSintatico {
       this.pos++;
       return [{
         isConst: isConst,
-        tipo: typeToken.text,
-        id: idToken.text,
+        tipo: typeString,
+        id: idString,
         lines: dim1,
         columns: dim2,
         initial: initial
       }]
-      .concat(this.parseDeclararion(typeToken, isConst));
+      .concat(this.parseDeclararion(typeString, isConst));
     } else {
        return [{
         isConst: isConst,
-        tipo: typeToken.text,
-        id: idToken.text,
+        tipo: typeString,
+        id: idString,
         lines: dim1,
         columns: dim2,
         initial: initial
@@ -292,10 +294,11 @@ export class AnalisadorSintatico {
   parseFunctions () {
     let list = [];
     while(true) {
-      const f = this.parseFunction();
-      if(f === null)
+      const func = this.parseFunction();
+      if(func === null)
         break;
-
+      else
+        list.push(func);
     }
     return list;
   }
@@ -306,6 +309,7 @@ export class AnalisadorSintatico {
   * The block object has two attributes: declarations and commands
   **/
   parseFunction () {
+    let formalParams = [];
     const token = this.getToken();
     if(token.type !== this.lexerClass.PR_FUNCAO) {
       //throw SyntaxError.createError(this.lexer.literalNames[this.lexerClass.PR_FUNCAO], token);
@@ -313,23 +317,64 @@ export class AnalisadorSintatico {
     }
     this.pos++;
     this.consumeNewLines();
-    const returnType = this.parseFunctionType();
+    const returnType = this.parseType(true);
     this.consumeNewLines();
-    const functionID = this.parseFunctionID();
+    const functionID = this.parseID();
     this.consumeNewLines();
     this.checkOpenParenthesis();
     this.pos++;
     this.consumeNewLines();
-    const formalParams = []; // formal parameters list
-    this.consumeNewLines();
-    this.checkCloseParenthesis();
-    this.pos++;
+    if (!this.checkCloseParenthesis(true)) {
+      formalParams = this.parseFormalParameters(); // formal parameters 
+      this.consumeNewLines();
+      this.checkCloseParenthesis();
+      this.pos++;
+    } else {
+      this.pos++;
+    }
     this.consumeNewLines();
     const commandsBlock = this.parseFunctionBody();
     return {returnType: returnType, id: functionID, formalParams: formalParams, block: commandsBlock};
   }
 
-  parseFunctionID () {
+  /*
+  * Parse the formal parameters of a function.
+  * @returns a list of objects with the following attributes: type, id and dimensions.
+  **/
+  parseFormalParameters () {
+    const list = [];
+    while(true) {
+      let dimensions = 0;
+      this.consumeNewLines();
+      const typeString = this.parseType();
+      this.pos++;
+      this.consumeNewLines();
+      const idString = this.parseID();
+      this.pos++;
+      this.consumeNewLines();
+      if (this.checkOpenBrace(true)) {
+        this.pos++;
+        dimensions++;
+        this.checkCloseBrace();
+        this.pos++;
+        if(this.checkOpenBrace(true)) {
+          this.pos++;
+          dimensions++;
+          this.checkCloseBrace();
+          this.pos++;
+        }
+      }
+      list.push({type: typeString, id: idString, dimensions: dimensions});
+      this.consumeNewLines();
+      const commaToken = this.getToken();
+      if (commaToken.type !== this.lexerClass.VIRGULA)
+        break;
+      this.pos++;
+    }
+    return list;
+  }
+
+  parseID () {
     const token = this.getToken();
     if(token.type !== this.lexerClass.ID) {
       throw SyntaxError.createError('ID', token);
@@ -338,11 +383,11 @@ export class AnalisadorSintatico {
     return token.text;
   }
 
-  parseFunctionType () {
+  parseType (isFunction = false) {
     const token = this.getToken();
-    if(token.type === this.lexerClass.ID) {
+    if(token.type === this.lexerClass.ID && isFunction) {
       return 'void';
-    } else if (token.type === this.lexerClass.PR_VAZIO) {
+    } else if (token.type === this.lexerClass.PR_VAZIO && isFunction) {
       this.pos++;
       return 'void';
     } else if (this.isVariableType(token)) {
@@ -356,21 +401,41 @@ export class AnalisadorSintatico {
           return 'real';
         case this.lexerClass.PR_CADEIA:
           return 'string';
+        default:
+          break;
       }
     }
+    
+    throw SyntaxError.createError(this.getTypesAsString(isFunction), token);
   }
 
   parseFunctionBody () {
+    let variablesDecl = [];
     this.checkOpenCurly();
     this.pos++;
     while(true) {
       this.consumeNewLines();
       const token = this.getToken();
+      if (isVariableType(token)) {
+        this.pos++;
+        variablesDecl = variablesDecl.concat(this.parseDeclararion(token));
+      } else if (token.type === this.lexerClass.ID) {
+        this.pos++;
+        const equalOrParenthesis = this.getToken();
+        if (equalOrParenthesis.type === this.lexerClass.ATRIBUICAO) {
+
+        } else if (equalOrParenthesis.type === this.lexerClass.ABRE_PAR) {
+
+        } else {
+          throw SyntaxError.createError("= or (", equalOrParenthesis);
+        }
+      }
     }
   }
 
-  getTypesAsString () {
-    return this.variableTypes.map( x => this.lexer.literalNames[x])
+  getTypesAsString (isFunction = false) {
+    const types = isFunction ? this.functionTypes : this.variableTypes;
+    return types.map( x => this.lexer.literalNames[x])
       .reduce((o, n) => {
         if (o.length <= 0)
           return n;

+ 1 - 9
js/main.js

@@ -11,15 +11,7 @@ const ivprogLexer = Lexers[lang];
 
 const input = `programa {
   const real PI
-  inteiro i
-  const inteiro a[5][5], b, c[i]
-
-  funcao
-  inteiro teste()
-  {
-    inteiro i[5];
-  }
-
+  const inteiro a[5][5]
 }`;
 const lexer = new ivprogLexer(new InputStream(input));
 const stream = new CommonTokenStream(lexer);

+ 88 - 0
karma.conf.js

@@ -0,0 +1,88 @@
+const webpackConfig = require('./webpack.config.js');
+ 
+module.exports = function(config) {
+  config.set({
+    //root path location to resolve paths defined in files and exclude
+    basePath: '',
+    //files/patterns to exclude from loaded files
+    exclude: [],
+    //files/patterns to load in the browser
+    files: [
+      {pattern: 'tests/*.js',watched:true,served:true,included:true}
+      /*parameters*/
+          //watched: if autoWatch is true all files that have set watched to true will be watched for changes
+          //served: should the files be served by Karma's webserver?
+          //included: should the files be included in the browser using <script> tag?
+          //nocache: should the files be served from disk on each request by Karma's webserver?
+      /*assets*/
+          //{pattern: '*.html', watched:true, served:true, included:false}
+          //{pattern: 'images/*', watched:false, served:true, included:false}      
+    ],
+    
+    //executes the tests whenever one of watched files changes
+    autoWatch: true,
+    //if true, Karma will run tests and then exit browser
+    singleRun:false,
+    //if true, Karma fails on running empty test-suites
+    failOnEmptyTestSuite:false,
+    //reduce the kind of information passed to the bash
+    logLevel: config.LOG_WARN, //config.LOG_DISABLE, config.LOG_ERROR, config.LOG_INFO, config.LOG_DEBUG
+                                      
+    //list of frameworks you want to use, only jasmine is installed automatically
+    frameworks: ['jasmine'],
+    //list of browsers to launch and capture
+    browsers: ['Firefox'/*,'PhantomJS','Firefox','Edge','ChromeCanary','Opera','IE','Safari'*/],
+    //list of reporters to use
+    reporters: ['mocha','kjhtml'/*,'dots','progress','spec'*/],
+    
+    //address that the server will listen on, '0.0.0.0' is default
+    listenAddress: '0.0.0.0',
+    //hostname to be used when capturing browsers, 'localhost' is default
+    hostname: 'localhost',
+    //the port where the web server will be listening, 9876 is default
+    port: 9876,
+    //when a browser crashes, karma will try to relaunch, 2 is default
+    retryLimit:0,
+    //how long does Karma wait for a browser to reconnect, 2000 is default
+    browserDisconnectTimeout: 5000,
+    //how long will Karma wait for a message from a browser before disconnecting from it, 10000 is default
+    browserNoActivityTimeout: 10000,
+    //timeout for capturing a browser, 60000 is default
+    captureTimeout: 60000,
+ 
+    client: {
+      //capture all console output and pipe it to the terminal, true is default
+      captureConsole:false,
+      //if true, Karma clears the context window upon the completion of running the tests, true is default
+      clearContext:false,
+      //run the tests on the same window as the client, without using iframe or a new window, false is default
+      runInParent: false,
+      //true: runs the tests inside an iFrame; false: runs the tests in a new window, true is default
+      useIframe:true,
+      jasmine:{
+        //tells jasmine to run specs in semi random order, false is default
+        random: false
+      }
+    },
+ 
+    /*karma-webpack config*/
+    //pass your webpack configuration for karma
+    webpack: webpackConfig,
+    preprocessors: {
+      //use webpack to support require() in test-suits .js files
+      //use babel-loader from webpack to compile es2015 features in .js files
+      //add webpack as preprocessor
+      './tests/*.js': ['webpack']
+    },
+    webpackMiddleware: {
+      //turn off webpack bash output when run the tests
+      noInfo: true,
+      stats: 'errors-only'
+    },
+ 
+    /*karma-mocha-reporter config*/
+    mochaReporter: {
+      output: 'noFailures'  //full, autowatch, minimal
+    }
+  });
+};

Diferenças do arquivo suprimidas por serem muito extensas
+ 1864 - 36
package-lock.json


+ 10 - 0
package.json

@@ -29,6 +29,16 @@
     "babel-core": "^6.26.3",
     "babel-loader": "^7.1.5",
     "babel-preset-env": "^1.7.0",
+    "jasmine-core": "^3.2.1",
+    "karma": "^3.0.0",
+    "karma-chrome-launcher": "^2.2.0",
+    "karma-coverage-istanbul-reporter": "^2.0.1",
+    "karma-firefox-launcher": "^1.1.0",
+    "karma-jasmine": "^1.1.2",
+    "karma-jasmine-html-reporter": "^1.3.1",
+    "karma-mocha-reporter": "^2.2.5",
+    "karma-phantomjs-launcher": "^1.0.4",
+    "karma-webpack": "^3.0.0",
     "webpack": "^4.16.5",
     "webpack-cli": "^3.1.0"
   },

+ 46 - 0
tests/syntax.spec.js

@@ -0,0 +1,46 @@
+import Lexers from './../grammar/';
+import {
+    AnalisadorSintatico
+} from './../js/asa/analisadorSintatico';
+describe("Testing Syntax Analysis for default", () => {
+
+    var input;
+
+    var asa;
+    var lexer;
+
+    it("it should produce a valid tree", () => {
+      lexer  = Lexers['pt_br'];
+      input = `programa {
+      const real PI
+      const inteiro a[5][5]
+      }`;
+
+      asa = {
+          global: [{
+              isConst: true,
+              tipo: 'real',
+              id: 'PI',
+              lines: null,
+              columns: null,
+              initial: null
+          }, {
+              isConst: true,
+              tipo: 'int',
+              id: 'a',
+              lines: {
+                  type: 'int',
+                  value: 5
+              },
+              columns: {
+                  type: 'int',
+                  value: 5
+              },
+              initial: null
+          }],
+          functions: []
+        };
+        const as = new AnalisadorSintatico(input, lexer);
+        expect(as.parseTree()).toEqual(asa);
+    });
+});

+ 0 - 13
tests/tokensType.spec.js

@@ -1,13 +0,0 @@
-describe("Testing Syntax Analysis: programa and global declararions...", () => {
-  
-  const input = `programa {
-    const real PI
-    const inteiro a[5][5]
-  }`;
-
-  it("it should returns a program with two const variables declararions", () => {
-    
-  });
-});
-    
-

+ 11 - 11
webpack.config.js

@@ -11,19 +11,19 @@ module.exports = {
         fs: 'empty',
     },
     module: {
-        rules: [
-            {
-                test: /\.js$/,
-                loader: 'babel-loader',
-                query: {
-                    presets: ['env']
-                }
-            },
-            { test: /\.g4/, loader: 'antlr4-webpack-loader' }
-        ]
+        rules: [{
+            test: /\.js$/,
+            loader: 'babel-loader',
+            query: {
+                presets: ['env']
+            }
+        }, {
+            test: /\.g4/,
+            loader: 'antlr4-webpack-loader'
+        }]
     },
     stats: {
         colors: true
     },
     devtool: 'source-map'
-};
+};