Code Syntax Validator

Paste a snippet. JavaScript, JSON and XML hit a parser the browser already ships. Python, PHP, CSS, HTML and SQL get a pattern scan. A green banner means the paste did not explode. Merge after the compiler and the tests agree.

Syntax desk

JavaScript · engine parse · new Function

Waiting for a paste

Findings land here. Click a row to jump to the line.

The banner is a parse, not a blessing

Most syntax pages paint eight languages with one brush. The brush is a regular expression. A regular expression does not know Python's indent stack. A regular expression does not run the PHP tokenizer. This desk refuses to pretend otherwise.

Three modes call a parser the browser already has. JavaScript goes through new Function. JSON goes through JSON.parse. XML goes through DOMParser in text/xml mode. Those three fail for the same class of mistake your runtime fails on: a missing parenthesis, a trailing comma, an unclosed tag.

Python, PHP, CSS, HTML and SQL run a pattern scan. A scan catches a header with no colon, a brace pile that does not return to zero, a SELECT with no FROM in the paste. A scan does not compile. A scan does not execute.

Read the stamp on the rail before you trust the color. Parse means an engine. Scan means patterns. Mixing the two in your head is how a green banner ends up in a pull request description.

Eight modes. Two jobs.

The rail is grouped on purpose. Parse first. Scan second. If you live in one language, a specialist page will beat this desk. The split below is the contract.

JavaScript

Parse

new Function compiles the paste as a function body. A missing ) fails. A wrong DOM API does not. Modules and stray return statements lie in opposite directions. The next heading is the whole reason this mode exists as a warning label.

JSON

Parse

JSON.parse is the same call your frontend already uses. A trailing comma fails. Comments fail. Single quotes fail. Those three are the usual reasons a file someone named .json dies here after living happily in an editor with a looser grammar.

XML

Parse

DOMParser in XML mode. Unclosed tags fail. HTML shortcuts such as a bare <br> fail. A misspelled xmlns fails. This is not HTML5's forgiving tree builder. Feed HTML to the HTML mode instead.

Python

Scan

Looks for a colon after def, if, for, class and the rest of the block headers. Flags a block whose next line never indented. Warns when tabs and spaces mix. Does not run CPython. A NameError is invisible here because a name error is not a syntax error.

PHP

Scan

Looks for <?php and for braces that return to zero. Does not load the PHP binary. A parse error hiding inside interpolation, or a missing semicolon the tokenizer would catch, still goes green. php -l remains the gate for a file you intend to ship.

CSS

Scan

Counts { and }. Flags a property line with no semicolon. Does not understand nesting, @layer, or a custom at-rule your bundler accepts. An unknown property such as colr is valid CSS. The browser ignores it. The scan stays quiet. The page looks unstyled.

HTML

Scan

Warns if the doctype is missing. Counts open tags after skipping void tags (img, br, meta, input, link, and the rest of the spec list). A div opened inside a Blade @if and closed in another branch still hides. Templates need a render.

SQL

Scan

Looks at clause shape. A SELECT with no FROM in the paste raises a warning unless the select list looks like an expression (SELECT 1, SELECT NOW()). Identifier quoting, dialect functions, and CTE bodies sit outside the scan. For query shape, open the SQL query builder.

new Function is a wrapper. Your file is not.

People paste an ES module, watch the banner go red, and assume the module is broken. The failure is the wrapper.

new Function(source) compiles source as the body of an anonymous function. Function bodies allow return. Function bodies reject import and export. Node loading file.mjs, Vite bundling a component, and a classic <script src> each disagree with this wrapper in a different direction.

Paste A · module
import { scan } from './lex.js'
export function run(src) {return scan(src)}

This desk fails. node --check lex.mjs succeeds. Vite succeeds. The banner is answering a different question.

Paste B · stray return
const n = 3
return n * 2

This desk succeeds. Load the same bytes with <script src="app.js"> and the browser throws Illegal return statement.

Load the Module (fails here) sample in the JavaScript rail if you want to see the first case without typing. If a real paste starts with import, leave. Run the file under Node, or take it to the JavaScript validator. Debating the banner on this page wastes a stand-up.

TypeScript belongs in the same bucket. const n: number = 1 is legal in tsc and illegal in new Function. There is no TypeScript mode. Strip the types, or compile, before you paste.

Paste the forty lines the stack trace named. A four-thousand-line Laravel controller floods every scanner on the rail. The well is a snippet desk, not a repository host.

Back to the editor if the paste is still sitting in your clipboard.

Counting tags used to punish <img>

An earlier build of this page compared the number of opening tags to the number of closing tags. <img> has no closer. Neither does <br>, <meta>, <input>, or <link>. A valid article full of figures looked unclosed. The scan now skips the void list from the HTML spec. A self-closing <img /> is treated the same way.

