> 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.
> 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?
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.
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.
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.
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.
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).
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.
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.
> 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.
I think they are essentially dependent types, I'm just coming at it from the "idiot with a laptop who hasn't read up on type theory" direction, and trying to fit everything into the "but what if it was just another place the user can write regular code" hole.
Yeah zig was definitely a big inspiration - implementing type system stuff as comptime code execution gives you a bunch for free that would otherwise be pretty tricky to implement.
You guys should take a look on Blend2D as well. Blend2D never hits the news, but it has been exploring similar topics for years. His work is so underappreciated.
My text. There I explain the basics on how to properly sandbox (discretionary privilege dropping) programs written using Emilua (the ASIO-based execution engine for Lua programs).
I've tried my best to make the text hopefully useful to programmers from any language so the few Lua samples in the article are only there to illustrate the concepts. Some of the topics found in the article:
* UNIX basics focusing on Linux and FreeBSD.
* General sandboxing concepts and how to apply them in Linux and FreeBSD.
* The actor model and capability-based security (and how to combine them).
* Oblivious sandboxing to run/reuse unmodified code within sandboxes.
* Threat models.
* Seccomp, Landlock, Capsicum, ...
* A few guidelines for the C programmer.
I'm the author of this framework. Version 0.5 is under development right now and there are a few things I'd like to point out about the project's current state:
• I was developing an example Telegram client where components would run on sandboxes to dodge some attacks (such as the recent bug found on WebP that is used for Telegram stickers).
• Creating sandboxes using Linux namespaces (available from emilua 0.4) proved to be too cumbersome.
• I'm developing support for new sandboxing technologies that I hope to be easier to use.
•• Landlock (Linux): already usable from branch 0.5.
•• Capsicum (FreeBSD): in development.
• The lack of an (emilua) ecosystem is a real problem for new (emilua) programs, so I'll develop something like <http://lua-users.org/wiki/LunaticPython> that also translates Python promises from Python3's asyncio to fiber suspension/wakeup in Lua land and customizes the Python3's asyncio event loop to use the same Boost.Asio instances. This support will depend on <https://lwn.net/Articles/941090/> as there would be 1 Python subinterpreter for each Lua VM.
• I plan to do something similar to the previous item to NodeJS as well.
• Version 0.5 is mostly ready and I expect a release by December. The focus of this release is refactoring stuff to make it easier to develop a package manager for emilua.
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).
reply