This commit is contained in:
Abdussamed 2024-04-03 04:50:41 +03:00
parent fba60407e4
commit 833c7182de
3 changed files with 895 additions and 0 deletions

525
Hemex.js Normal file
View File

@ -0,0 +1,525 @@
export default function Hemex()
{
};
Hemex.EOL = "\n";
/**
* Hemex variable white space chars
* @type {Number[]}
*/
Hemex.WhiteSpace = [
9,10,11,12,13,32,133
];
/**
* Current cursor position
* @type {Number}
*/
Hemex.prototype.offset = 0;
/**
* Mapping offset points
* @type {Number[]}
*/
Hemex.prototype.offsetMap = [];
Hemex.prototype.beginPosition = function(){
this.offsetMap.push(
this.getLastPosition()
)
}
/**
* Adding current position to offset map
*/
Hemex.prototype.acceptPosition = function(){
let t = this.offsetMap.pop();
this.setLastPosition(t)
}
/**
* Get text range current and parent offsets
* @returns {[Number,Number]}
*/
Hemex.prototype.positionRange = function(){
let len = this.offsetMap.length;
if(len == 0)
{
return [0,this.offset]
}else if(len == 1){
return [this.offset,this.offsetMap[len - 1]]
}else{
return [this.offsetMap[len - 2],this.offsetMap[len - 1]]
}
}
/**
* Get text range between current offset and parent offset
* @returns {String}
*/
Hemex.prototype.getPositionRange = function(){
let u = this.positionRange();
return this.text.slice(u[0],u[1])
}
/**
* Cancel current position and return to parent offset
*/
Hemex.prototype.rejectPosition = function(){
this.offsetMap.pop()
}
/**
* Get current layer of position from last offset of map
* @returns {Number}
*/
Hemex.prototype.getLastPosition = function(){
return this.offsetMap.length == 0 ? this.offset : this.offsetMap.slice(-1)[0]
}
/**
* Set last position offset from offset map last layer
* @param {Number} n
*/
Hemex.prototype.setLastPosition = function(n){
if(this.offsetMap.length == 0)
this.offset = n
else this.offsetMap[this.offsetMap.length - 1] = n
}
/**
* Get current layer of position from last offset of map
* Some as getLastPosition()
* @returns {Number}
*/
Hemex.prototype.getOffset = function(){
return this.getLastPosition()
}
/**
* Set last position offset from offset map last layer and return it value
* @param {Number} n
* @returns {Number}
*/
Hemex.prototype.setOffset = function(n){
this.setLastPosition(n);
return this.getLastPosition()
}
/**
* Get text length
* @type {Number}
*/
Hemex.prototype.length = 0;
/**
* Hemex lexing data
* @type {String}
*/
Hemex.prototype.text = "";
/**
* set lexing data
* @param {String} text
* @returns {void}
*/
Hemex.prototype.setText = function(text){
this.offset = 0;
this.length = text.length;
this.offsetMap = [];
this.text = text;
}
/**
* get lexing all data
* @returns {String}
*/
Hemex.prototype.getText = function(){
return this.text;
}
/**
* Get one character from cursor
* @param {Number} n
* @returns {String}
*/
Hemex.prototype.getChar = function(n){
return this.text.charAt(n?this.getOffset()+n:this.getOffset())
}
/**
* Boolean
* @param {Number} n
* @returns {String}
*/
Hemex.prototype.isChar = function(b){
return this.getChar() == b
}
/**
* Dump all data from cursor position to end of char
* @param {Number} n
*/
Hemex.prototype.dump = function(n){
return this.text.slice(this.getOffset(),this.getOffset()+n)
}
/**
* Control coming end of line
* @returns {Bollean}
*/
Hemex.prototype.isEnd = function(){
return this.length > this.getOffset()
}
/**
* Forward one char
*/
Hemex.prototype.nextChar = function(){
this.setOffset(this.getOffset() + 1);
}
/**
* Forward n char
*/
Hemex.prototype.toChar = function(n){
this.setOffset(this.getOffset() + n);
}
/**
* Reading while end of line
* @returns {String}
*/
Hemex.prototype.getLine = function(){
return this.of(function(){
switch(this.getChar())
{
case Hemex.EOL: return false;
default: return true;
}
}.bind(this))
}
/**
* Read all data until the function returns false
* @param {Boolean} p
* @param {(char:String)=>Boolean} e
* @returns {String}
*/
Hemex.prototype.of = function(e,p){
let k = [],count=0;
while(this.isEnd()){
if(e(p,count)) k.push(this.getChar());
else return k.join('');
count++;
this.nextChar();
};
return k.length == 0 ? false : k.join('')
}
Hemex.prototype.each = function(e,p){
let k = [];
while(this.isEnd())
if(!e(p)) return;
else this.nextChar();
}
Hemex.prototype.while = function(e,p){
let k = [];
while(this.isEnd())
if(!e(p)) return;
}
/**
* Controlling for current char type
* @param {Boolean} reverse
* @returns {Boolean}
*/
Hemex.prototype.isNumber = function(reverse){
let c = this.getChar().charCodeAt(0);
let result = c >= 48 && c <= 57;
return reverse ? !result : result;
}
/**
* Read all data until char type is not number
* @param {Boolean} reverse
* @returns {String}
*/
Hemex.prototype.readNumbers = function(reverse){
return this.of(this.isNumber.bind(this),reverse)
}
/**
* Controlling for current char type
* @param {Boolean} reverse
* @returns {Boolean}
*/
Hemex.prototype.isBigLetter = function(reverse){
let c = this.getChar().charCodeAt(0);
let result = c >= 97 && c <= 122;
return reverse ? !result : result;
}
/**
* Controlling for current char type
* @param {Boolean} reverse
* @returns {Boolean}
*/
Hemex.prototype.isSmallLetter = function(reverse){
let c = this.getChar().charCodeAt(0);
let result = c >= 65 && c <= 90;
return reverse ? !result : result;
}
/**
* Controlling for current char type
* @param {Boolean} reverse
* @returns {Boolean}
*/
Hemex.prototype.isLetter = function(reverse){
let result = this.isSmallLetter() || this.isBigLetter()
return reverse ? !result : result;
}
/**
* Controlling for current char type
* @param {Boolean} reverse
* @returns {Boolean}
*/
Hemex.prototype.isLetterRegex = function(reverse){
let result = /^[\p{L}]$/u.test(this.getChar());
return reverse ? !result : result;
}
/**
* Read all data until char type is not letter
* @param {Boolean} reverse
* @returns {String}
*/
Hemex.prototype.readLetters = function(reverse){
return this.of(this.isLetter.bind(this),reverse)
}
/**
* Controlling for current char type
* @param {Boolean} reverse
* @returns {Boolean}
*/
Hemex.prototype.isWhiteSpace = function(reverse){
let c = this.getChar(),ct = c.charCodeAt(0);
let result = (
c == '\n' ||
c == '\r' ||
c == '\t' ||
c == ' ' ||
Hemex.WhiteSpace.includes(ct)
)
return reverse ? !result : result;
}
/**
* Read all data until char type is not white space
* @param {Boolean} reverse
* @returns {String}
*/
Hemex.prototype.readWhiteSpace = function(reverse){
return this.of(this.isWhiteSpace.bind(this),reverse)
}
/**
* Controlling data
* @param {Boolean} reverse
* @returns {String}
*/
Hemex.prototype.include = function(words,next){
this.beginPosition();
for(let i = 0; i<words.length; i++)
{
if(words[i] != this.getChar())
{
this.rejectPosition();
return false;
};
this.nextChar();
};
if(next) this.acceptPosition();
else this.rejectPosition();
return true;
}
/**
* Controlling data
* @param {Boolean} reverse
* @returns {String|boolean}
*/
Hemex.prototype.includes = function(arrays,accept){
if(!this.isEnd()){
return false;
};
this.beginPosition();
let flags = Array.from(arrays).fill(true);
let index = 0;
this.each(function(){
let stopLoop = true;
for(let T in arrays)
{
if(!flags[T] || arrays[T].length <= index) continue;
stopLoop = false;
flags[T] &= arrays[T][index] == this.getChar()
};
index++;
return !stopLoop && flags.filter(function(val){return val}).length != 0;
}.bind(this));
let result = arrays.filter(function(_,index){return flags[index]});
if(accept) this.acceptPosition();
else this.rejectPosition();
return result.length == 0 ? false : result;
}
/**
* Parsing number formats like; 12 75.1 0xE7 0b10 +3.46
* @returns {[String,Number]}
*/
Hemex.prototype.readNumber = function(){
let data = [];
let base = 10;
let nextDot = false;
let c = this.getChar();
if(this.isChar('0'))
{
this.nextChar();
switch(this.getChar())
{
case 'x':{
base = 16;
this.nextChar();
data.push('0x')
break;
}
case 'b':{
base = 2;
this.nextChar();
data.push('0b')
break;
}
default:{
base = 8;
this.nextChar();
data.push('0')
}
}
}else base = 10;
this.each(()=>{
switch(c = this.getChar())
{
case '0':
case '1':{
data.push(c);
break;
}
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':{
if(base >= 8){
data.push(c);
break;
}else return false;
}
case '8':
case '9':{
if(base >= 10){
data.push(c);
break;
}else return false;
}
case 'A':
case 'a':
case 'B':
case 'b':
case 'C':
case 'c':
case 'D':
case 'd':
/* case 'E': case 'e': */
case 'F':
case 'f':{
if(base >= 16){
data.push(c);
break;
}else return false;
}
case '.':{
if(!nextDot){
if(data.length == 0){
data.push("0");
}else data.push(".");
nextDot = true;
isFloat = true;
}else{
throw new Error("Float number in Double dot");
};
break;
}
case 'E':
case 'e':{
if(this.getChar(1)!='+'){
if(base == 16){
data.push(c);
break;
}else return false;
};
if(data.length == 0){
this.rejectPosition();
return false;
};
data.push('e');
this.nextChar();
if(this.getChar()=='+' || this.getChar()=='-'){
data.push(char());
this.nextChar();
};
let result = null;
this.each(()=>{
switch(this.getChar()){
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':{
data.push(this.getChar());
this.nextChar();
break;
}
default:{
return false;
}
}
})
}
default:{
return false;
}
};
return true
});
return data.length == 0 ? false : [data.join(''),base]
}
Hemex.prototype.syntaxs = new Map();
/**
*
* @param {string} name
* @param {(hmx:Hemex, result: (result:any) => any,...args:any[]) => any} callback
*/
Hemex.prototype.syntax = function(name, callback){
this.syntaxs.set(name, callback)
}
Hemex.prototype.give = function(name, ...args){
let sandbox = this.syntaxs.get(name);
if(sandbox)
{
let res = undefined;
this.beginPosition();
if(sandbox(
this,
arg => {
res = arg
},
...args
))
{
this.acceptPosition();
return res;
}else{
this.rejectPosition();
return res;
}
}
}
/**
* @param {Error} message
*/
Hemex.prototype.throw = function(message){
throw new Error(message)
};

