Added a simple syntax checker.
authorRoberto Raggi <roberto.raggi@nokia.com>
Tue, 8 May 2012 11:21:52 +0000 (13:21 +0200)
committerRoberto Raggi <roberto.raggi@nokia.com>
Tue, 8 May 2012 11:24:48 +0000 (13:24 +0200)
main.cpp
qv4syntaxchecker.cpp [new file with mode: 0644]
qv4syntaxchecker_p.h [new file with mode: 0644]
v4.pro

index 88b0bc7..5ca3926 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -2,6 +2,7 @@
 #include "qmljs_objects.h"
 #include "qv4codegen_p.h"
 #include "qv4isel_p.h"
+#include "qv4syntaxchecker_p.h"
 
 #include <QtCore>
 #include <private/qqmljsengine_p.h>
@@ -39,6 +40,60 @@ struct Print: FunctionObject
 };
 } // builtins
 
+
+void evaluate(QQmlJS::Engine *engine, const QString &fileName, const QString &code)
+{
+    using namespace QQmlJS;
+
+    Lexer lexer(engine);
+    lexer.setCode(code, 1, false);
+    Parser parser(engine);
+
+    const bool parsed = parser.parseProgram();
+
+    foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) {
+        std::cerr << qPrintable(fileName) << ':' << m.loc.startLine << ':' << m.loc.startColumn
+                  << ": error: " << qPrintable(m.message) << std::endl;
+    }
+
+    if (parsed) {
+        using namespace AST;
+        Program *program = AST::cast<Program *>(parser.rootNode());
+
+        Codegen cg;
+        IR::Module module;
+        cg(program, &module);
+
+        const size_t codeSize = 10 * getpagesize();
+        uchar *code = (uchar *) malloc(codeSize);
+
+        x86_64::InstructionSelection isel(&module, code);
+        QHash<QString, IR::Function *> codeByName;
+        foreach (IR::Function *function, module.functions) {
+            isel(function);
+            if (function->name && ! function->name->isEmpty()) {
+                codeByName.insert(*function->name, function);
+            }
+        }
+
+        if (! protect(code, codeSize))
+            Q_UNREACHABLE();
+
+        VM::Context *ctx = new VM::Context;
+        ctx->init();
+        ctx->activation = VM::Value::object(ctx, new VM::ArgumentsObject(ctx));
+        ctx->activation.objectValue->put(VM::String::get(ctx, QLatin1String("print")),
+                                         VM::Value::object(ctx, new builtins::Print()));
+        foreach (IR::Function *function, module.functions) {
+            if (function->name && ! function->name->isEmpty()) {
+                ctx->activation.objectValue->put(VM::String::get(ctx, *function->name),
+                                                 VM::Value::object(ctx, new VM::ScriptFunction(function)));
+            }
+        }
+        codeByName.value(QLatin1String("%entry"))->code(ctx);
+    }
+}
+
 int main(int argc, char *argv[])
 {
     using namespace QQmlJS;
@@ -53,54 +108,7 @@ int main(int argc, char *argv[])
         if (file.open(QFile::ReadOnly)) {
             const QString code = QString::fromUtf8(file.readAll());
             file.close();
-
-            Lexer lexer(&engine);
-            lexer.setCode(code, 1, false);
-            Parser parser(&engine);
-
-            const bool parsed = parser.parseProgram();
-
-            foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) {
-                std::cerr << qPrintable(fn) << ':' << m.loc.startLine << ':' << m.loc.startColumn
-                          << ": error: " << qPrintable(m.message) << std::endl;
-            }
-
-            if (parsed) {
-                using namespace AST;
-                Program *program = AST::cast<Program *>(parser.rootNode());
-
-                Codegen cg;
-                IR::Module module;
-                cg(program, &module);
-
-                const size_t codeSize = 10 * getpagesize();
-                uchar *code = (uchar *) malloc(codeSize);
-
-                x86_64::InstructionSelection isel(&module, code);
-                QHash<QString, IR::Function *> codeByName;
-                foreach (IR::Function *function, module.functions) {
-                    isel(function);
-                    if (function->name && ! function->name->isEmpty()) {
-                        codeByName.insert(*function->name, function);
-                    }
-                }
-
-                if (! protect(code, codeSize))
-                    Q_UNREACHABLE();
-
-                VM::Context *ctx = new VM::Context;
-                ctx->init();
-                ctx->activation = VM::Value::object(ctx, new VM::ArgumentsObject(ctx));
-                ctx->activation.objectValue->put(VM::String::get(ctx, QLatin1String("print")),
-                                                 VM::Value::object(ctx, new builtins::Print()));
-                foreach (IR::Function *function, module.functions) {
-                    if (function->name && ! function->name->isEmpty()) {
-                        ctx->activation.objectValue->put(VM::String::get(ctx, *function->name),
-                                                         VM::Value::object(ctx, new VM::ScriptFunction(function)));
-                    }
-                }
-                codeByName.value(QLatin1String("%entry"))->code(ctx);
-            }
+            evaluate(&engine, fn, code);
         }
     }
 }
