Specification of an esoteric "language" to be interpreted by multiple languages within a single file. This is a 7-languages polyglot implementing (7 times) this "language".
Admittedly, that is a better approach to the same problem (adds 1 language, makes it Turing complete and way simpler)... Anyway, this is not supposed to be an optimal answer, it was interesting to think about...
Repository
Folders
.
├── build/ # the neededs by and results of 'compilation'
│ └── template # template with language markers
├── editor/ # a quick-and-dirty polyglot editor
├── examples/ # some examples (code only)
├── src/ # the various interpreters
│ ├── xyz # each language is in its folder
│ . ├── config # configuration for this language
│ . ├── main.xyz # a readable and testable version
│ └── part.xyz # an inlineable version
├── test/ # test folder
│ └── scaffold.txt # a suite of tests
├── please # automation script (see `$ python please`)
└── README.md # this file
Under src/
are the files used to develop, test and assemble the single
interpreters. Folders with name ending with .no
are ignored at build time.
language | extension |
---|---|
JavaScript | .js |
Lua | .lua |
PHP | .php |
PowerShell | .ps1 |
Python | .py |
Ruby | .rb |
shell | .sh |
Building
$ python please build
The template presents multiple %xyz%
markers where xyz
is a language's
"extension" (dot not included) from the table above.
The building process basically inlines the corresponding part.xyz
found in
the relevant src/xyz/
.
Note that the template should also contains a %heretest%
marker (see
integrated).
Testing
2 kind of testing: isolated and integrated.
Note to self: testing adds the trailing
','
automatically when replacing the %heretest% marker.
Isolated
$ python please test_iso
# or specifying which to test:
$ python please test_iso,js,lua,py
Using the target's main.xyz
, isolated test inlines the test to perform at
the %heretest%
marker. The whole line containing the marker is replaced
with the snippet to be tested.
The test suite used is in the test/scaffold.txt
file. It contains a list of
test = result
where both test
and result
are program. The results of
executing both are compared and expected to be identical.
Integrated
$ python please test_int
The template itself also contains a %heretest%
marker.
The integrated test is made on the build interpreters and perform the same test suite as for every included interpreter.
Language
Pyjama
Grammar
something ::= identifier '(' [ something {',' something} ] ')' | literal
literal ::= ('"' ... '"') | (['-'] 1-9 {0-9} | 0)
identifier ::= a-z {a-z} '_'
program ::= something ','
Essentially, a program consists of what resembles (for most C-like languages) a single suite of nested call statements, only string and integer literals:
does_(
s_("computes the factorial of the first element from the stack"),
hold_(v_(prd_(span_(1, l_(drop_(0, 1)), 1))))
)
Note that a program always ends with a single trailing
','
. It is automatically added for the testing, but is essential for an actual program.
String literals cannot span multiple lines and cannot contain escape sequences
(thus cannot contain a '"'
, a '\n'
nor a '\n'
).
Integer literals are only allowed in decimal (base 10). No operators (except
unary minus), no floating point, no starting with a 0 (except 0
by itself).
Comments
There is no specified way to have proper comments. A solution is to use s_
and discard its result (see does_
).
Types
There are only 2 defined types: the integer and list of integer (or vector).
0 is falsy and any other value are truthy. The standard value for true is 1
(as in no_(no_(42))
yields exactly 1).
A vector is a list of integers of known and fixed size.
Miscelianous
Program Delimitation
The program is contain within the interpreters itself, from the first line
following the line program_(
to right before the line voila_()
.
Note: this may requires the additional definition of:
program_ ( RESULT , voila )
executed last, receives the program's resultvoila_ ( )
executed after the program, but beforeprogram_
itself... none of which is expected to do anything in particular.
Backing Stack
The execution is backed by a stack accessed through hold_
and drop_
.
Input and Output
None, left to implementation. An idea for emulating standard I/O is using the stack. Just dumping the result of the program as a space separated list is often enough (behavior of the default implementation).
Operations
no_ ( LIST )
The unary logical not applied to the provided vector. The result is thus a vector of same length containing only 1s and 0s.
cmp_ add_ sub_ mul_ div_ mod_ pow_ ( LEFTS , RIGHTS )
The usual mathematical binary operators, applied to vectors of same length.
cmp_
returns -1 when the element of LEFTS
is less than, 1 when greater
than and 0 when equal to the corresponding element of RIGHTS
.
sum_ prd_ max_ min_ ( LIST )
Folds the provided vector, returning a single number (not in a vector itself).
Given an empty vector, sum_
returns 0, prd_
returns 1 and the result of
min_
and max_
are unspecified.
v_ ( ...elements )
Builds a vector from a sequence of numbers.
s_ ( string )
Builds a vector from a string literal. The standard behavior is to use the ASCII code of the character, outside of which set the results are unspecified.
l_ ( LIST )
Gets the last element of the provided vector, returning a single number (not in a vector itself)
span_ ( start , stop , step )
Builds a vector starting from start
included, stepping by step
until
stop
(excluded). step
should be non-zero and of the same sign as
stop-start
.
copy_ ( LIST , count )
Builds a vector by repeating the sequence from LIST
count
times. count
should not be negative.
pick_ ( LIST , INDICES )
Builds a vector taking the values from LIST
with the index from INDICES
:
LIST
: 1 2 3 4INDICES
: 2 1 2 3 3- result: 3 2 3 4 4
Indices are 0-based, the result is always the size of INDICES
. The indices
should never be out-of-range (equal to or greater than the length of LIST
)
or negatives.
stow_ ( LIST , INDICES , VALUES )
Returns a new list build from LIST
with the values at indices INDICES
replaced with the corresponding values from VALUES
:
LIST
: 1 2 3 4INDICES
: 2 1 2 3 3VALUES
: 7 3 5 3 7- result: 1 3 5 7
Indices are 0-based, the result is always the size of LIST
. The indices
should never be out-of-range (equal to or greater than the length of LIST
)
or negatives and INDICES
and VALUES
should be the same length. A same
index may appear multiple times, the last one (in the vector INDICES
) and
its corresponding value prevails.
find_ ( LIST , value )
Builds a vector consisting of the indices at which value
is found in LIST
.
The result is always sorted from first to last occurrence (smallest index
first, 0-based).
size_ ( LIST )
Gets the size (or length) of the provided vector, returning a single number (not in a vector itself).
hold_ ( LIST )
Holds (pushes to the backing stack) the values from the vector LIST
in order
(starting at index 0).
drop_ ( offset , count )
Retrieves and remove (pops from the backing stack) a vector of length count
,
leaving the offset
first values untouched:
- before: 1 2 3 4
drop_(2, 1)
- result: 2 3
- after: 1 4
does_ ( ...LISTS )
Takes a varying amount of vectors and returns the last one. The rest is discared, but still executed. This is used to construct a sequence of expression:
does_(
something_first,
something_second,
...
something_returned
)
make sure to find yourself an editor showing matching parenthesis though :3