frontpage.
newsnewestaskshowjobs

Open Source @Github

fp.

AI is forcing us to pay off 40 years of technical debt

https://spectrum.ieee.org/ai-in-cybersecurity-php2shell-vulnerability
893•pseudolus•8h ago•538 comments

A new Go API for JSON

https://go.dev/blog/jsonv2
117•olexsmir•2h ago•34 comments

Ripple-65B Preview: U.S. Open-Weight SOTA on Agentic Coding

https://www.ricursive.com/ripple
57•Philpax•1h ago•24 comments

Insects Don't Swarm at LED Lights

https://www.birdsoutsidemywindow.org/2026/11/03/insects-dont-swarm-at-led-lights/
22•speckx•38m ago•15 comments

Progress on provable inference for large language models

https://www.anthropic.com/research/provable-inference
129•ltononro•3h ago•58 comments

Nvidia RTX Spark encrypted firmware slows inference speed to 35% on Linux

https://www.mindstudio.ai/blog/linux-nvidia-rtx-spark-laptop
529•novaRom•11h ago•242 comments

Launch HN: Kino AI (YC S23) – AI media manager and video editor

35•lukeigel•2h ago•22 comments

Have I Befriended a Murderer?

https://longreads.com/2026/11/03/murder-friendship-doubt/
45•gmays•1h ago•11 comments

AMD expects to outsell Intel in Q4 for the first time in history

https://www.tomshardware.com/pc-components/cpus/amd-expects-to-outsell-intel-in-q4-for-the-first-...
333•rbanffy•4h ago•112 comments

Tencent Hy4

https://hy.tencent.ai/research/hy4
120•Philpax•6h ago•36 comments

OpenAI to acquire Pierre Computer Company

https://openai.com/index/openai-to-acquire-pierre/
88•itunpredictable•5h ago•37 comments

Guerrilla bike lanes: San Francisco makes illicit infrastructure permanent (2016)

https://99percentinvisible.org/article/guerrilla-bike-lanes-san-francisco-makes-illicit-infrastru...
201•zenincognito•5h ago•136 comments

North Korean fake job interview led to $6.3B Binance heist

https://www.theregister.com/security/2026/11/03/north-korean-fake-job-interview-led-to-63b-binanc...
469•smalltech•6h ago•279 comments

Verification Is Not Knowledge: Science needs more than answers

https://blog.cosmos-institute.org/p/verification-is-not-knowledge
199•ersinesen•11h ago•113 comments

Incident with Pull Requests, Git Operations and Issues

https://www.githubstatus.com/incidents/smtdswd1h2w4
230•kevcampb•10h ago•117 comments

Kimi K3.1 Pro model weights are out

https://huggingface.co/moonshotai/Kimi-K3.1-Pro
243•abrarthought•4h ago•66 comments

Tell HN: Gemini trains on chat history, even for subscribers (personal accounts)

548•jameslk•10h ago•139 comments

Qualcomm Cloud AI 100 Ultra – 150W AI Accelerator Card with 128GB at 548GB/s

https://www.qualcomm.com/data-center/products/cloud-ai-100-ultra
5•peter_d_sherman•34m ago•0 comments

Evergreen: A Provably Safe Systems Language

https://evergreenlang.net
76•jordan_bonecut•5h ago•70 comments

Basically no one is using AI agents for their DoorDash orders

https://arstechnica.com/ai/2026/10/doordash-says-outside-ai-agents-have-only-led-to-hundreds-of-o...
7•PaulHoule•2d ago•1 comments

Show HN: I built a tool that prices a prebuilt PC part by part

https://pcdealcheck.com/
68•adlep•5h ago•48 comments

Jelly Sheet

https://mrdoob.github.io/codevember-2026/01/
434•memalign•16h ago•109 comments

Conductor Is Joining Meta

https://www.conductor.build/announcement
18•kenrose•2h ago•11 comments

Letting users trust their own MCP clients, without opening a phishing hole