The census still cannot see a tag opened inside a Blade @if and closed in a different branch. Comments and <script> bodies still confuse a tally. Server-rendered HTML needs a render of the page, or the dedicated HTML validator, not a tag census.

Same honesty on CSS. Nested &:hover and @layer are grammar your bundler understands. The brace counter here does not. For a stylesheet you intend to ship, the CSS validator is the next hop.

Three greens that still fail on the machine

Syntax is a gate. Runtime, dialect, and unknown properties walk through it every day. These three pastes stay quiet here and then fail where the code runs.

  1. Python, a name nobody bound

    def greet():print(user_name)greet()

    The colon is present. The body is indented. The scan has nothing to say. CPython raises NameError: name 'user_name' is not defined the moment you call greet. Syntax was never the problem. Execution was. This desk does not execute.

  2. SQL, a Postgres interval on MySQL

    SELECT slug, title
    FROM tools
    WHERE updated_at > NOW() - INTERVAL '7 days'

    SELECT has a FROM. Quotes balance. The scan is quiet. Postgres accepts the interval literal. MySQL wants INTERVAL 7 DAY with no quotes around the number. Dialect is not syntax in the sense this rail measures. Run the statement on the engine you ship to.

  3. CSS, a property the browser drops

    .hero-subtitle {colr: #3d4450;max-width: 44rem;}

    Braces balance. Semicolons are present. The scan is quiet. colr is not a CSS error. Unknown properties are ignored. The subtitle stays the wrong color and nobody gets a red banner, here or in DevTools, until someone reads the computed style.

A fourth case belongs next to these even though the banner goes red: JSON with comments. VS Code previews them. JSON.parse refuses them. The comments are not JSON. They are a JSONC habit. Strip them, or keep the file as JSONC and stop calling it JSON. The JSON fixer is built for the trailing-comma variant of the same fight.

Pretty-print is a JSON privilege

The Format control runs JSON.stringify after a successful parse. XML gets a shallow indent if DOMParser accepted the paste. That is the entire formatter.

There is no Black in this tab. No Pint. No php-cs-fixer. No Prettier. A page claiming to reindent Python in the browser without CPython is performing. If the control does nothing, check the rail. Format is silent on JavaScript, Python, PHP, CSS, HTML and SQL on purpose, with a toast that says so, rather than a fake pretty-printer that drifts from what your editor would emit.

Need JSON readable after a minify accident? Stay on JSON and hit Format. Need a package.json with the right keys rather than a pretty bag of guesses? The package.json generator writes the manifest. Need a pattern pulled out of the snippet? The regex tester is the desk for that, not a syntax rail.

Questions the green banner usually causes

Modules, NameError, JSONC, Format, and what a scan refuses to claim.

Why does a file with import fail while Vite builds it?

The JavaScript mode compiles the paste as a function body through new Function. Function bodies reject import and export. Vite, Node ESM, and bundlers accept them. Load the Module sample to see the failure on purpose. Run node --check on the real file, or use the dedicated JavaScript validator, when the paste is a module.

My Python looks fine. Why did CPython still raise NameError?

NameError is a runtime miss, not a syntax miss. The scan looks for colons, indent after a block header, and mixed tabs. The scan never executes the snippet, so a name nobody bound stays invisible. python file.py is the check that sees it.

JSON with comments works in VS Code. Why does this page refuse it?

JSON.parse refuses comments and trailing commas. VS Code is often previewing JSONC, a different grammar. Strip the comments, or keep the file as JSONC and stop naming it .json. The JSON fixer on Toolexe is the next stop for trailing commas.

Format did nothing to my PHP.

Format pretty-prints JSON after a successful parse, and indents XML after DOMParser accepts the paste. PHP, Python, JavaScript, CSS, HTML and SQL are left alone. There is no php-cs-fixer in the tab. A toast says so rather than emitting a fake indent.

Does SELECT 1 count as a missing FROM?

No. A SELECT whose list looks like an expression, such as SELECT 1 or SELECT NOW(), is allowed to skip FROM. A SELECT of column names with no FROM in the paste still raises a warning. Dialect functions are not validated.

Does anyone at Toolexe receive the snippet?

No. Validation runs in the page. Nothing is posted. Refreshing the tab discards the editor. Confirm in the network panel while you click Validate.

Is TypeScript on the list?

No. A type annotation is a syntax error to new Function and a non-event to tsc. Compile or strip types before pasting, or keep the check in tsc --noEmit. Adding a pretend TypeScript mode would invent errors the compiler does not share.

How is a green result different from ESLint passing?

ESLint is a linter. It reports unused variables, missing semicolons under a chosen style, and a long list of rules you configured. This desk reports parse failures for three languages and pattern misses for five. Unused names, wrong APIs, and style are out of scope on purpose.