Phantom Syntax
Notice
This feature is highly experimental and is not recommended for use yet.
Phantom syntax is a comment based declaration to declare custom syntaxes. It uses comments and .h.coffee
or coffee header files to declare a comment-based declaration syntax.
Example:
It mostly works like the C
's declare function.
#declare* IDENTIFIER "func" = "function";
Declaration
To declare a lookup, you first need the declare
keyword and then you need to know what token you are replacing. A sign? An identifier? A string?
Once you determine that, you can go ahead and:
#declare IDENTIFIER "say.hello" = print("Hello")
Showcase
say.hello
# will be converted to
print("Hello")
Tokens
All the token types:
IDENTIFIER
: can also be used askey
COMMENT
TRIPLE_STRING
STRING
REGEX
WHITESPACE
OTHER
Declaration Scopes
When you declare a syntax, you are declaring it only to the local file, meaning that it'll only work on the same file that it's written on. To pass that, you can use declare*
.
# Public Declare
#declare* key "myKey" = replacementValue;
No we can use this syntax in other files too.
Including other files
Unlike normal exports, phantom script is not dynamically imported. So it has it's own way of importing.
#include ./declarations.coffee
# OR
#include *./declarations.coffee
# OR
#include app.package
#include app.package/some.coffee
Why *
?
include
appends the whole file into your current file as a string, in order to ignore the imported file and only extract phantom scripts from it, use *
at the start of your file.
Header coffee files
Header coffee files are normal coffee files but with the additional functionality of being auto imported.
# this is a header file
#declare* key "myKey" = myValue;
print 'hi' # won't be executed as only comments work
Example:
# This will look for myFile.h.coffee in addition
myFile = imp '#./myFile.coffee'
# or
import myModule from "#./myFile"
This way, we are capable of importing modules with the addition of including the headers for this file if exists.
Notice
This only works with both imp
and inc
, meaning it will work with JS import syntax.
Presets
Declaration presets are default setups for functionalities to replace tokens. So instead of just replacing a token. You can pass a function:
#declare* key "myToken" = ${
# return '"A string replacement"'
#};
This is a function declaration. whenever an IDENTIFIER
with calue myToken
is found. This function runs, and it's returns goes in place of myToken
.
If you wish to make something here, these are the variables in this scope:
token: Current Token
value: string
index: number
- Current Index
tokens: Token[]
- An array of tokens
code: string
- Current code
hooks: Hooks
- A hook
setIndex: Function
- To skip to a token
Hooks
Hooks are items that get added at a certain token, like for example if you want to add a value at a future token, you can use hooks.
hooks.push({
index: number,
value: string
});
Aliases
Aliases are quick ways to replace a token to another.
#alias ns = namespace
using ns ...
#alias bracket = (
myFunc bracket '') # translates to myFunc ('')
Declarators
A declarator is a function that can be used to declare a variable.
#declare* key "=myDeclarator" = myDeclarator;
This will define a test case that tests for anything with myDeclarator variableName = ...
and converts it to variableName = myDeclarator ...
.
Example:
#declare* key "=myClass" = createMyClass;
myClass myInstance = 'my value'
This will translate to:
myInstance = createMyClass 'my value'
Definition Declarators
Some declarators can be non assignable. Like this:
myClass myInstance
So to achieve this, we can use:
#declare* key "=myClass*" = createMyClass;
Now it will translate to:
myInstance = createMyClass()
Passing parameters in Declarators
You can pass parameters for declarators to define them.
myClass('some value') myInstance = 'my value'
Which translates to:
myInstance = createMyClass('my value', 'some value')
Constructor Declarators
You can make classes into declarators and force it to add a new
on translation, like:
#declare* key "=MyClass!" = MyClass;
Now this:
MyClass instance = 'some-arg'
Will be translated to:
instance = new MyClass('some-arg')
You can also use it as a definition declarator with:
#declare* key "=MyClass!*" = MyClass;
Remember to use the
*
at last. The order is important.
Known Problems
- Spaces are required during declaration when using a declarator
- Strings don't work well with declare
- The entire feature can make unexpected syntax errors.