// jscheck.js
// 
// Copyright (c) 2010 Guillaume Lathoud
// MIT License
//
// Check the syntax of an ECMAScript 3 code source
// using the Narcissus parser to detect syntax errors,
// and Narcissus-based tools to issue some warnings.
//
// JSCheck can be used indifferently in a browser 
// (see e.g. ./jscheck_web.js and ./jscheck.xhtml)
// or in a command-line engine like the Google V8 Engine:
//     d8 -e "load('jscheck.js');print(jscheck(read('jscheck.js')).errors.length);"
//
// See also ./test.unit.js for an integrated example

/*global from fun jscheck load log narcissus tool*/

if ((typeof load !== 'undefined') && (typeof from === 'undefined'))
    load('from.js'); // Google V8 Engine, Rhino

from.req('fun.js', 'narcissus.jsparse.js', 'narcissus.tree.js', 'tool.text.js', 'narcissus.tree.scope.js')
('jscheck', function () {

    jscheck = function(/*string*/source) {
        var tree, portion, line_start, line_end, line, column, line_total, line_top;

        var ret = {
            errors: [],
            warnings: []
        };
        
        try {
            tree = narcissus.jsparse( source, 'code' );

        } catch( e ) {
            
            // Parse error: breaks ECMAScript 3
            
            tree = null;

            ret.errors.push( 
                {
                    message:    e.message,
                    cursor:     e.cursor,
                    toString:   function () { return e.message; }
                });
        }

        if (tree) {

            ret.tree = tree;

            // No parse error: check for warnings
            ret.warnings = ret.warnings
                .concat( narcissus.tree.detect_extra_comma( tree ) )
                .concat( narcissus.tree.detect_extra_comma_in_fun_params( tree ) )
                .concat( narcissus.tree.detect_undeclared_global( tree ) )
                .concat( narcissus.tree.detect_declaration_repetition( tree ) )
                .concat( narcissus.tree.detect_missing_semicolon( tree ) )
                .concat( narcissus.tree.scope.detect_var_too_late( tree ) )
            ;
        }
        
        // Add extra information to facilitate display

        var a = [ret.errors, ret.warnings], b, c, e;

        while (b = a.pop()) {

            for (c = b.length; c--;) {
                e = b[c];
                
                e.line   = tool.text.line_no( source, e.cursor );
                e.column = tool.text.column_no( source, e.cursor );

                e.line_start = tool.text.line_start(source, e.cursor);
                e.line_end   = tool.text.line_end(source, e.cursor);
                
                e.codepiece             = source.substring( e.line_start, e.cursor );
                e.codepiece_to_line_end = source.substring( e.cursor, e.line_end );
                
            }
        }
        
        ret.has_error   = ret.errors.length > 0;
        ret.has_warning = ret.warnings.length > 0;

        return ret;
    };

});

