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
Parsenew 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
ParseJSON.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
ParseDOMParser 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
ScanLooks 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
ScanLooks 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
ScanCounts { 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
ScanWarns 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
ScanLooks 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.
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.
const n = 3
return n * 2This 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.
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 definedthe moment you callgreet. Syntax was never the problem. Execution was. This desk does not execute.SQL, a Postgres interval on MySQL
SELECT slug, title FROM tools WHERE updated_at > NOW() - INTERVAL '7 days'SELECThas aFROM. Quotes balance. The scan is quiet. Postgres accepts the interval literal. MySQL wantsINTERVAL 7 DAYwith no quotes around the number. Dialect is not syntax in the sense this rail measures. Run the statement on the engine you ship to.CSS, a property the browser drops
.hero-subtitle {colr: #3d4450;max-width: 44rem;}Braces balance. Semicolons are present. The scan is quiet.
colris 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.