https://streamvessel.com/blog/mcp-trusted-redirect-uris
27•isalexlog•4h ago•2 comments

Stringman, an $800 open-source room scale gripper robot

https://neufangled.com/
786•nharada•1d ago•233 comments

Adding Window Functions to a Spreadsheet

https://www.getgrist.com/blog/running-totals-you-dont-have-to-chase/
38•reedciccio•5h ago•23 comments

Kitty 0.50

https://sw.kovidgoyal.net/kitty/changelog/#
334•porridgeraisin•11h ago•209 comments

Benchfmt.rs: Bringing Go's benchmarking and benchstat workflow to Rust

https://www.bwplotka.dev/2026/rust-microbenchmarks-benchstat/
17•witcher•2h ago•2 comments

This Month in Ladybird – October 2026

https://ladybird.org/newsletter/2026-10-31/
70•BitPirate•2h ago•16 comments

The Internals of PostgreSQL

https://www.interdb.jp/pg/index.html
364•ibobev•22h ago•32 comments
Open in hackernews

Pratt parsing is a black box

https://vinipsmaker.github.io/blog/article/pratt-parsing-is-a-black-box/
60•ibobev•1d ago

Comments

throwup238•1d ago
> Traditional parsing techniques register semantic actions to production rules.

Are there any professional slop interpreters who can explain what this means?

vkazanov•23h ago
The article doesnt read like slop. And this is a very clear sentence to anybody who knows any amount of parser theory.

"Production rules" are rules in grammar descriptions for tools like yacc. "Semantic actions" correspond to pieces of code/logic executed whenever a given rule is fully parsed from input.

jiggawatts•23h ago
The blog reads like organic human text, not mechanical slop.

The terminology is baroque, but pretty standard in parsing circles.

Essentially, different parsing algorithms "run side effects" at different stages of the parsing algorithm application, which can make certain things easy or difficult depending on the timing.

E.g.: If parsing runs side effects before it has 100% decided on what rules have been applied, it may need to "back track" and undo side-effects. This can be very painful to code around, so a common solution is to build (only) a temporary abstract syntax tree (AST) in a pure functional manner, and later come back and process that instead of trying to handle every "do and undo" scenario during initial parsing.

Parsers that only run side-effects when a decision is final allow very efficient compilers that directly "do the thing" instead of building temporary structures.

IIRC, Pascal was compiled this way, in a single pass.

adrian_b•23h ago
I assume that they refer to what you do when you write a Yacc or Bison file or any other input file for a similar parser generator.

A Yacc file contains the grammar of the language to be parsed, but to each grammar rule you can attach a C function that will be invoked by the generated parser when matching that rule.

That C function is the "semantic action" registered for the corresponding grammar production rule.

For instance, by registering appropriate "semantic actions" the generated parser can become a language interpreter, e.g. when matching a multiplication expression a function that does multiplication is invoked, and so on.

If the parser is intended for a compiler, not for an interpreter, the "semantic actions" may generate some intermediate format of the compiled program, like that used by LLVM.

direwolf20•20h ago
Executing the operation inside the semantic action only works for simple calculators. Otherwise you have the problem that the line of code may run multiple times even though it's only parsed once. Usually you make the semantic actions build an AST - a data value that holds the program fragment you just parsed, e.g. new NumberLiteral(atoi(parsed_text)), new FunctionCall(function_identifier_ast, [arg1_ast, arg2_ast]), new BinaryExpression(left_ast, '+', right_ast)

Older compilers with less memory may have compiled each function as it was parsed, but it's pretty typical these days for the parser to output a whole-file AST that you process separately.

vinipsmaker•23h ago
Others here already answered your question. In the blogpost, the text that immediately follows already demonstrates the concept, then it contrasts to what's done in Pratt parsing (semantic actions to to terminals instead of non-terminals). Non-terminals is another way to refer to production rules. This difference is a pretty big deal so it's important to mention. The rest of the section goes on to explain the consequences of this difference. None of this is mentioned in other Pratt tutorials that I read before so I doubt a slop machine would write articles of Pratt parsing even mentioning this trait at all.
stschaef•23h ago
That is not slop btw
nostrademons•22h ago
This is standard CS terminology - if you take a compiler course it'll be explained in the first couple lectures.