11
document.html Normal file
View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="expol.js" type="module"></script>
</body>
</html>

359
expol.js Normal file
View File

@ -0,0 +1,359 @@
import Hemex from "./Hemex.js"
const style = document.createElement("style")
document.head.append(style)
style.sheet.insertRule(`
.expol-container{
box-sizing: border-box;
padding: 5px;
border: solid 1px black;
overflow: hidden;
border-radius: 5px;
}
`)
style.sheet.insertRule(`
.expol-container .expol{
box-sizing: border-box;
position: relative;
min-height: 1em;
font-family: inherit;3em
font-size: inherit;
font-weight: inherit;
white-space: nowrap;
}
`)
/**
* @type {CSSRule}
*/
let fontRule = Array.from(style.sheet.cssRules).filter(e => e.selectorText == '.expol-container .expol')[0]
function updateFont(family,size,weight)
{
fontRule.style.fontFamily = family
fontRule.style.fontSize = size
fontRule.style.fontWeight = weight
}
style.sheet.insertRule(`
.expol .viewport{
position: absolute;
top: 0px;
left:0;
right:0;
height:0;
width: 100%;
height: 100%;
padding: 0
margin: 0;
color: black;
z-index: 0;
font-family: inherit;
font-size: inherit;
font-weight: inherit;
}
`)
style.sheet.insertRule(`
.expol .textinput{
position: absolute;
top: 0px;
left:0;
right:0;
height:0;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
color: transparent;
z-index: 1;
font-family: inherit;
font-size: inherit;
font-weight: inherit;
outline: none;
caret-color: black;
}
`)
style.sheet.insertRule(`
.expol pre{
margin: 0;
font-family: inherit;
display: inline;
}
`)
style.sheet.insertRule(`
.expol .col1{
color: #00a902;
}
`)
style.sheet.insertRule(`
.expol .col2{
color: #ff0000;
}
`)
style.sheet.insertRule(`
.expol .col3{
color: black;
}
`)
style.sheet.insertRule(`
.expol .col4{
color: black;
opacity: 0.5;
}
`)
style.sheet.insertRule(`
.expol .col5{
text-decoration: underline;
color: #0016ff;
}
`)
updateFont("monospace","2em","500")
let container = document.createElement("div")
container.classList.add("expol-container")
document.body.appendChild(container)
let expol = document.createElement("div")
expol.classList.add("expol")
container.appendChild(expol)
let viewport = document.createElement("pre")
viewport.classList.add("viewport")
expol.appendChild(viewport)
let textinput = document.createElement("pre")
textinput.classList.add("textinput")
textinput.contentEditable = true
expol.appendChild(textinput)
textinput.addEventListener("keyup",synctoViewPort)
textinput.addEventListener("keypress",synctoViewPort)
textinput.addEventListener("keydown",synctoViewPort)
textinput.addEventListener("paste",(pasteevent)=>{
let text = pasteevent.clipboardData.getData('text/plain')
textinput.innerText = text
synctoViewPort()
pasteevent.preventDefault()
})
function synctoViewPort()
{
let fragment = renderColorizer(textinput.innerText)
for (const child of Array.from(viewport.childNodes))
{
child.remove()
}
viewport.append(fragment)
}
function renderColorizer(text)
{
let fragment = document.createDocumentFragment()
let hmx = new Hemex()
hmx.setText(text)
let blank = true
let query = []
let term = []
hmx.syntax("term",function(hmx,result){
if(hmx.isChar('-'))
{
hmx.nextChar()
let name = hmx.of(() => hmx.isLetterRegex() || hmx.isNumber() || hmx.includes(['-','_']))
let operator = 'has'
let value = ""
if(name)
{
let pre = document.createElement("pre")
pre.classList.add("col1")
pre.setAttribute("role","name")
let textDoc = document.createTextNode('-' + name)
pre.appendChild(textDoc);
fragment.appendChild(pre)
}else{
let pre = document.createElement("pre")
pre.classList.add("col1")
pre.setAttribute("role","name")
let textDoc = document.createTextNode('-');
pre.appendChild(textDoc);
fragment.appendChild(pre)
}
let k
if(k = hmx.includes([':','>','<','>=','<=','!=']))
{
operator = k[0]
let pre = document.createElement("pre")
pre.classList.add("col2")
pre.setAttribute("role","operator")
let textDoc = document.createTextNode(k[0])
pre.appendChild(textDoc)
fragment.appendChild(pre)
hmx.nextChar()
value = hmx.give('strorword')
}
query.push({
query:[name,operator,value]
})
return true
}
})
hmx.syntax("mention",function(hmx,result){
if(hmx.isChar('@'))
{
hmx.nextChar()
let name = hmx.of(() => hmx.isLetterRegex() || hmx.isNumber() || hmx.includes(['-','_']))
if(name)
{
let pre = document.createElement("pre")
pre.classList.add("col5")
pre.setAttribute("role","mention")
let textDoc = document.createTextNode('@' + name)
pre.appendChild(textDoc);
fragment.appendChild(pre)
}else{
let pre = document.createElement("pre")
pre.classList.add("col5")
pre.setAttribute("role","mention")
let textDoc = document.createTextNode('@');
pre.appendChild(textDoc);
fragment.appendChild(pre)
}
query.push({
mention: name
})
return true
}
})
// cute --username:"absorbe"
hmx.syntax("strorword",function(hmx,result){
let k = hmx.includes(['\'','"','`'])
if(k.length == 0 || k == false){
let name = hmx.of(() => !hmx.isWhiteSpace())
if(name)
{
let pre = document.createElement("pre")
pre.classList.add("col3")
pre.setAttribute("role","value")
let textDoc = document.createTextNode(name||"");
pre.appendChild(textDoc)
fragment.appendChild(pre)
result(name||false)
}
return true
}else{
let char = hmx.getChar()
hmx.nextChar()
let name = hmx.of(() => !hmx.isChar(char))
let kt = "";
if(name)
{
result(name||false)
kt = char + name;
if(hmx.isChar(char)){
hmx.nextChar()
kt += char;
}
}else{
kt += char;
hmx.nextChar()
}
let pre = document.createElement("pre")
pre.classList.add("col3")
pre.setAttribute("role","value")
let textDoc = document.createTextNode(kt)
pre.appendChild(textDoc)
fragment.appendChild(pre)
return true
}
})
hmx.syntax("scope",function(hmx,result){
let description = []
hmx.while(()=>{
switch(hmx.getChar())
{
case "-":{
if(description.length)
{
let pre = document.createElement("pre")
pre.classList.add("col4")
pre.setAttribute("role","description")
let textDoc = document.createTextNode(description.join(''))
pre.appendChild(textDoc)
fragment.appendChild(pre)
if(description.join('').trim().length) term.push(description.join(''))
description = []
}
if(blank)
{
hmx.give("term")
return true
}
}
case "@":{
if(description.length)
{
let pre = document.createElement("pre")
pre.classList.add("col4")
pre.setAttribute("role","description")
let textDoc = document.createTextNode(description.join(''))
pre.appendChild(textDoc)
fragment.appendChild(pre)
if(description.join('').trim().length) term.push(description.join(''))
description = []
}
if(blank)
{
hmx.give("mention")
return true
}
}
default:{
blank = hmx.isWhiteSpace()
description.push(hmx.getChar())
hmx.nextChar()
return true
}
}
})
if(description.length)
{
if(description.join('').trim().length) term.push(description.join(''))
let pre = document.createElement("pre")
pre.classList.add("col4")
pre.setAttribute("role","description")
let textDoc = document.createTextNode(description.join(''))
pre.appendChild(textDoc)
fragment.appendChild(pre)
}
query.push({
term
})
})
hmx.give('scope')
console.log("Query:",query)
return fragment
}