443 research outputs found
Trends in compiler construction
Compiler writing techniques have undergone a number of major revisions over the past
forty years. The introduction of object-oriented design and implementation techniques promises to improve the quality of compilers, while malting large-scale compiler development more manageable.
In this paper, we will show that a new way of thinking of a compiler's structure is required to achieve complete object-orientation. This new view on compiling can lead to alternative formulations of parsing and code generation. In practice, the object-oriented formulations have not only proven to be highly efficient, but they have also been particularly easy to teach to students
An Object-oriented LL(1) parser generator
This paper describes oops, an object-oriented parser generator implemented in Java [1]. Oops takes a grammar written in EBNF, checks that it is indeed LL(1), i.e., suitable for recursive descent parsing, and produces a parser as a set of serialized objects. A scanner must be provided and classes satisfying certain interfaces can be implemented which the parser uses to build parse trees. The paper discusses the ideas behind oops — which are not specific to an implementation in Java — and shows the advantages of an object-oriented approach to grammar verification and parsing
Jparsec - a parser combinator for Javascript
Parser combinators have been a popular parsing approach in recent years. Compared with traditional parsers, a parser combinator has both readability and maintenance advantages.
This project aims to construct a lightweight parser construct library for Javascript called Jparsec. Based on the modular nature of a parser combinator, the implementation uses higher-order functions. JavaScript provides a friendly and simple way to use higher-order functions, so the main construction method of this project will use JavaScript\u27s lambda functions. In practical applications, a parser combinator is mainly used as a tool, such as parsing JSON files.
In order to verify the utility of parser combinators, this project uses a parser combinator to parse a partial Lua grammar. Lua is a widely used programming language, serving as a good test case for my parser combinator
Functional programming, program transformations and compiler construction
Dit proefschrift handelt over het ontwerp van de compilergenerator Elegant. Een compiler generator is een computer programma dat vanuit een speci??catie een compiler kan genereren. Een compiler is een computer programma dat een gestructureerde invoertekst kan vertalen in een uitvoertekst. Een compiler generator is zelf een compiler welke de speci??catie vertaalt in de programmatekst van de gegenereerde compiler. Dit heeft het mogelijk gemaakt om Elegant met zichzelf te genereren. Van een compilergenerator wordt verlangd dat deze een krachtig speci??catie formalisme vertaalt in een eÆci??ent programma, een eis waar Elegant aan voldoet. Een compiler bestaat uit een aantal onderdelen, te weten een scanner, een parser, een attribuutevaluator, een optimalisator en een codegenerator. Deze onderdelen kunnen door het Elegant systeem geneneerd worden, ieder uit een aparte speci??catie, met uitzondering van de parser en attribuutevaluator, welke gezamenlijk worden beschreven in de vorm van een zogenaamde attribuutgrammatica. De scanner wordt gegenereerd met behulp van een scannergenerator en heeft tot taak de invoertekst te splitsen in een rij symbolen. Deze rij symbolen kan vervolgens ontleed worden door een parser. Daarna berekent de attribuutevaluator eigenschappen van de invoertekst in de vorm van zogenaamde attributen. De attributenwaarden vormen een datastructuur. De vorm van deze datastructuur wordt gede??nieerd met behulp van typeringsregels in de Elegant programmeertaal. De optimalisator en codegenerator voeren operaties op deze datastructuur uit welke eveneens beschreven worden in de Elegant programmeertaal. Dit proefschrift beschrijft de invloed die functionele programmeertalen hebben gehad op het ontwerp van Elegant. Functionele talen zijn programmeertalen met als belangrijkste eigenschap dat functies een centrale rol vervullen. Functies kunnen worden samengesteld tot nieuwe functies, ze kunnen worden doorgegeven aan functies en worden opgeleverd als functieresultaat. Daarnaast staan functionele talen niet toe dat de waarde van een variable wordt gewijzigd, het zogenaamde nevene??ect, in tegenstelling tot imperatieve talen die zo'n nevene??ect wel toestaan. Deze laatste beperking maakt het mogelijk om met behulp van algebra??ische regels een functioneel programma te herschrijven in een ander functioneel programma met dezelfde betekenis. Dit herschrijfproces wordt ook wel progammatransformatie genoemd. De invloed van functionele talen op Elegant omvat: ?? Het beschrijven van ontleedalgorithmen als functionele programma's. Traditioneel worden ontleedalgorithmen beschreven met behulp van de theorie van stapelautomaten. In hoofdstuk 3 wordt aangetoond dat deze theorie niet nodig is. Met behulp van programmatransformaties zijn vele uit de literauur bekende ontleedalgorithmen af te leiden en worden ook nieuwe ontleedalgorithmen gevonden. Deze aanpak maakt het bovendien mogelijk om de vele verschillende ontleedalgorithmen met elkaar te combineren. ?? De evaluatie van attributen volgens de regels van een attribuutgrammatica blijkt eveneens goed te kunnen worden beschreven met behulp van functionele talen. Traditioneel bouwt een ontleedalgorithme tijdens het ontleden een zogenaamde ontleedboom op. Deze ontleedboom beschrijft de structuur van de invoertekst. Daarna wordt deze ontleedboom geanalyseerd en worden eigenschappen ervan in de vorm van attributen berekend. In hoofdstuk 4 van het proefschrift wordt aangetoond dat het niet nodig is de ontleedboom te construeren. In plaats daarvan is het mogelijk om tijdens het ontleden functies die attributen kunnen berekenen samen te stellen tot nieuwe functies. Uiteindelijk wordt er zo ??e??en functie geconstrueerd voor een gehele invoertekst. Deze functie wordt vervolgens gebruikt om de attribuutwaarden te berekenen. Voor de uitvoering van deze functie is het noodzakelijk gebruik te maken van zogenaamde "luie evaluatie". Dit is een mechanisme dat attribuutwaarden slechts dan berekent wanneer deze werkelijk noodzakelijk zijn. Dit verklaart de naam Elegant, welke een acroniem is voor "Exploiting Lazy Evaluation for the Grammar Attributes of Non- Terminals". ?? Scanners worden traditioneel gespeci??ceerd met behulp van zogenaamde reguliere expressies. Deze reguliere expressies kunnen worden afgebeeld op een eindige automaat. Met behulp van deze automaat kan de invoertekst worden geanalyseerd en gesplitst in symbolen. In hoofdstuk 5 wordt uiteengezet hoe functionele talen het mogelijk maken om scanneralgorithmen te construeren zonder gebruik te maken van automatentheorie. Door een reguliere expressie af te beelden op een functie en de functies voor de onderdelen van samengestelde reguliere expressies samen te stellen tot nieuwe functies kan een scannerfunctie geconstrueerd worden. Door gebruik te maken van programmatransformaties kan deze scanner deterministisch worden gemaakt en minimaal worden gehouden. ?? Het typeringssysteem van Elegant wordt beschreven in hoodstuk 6 en vormt een combinatie van systemen die in functionele en imperatieve talen worden gevonden. Functionele typeringssystemen omvatten typen welke bestaan uit een aantal varianten. Elk van deze varianten bestaat uit een aantal waarden. Bij een dergelijk typeringssysteem wordt een functie gede??ni??eerd door middel van een aantal deeelfuncties. Elke deelfunctie kan met behulp van zogenaamde patronen beschrijven voor welke van de varianten hij gede??ni??eerd is. Het blijkt dat imperatieve typesystemen welke subtypering mogelijk maken een generalisatie zijn van functionele typesystemen. In deze generalisatie kan een patroon worden opgevat als een subtype en een deelfunctie als een parti??ele functie. Het Elegant typesystemen maakt deze vorm van typering en functiebeschrijving mogelijk. Bij toepassing van een functie wordt de bijbehorende deelfunctie geselecteerd door de patronen te passen met de waarden van de actuele functieargumenten. In dit proefschrift wordt een eÆci??ent algorithme voor dit patroonpassen met behulp van programmatransformaties afgeleid uit de de??nitie van patronen. Het Elegant typeringssystemen bevat ook typen voor de modellering van luie evaluatie. De aanwezigheid van nevene??ekten maakt het mogelijk om drie verschillende luie typen te onderscheiden, welke verschillen in de wijze waarop de waarde van een lui object stabiliseert. ?? In hoofdstuk 7 wordt aangetoond dat de regels uit een attribuutgrammatica ook kunnen worden gebruikt om eigenschappen van een datastructuur te berekenen in plaats van eigenschappen van een invoertekst. Elegant biedt de mogelijkheid om zulke attribuutregels te gebruiken voor dit doel. ?? In hoofdstuk 8 tenslotte worden de Elegant programmeertaal en de eÆci??entie van de Elegant vertaler en door Elegant gegenereerde vertalers ge??evalueerd. Het blijkt dat de imperatieve Elegant programmeertaal dankzij abstractie mechanismen uit functionele talen een zeer rijke en krachtige taal is. Daarnaast zijn zowel Elegant zelf als de door Elegant gegenereerde vertalers van hoge eÆci??entie en blijken geschikt voor het maken van compilers voor professionele toepassingen
OrdinalFix: Fixing Compilation Errors via Shortest-Path CFL Reachability
The development of correct and efficient software can be hindered by
compilation errors, which must be fixed to ensure the code's syntactic
correctness and program language constraints. Neural network-based approaches
have been used to tackle this problem, but they lack guarantees of output
correctness and can require an unlimited number of modifications. Fixing
compilation errors within a given number of modifications is a challenging
task. We demonstrate that finding the minimum number of modifications to fix a
compilation error is NP-hard. To address compilation error fixing problem, we
propose OrdinalFix, a complete algorithm based on shortest-path CFL
(context-free language) reachability with attribute checking that is guaranteed
to output a program with the minimum number of modifications required.
Specifically, OrdinalFix searches possible fixes from the smallest to the
largest number of modifications. By incorporating merged attribute checking to
enhance efficiency, the time complexity of OrdinalFix is acceptable for
application. We evaluate OrdinalFix on two datasets and demonstrate its ability
to fix compilation errors within reasonable time limit. Comparing with existing
approaches, OrdinalFix achieves a success rate of 83.5%, surpassing all
existing approaches (71.7%).Comment: Accepted by ASE 202
LLLR Parsing: a Combination of LL and LR Parsing
A new parsing method called LLLR parsing is defined and a method for producing LLLR parsers is described. An LLLR parser uses an LL parser as its backbone and parses as much of its input string using LL parsing as possible. To resolve LL conflicts it triggers small embedded LR parsers. An embedded LR parser starts parsing the remaining input and once the LL conflict is resolved, the LR parser produces the left parse of the substring it has just parsed and passes the control back to the backbone LL parser. The LLLR(k) parser can be constructed for any LR(k) grammar. It produces the left parse of the input string without any backtracking and, if used for a syntax-directed translation, it evaluates semantic actions using the top-down strategy just like the canonical LL(k) parser. An LLLR(k) parser is appropriate for grammars where the LL(k) conflicting nonterminals either appear relatively close to the bottom of the derivation trees or produce short substrings. In such cases an LLLR parser can perform a significantly better error recovery than an LR parser since the most part of the input string is parsed with the backbone LL parser. LLLR parsing is similar to LL(^*) parsing except that it (a) uses LR(k) parsers instead of finite automata to resolve the LL(k) conflicts and (b) does not perform any backtracking
Practical LR Parser Generation
Parsing is a fundamental building block in modern compilers, and for
industrial programming languages, it is a surprisingly involved task. There are
known approaches to generate parsers automatically, but the prevailing
consensus is that automatic parser generation is not practical for real
programming languages: LR/LALR parsers are considered to be far too restrictive
in the grammars they support, and LR parsers are often considered too
inefficient in practice. As a result, virtually all modern languages use
recursive-descent parsers written by hand, a lengthy and error-prone process
that dramatically increases the barrier to new programming language
development.
In this work we demonstrate that, contrary to the prevailing consensus, we
can have the best of both worlds: for a very general, practical class of
grammars -- a strict superset of Knuth's canonical LR -- we can generate
parsers automatically, and the resulting parser code, as well as the generation
procedure itself, is highly efficient. This advance relies on several new
ideas, including novel automata optimization procedures; a new grammar
transformation ("CPS"); per-symbol attributes; recursive-descent actions; and
an extension of canonical LR parsing, which we refer to as XLR, which endows
shift/reduce parsers with the power of bounded nondeterministic choice.
With these ingredients, we can automatically generate efficient parsers for
virtually all programming languages that are intuitively easy to parse -- a
claim we support experimentally, by implementing the new algorithms in a new
software tool called langcc, and running them on syntax specifications for
Golang 1.17.8 and Python 3.9.12. The tool handles both languages automatically,
and the generated code, when run on standard codebases, is 1.2x faster than the
corresponding hand-written parser for Golang, and 4.3x faster than the CPython
parser, respectively
- …