The ELI5 is that "semantic actions" are bits of code that a parser generator runs, and "production rules" are patterns that tell it when to run them.

thaumasiotes•16h ago
I would usually think of a 'parser' as simply something that tells you how to produce the input given a set of production rules.

For a simple ambiguous arithmetical grammar, the output of a parser fed "1 + 2 * 3" might be something like "[Mul [Add 1 2] 3]", or "AddExpr(1, MulExpr(2, 3))", or "1 2 3 Mul Add", or "1. Start with 1; 2. Add 2; 3. Multiply by 3", or (more likely in practice) in-memory data structures corresponding to one of those. The form of the representation doesn't matter; it's just an indication of the sequence of steps that gets you from 'nothing' to whatever input string was provided.

But there's not really a concept of 'actions' to go with parsing. Parsing tells you how some string was constructed, or how it might have been constructed. What you do with that information is up to you.

In the example above, the parser's interest in a MulExpr is that it can produce a string containing the token '*'. The fact that mathematically two times three equals six doesn't come up.

Jtsummers•15h ago
> But there's not really a concept of 'actions' to go with parsing.

I'll appeal to authority here and quote from the Dragon Book:

>> Program fragments embedded within production bodies are called semantic actions.

In CS, particularly around compilers, it's an old concept.

thaumasiotes•15h ago
Well, quoting from the Dragon Book, page 8:

--- begin quote ---

The second phase of the compiler is syntax analysis or parsing. The parser uses the first components of the tokens produced by the lexical analyzer to create a tree-like intermediate representation that depicts the grammatical structure of the token stream. A typical representation is a syntax tree in which each interior node represents an operation and the children of the node represent the arguments of the operation. A syntax tree for the token stream (1.2) is shown as the output of the syntactic analyzer in Fig. 1.7.

This tree shows the order in which the operations in the assignment

    position = initial + rate * 60
are to be performed.

--- end quote ---

For context, token stream 1.2 is the sequence of lexer output "ID1 = ID2 + ID3 * 60" (7 tokens long), and Figure 1.7 shows that sequence being transformed by a parser into the tree "[= ID1 [+ ID2 [* ID3 60]]]".

Back to the textbook:

--- resume quote ---

The subsequent phases of the compiler use the grammatical structure to help analyze the source program and generate the target program.

--- end quote ---

I'm struggling to see where this disagrees with my earlier comment in even the most minor detail.

I notice that the quote you provide comes from a later chapter, "A Simple Syntax-Directed Translator", and that there is no suggestion there that parsing involves semantic actions. Rather, semantic actions annotated on to a finished parse tree are presented as a tool for generating translations.

Had you moved on to the chapter section immediately following the one you quoted from -- the one titled "Parsing" (page 60) -- you might have seen that it opens by saying this:

Parsing is the process of determining how a string of terminals can be generated by a grammar.

vinipsmaker•23h ago
Author here.

I was using Pratt parsing for almost a year in a macro-heavy language that I've been developing before finally sitting down on my chair to write my thoughts on Pratt parsing. Code is closed right now because I don't want LLMs training on my code before 0.1, but I'll open source it again later.

Happy to answer questions.

p1necone•22h ago
Tangential - on the 'use [] instead of <>' advice that you referenced in the post, I wish more languages would experiment with making types first class values that just get passed in regular () braces instead.

With erased static types, you can do arbitrary operations on types-as-values at compile time and just compile the static result into the runtime code, so it doesn't even cost anything at runtime to do this. Generic functions just turn into curried functions with the type params as their first param set.

My wip language uses '()' for 'always explicit params' and '{}' for 'can be implicit params', but both types and values can be passed into both (of course inference only makes sense for statically known types/values, so 'value' params in curly braces must be known at compile time and are treated like singleton types as well as their actual value in runtime code that uses them).

