Edit: i am very wrong. i also had no clue it was meant as a joke. I just figured someone had a use case.
[InterceptsLocation("/Users/khalidabuhakmeh/RiderProjects/ConsoleApp12/ConsoleApp12/Program.cs", line: 3, character: 3)]
they added a language feature which is sensitive to precise line/character offsets in your source code, so the tiniest change to the source code invalidates your code…
I’m speechless. Whatever they are aiming to achieve here, surely there is a more elegant, less ugly way
You can read about how they work here: https://github.com/dotnet/roslyn/blob/main/docs/features/inc...
Basically they get the files (and ambient metadata) that are part of the compilation, filter to the parts it depends on, transforms to a in-mem representation of the data needed for the code generation, and then finally adds new files to the compilation. Since they can only add new files they cannot e.g. add code to be executed before or after user code is executed like with AOP. Interceptors are a solution to that problem.
I don't think there's much that's scary about generating source code in general. If it's self-contained and you have to actually call the generated code to use it, it's not really much different than any other code. But the idea of having code A change the behavior of code B is what's horrifying, regardless of whether code A is generated or not. If I'm reading code B I want to be able to reason about what I see without having to worry about some spooky action at a distance coming from somewhere else.
Things are constantly doing this. Frameworks use reflection or markup or all other kinds of things that count as magic if you don't bother to understand what's going on.
It usually only works for files with fixed length records, and reads the records in reverse sequential order, but the bits/bytes within the record in forward order. In theory, it could be made to work for variable-length record files as well, but I’m not aware of any implementation which does.
The original motivation was to support reading magnetic tapes backwards, so you could write data to a tape, then read it back in without a time-consuming rewind - which was important in the early years of computing, when memory and disk sizes were so small, magnetic tapes were commonly used for temporary storage / work files.
Most tape drives nowadays don’t support reading tapes backwards, even though there are standard SCSI commands defined to do so-but I believe IBM 3592 tape drive series still supports this, as do virtual tape servers targeted at mainframes. The drive physically reads the bits off the tape in reverse order, but then reverses the data before sending it to the computer.
I’m not aware of any other language which supports reading files backwards, other than COBOL. Well, mainframe assembly does, and you can invoke the relevant mainframe IO calls from languages such as C-but COBOL is the only language I know of which has it as a language feature.
No matter what - a consideration befitting the subject.
No, not at all. Regexes describe regular languages, but that doesn't mean that their own syntax needs to be a regular language.
Eg many regexes allow parens, so you can have both 'ab|c' and 'a(b|c)'. But if you require your parens to be balanced, that's no longer a regular language.
From a certain pedantic perspective, it still is a regular language, since there is a finite (implementation dependent) upper bound on nesting depth, and balanced parens with a finite upper bound of nesting is regular.
OTOH, one can appeal to the linguistic distinction between competence and performance, and say that regexes are irregular in pure competence, even though all (practically usable) languages turn out to be regular once we constrain them by performance as well.
That's a silly point, because it makes all languages trivially finite, even monsters like C++. (Finity is even more of a restriction than being regular.)
What is silly is not finitism (or ultrafinitism), but trying to apply concepts like 'regular language' or 'context free language' without modification in such a setting.
I suspect you should be able to suitably alter most of these concepts to make sense in the strictly finite setting. But they are going to become a lot more complicated.
For a similar flavour, you can also check out the differences and similarities between differential equations and difference equations.
Let’s say a compiler has a (very plausible) implementation restriction that the source code of a program can be no longer than 2^32 bytes. Then, even if the abstract grammar is (e.g.) an unbounded context-free language, the actually accepted language has a finite upper bound on length-and it is well-known that applying a length constraint turns a context-free language into a regular language. This doesn’t require us to “to suitably alter most of these concepts to make sense in the strictly finite setting”, because formal language theory in an infinite setting permits constraining languages to be finite, and we have a very good understanding of what that the results of that are - this doesn’t require any new or different math, just applying the same math.
Now, it is true that, we will say that certain algorithms only work for regular languages, not context-free - but once we impose a finite bound on length, those algorithms do actually work in principle. In practice, of course, they are likely impractically slow - but the formalism we are talking about (the Chomsky hierarchy) is based on computability (can it answer the question in a finite but unbounded amount of time), not asymptotic computational complexity (nor real-world performance, which isn’t the same thing, as e.g. galactic algorithms demonstrate)
TIL: https://en.wikipedia.org/wiki/Pumping_lemma_for_regular_lang...
As someone who has only recently learned the formal definition of regular language ( Thanks to https://www.youtube.com/watch?v=9syvZr-9xwk as mentioned on this board recently ), I'm interested in the formal proof for this?
It feels intuitively true, but I haven't finished the course yet and therefore haven't come across it and can't yet reason well about it.
Is the problem then in trying to "encode" whether brackets are balanced, that we would need infinite "levels" of how many more brackets we've opened rather than closed, (i.e a stack) and that violates the finite nature of finite automota?
So I've googled it now, and I've found the result is due to the "Pumping lemma": https://en.wikipedia.org/wiki/Pumping_lemma_for_regular_lang...
Assume for purpose of contraction that our language L of balanced parentheses is regular. Then L has pumping length p ≥ 1 such that every w ∈ L where |w| ≥ p can be decomposed w = xyz where |y| ≥ 1 and |xy| ≤ p. It is called the pumping lemma because we can “pump” the non-empty substring y either up or down, i.e., xy⁰z = xz and xyⁿz are also all in L. In formal languages, exponentiation notates character or string repetition.
We don’t know p and may not cherry pick it. Pretend you are playing a game against an adversary who gets to select arbitrary p ≥ 1, and then you use that p to choose w ∈ L to spoil the regular language party. With the language of balanced parentheses, no matter what p the adversary selects, you can force the leading xy prefix to contain all left-parentheses by choosing w = (ᵖ)ᵖ. The substring y cannot be empty, so pumping down gives xy⁰z = (ᵖ⁻ᵏ)ᵖ belongs to L, a contradiction.
One of my favorite test questions for automata theory classes is to have ChatGPT attempt a proof that a language is not regular and then have students correct the proof. You linked to a lecture by Michael Sipser, and the undergraduate version of the course I teach uses his excellent textbook.
https://www.amazon.com/Introduction-Theory-Computation-Micha...
His lecture on the pumping lemma for regular languages is
We've seen you can't detect if there is a balanced number of 0's and 1's in a string of 0's and 1's with a FA/Regular expression, howver you CAN detect if there are a balanced number of "01" and "10" substrings in a string of 0's and 1's.
Proving that 01 and 10 balancing was left as an exercise to the reader, which I will try to work through here, hopefully I've not made a mistake. My intuition here says that it is equivalent to checking whether it starts and ends with the same symbol, because 01 leaves you having last read a 1, and therefore then requires a "10" substring to get back to having last read a 0, and vice-versa.
The FA I believe therefore can be constructed with transitions
Q0 -> epsilon -> Q1
Q0 -> epsilon -> Q3
Q1 -> 0 -> Q2
Q2 -> 1 -> Q1
Q1 -> epsilon -> Q5
Q3 -> 1 -> Q4
Q4 -> 0 -> Q3
Q3 -> epsilon -> Q5
( Starting Q0, Accepting Q5 )
Or presented differently, (0(0U1)0) U (1(0U1)1) U epsilonBecause IIRC, if you don't put enough or too much "PLEASE", the compiler won't compile.
But this line at least outputs the error message:
https://github.com/rottytooth/INTERCAL72/blob/f94e0c8eaaf134...
edit: probably slightly complicated :D
but this makes me wonder if it's related to the source mutliplying some variable/register value by 3 in the error line:
> You will need at least 3 lines of code, with one PLEASE statement among them.
(from README)
Just searching "PLEASE" also shows increments and a comparison with a number written in hex... so it should be easy to figure out the intercal "please" halting problem instance, right? :)
entrepy123•1d ago