diff --git a/qv4syntaxchecker.cpp b/qv4syntaxchecker.cpp
new file mode 100644 (file)
index 0000000..64247d6
--- /dev/null
@@ -0,0 +1,79 @@
+
+#include "qv4syntaxchecker_p.h"
+
+using namespace QQmlJS;
+
+SyntaxChecker::SyntaxChecker()
+    : Lexer(&m_engine)
+    , m_stateStack(128)
+{
+}
+
+void QQmlJS::SyntaxChecker::clearText()
+{
+    m_code.clear();
+    m_tokens.clear();
+}
+
+void SyntaxChecker::appendText(const QString &text)
+{
+    m_code += text;
+}
+
+QString SyntaxChecker::text() const
+{
+    return m_code;
+}
+
+bool SyntaxChecker::canEvaluate()
+{
+    int yyaction = 0;
+    int yytoken = -1;
+    int yytos = -1;
+
+    setCode(m_code, 1);
+
+    m_tokens.clear();
+    m_tokens.append(T_FEED_JS_PROGRAM);
+
+    do {
+        if (++yytos == m_stateStack.size())
+            m_stateStack.resize(m_stateStack.size() * 2);
+
+        m_stateStack[yytos] = yyaction;
+
+again:
+        if (yytoken == -1 && action_index[yyaction] != -TERMINAL_COUNT) {
+            if (m_tokens.isEmpty())
+                yytoken = lex();
+            else
+                yytoken = m_tokens.takeFirst();
+        }
+
+        yyaction = t_action(yyaction, yytoken);
+        if (yyaction > 0) {
+            if (yyaction == ACCEPT_STATE) {
+                --yytos;
+                return true;
+            }
+            yytoken = -1;
+        } else if (yyaction < 0) {
+            const int ruleno = -yyaction - 1;
+            yytos -= rhs[ruleno];
+            yyaction = nt_action(m_stateStack[yytos], lhs[ruleno] - TERMINAL_COUNT);
+        }
+    } while (yyaction);
+
+    const int errorState = m_stateStack[yytos];
+    if (t_action(errorState, T_AUTOMATIC_SEMICOLON) && canInsertAutomaticSemicolon(yytoken)) {
+        yyaction = errorState;
+        m_tokens.prepend(yytoken);
+        yytoken = T_SEMICOLON;
+        goto again;
+    }
+
+    if (yytoken != EOF_SYMBOL)
+        return true;
+
+    return false;
+}
diff --git a/qv4syntaxchecker_p.h b/qv4syntaxchecker_p.h
new file mode 100644 (file)
index 0000000..508e9f8
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef QV4SYNTAXCHECKER_P_H
+#define QV4SYNTAXCHECKER_P_H
+
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsengine_p.h>
+
+#include <QtCore/QVector>
+#include <QtCore/QString>
+#include <QtCore/QList>
+
+namespace QQmlJS {
+
+class SyntaxChecker: Lexer
+{
+public:
+    SyntaxChecker();
+
+    QString text() const;
+    void clearText();
+    void appendText(const QString &text);
+
+    bool canEvaluate();
+
+private:
+    Engine m_engine;
+    QVector<int> m_stateStack;
+    QList<int> m_tokens;
+    QString m_code;
+};
+
+} // end of QQmlJS namespace
+
+#endif // QV4SYNTAXCHECKER_P_H
diff --git a/v4.pro b/v4.pro
index b50ea3e..c9d9b1a 100644 (file)
--- a/v4.pro
+++ b/v4.pro
@@ -12,7 +12,8 @@ SOURCES += main.cpp \
     qv4ir.cpp \
     qmljs_runtime.cpp \
     qmljs_objects.cpp \
-    qv4isel.cpp
+    qv4isel.cpp \
+    qv4syntaxchecker.cpp
 
 HEADERS += \
     qv4codegen_p.h \
@@ -21,7 +22,8 @@ HEADERS += \
     qmljs_objects.h \
     qv4isel_p.h \
     x86-codegen.h \
-    amd64-codegen.h
+    amd64-codegen.h \
+    qv4syntaxchecker_p.h