convolvatron•21h ago
in logic programming you can just use normal predicates for arbitrary types also.

my understanding is that this makes certain typing decisions undecidable? I agree though the the special parallel type language is a huge amount of added complexity and source of pain.

p1necone•21h ago
> special parallel type language is a huge amount of added complexity and source of pain

This was exactly what initially motivated me to write my own language. I was spending a bunch of time writing complex types in Typescript (for SQL query builder type stuff - essentially data schema -> TS type transformation) as what was essentially just a bunch of regular boolean logic and iteration that would be simple, but because the 'type' world uses a completely different syntax to the 'value' world it gets unreadable really fast.

My language was originally basically "what if Typescripts type system treated types as first class values and let you use standard syntax for interacting with them", but I've been finding various other rabbit holes to fall into along the way.

I'd be curious as to what is undecidable as a consequence of this, that sounds like something that's going to bite me in the ass at some point.

e12e•22h ago
I'd like to see a bit more about how compares to peg or something like ohm/ometa - am I correct assuming you pretty much give up the benefits of a readable grammar as documentation?

Maybe I just need to see something super simple like a calculator with expressions side by side to grasp the trade-off?

https://ohmjs.org/

vinipsmaker•17h ago
> am I correct assuming you pretty much give up the benefits of a readable grammar as documentation?

This is more of a question language than a parser language. If you have a language that requires a Turing-complete parser than you gave up on grammar. You can use Pratt to parse languages that have grammars.

The language that I'm implementing has procedural macros (hence the parser is Turing-complete). However it has some "structure" that I can port to a tree-sitter parser. The tree-sitter parser for my language won't be 100% accurate, but it'll capture the most important structures.

e12e•16h ago
> You can use Pratt to parse languages that have grammars

Of course. I meant more that unlike with yacc or ohm - you won't have a grammar as an intrinsic artifact - you would have to maintain it and the Pratt code in sync manually?

From looking a bit more at ohm - it generates peg - and is apparently equivalent to Pratt - so I guess one can have the cake and parse it too?

vinipsmaker•6h ago
I'm not familiar with ohm/ometa, but yes, if your language is "simple" it's possible to design a framework that generates both the Pratt parsers + a grammar so you don't need to manually keep them in sync. The complexity toll will be spent on this framework though. Plus it's unlikely that you're gonna explore traits that are exclusive to Pratt parsing so Pratt parsing would be just an implementation detail that you don't need to even bother to learn. The only upside of adopting Pratt parsing here is that Pratt parsing is fast.
ghusbands•21h ago
I've written a few Pratt parsers in my time. You don't need a lexer and you can have more parser states than are implied by 'nud' and 'led'. They can be incredibly extensible. For example, if you can parse the expression "{a:1, b:2}" then you might naturally have an Expr state (nud), a Between state (led) and a DictKey state; that might initially accept just an identifier and a colon before recursing into Expr state, but you could later add dynamic expressions, perhaps via a macro, to support [expr] as a key.
direwolf20•20h ago
Is this the same algorithm commonly known as "recursive descent" parsing and one of the oldest parsing algorithms? The array index example is just recursive descent and the expression parsing is recursive descent with abstracted operators instead of a parsing function per precedence level.

GCC used to use a type of parser generator but switched to recursive descent because the C language is just a little bit too dynamic to make it work well (EG typedefs). They specifically cited the advantage that because you are writing actual code to parse each production rule, you can break the conventions when needed.

YuechenLi•20h ago
Recursive descent and Pratt are two different but related parsing methods. Pratt is simpler and recursive descent is more explicit, but it's not an "either/or" thing since a parser can use both methods.
ogogmad•2h ago
I personally think that Pratt is a combination of recursive descent and precedence-climbing. Maybe even the combination? Those ideas are individually older than Pratt.
ogogmad•2h ago
"Pratt can't parse statements" - can't you treat ';' as an operator whose meaning is "then"? You can then read "DOTHIS(); DOTHAT()" as an expression.
vinipsmaker•2h ago
You need to forbid stuff such as:

  return return 1;
return itself isn't an expression (it's a statement) so it can't be used in places that expect expressions (such as the "argument" to return itself).

However extending Pratt parsing to handle this case is trivial: https://vinipsmaker.github.io/blog/article/statements-in-pra...

It's still an extension so not really pure Pratt parsing IMO (other practitioners of Pratt parsing disagree with me).

Jtsummers•14h ago
> I'm struggling to see where this disagrees with my earlier comment in even the most minor detail.

I was specifically responding to your earlier statement:

>> But there's not really a concept of 'actions' to go with parsing.

And pointing out that even the Dragon Book talks about associating actions with parsing by attaching them to production rules for the parser. You don't seem to accept the literal text of the book, and that's not something I can understand. But that's your choice.

thaumasiotes•14h ago
> [I was] pointing out that even the Dragon Book talks about associating actions with parsing by attaching them to production rules for the parser.

But that's just a false claim. The book does no such thing. The book, in the quote you pulled, talks about attaching semantic actions to finished parser output. At that point, the parser has already completed its work. It will never do anything again. The actions will neither affect it nor be seen by it. It's kind of like saying that illustration is part of writing a novel because an artist inserts their work into the finished manuscript.

Did you not notice that your quote doesn't contain any form of the word "parse"?

In the diagram displayed in section 1.2 ("The Structure of a Compiler"; the diagram is Figure 1.6 and also Figure 1.7), we see a statement processed in turn by a lexical analyzer, a syntax analyzer, a semantic analyzer, an intermediate code generator, a code optimizer, and finally a code generator.

The book informs us explicitly that a "syntax analyzer" and a "parser" are the same thing.

But those semantic actions are associated with the intermediate code generator two steps down the pipeline. They've got nothing to do with the parser.

nagaiaida•12h ago
for another perspective, raku lets you carry around reusable grammars, but each time you parse something with them you have the option to specify an actions class, the methods of which are invoked and passed the corresponding matches from the grammar (this lets you reuse the actual grammar matched against; sometimes you just want to typecheck cli arguments to main against a grammar and aren't actually going to use the match as anything other than an ornate boolean, but you might as well keep the actual grammar in sync between that nop/bool parser and another parser that actually builds something useful later).

so that's another community's vote for actions as something belonging to The Parser over and above the strict grammar by which that parsing is done.

nostrademons•3h ago
At least in imperative languages, a parser generator can execute arbitrary code, including side effects. There's nothing stopping that code from simply returning a data structure; indeed, most productions will have semantic actions that just say "return new Mul($1, $2)". However, the ability to add imperative side effects is what allows things like keeping a comment counter for nested comments; keeping a symbol table in case the same token might be a keyword or variable; handling indent levels in newline-sensitive languages; and so on.

Even in declarative languages like Haskell, your parser combinators will usually operate in a state monad so you can do things like this. True context-free grammars are rare; many real programmer convenience features have some level of context sensitivity, which is usually handled more easily by side effects in the semantic actions than by generalizing to a context-sensitive parser.

tyromaniac•19h ago
I believe zig does this
vinipsmaker•17h ago
My own language doesn't try to unify both worlds (values from "type" world and values elsewhere), but my macros can be configured to happen in the type world. From a macro for the type world, you can run normal code to return whatever type you want. It's not the same thing you're talking about, but it's another interesting thing you can do.
ogogmad•2h ago
Wondering how that compares to dependent types.
e12e•2m ago
I recommend having a quick look at https://ohmjs.org/ just because it's such a joyful project that present parsing in such a simple fashion.

As to benefits of Pratt, as best I can tell ohm is fast and lean - but handwriting a Pratt parser will be faster and leaner (esp lighter memory requirements).

Ohm does however handle left recursion, which could be considered a major feature of Pratt.