mirror of
https://github.com/osmarks/website
synced 2025-01-25 16:36:53 +00:00
RPNCalcv4 and stuff
This commit is contained in:
parent
c179ee8d45
commit
f3bde26728
686
experiments/apioform/index.html
Normal file
686
experiments/apioform/index.html
Normal file
@ -0,0 +1,686 @@
|
|||||||
|
---
|
||||||
|
title: Apioform Game
|
||||||
|
description: A game about... apioforms... by Heavpoot.
|
||||||
|
---
|
||||||
|
<canvas style="border:3px solid black" width=500 height=500 id=a></canvas>
|
||||||
|
<p id="kills">Kills: </p>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
turn=(()=>{
|
||||||
|
const canvas=document.getElementById("a")
|
||||||
|
const ctx=canvas.getContext("2d")
|
||||||
|
/*var framecount=0
|
||||||
|
var entities={}
|
||||||
|
var particles=new Set()
|
||||||
|
var attacks=new Set()
|
||||||
|
var kills=0;*/
|
||||||
|
let frigidmode=false
|
||||||
|
let paritychallenge=false
|
||||||
|
let parityoverlay=false
|
||||||
|
let prevkilled=false // parity challenge thingy.
|
||||||
|
if (localStorage.tutorial===undefined){localStorage.tutorial=true;}
|
||||||
|
const solid={
|
||||||
|
"player":true,
|
||||||
|
"bee":true,
|
||||||
|
"barrier":true,
|
||||||
|
"apioform":true,
|
||||||
|
"apiodiagoform":true,
|
||||||
|
"apiokinetoform":true,
|
||||||
|
"apiopyroform":true,
|
||||||
|
"cryoapioform":true,
|
||||||
|
"apiopariform":true,
|
||||||
|
}
|
||||||
|
const killable={
|
||||||
|
"bee":true,
|
||||||
|
"apioform":true,
|
||||||
|
"apiodiagoform":true,
|
||||||
|
"apiokinetoform":true,
|
||||||
|
"apiopyroform":true,
|
||||||
|
"cryoapioform":true,
|
||||||
|
"apiopariform":true,
|
||||||
|
}
|
||||||
|
const freezable={
|
||||||
|
"player":true,
|
||||||
|
"bee":true,
|
||||||
|
"apioform":true,
|
||||||
|
"apiodiagoform":true,
|
||||||
|
"apiokinetoform":true,
|
||||||
|
"apiopyroform":true,
|
||||||
|
"cryoapioform":true,
|
||||||
|
"apiopariform":true,
|
||||||
|
}
|
||||||
|
class Tile {
|
||||||
|
constructor(x,y,type,meta){
|
||||||
|
this.x=x;
|
||||||
|
this.y=y;
|
||||||
|
this.type=type;
|
||||||
|
this.meta=meta;
|
||||||
|
this.cx=x;
|
||||||
|
this.cy=y;
|
||||||
|
this.meta.clock=this.meta.clock===undefined?0:this.meta.clock;
|
||||||
|
this.meta.cooldown=0
|
||||||
|
this.meta.frozen=0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Attack {
|
||||||
|
constructor(x,y,type,meta){
|
||||||
|
this.x=x;
|
||||||
|
this.y=y;
|
||||||
|
this.type=type;
|
||||||
|
this.meta=meta;
|
||||||
|
if (this.type=="fireball"||this.type=="iceball"){
|
||||||
|
this.meta.clock=-1;
|
||||||
|
}
|
||||||
|
this.turn=function(){
|
||||||
|
switch (this.type){
|
||||||
|
case "fireball":
|
||||||
|
var suicide=true
|
||||||
|
for (i in entities[this.x+" "+this.y]){
|
||||||
|
if (entities[this.x+" "+this.y][i].type=="apiopyroform"){
|
||||||
|
suicide=false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.meta.clock==2||suicide){
|
||||||
|
attacks.delete(this)
|
||||||
|
}else{
|
||||||
|
if (this.meta.clock==1){
|
||||||
|
if (this.x==px&&this.meta.dx==1){
|
||||||
|
php-=2
|
||||||
|
}
|
||||||
|
if (this.y==py&&this.meta.dy==1){
|
||||||
|
php-=2
|
||||||
|
}
|
||||||
|
for (i in entities){
|
||||||
|
for (j=entities[i].length-1;j>=0;j--){
|
||||||
|
if (killable[entities[i][j].type]===true&&i!==this.x+" "+this.y&&((this.meta.dx==1&&this.x==entities[i][j].x)||(this.meta.dy==1&&this.y==entities[i][j].y))){
|
||||||
|
spawneffect("death",entities[i][j].x,entities[i][j].y)
|
||||||
|
entities[i].splice(j,1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.meta.clock++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "iceball":
|
||||||
|
var suicide=true
|
||||||
|
for (i in entities[this.x+" "+this.y]){
|
||||||
|
if (entities[this.x+" "+this.y][i].type=="cryoapioform"){
|
||||||
|
suicide=false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.meta.clock==2||suicide){
|
||||||
|
attacks.delete(this)
|
||||||
|
}else{
|
||||||
|
if (this.meta.clock==1){
|
||||||
|
for (i in entities){
|
||||||
|
for (j=entities[i].length-1;j>=0;j--){
|
||||||
|
if (freezable[entities[i][j].type]===true&&i!==this.x+" "+this.y&&((this.meta.dx==1&&this.x==entities[i][j].x)||(this.meta.dy==1&&this.y==entities[i][j].y))){
|
||||||
|
entities[i][j].meta.frozen=4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.meta.clock++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.tick=function(){
|
||||||
|
switch (this.type){
|
||||||
|
case "fireball":
|
||||||
|
if (this.meta.clock==0){
|
||||||
|
for (var i=0;i<20;i++){
|
||||||
|
particles.add(new Particle((Math.random()*1000-500)*this.meta.dy+this.x*10+5,(Math.random()*1000-500)*this.meta.dx+this.y*10+5,80,80,80,10,Math.random()-0.5*this.meta.dx+(Math.random()*0.125-0.0625),Math.random()-0.5*this.meta.dy+(Math.random()*0.125-0.0625),some(["basic","big","basic"])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.meta.clock==1){
|
||||||
|
for (var i=0;i<60;i++){
|
||||||
|
particles.add(new Particle((Math.random()*1000-500)*this.meta.dy+this.x*10+5,(Math.random()*1000-500)*this.meta.dx+this.y*10+5,128,128,128,20,Math.random()-0.5*this.meta.dx+(Math.random()*0.125-0.0625),Math.random()-0.5*this.meta.dy+(Math.random()*0.125-0.0625),some(["basic","big","basic"])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.meta.clock==2){
|
||||||
|
for (var i=0;i<80;i++){
|
||||||
|
let r=some([255,200,180])
|
||||||
|
let g=some([r,128])
|
||||||
|
particles.add(new Particle((Math.random()*1000-500)*this.meta.dy+this.x*10+5,(Math.random()*1000-500)*this.meta.dx+this.y*10+5,r,g,128,20,(Math.random()*2-1)*this.meta.dx+(Math.random()*0.125-0.0625),(Math.random()*2-1)*this.meta.dy+(Math.random()*0.125-0.0625),some(["big","big","basic"])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "iceball":
|
||||||
|
if (this.meta.clock==0){
|
||||||
|
for (var i=0;i<20;i++){
|
||||||
|
particles.add(new Particle((Math.random()*1000-500)*this.meta.dy+this.x*10+5,(Math.random()*1000-500)*this.meta.dx+this.y*10+5,80,80,120,10,Math.random()-0.5*this.meta.dx+(Math.random()*0.125-0.0625),Math.random()-0.5*this.meta.dy+(Math.random()*0.125-0.0625),some(["basic","big","basic"])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.meta.clock==1){
|
||||||
|
for (var i=0;i<60;i++){
|
||||||
|
particles.add(new Particle((Math.random()*1000-500)*this.meta.dy+this.x*10+5,(Math.random()*1000-500)*this.meta.dx+this.y*10+5,128,128,200,20,Math.random()-0.5*this.meta.dx+(Math.random()*0.125-0.0625),Math.random()-0.5*this.meta.dy+(Math.random()*0.125-0.0625),some(["basic","big","basic"])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.meta.clock==2){
|
||||||
|
for (var i=0;i<80;i++){
|
||||||
|
let b=some([255,200,180])
|
||||||
|
let g=some([b,128,0])
|
||||||
|
particles.add(new Particle((Math.random()*1000-500)*this.meta.dy+this.x*10+5,(Math.random()*1000-500)*this.meta.dx+this.y*10+5,0,g,b,20,(Math.random()*2-1)*this.meta.dx+(Math.random()*0.125-0.0625),(Math.random()*2-1)*this.meta.dy+(Math.random()*0.125-0.0625),some(["big","big","basic"])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Particle {
|
||||||
|
constructor(x,y,r,g,b,fade,xv,yv,type){
|
||||||
|
this.x=x;
|
||||||
|
this.y=y;
|
||||||
|
this.r=r
|
||||||
|
this.g=g
|
||||||
|
this.b=b
|
||||||
|
this.fade=fade;
|
||||||
|
this.cfade=fade;
|
||||||
|
this.xv=xv;
|
||||||
|
this.yv=yv;
|
||||||
|
this.type=type;
|
||||||
|
this.tick=function(){
|
||||||
|
if (this.cfade==0){
|
||||||
|
particles.delete(this)
|
||||||
|
}else{
|
||||||
|
switch (this.type){
|
||||||
|
case "basic":
|
||||||
|
this.xv*=0.99;
|
||||||
|
this.yv*=0.99;
|
||||||
|
this.x+=this.xv;
|
||||||
|
this.y+=this.yv;
|
||||||
|
ctx.fillStyle=`rgba(${this.r},${this.g},${this.b},${this.cfade/this.fade})`
|
||||||
|
ctx.fillRect(this.x,this.y,1,1)
|
||||||
|
break;
|
||||||
|
case "big":
|
||||||
|
this.xv*=0.99;
|
||||||
|
this.yv*=0.99;
|
||||||
|
this.x+=this.xv;
|
||||||
|
this.y+=this.yv;
|
||||||
|
ctx.fillStyle=`rgba(${this.r},${this.g},${this.b},${this.cfade/this.fade})`
|
||||||
|
ctx.fillRect(this.x-1,this.y-1,3,3)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.cfade--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var cam={
|
||||||
|
"x":0,"y":0
|
||||||
|
}
|
||||||
|
let px; let php; let py; let entities; let particles; let framecount; let attacks; let kills;
|
||||||
|
|
||||||
|
function render(ent){
|
||||||
|
switch (ent.type){
|
||||||
|
case "player":
|
||||||
|
ctx.fillStyle="#000000"
|
||||||
|
ctx.fillRect(ent.cx*10,ent.cy*10,10,10)
|
||||||
|
if (parityoverlay&&paritychallenge){
|
||||||
|
ctx.fillStyle="#ffffff"
|
||||||
|
ctx.textBaseline="middle"
|
||||||
|
ctx.textAlign="center"
|
||||||
|
ctx.font="10px monospace";
|
||||||
|
if (ent.x%2==0&&ent.y%2==0) ctx.fillText("a",ent.cx*10+5,ent.cy*10+5)
|
||||||
|
if (ent.x%2==1&&ent.y%2==0) ctx.fillText("b",ent.cx*10+5,ent.cy*10+5)
|
||||||
|
if (ent.x%2==0&&ent.y%2==1) ctx.fillText("d",ent.cx*10+5,ent.cy*10+5)
|
||||||
|
if (ent.x%2==1&&ent.y%2==1) ctx.fillText("c",ent.cx*10+5,ent.cy*10+5)
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "bee":
|
||||||
|
if (ent.meta.clock%2==0) ctx.fillStyle="#808020"
|
||||||
|
else ctx.fillStyle="#A0A050"
|
||||||
|
ctx.fillRect(ent.cx*10,ent.cy*10,10,10)
|
||||||
|
break;
|
||||||
|
case "apioform":
|
||||||
|
ctx.fillStyle="#A08020"
|
||||||
|
ctx.fillRect(ent.cx*10,ent.cy*10,10,10)
|
||||||
|
break;
|
||||||
|
case "apiodiagoform":
|
||||||
|
ctx.fillStyle="#A080A0"
|
||||||
|
ctx.fillRect(ent.cx*10,ent.cy*10,10,10)
|
||||||
|
break;
|
||||||
|
case "apiokinetoform":
|
||||||
|
if (ent.meta.clock%2==0) ctx.fillStyle="#3030FF"
|
||||||
|
else ctx.fillStyle="#101080"
|
||||||
|
ctx.fillRect(ent.cx*10,ent.cy*10,10,10)
|
||||||
|
break;
|
||||||
|
case "apiopyroform":
|
||||||
|
ctx.fillStyle="#F02030"
|
||||||
|
ctx.fillRect(ent.cx*10,ent.cy*10,10,10)
|
||||||
|
break;
|
||||||
|
case "cryoapioform":
|
||||||
|
ctx.fillStyle="#0080FF"
|
||||||
|
ctx.fillRect(ent.cx*10,ent.cy*10,10,10)
|
||||||
|
break;
|
||||||
|
case "apiopariform":
|
||||||
|
ctx.fillStyle="#808080"
|
||||||
|
ctx.fillRect(ent.cx*10,ent.cy*10,10,10)
|
||||||
|
if (parityoverlay&&paritychallenge){
|
||||||
|
ctx.fillStyle="#ffffff"
|
||||||
|
ctx.textBaseline="middle"
|
||||||
|
ctx.textAlign="center"
|
||||||
|
ctx.font="10px monospace";
|
||||||
|
if (ent.x%2==0&&ent.y%2==0) ctx.fillText("a",ent.cx*10+5,ent.cy*10+5)
|
||||||
|
if (ent.x%2==1&&ent.y%2==0) ctx.fillText("b",ent.cx*10+5,ent.cy*10+5)
|
||||||
|
if (ent.x%2==0&&ent.y%2==1) ctx.fillText("d",ent.cx*10+5,ent.cy*10+5)
|
||||||
|
if (ent.x%2==1&&ent.y%2==1) ctx.fillText("c",ent.cx*10+5,ent.cy*10+5)
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "apiocryokinetoform":
|
||||||
|
ctx.fillStyle="#FFFFFF"
|
||||||
|
ctx.fillRect(ent.cx*10,ent.cy*10,10,10)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ent.cx+=(ent.x-ent.cx)*0.4
|
||||||
|
ent.cy+=(ent.y-ent.cy)*0.4
|
||||||
|
|
||||||
|
if (ent.meta.frozen){
|
||||||
|
ctx.fillStyle="rgba(0,255,255,0.3)"
|
||||||
|
ctx.fillRect(ent.x*10,ent.y*10,10,10)
|
||||||
|
spawneffect("frigid",ent.x,ent.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function makeent(x,y,type,meta){
|
||||||
|
let ent=new Tile(x,y,type,meta)
|
||||||
|
if (entities[x+" "+y]===undefined){
|
||||||
|
entities[x+" "+y]=[]
|
||||||
|
}
|
||||||
|
entities[x+" "+y].push(ent)
|
||||||
|
}
|
||||||
|
function issolid(i){
|
||||||
|
if (entities[i]!==undefined){
|
||||||
|
for (j in entities[i]){
|
||||||
|
if (solid[entities[i][j].type]) return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
function move(tile,subtile,dx,dy){
|
||||||
|
let tilen=(dx+tile.x)+" "+(dy+tile.y)
|
||||||
|
if (!issolid(tilen)&&entities[tile.x+" "+tile.y][subtile].meta.frozen==0){
|
||||||
|
if (entities[tilen]===undefined){
|
||||||
|
entities[tilen]=[]
|
||||||
|
}
|
||||||
|
|
||||||
|
entities[tilen].push(entities[tile.x+" "+tile.y].splice(subtile,1)[0])
|
||||||
|
entities[tilen][entities[tilen].length-1].x+=dx
|
||||||
|
entities[tilen][entities[tilen].length-1].y+=dy
|
||||||
|
|
||||||
|
if (entities[tilen][entities[tilen].length-1].type=="player"){
|
||||||
|
px+=dx
|
||||||
|
py+=dy
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function move_atk(tile,subtile,dx,dy){
|
||||||
|
if (entities[tile.x+" "+tile.y][subtile].meta.frozen==0&&tile.x+dx==px&&tile.y+dy==py){
|
||||||
|
for (i in entities[px+" "+py]){
|
||||||
|
if (entities[px+" "+py][i].type=="player"){
|
||||||
|
php--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
move(tile,subtile,dx,dy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function partrender(particle){
|
||||||
|
particle.tick()
|
||||||
|
}
|
||||||
|
function some(a){
|
||||||
|
return a[Math.floor(Math.random()*a.length)]
|
||||||
|
}
|
||||||
|
function call_turn(a){
|
||||||
|
a.turn()
|
||||||
|
}
|
||||||
|
function turn(e){
|
||||||
|
let maketurn=false
|
||||||
|
let dx=0;
|
||||||
|
let dy=0;
|
||||||
|
switch (e.keyCode){
|
||||||
|
case 87: case 38:
|
||||||
|
maketurn=true;dy=-1;
|
||||||
|
break;
|
||||||
|
case 65: case 37:
|
||||||
|
maketurn=true;dx=-1;
|
||||||
|
break;
|
||||||
|
case 83: case 40:
|
||||||
|
maketurn=true;dy=1;
|
||||||
|
break;
|
||||||
|
case 68: case 39:
|
||||||
|
maketurn=true;dx=1;
|
||||||
|
break;
|
||||||
|
case 82:
|
||||||
|
php=1
|
||||||
|
framecount=0
|
||||||
|
entities={}
|
||||||
|
particles=new Set()
|
||||||
|
attacks=new Set()
|
||||||
|
kills=0;
|
||||||
|
makeent(3,3,"player",{})
|
||||||
|
px=3
|
||||||
|
py=3
|
||||||
|
prevkilled=false
|
||||||
|
if (!paritychallenge){
|
||||||
|
for (var i=0;i<14;i++){
|
||||||
|
try_spawn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 80:
|
||||||
|
paritychallenge=!paritychallenge
|
||||||
|
turn({keyCode:82})
|
||||||
|
break;
|
||||||
|
case 79:
|
||||||
|
parityoverlay=!parityoverlay
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (px+dx<0||px+dx>49||py+dy<0||py+dy>49){
|
||||||
|
maketurn=false
|
||||||
|
}
|
||||||
|
if (maketurn&&php>0){
|
||||||
|
|
||||||
|
for (i in entities[px+" "+py]){
|
||||||
|
if (entities[px+" "+py][i].type=="player"){
|
||||||
|
if (!move({"x":px,"y":py},i,dx,dy)&&entities[px+" "+py][i].meta.frozen==0){
|
||||||
|
let nent=(px+dx)+" "+(py+dy)
|
||||||
|
for (i in entities[nent]){
|
||||||
|
if (killable[entities[nent][i].type]===true){
|
||||||
|
entities[nent][i].meta.dead=true
|
||||||
|
spawneffect("death",px+dx,py+dy)
|
||||||
|
kills++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var i=entities[nent].length-1;i>=0;i--){
|
||||||
|
if (entities[nent][i].meta.dead!==undefined){
|
||||||
|
entities[nent].splice(i,1)
|
||||||
|
prevkilled=true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in entities){
|
||||||
|
for (j in entities[i]){
|
||||||
|
switch (entities[i][j].type){
|
||||||
|
case "bee":
|
||||||
|
entities[i][j].meta.moved=false
|
||||||
|
var edx=0;
|
||||||
|
var edy=0;
|
||||||
|
entities[i][j].meta.clock++;
|
||||||
|
if (entities[i][j].meta.clock%4==0){
|
||||||
|
edy=1
|
||||||
|
} else if (entities[i][j].meta.clock%4==2){
|
||||||
|
edy=-1
|
||||||
|
}
|
||||||
|
entities[i][j].meta.dx=edx;
|
||||||
|
entities[i][j].meta.dy=edy;
|
||||||
|
entities[i][j].atk=true
|
||||||
|
break;
|
||||||
|
case "apioform":
|
||||||
|
entities[i][j].meta.moved=false
|
||||||
|
var edx=0;
|
||||||
|
var edy=0;
|
||||||
|
entities[i][j].meta.clock++;
|
||||||
|
if (entities[i][j].meta.clock%4==0){
|
||||||
|
edy=1
|
||||||
|
} else if (entities[i][j].meta.clock%4==1){
|
||||||
|
edx=1
|
||||||
|
} else if (entities[i][j].meta.clock%4==2){
|
||||||
|
edy=-1
|
||||||
|
} else if (entities[i][j].meta.clock%4==3){
|
||||||
|
edx=-1
|
||||||
|
}
|
||||||
|
entities[i][j].meta.dx=edx;
|
||||||
|
entities[i][j].meta.dy=edy;
|
||||||
|
entities[i][j].atk=true
|
||||||
|
break;
|
||||||
|
case "apiodiagoform":
|
||||||
|
entities[i][j].meta.moved=false
|
||||||
|
var edx=0;
|
||||||
|
var edy=0;
|
||||||
|
entities[i][j].meta.clock++;
|
||||||
|
if (entities[i][j].meta.clock%4==0){
|
||||||
|
edy=1;edx=1;
|
||||||
|
} else if (entities[i][j].meta.clock%4==1){
|
||||||
|
edx=1;edy=-1;
|
||||||
|
} else if (entities[i][j].meta.clock%4==2){
|
||||||
|
edy=-1;edx=-1;
|
||||||
|
} else if (entities[i][j].meta.clock%4==3){
|
||||||
|
edx=-1;edy=1;
|
||||||
|
}
|
||||||
|
entities[i][j].meta.dx=edx;
|
||||||
|
entities[i][j].meta.dy=edy;
|
||||||
|
entities[i][j].atk=true
|
||||||
|
break;
|
||||||
|
case "apiokinetoform":
|
||||||
|
entities[i][j].meta.moved=false
|
||||||
|
var edx=0;
|
||||||
|
var edy=0;
|
||||||
|
entities[i][j].meta.clock++;
|
||||||
|
if (entities[i][j].meta.clock%2==0){
|
||||||
|
if (entities[i][j].x>px) edx=-1
|
||||||
|
if (entities[i][j].x<px) edx=1
|
||||||
|
if (entities[i][j].y>py) edy=-1
|
||||||
|
if (entities[i][j].y<py) edy=1
|
||||||
|
}
|
||||||
|
entities[i][j].meta.dx=edx;
|
||||||
|
entities[i][j].meta.dy=edy;
|
||||||
|
entities[i][j].atk=true
|
||||||
|
break;
|
||||||
|
case "apiopyroform":
|
||||||
|
entities[i][j].meta.moved=false
|
||||||
|
var edx=0;
|
||||||
|
var edy=0;
|
||||||
|
entities[i][j].meta.clock++;
|
||||||
|
if (entities[i][j].meta.cooldown!=0){
|
||||||
|
entities[i][j].meta.cooldown--;
|
||||||
|
}else if (entities[i][j].meta.frozen!=0){
|
||||||
|
|
||||||
|
}else{
|
||||||
|
if (entities[i][j].x==px) {
|
||||||
|
entities[i][j].meta.cooldown=2
|
||||||
|
attacks.add(new Attack(entities[i][j].x,entities[i][j].y,"fireball",{"dx":1,"dy":0}))
|
||||||
|
}else if (entities[i][j].y==py){
|
||||||
|
entities[i][j].meta.cooldown=2
|
||||||
|
attacks.add(new Attack(entities[i][j].x,entities[i][j].y,"fireball",{"dx":0,"dy":1}))
|
||||||
|
} else {
|
||||||
|
if (entities[i][j].meta.clock%2==0){
|
||||||
|
if (entities[i][j].x>px&&entities[i][j].y>py) {if (Math.random()<0.5) {edx-=1} else {edy-=1}}
|
||||||
|
else if (entities[i][j].x>px&&entities[i][j].y<py) {if (Math.random()<0.5) {edx-=1} else {edy+=1}}
|
||||||
|
else if (entities[i][j].x<px&&entities[i][j].y>py) {if (Math.random()<0.5) {edx+=1} else {edy-=1}}
|
||||||
|
else if (entities[i][j].x<px&&entities[i][j].y<py) {if (Math.random()<0.5) {edx+=1} else {edy+=1}}
|
||||||
|
else if (entities[i][j].x<px) edx=1
|
||||||
|
else if (entities[i][j].y>py) edy=-1
|
||||||
|
else if (entities[i][j].y<py) edy=1
|
||||||
|
else if (entities[i][j].x>px) edx=-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entities[i][j].meta.dx=edx;
|
||||||
|
entities[i][j].meta.dy=edy;
|
||||||
|
entities[i][j].atk=true
|
||||||
|
break;
|
||||||
|
case "cryoapioform":
|
||||||
|
entities[i][j].meta.moved=false
|
||||||
|
var edx=0;
|
||||||
|
var edy=0;
|
||||||
|
entities[i][j].meta.clock++;
|
||||||
|
if (entities[i][j].meta.cooldown!=0){
|
||||||
|
entities[i][j].meta.cooldown--;
|
||||||
|
}else if (entities[i][j].meta.frozen!=0){
|
||||||
|
|
||||||
|
}else{
|
||||||
|
if (entities[i][j].x==px) {
|
||||||
|
entities[i][j].meta.cooldown=4
|
||||||
|
attacks.add(new Attack(entities[i][j].x,entities[i][j].y,"iceball",{"dx":1,"dy":0}))
|
||||||
|
}else if (entities[i][j].y==py){
|
||||||
|
entities[i][j].meta.cooldown=4
|
||||||
|
attacks.add(new Attack(entities[i][j].x,entities[i][j].y,"iceball",{"dx":0,"dy":1}))
|
||||||
|
} else {
|
||||||
|
if (entities[i][j].meta.clock%2==0){
|
||||||
|
if (entities[i][j].x>px&&entities[i][j].y>py) {if (Math.random()<0.5) {edx-=1} else {edy-=1}}
|
||||||
|
else if (entities[i][j].x>px&&entities[i][j].y<py) {if (Math.random()<0.5) {edx-=1} else {edy+=1}}
|
||||||
|
else if (entities[i][j].x<px&&entities[i][j].y>py) {if (Math.random()<0.5) {edx+=1} else {edy-=1}}
|
||||||
|
else if (entities[i][j].x<px&&entities[i][j].y<py) {if (Math.random()<0.5) {edx+=1} else {edy+=1}}
|
||||||
|
else if (entities[i][j].x<px) edx=1
|
||||||
|
else if (entities[i][j].y>py) edy=-1
|
||||||
|
else if (entities[i][j].y<py) edy=1
|
||||||
|
else if (entities[i][j].x>px) edx=-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entities[i][j].meta.dx=edx;
|
||||||
|
entities[i][j].meta.dy=edy;
|
||||||
|
entities[i][j].atk=true
|
||||||
|
break;
|
||||||
|
case "apiopariform":
|
||||||
|
entities[i][j].meta.moved=false
|
||||||
|
var edx=0;
|
||||||
|
var edy=0;
|
||||||
|
if (entities[i][j].x>px&&entities[i][j].y>py) {if (Math.random()<0.5) {edx-=2} else {edy-=2}}
|
||||||
|
else if (entities[i][j].x>px&&entities[i][j].y<py) {if (Math.random()<0.5) {edx-=2} else {edy+=2}}
|
||||||
|
else if (entities[i][j].x<px&&entities[i][j].y>py) {if (Math.random()<0.5) {edx+=2} else {edy-=2}}
|
||||||
|
else if (entities[i][j].x<px&&entities[i][j].y<py) {if (Math.random()<0.5) {edx+=2} else {edy+=2}}
|
||||||
|
else if (entities[i][j].x<px) edx=2
|
||||||
|
else if (entities[i][j].y>py) edy=-2
|
||||||
|
else if (entities[i][j].y<py) edy=2
|
||||||
|
else if (entities[i][j].x>px) edx=-2
|
||||||
|
entities[i][j].meta.dx=edx;
|
||||||
|
entities[i][j].meta.dy=edy;
|
||||||
|
entities[i][j].atk=true
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
if (entities[i][j].meta.frozen>0) entities[i][j].meta.frozen--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i in entities){
|
||||||
|
for (j in entities[i]){
|
||||||
|
if (entities[i][j].meta.dx!==undefined){
|
||||||
|
if (entities[i][j].meta.moved==false){
|
||||||
|
entities[i][j].meta.moved=true
|
||||||
|
if (entities[i][j].atk) move_atk({"x":entities[i][j].x,"y":entities[i][j].y},j,entities[i][j].meta.dx,entities[i][j].meta.dy)
|
||||||
|
else move({"x":entities[i][j].x,"y":entities[i][j].y},j,entities[i][j].meta.dx,entities[i][j].meta.dy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attacks.forEach(call_turn)
|
||||||
|
if (localStorage.tutorial=="true"&&!frigidmode&&!paritychallenge&&((Math.random()<0.1&&kills<15)||(Math.random()<0.2&&kills>14)||(Math.random()<0.35&&kills>24))){
|
||||||
|
try_spawn()
|
||||||
|
}
|
||||||
|
if (localStorage.tutorial=="false"&&!frigidmode&&!paritychallenge&&(Math.random()<0.35)){
|
||||||
|
try_spawn()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frigidmode){
|
||||||
|
while (true){
|
||||||
|
let cx=Math.floor(Math.random()*48)+1
|
||||||
|
let cy=Math.floor(Math.random()*48)+1
|
||||||
|
let s=issolid(cx+" "+cy)
|
||||||
|
if (!s){
|
||||||
|
makeent(cx,cy,"cryoapioform",{})
|
||||||
|
} else continue
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (paritychallenge){
|
||||||
|
if (prevkilled){
|
||||||
|
prevkilled=false
|
||||||
|
}else{
|
||||||
|
while (true){
|
||||||
|
let cx=Math.floor(Math.random()*48)+1
|
||||||
|
let cy=Math.floor(Math.random()*48)+1
|
||||||
|
let s=issolid(cx+" "+cy)
|
||||||
|
if (!s){
|
||||||
|
makeent(cx,cy,"apiopariform",{})
|
||||||
|
} else continue
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (php<=0||px<0||px>49||py<0||py>49){
|
||||||
|
for (i in entities[px+" "+py]){
|
||||||
|
if (entities[px+" "+py][i].type=="player"){
|
||||||
|
entities[px+" "+py].splice(i,1)
|
||||||
|
spawneffect("death",px,py)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(kills)
|
||||||
|
if (localStorage.tutorial=="true"&&kills>30){
|
||||||
|
localStorage.tutorial=false
|
||||||
|
kills=0
|
||||||
|
}
|
||||||
|
let k=document.getElementById("kills")
|
||||||
|
k.innerHTML=`Kills: ${paritychallenge?kills:(localStorage.tutorial=="true"?"[TUTORIAL]":kills)}`
|
||||||
|
}
|
||||||
|
function call_tick(a){
|
||||||
|
a.tick()
|
||||||
|
}
|
||||||
|
function draw(){
|
||||||
|
requestAnimationFrame(draw)
|
||||||
|
ctx.fillStyle="#bbbbbb"
|
||||||
|
ctx.fillRect(0,0,500,500)
|
||||||
|
particles.forEach(partrender)
|
||||||
|
attacks.forEach(call_tick)
|
||||||
|
for (i in entities){
|
||||||
|
for (j in entities[i]){
|
||||||
|
render(entities[i][j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function try_spawn(){
|
||||||
|
while (true){
|
||||||
|
let cx=Math.floor(Math.random()*48)+1
|
||||||
|
let cy=Math.floor(Math.random()*48)+1
|
||||||
|
let s=issolid(cx+" "+cy)
|
||||||
|
if (!s){
|
||||||
|
if (localStorage.tutorial=="true"){
|
||||||
|
if (kills<5){
|
||||||
|
makeent(cx,cy,"bee",{"clock":Math.floor(Math.random()*2)})
|
||||||
|
}
|
||||||
|
if (kills>4&&kills<15){
|
||||||
|
makeent(cx,cy,some(["bee","bee","apioform","apioform","apiodiagoform"]),{})
|
||||||
|
}
|
||||||
|
if (kills>14&&kills<25){
|
||||||
|
makeent(cx,cy,some(["bee","apioform","apioform","apiodiagoform","apiokinetoform","apiokinetoform"]),{})
|
||||||
|
}
|
||||||
|
if (kills>24){
|
||||||
|
makeent(cx,cy,some(["apioform","apioform","apiodiagoform","apiodiagoform","apiokinetoform","apiokinetoform","apioform","apioform","apiodiagoform","apiodiagoform","apiokinetoform","apiokinetoform","apiopyroform"]),{})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
makeent(cx,cy,some(["apioform","apiodiagoform","apioform","apiodiagoform","apioform","apiodiagoform","apiokinetoform","apiopyroform","cryoapioform","apiopariform","apiopariform"]),{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Math.random()<0.1||s){continue;}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function spawneffect(e,x,y){
|
||||||
|
switch (e){
|
||||||
|
case "death":
|
||||||
|
for (var i=0;i<30;i++){particles.add(new Particle(x*10+5,y*10+5,some([40,0]),some([40,0]),some([40,0]),100,Math.random()-0.5,Math.random()-0.5,some(["basic","basic","big"])))}
|
||||||
|
break;
|
||||||
|
case "frigid":
|
||||||
|
for (var i=0;i<4;i++){particles.add(new Particle(x*10+5,y*10+5,0,0,some([255,128,128,0]),70,Math.random()/2-0.25,Math.random()/2-0.25,some(["basic","big","big"])))}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
turn({"keyCode":82})
|
||||||
|
requestAnimationFrame(draw)
|
||||||
|
return turn
|
||||||
|
})()
|
||||||
|
|
||||||
|
document.body.addEventListener("keydown", turn)
|
||||||
|
</script>
|
128
experiments/rpncalc-v4/builtin.mjs
Normal file
128
experiments/rpncalc-v4/builtin.mjs
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import {defnOp, makeOp, defn} from './shiny.mjs';
|
||||||
|
import {parseExprs} from './parse.mjs';
|
||||||
|
import {tokenize} from './token.mjs';
|
||||||
|
|
||||||
|
const objEq = (a, b) => {
|
||||||
|
if (typeof a !== 'object' && typeof b !== 'object') {
|
||||||
|
return a === b;
|
||||||
|
} else if (typeof a !== 'object' || typeof b !== 'object') {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
const aprop = Object.getOwnPropertyNames(a);
|
||||||
|
const bprop = Object.getOwnPropertyNames(b);
|
||||||
|
|
||||||
|
if (bprop.length !== aprop.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < aprop.length; i++) {
|
||||||
|
if (!objEq(a[aprop[i]], b[aprop[i]])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export let scope = {};
|
||||||
|
|
||||||
|
export const customHandler = (ins) => {
|
||||||
|
return [ins];
|
||||||
|
}
|
||||||
|
|
||||||
|
const addRPNDefn = (name, def) => {
|
||||||
|
let toks = tokenize(def);
|
||||||
|
if (!toks) {
|
||||||
|
throw 'could not load builtin'
|
||||||
|
}
|
||||||
|
toks = toks.map(elem => {
|
||||||
|
elem.startPos = 0;
|
||||||
|
elem.endPos = 0;
|
||||||
|
return elem;
|
||||||
|
});
|
||||||
|
let ast = parseExprs(toks);
|
||||||
|
if (!ast.parsed) {
|
||||||
|
throw 'could not load builtin'
|
||||||
|
}
|
||||||
|
scope = defn(name, ast.parsed.arr, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
const assertType = (type) => (elem) => {
|
||||||
|
if (elem.type !== type) {
|
||||||
|
throw 'typeerror'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const addDefn = (name, args, fn) => {
|
||||||
|
if (Array.isArray(args)) {
|
||||||
|
const nargs = [...Array(args.length).keys()];
|
||||||
|
const liftFn = (scope) => {
|
||||||
|
let newscope = Object.create(scope);
|
||||||
|
for (let i = 0; i < args.length; i++) {
|
||||||
|
assertType(args[i])(scope[i][0]);
|
||||||
|
newscope[i] = scope[i][0].val;
|
||||||
|
}
|
||||||
|
return fn(newscope);
|
||||||
|
}
|
||||||
|
const op = makeOp(nargs, liftFn);
|
||||||
|
defnOp(name, op);
|
||||||
|
} else {
|
||||||
|
const nargs = [...Array(args).keys()];
|
||||||
|
const liftFn = (scope) => {
|
||||||
|
let newscope = Object.create(scope);
|
||||||
|
for (let i = 0; i < args; i++) {
|
||||||
|
newscope[i] = scope[i][0];
|
||||||
|
}
|
||||||
|
return fn(newscope);
|
||||||
|
}
|
||||||
|
const op = makeOp(nargs, liftFn);
|
||||||
|
defnOp(name, op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const add = (args) => [{type:"num", val:args[0] + args[1]}];
|
||||||
|
const sub = (args) => [{type:"num", val:args[0] - args[1]}];
|
||||||
|
const div = (args) => [{type:"num", val:args[0] / args[1]}];
|
||||||
|
const mult = (args) => [{type:"num", val:args[0] * args[1]}];
|
||||||
|
const pow = (args) => [{type:"num", val:Math.pow(args[0], args[1])}];
|
||||||
|
const root = (args) => [{type:"num", val:Math.sqrt(args[0])}];
|
||||||
|
const type = (args) => [{type:"type", val:args[0].type}];
|
||||||
|
const pair = (args) => [{type:"pair", val:{fst:args[0], snd:args[1]}}];
|
||||||
|
const fst = (args) => [args[0].fst];
|
||||||
|
const snd = (args) => [args[0].snd];
|
||||||
|
const tuple = (args) => makeOp([...Array(args[0]).keys()], (args) => {return [{type:"tuple", val:args}]});
|
||||||
|
const index = (args) => args[0][args[1]];
|
||||||
|
const len = (args) => [{type:"num", val:args[0].length}];
|
||||||
|
|
||||||
|
const eq = (args) => {
|
||||||
|
if (args[2].type === args[3].type && objEq(args[2].val, args[3].val)) {
|
||||||
|
return [args[0]];
|
||||||
|
} else {
|
||||||
|
return [args[1]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addDefn("+", ["num", "num"], add);
|
||||||
|
addDefn("-", ["num", "num"], sub);
|
||||||
|
addDefn("/", ["num", "num"], div);
|
||||||
|
addDefn("*", ["num", "num"], mult);
|
||||||
|
addDefn("^", ["num", "num"], pow);
|
||||||
|
addDefn("sqrt", ["num"], root);
|
||||||
|
addDefn("==", 4, eq);
|
||||||
|
addDefn("pair", 2, pair);
|
||||||
|
addDefn("fst", ["pair"], fst);
|
||||||
|
addDefn("snd", ["pair"], snd);
|
||||||
|
addDefn("tuple", ["num"], tuple);
|
||||||
|
addDefn("!!", ["tuple", "num"], index);
|
||||||
|
addDefn("len", ["tuple"], len);
|
||||||
|
addDefn("typeof", 1, type);
|
||||||
|
addRPNDefn("stop", "\"stop");
|
||||||
|
addRPNDefn("inv", "(x -> 1 x /)");
|
||||||
|
addRPNDefn("fold", "(x acc fn -> acc '(-> x acc fn 'fn fold) 'x stop ==)");
|
||||||
|
addRPNDefn("range", "(x y -> x '(->x x 1 + y range) 'x y ==)");
|
||||||
|
addRPNDefn("listthen", "(fn -> (internal; x acc -> '(->acc fn) '(->x acc pair internal) x stop ==) 0 tuple internal)");
|
||||||
|
addRPNDefn("list", "'(a -> a) listthen");
|
||||||
|
addRPNDefn("lmap", "(list fn -> list '(->list fst fn list snd 'fn lmap pair) list 0 tuple ==)");
|
||||||
|
addRPNDefn("unlist", "(l -> (internal; list -> '(->) '(->list fst list snd internal) list 0 tuple ==) stop l internal)");
|
||||||
|
addRPNDefn("map", "fn -> '(l->l 'fn lmap unlist) listthen");
|
48
experiments/rpncalc-v4/index.html
Normal file
48
experiments/rpncalc-v4/index.html
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
title: RPNCalc v4
|
||||||
|
slug: rpncalc4
|
||||||
|
description: Reverse Polish Notation calculator, version 4 - increasingly esoteric and incomprehensible. Contributed by Aidan.
|
||||||
|
---
|
||||||
|
<style>
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 30%;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
background-color:rgb(230, 230, 230);
|
||||||
|
padding: 0 0.125rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<textarea id="inbox"></textarea>
|
||||||
|
<button id="step">step</button>
|
||||||
|
<button id="play">play</button>
|
||||||
|
<button id="load">load</button>
|
||||||
|
<input type="checkbox" id="use-std" name="use-std" checked>
|
||||||
|
<label for="use-std">use stdlib?</label>
|
||||||
|
<pre id="insbox"></pre>
|
||||||
|
<pre id="outbox"></pre>
|
||||||
|
<script src="./main.js" type="module"></script>
|
||||||
|
<h3>documentation</h3>
|
||||||
|
<p>use <code>(name; value)</code> to define something. the definition can be recursive. <code>value</code> is executed and <code>name</code> is set to the final state of the stack, i.e. <code>(name; 1 3)</code> is possible</p>
|
||||||
|
<p>use <code>'</code> to push instead of apply to the stack, e.g. <code>'(a -> a)</code>. This is useful for lazyness, i.e. <code>'(->lazy evaluated thing)</code></p>
|
||||||
|
<ul>
|
||||||
|
<li><code>+, -, *, /, ^, sqrt</code>: mathematical operations</li>
|
||||||
|
<li><code>==</code>: equality (automatically derived for all types); returns <code>a b -> a</code> if true, <code>a b -> b</code> if false</li>
|
||||||
|
<li><code>typeof</code>: returns the type of the object</li>
|
||||||
|
<li><code>pair, fst, snd</code>: pairs two objects, gets first or second item of pair</li>
|
||||||
|
<li><code>tuple</code>: used like <code>... 3 tuple</code>; creates an n tuple of n items on the stack</li>
|
||||||
|
<li><code>!!</code>: index into a tuple</li>
|
||||||
|
<li><code>len</code>: length of a tuple</li>
|
||||||
|
</ul>
|
||||||
|
<h3>stdlib</h3>
|
||||||
|
<ul>
|
||||||
|
<li><code>stop; "stop</code></li>
|
||||||
|
<li><code>inv; x -> 1 x /</code></li>
|
||||||
|
<li><code>fold; x acc fn -> acc '(-> x acc fn 'fn fold) 'x \"stop ==</code></li>
|
||||||
|
<li><code>range; x y -> x '(->x x 1 + y range) 'x y ==</code></li>
|
||||||
|
<li><code>listthen; fn -> (internal; x acc -> '(->acc fn) '(->x acc pair internal) x stop ==) 0 tuple internal</code></li>
|
||||||
|
<li><code>list; (a -> a) listthen</code></li>
|
||||||
|
<li><code>lmap; list fn -> list '(->list fst fn list snd 'fn lmap pair) list 0 tuple ==</code></li>
|
||||||
|
<li><code>unlist; l -> (internal; list -> '(->) '(->list fst list snd internal) list 0 tuple ==) stop l internal</code></li>
|
||||||
|
<li><code>map; fn -> '(l->l 'fn lmap unlist) listthen</code></li>
|
||||||
|
</ul>
|
130
experiments/rpncalc-v4/main.js
Normal file
130
experiments/rpncalc-v4/main.js
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
import {execRPN, step} from './shiny.mjs';
|
||||||
|
import {parseExprs} from './parse.mjs';
|
||||||
|
import {tokenize} from './token.mjs';
|
||||||
|
import {scope, customHandler} from './builtin.mjs';
|
||||||
|
|
||||||
|
//const scope = {};
|
||||||
|
//const customHandler = a => [a];
|
||||||
|
|
||||||
|
const inbox = document.getElementById("inbox")
|
||||||
|
const insbox = document.getElementById("insbox")
|
||||||
|
const outbox = document.getElementById("outbox")
|
||||||
|
const stepb = document.getElementById("step")
|
||||||
|
const play = document.getElementById("play")
|
||||||
|
const load = document.getElementById("load")
|
||||||
|
|
||||||
|
const usestd = document.getElementById("use-std")
|
||||||
|
|
||||||
|
let state = null;
|
||||||
|
let input = null;
|
||||||
|
|
||||||
|
const highlight = (str, start, end, color) => {
|
||||||
|
return str.slice(0, start) + `<span style='background-color:${color}'>` + str.slice(start, end) + "</span>" + str.slice(end);
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadState = () => {
|
||||||
|
input = inbox.value;
|
||||||
|
let toks = tokenize(input);
|
||||||
|
let ast = parseExprs(toks);
|
||||||
|
console.log(ast);
|
||||||
|
if (ast.parsed === null) {
|
||||||
|
insbox.innerHTML = "could not parse AST!"
|
||||||
|
} else if (ast.stream.length !== 0) {
|
||||||
|
insbox.innerHTML = highlight(input, ast.stream[0].startPos, ast.stream[0].endPos, "red");
|
||||||
|
insbox.innerHTML += "<br>unexpected token"
|
||||||
|
input = null;
|
||||||
|
state = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
insbox.innerHTML = input;
|
||||||
|
outbox.innerHTML = "";
|
||||||
|
if (usestd.checked) {
|
||||||
|
state = {scopes:[scope], stacks:[[]], calls:[ast.parsed.arr]};
|
||||||
|
} else {
|
||||||
|
state = {scopes:[{}], stacks:[[]], calls:[ast.parsed.arr]};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const showIns = (ins) => {
|
||||||
|
if (ins.val) {
|
||||||
|
return ins.val;
|
||||||
|
} else if (ins.ident) {
|
||||||
|
return ins.ident;
|
||||||
|
} else if (ins.name) {
|
||||||
|
return ins.name;
|
||||||
|
} else {
|
||||||
|
return 'anon';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
load.onclick = _ => {
|
||||||
|
loadState();
|
||||||
|
}
|
||||||
|
|
||||||
|
play.onclick = _ => {
|
||||||
|
if (state === null) {
|
||||||
|
loadState();
|
||||||
|
}
|
||||||
|
insbox.innerHTML = "";
|
||||||
|
while (state.calls[0].length > 0 || state.calls.length > 1) {
|
||||||
|
try {
|
||||||
|
step(state, customHandler, showIns);
|
||||||
|
} catch (err) {
|
||||||
|
insbox.innerHTML = err;
|
||||||
|
state = null;
|
||||||
|
input = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outbox.innerHTML = prettyprint(state.stacks[0]);
|
||||||
|
state = null;
|
||||||
|
input = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
stepb.onclick = _ => {
|
||||||
|
if (state === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state.calls[0].length > 0 || state.calls.length > 1) {
|
||||||
|
let pos;
|
||||||
|
try {
|
||||||
|
pos = step(state, customHandler, showIns);
|
||||||
|
} catch (err) {
|
||||||
|
insbox.innerHTML = err;
|
||||||
|
state = null;
|
||||||
|
input = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(pos.start === 0 && pos.end === 0)) {
|
||||||
|
insbox.innerHTML = highlight(input, pos.start, pos.end, "green");
|
||||||
|
}
|
||||||
|
if (state.stacks.length > 1) {
|
||||||
|
outbox.innerHTML = "... " + prettyprint(state.stacks[state.stacks.length-1]);
|
||||||
|
} else {
|
||||||
|
outbox.innerHTML = prettyprint(state.stacks[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const show = (elem) => {
|
||||||
|
if (elem.type === "pair") {
|
||||||
|
return "{" + show(elem.val.fst) + ", " + show(elem.val.snd) + "}"
|
||||||
|
} else if (elem.type === "closure") {
|
||||||
|
return "(needs " + elem.val.args.length + ")"
|
||||||
|
} else if (elem.type === "array") {
|
||||||
|
return "[" + prettyprint(elem.val) + "]"
|
||||||
|
} else {
|
||||||
|
return "(" + elem.val + ": " + elem.type + ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const prettyprint = (out) => {
|
||||||
|
let str = "";
|
||||||
|
for (let i = 0; i < out.length; i++) {
|
||||||
|
str += show(out[i]);
|
||||||
|
if (i < out.length - 1) {
|
||||||
|
str += " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
231
experiments/rpncalc-v4/parse.mjs
Normal file
231
experiments/rpncalc-v4/parse.mjs
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
/* make parser safe */
|
||||||
|
const debug = false;
|
||||||
|
|
||||||
|
const attempt = (parser) => (stream) => {
|
||||||
|
if (debug) {
|
||||||
|
console.log("attempt");
|
||||||
|
console.log(stream);
|
||||||
|
}
|
||||||
|
let streamclone = [...stream];
|
||||||
|
try {
|
||||||
|
let out = parser(stream);
|
||||||
|
return out;
|
||||||
|
} catch(err) {
|
||||||
|
return {parsed:null,stream:streamclone};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* chain */
|
||||||
|
const or = (a, b) => (stream) => {
|
||||||
|
let streamclone = [...stream];
|
||||||
|
if (debug) {
|
||||||
|
console.log("or");
|
||||||
|
console.log(stream);
|
||||||
|
}
|
||||||
|
let aout = a(stream);
|
||||||
|
if (aout.parsed === null) {
|
||||||
|
return b(streamclone);
|
||||||
|
} else {
|
||||||
|
return aout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (parser) */
|
||||||
|
const parens = (parser) => (stream) => {
|
||||||
|
if (debug) {
|
||||||
|
console.log("parens");
|
||||||
|
console.log(stream);
|
||||||
|
}
|
||||||
|
let a = parseSyntax("(")(stream);
|
||||||
|
if (a.parsed === null) {
|
||||||
|
return {parsed:null, stream:a.stream};
|
||||||
|
}
|
||||||
|
let dat = parser(a.stream);
|
||||||
|
let b = parseSyntax(")")(dat.stream);
|
||||||
|
if (b.parsed === null) {
|
||||||
|
throw 'mismatched parens!';
|
||||||
|
}
|
||||||
|
dat.parsed.pos.start = a.parsed.pos.start;
|
||||||
|
dat.parsed.pos.end = b.parsed.pos.end;
|
||||||
|
return {parsed:dat.parsed, stream:b.stream};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [parser] */
|
||||||
|
const many = (parser) => (stream) => {
|
||||||
|
if (debug) {
|
||||||
|
console.log("many");
|
||||||
|
console.log(stream);
|
||||||
|
}
|
||||||
|
let parsed = [];
|
||||||
|
while (true) {
|
||||||
|
let i = parser(stream);
|
||||||
|
stream = i.stream;
|
||||||
|
if (i.parsed === null) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
parsed.push(i.parsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let startPos = 0;
|
||||||
|
let endPos = 0;
|
||||||
|
if (parsed.length > 1) {
|
||||||
|
startPos = parsed[0].pos.start;
|
||||||
|
endPos = parsed[parsed.length-1].pos.start;
|
||||||
|
}
|
||||||
|
return {parsed:{arr:parsed, pos:{start:startPos, end:endPos}}, stream:stream};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* takes in stream, outputs parsed item or null */
|
||||||
|
const parseIdent = (stream) => {
|
||||||
|
if (debug) {
|
||||||
|
console.log("ident");
|
||||||
|
console.log(stream);
|
||||||
|
}
|
||||||
|
let e = stream[0];
|
||||||
|
if (e === undefined) {
|
||||||
|
return {parsed:null, stream:stream};
|
||||||
|
}
|
||||||
|
if (e.type !== "ident") {
|
||||||
|
return {parsed:null, stream:stream};
|
||||||
|
} else {
|
||||||
|
stream.shift();
|
||||||
|
return {parsed:{type:e.type, val:e.name, pos:{start:e.startPos, end:e.endPos}}, stream:stream};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* takes in stream, outputs parsed item or null */
|
||||||
|
const parseNum = (stream) => {
|
||||||
|
if (debug) {
|
||||||
|
console.log("num");
|
||||||
|
console.log(stream);
|
||||||
|
}
|
||||||
|
let e = stream[0];
|
||||||
|
if (e === undefined) {
|
||||||
|
return {parsed:null, stream:stream};
|
||||||
|
}
|
||||||
|
if (e.type !== "num") {
|
||||||
|
return {parsed:null, stream:stream};
|
||||||
|
} else {
|
||||||
|
stream.shift();
|
||||||
|
return {parsed:{type:e.type, val:e.val, pos:{start:e.startPos, end:e.endPos}}, stream:stream};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* takes in stream, outputs parsed item or null */
|
||||||
|
const parseSyntax = (syntax) => (stream) => {
|
||||||
|
if (debug) {
|
||||||
|
console.log("syntax", syntax);
|
||||||
|
console.log(stream);
|
||||||
|
}
|
||||||
|
let e = stream[0];
|
||||||
|
if (e === undefined) {
|
||||||
|
return {parsed:null, stream:stream};
|
||||||
|
}
|
||||||
|
if (e.type !== "syntax") {
|
||||||
|
return {parsed:null, stream:stream};
|
||||||
|
}
|
||||||
|
if (e.val !== syntax) {
|
||||||
|
return {parsed:null, stream:stream};
|
||||||
|
} else {
|
||||||
|
stream.shift();
|
||||||
|
return {parsed:{type:"syntax", val:syntax, pos:{start:e.startPos, end:e.endPos}}, stream:stream};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* takes in stream, outputs string or null - FAILABLE */
|
||||||
|
const parseName = (stream) => {
|
||||||
|
if (debug) {
|
||||||
|
console.log("name");
|
||||||
|
console.log(stream);
|
||||||
|
}
|
||||||
|
let id = parseIdent(stream);
|
||||||
|
if (id.parsed === null) {
|
||||||
|
return {parsed:null, stream:id.stream};
|
||||||
|
}
|
||||||
|
let syn = parseSyntax(";")(id.stream);
|
||||||
|
if (syn.parsed === null) {
|
||||||
|
throw 'could not parse name!'
|
||||||
|
}
|
||||||
|
return {parsed:{name:id.parsed.val, pos:{start:id.parsed.pos.start, end:syn.parsed.pos.end}}, stream:syn.stream};
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseType = (stream) => {
|
||||||
|
if (debug) {
|
||||||
|
console.log("type");
|
||||||
|
console.log(stream);
|
||||||
|
}
|
||||||
|
let syn = attempt(parseSyntax("\""))(stream);
|
||||||
|
if (syn.parsed === null) {
|
||||||
|
return {parsed:null, stream:syn.stream};
|
||||||
|
}
|
||||||
|
let id = or(parseIdent, parseNum)(syn.stream);
|
||||||
|
if (id.parsed === null) {
|
||||||
|
return {parsed:null, stream:id.stream};
|
||||||
|
}
|
||||||
|
return {parsed:{type:"type", val:id.parsed.val, pos:{start:syn.parsed.pos.start, end:id.parsed.pos.end}}, stream:id.stream};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* takes in stream, outputs parsed item or null - FAILABLE */
|
||||||
|
const parsePush = (stream) => {
|
||||||
|
if (debug) {
|
||||||
|
console.log("push");
|
||||||
|
console.log(stream);
|
||||||
|
}
|
||||||
|
let syn = attempt(parseSyntax("'"))(stream);
|
||||||
|
if (syn.parsed === null) {
|
||||||
|
return {parsed:null, stream:syn.stream};
|
||||||
|
}
|
||||||
|
let id = parseExpr(syn.stream);
|
||||||
|
if (id.parsed === null) {
|
||||||
|
return {parsed:null, stream:id.stream};
|
||||||
|
}
|
||||||
|
return {parsed:{type:"push", elem:id.parsed, pos:{start:syn.parsed.pos.start, end:id.parsed.pos.end}}, stream:id.stream};
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseDefn = (stream) => {
|
||||||
|
if (debug) {
|
||||||
|
console.log("defn");
|
||||||
|
console.log(stream);
|
||||||
|
}
|
||||||
|
let name = parseName(stream);
|
||||||
|
if (name.parsed === null) {
|
||||||
|
throw 'no name found!'
|
||||||
|
}
|
||||||
|
let expr = parseExprs(stream);
|
||||||
|
if (expr.parsed === null) {
|
||||||
|
throw 'no body found!'
|
||||||
|
}
|
||||||
|
return {parsed:{type:"defn", ident:name.parsed.name, defn:expr.parsed.arr, pos:{start:name.parsed.pos.start, end:expr.parsed.pos.end}}, stream:expr.stream}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* takes in stream, outputs parsed item or null - FAILABLE */
|
||||||
|
const parseLambda = (stream) => {
|
||||||
|
if (debug) {
|
||||||
|
console.log("lambda");
|
||||||
|
console.log(stream);
|
||||||
|
}
|
||||||
|
let args = many(parseIdent)(stream);
|
||||||
|
let syn = parseSyntax("->")(args.stream);
|
||||||
|
if (syn.parsed === null) {
|
||||||
|
throw 'no lambda body found!';
|
||||||
|
}
|
||||||
|
let body = parseExprs(syn.stream); // .parsed should never be null, but anyway...
|
||||||
|
if (body.parsed === null) {
|
||||||
|
throw 'no lambda body found!';
|
||||||
|
}
|
||||||
|
return {parsed:{type:"func", args:args.parsed.arr.map(x => x.val), body:body.parsed.arr, pos:{start:args.parsed.pos.start, end:body.parsed.pos.end}}, stream:body.stream};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* takes in stream, outputs parsed item or null */
|
||||||
|
const parseExpr = or(
|
||||||
|
attempt(parens(parseDefn)), or(
|
||||||
|
attempt(parens(parseLambda)), or(
|
||||||
|
attempt(parseLambda), or(
|
||||||
|
parseType, or(
|
||||||
|
parseIdent, or(
|
||||||
|
parseNum,
|
||||||
|
parsePush
|
||||||
|
))))));
|
||||||
|
|
||||||
|
/* takes in stream, outputs parsed items */
|
||||||
|
export const parseExprs = many(parseExpr);
|
158
experiments/rpncalc-v4/shiny.mjs
Normal file
158
experiments/rpncalc-v4/shiny.mjs
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
EVAL.js
|
||||||
|
-------
|
||||||
|
|
||||||
|
reads exprs from AST and executes them
|
||||||
|
|
||||||
|
types of elem:
|
||||||
|
all have type
|
||||||
|
all have val
|
||||||
|
|
||||||
|
TYPE VAL
|
||||||
|
"closure" {scope:{}, args:['x', 'y'], defn:<function-like>}
|
||||||
|
|
||||||
|
<function-like>:
|
||||||
|
{type:"ins", ins:[]}
|
||||||
|
{type:"builtin", fn:(scope) => {stack}}
|
||||||
|
|
||||||
|
exported functionality:
|
||||||
|
defnOp(name, function) - define a built-in operator
|
||||||
|
defn(name, ins, scope) - extend scope with AST
|
||||||
|
makeOp(name, args, fn) - lift a function to an operator
|
||||||
|
evalRPN(scope, ast)
|
||||||
|
*/
|
||||||
|
|
||||||
|
let builtins = {};
|
||||||
|
|
||||||
|
export const defnOp = (name, data) => {
|
||||||
|
builtins[name] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defn = (name, ins, scope) => {
|
||||||
|
let defscope = Object.create(scope);
|
||||||
|
let fnForm = {type:"closure", val:{scope:defscope, args:[], defn:{type:"ins", ins:ins}}};
|
||||||
|
defscope[name] = [fnForm];
|
||||||
|
let out = execRPN(defscope, ins);
|
||||||
|
defscope[name] = out.stacks[0];
|
||||||
|
return defscope;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const makeOp = (args, fn) => {
|
||||||
|
return [{type:"closure", val:{scope:{}, args:args, defn:{type:"builtin", fn:fn}}}];
|
||||||
|
}
|
||||||
|
|
||||||
|
const lookup = (name, scope) => {
|
||||||
|
let n = scope[name];
|
||||||
|
if (n) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
n = builtins[name];
|
||||||
|
if (n) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
console.log(scope);
|
||||||
|
throw '"' + name + '" not in scope'
|
||||||
|
}
|
||||||
|
|
||||||
|
const extend = (scope, name, elems) => {
|
||||||
|
let o = Object.create(scope);
|
||||||
|
o[name] = elems;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
const runFn = (defn, state) => {
|
||||||
|
if (defn.type === "ins") {
|
||||||
|
state.calls.push(defn.ins);
|
||||||
|
state.stacks.push([]);
|
||||||
|
} else if (defn.type === "builtin") {
|
||||||
|
let scope = state.scopes[state.scopes.length-1];
|
||||||
|
let out = defn.fn(scope);
|
||||||
|
state.calls.push([]);
|
||||||
|
state.stacks.push(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const giveArg = (arg, elem) => {
|
||||||
|
let argN = elem.val.args[elem.val.args.length-1];
|
||||||
|
let newscope = extend(elem.val.scope, argN, [arg]);
|
||||||
|
return {type:elem.type, val:{scope:newscope, args:elem.val.args.slice(0,-1), defn:elem.val.defn}};
|
||||||
|
}
|
||||||
|
|
||||||
|
const apply = (elem, state) => {
|
||||||
|
if (elem.type === "closure") {
|
||||||
|
if (elem.val.args.length === 0) {
|
||||||
|
state.scopes.push(elem.val.scope);
|
||||||
|
runFn(elem.val.defn, state);
|
||||||
|
} else if (state.stacks[state.stacks.length-1].length > 0) {
|
||||||
|
apply(giveArg(state.stacks[state.stacks.length-1].pop(), elem), state);
|
||||||
|
} else {
|
||||||
|
state.stacks[state.stacks.length-1].push(elem);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state.stacks[state.stacks.length-1].push(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const applyMany = (elems, state) => {
|
||||||
|
for (let i = 0; i < elems.length; i++) {
|
||||||
|
apply(elems[i], state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const makeStackElems = (ins, state, handler) => {
|
||||||
|
if (ins.type === "push") {
|
||||||
|
throw 'nested push error'
|
||||||
|
} else if (ins.type === "ident") {
|
||||||
|
return lookup(ins.val, state.scopes[state.scopes.length-1]);
|
||||||
|
} else if (ins.type === "func") {
|
||||||
|
return [{type:"closure", val:{scope:state.scopes[state.scopes.length-1], args:ins.args, defn:{type:"ins", ins:ins.body}}}];
|
||||||
|
} else {
|
||||||
|
return handler(ins);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const doIns = (ins, state, handler) => {
|
||||||
|
if (ins.type === "push") {
|
||||||
|
state.stacks[state.stacks.length-1] = state.stacks[state.stacks.length-1].concat(makeStackElems(ins.elem, state, handler));
|
||||||
|
} else if (ins.type === "defn") {
|
||||||
|
state.scopes[state.scopes.length-1] = defn(ins.ident, ins.defn, state.scopes[state.scopes.length-1]);
|
||||||
|
} else {
|
||||||
|
applyMany(makeStackElems(ins, state, handler), state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const step = (state, handler, showIns, maxdepth) => {
|
||||||
|
if (state.stacks.length > maxdepth) {
|
||||||
|
throw 'max recursion depth exceeded'
|
||||||
|
}
|
||||||
|
if (state.calls[state.calls.length-1].length === 0) {
|
||||||
|
if (state.calls.length === 1) {
|
||||||
|
throw 'finished execution'
|
||||||
|
}
|
||||||
|
if (state.stacks.length < 2) {
|
||||||
|
throw 'nothing to return'
|
||||||
|
}
|
||||||
|
state.calls.pop();
|
||||||
|
state.scopes.pop();
|
||||||
|
let out = state.stacks.pop();
|
||||||
|
applyMany(out, state);
|
||||||
|
return {start:0, end:0};
|
||||||
|
} else {
|
||||||
|
let ins = state.calls[state.calls.length-1][0];
|
||||||
|
state.calls[state.calls.length-1] = state.calls[state.calls.length-1].slice(1);
|
||||||
|
try {
|
||||||
|
doIns(ins, state, handler);
|
||||||
|
} catch (error) {
|
||||||
|
throw error + ' while executing "' + showIns(ins) + '"'
|
||||||
|
}
|
||||||
|
return ins.pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const execRPN = (scope, ins, handler=(x)=>[x], showIns=(x)=>x.name, maxdepth=16384) => {
|
||||||
|
let state = {scopes:[scope], stacks:[[]], calls:[ins]};
|
||||||
|
while (state.calls[0].length > 0 || state.calls.length > 1) {
|
||||||
|
step(state, handler, showIns, maxdepth);
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
57
experiments/rpncalc-v4/token.mjs
Normal file
57
experiments/rpncalc-v4/token.mjs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
const tokens = (stream) => {
|
||||||
|
let toks = [];
|
||||||
|
let currTok = {startPos:0, val:"", type:"str", endPos:0};
|
||||||
|
for (let i = 0; i < stream.length; i++) {
|
||||||
|
if ("()';\"".includes(stream[i])) {
|
||||||
|
if (currTok.val !== "") {
|
||||||
|
currTok.endPos = i;
|
||||||
|
toks.push(currTok);
|
||||||
|
}
|
||||||
|
toks.push({startPos:i, endPos:i+1, val:stream[i], type:"syntax"});
|
||||||
|
currTok = {startPos:i+1, val:"", type:"str"};
|
||||||
|
} else if (stream[i] === "-") {
|
||||||
|
if (stream[i+1] === ">") {
|
||||||
|
if (currTok.val !== "") {
|
||||||
|
currTok.endPos = i;
|
||||||
|
toks.push(currTok);
|
||||||
|
}
|
||||||
|
toks.push({startPos:i, endPos:i+1, val:"->", type:"syntax"});
|
||||||
|
i++;
|
||||||
|
currTok = {startPos:i+1, val:"", type:"str"};
|
||||||
|
} else {
|
||||||
|
currTok.val += "-";
|
||||||
|
}
|
||||||
|
} else if (/\s/.test(stream[i])) {
|
||||||
|
if (currTok.val !== "") {
|
||||||
|
currTok.endPos = i;
|
||||||
|
toks.push(currTok);
|
||||||
|
}
|
||||||
|
currTok = {startPos:i+1, val:"", type:"str"};
|
||||||
|
} else {
|
||||||
|
currTok.val += stream[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currTok.val !== "") {
|
||||||
|
currTok.endPos = stream.length;
|
||||||
|
toks.push(currTok);
|
||||||
|
}
|
||||||
|
return toks;
|
||||||
|
}
|
||||||
|
|
||||||
|
const classify = (tokens) => {
|
||||||
|
return tokens.map(tok => {
|
||||||
|
if (tok.type === "str") {
|
||||||
|
if (!isNaN(tok.val)) {
|
||||||
|
return {startPos:tok.startPos, endPos:tok.endPos, val:Number(tok.val), type:"num"};
|
||||||
|
} else {
|
||||||
|
return {startPos:tok.startPos, endPos:tok.endPos, name:tok.val, type:"ident"};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const tokenize = (stream) => {
|
||||||
|
return classify(tokens(stream));
|
||||||
|
}
|
@ -2,9 +2,7 @@
|
|||||||
<section class="atl">
|
<section class="atl">
|
||||||
{{range .Articles}}
|
{{range .Articles}}
|
||||||
<div class="art">
|
<div class="art">
|
||||||
<h4>
|
<a class="ttl" href="{{.Link}}" target="_blank" rel="noopener">{{.Title}}</a>
|
||||||
<a href="{{.Link}}" target="_blank" rel="noopener">{{.Title}}</a>
|
|
||||||
</h4>
|
|
||||||
<p class="sum">{{.Summary}}</p>
|
<p class="sum">{{.Summary}}</p>
|
||||||
<small class="src">
|
<small class="src">
|
||||||
via <a href="{{.SourceLink}}">{{.SourceTitle}}</a>
|
via <a href="{{.SourceLink}}">{{.SourceTitle}}</a>
|
||||||
|
@ -8,7 +8,8 @@
|
|||||||
"https://slatestarcodex.com/feed/",
|
"https://slatestarcodex.com/feed/",
|
||||||
"https://www.rifters.com/crawl/?feed=rss2",
|
"https://www.rifters.com/crawl/?feed=rss2",
|
||||||
"https://drewdevault.com/feed.xml",
|
"https://drewdevault.com/feed.xml",
|
||||||
"https://www.giantitp.com/comics/oots.rss",
|
"https://qntm.org/rss.php",
|
||||||
"https://qntm.org/rss.php"
|
"https://aphyr.com/posts.atom",
|
||||||
|
"https://os.phil-opp.com/rss.xml"
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -178,7 +178,7 @@ const runOpenring = async () => {
|
|||||||
console.log(arg)
|
console.log(arg)
|
||||||
const out = await util.promisify(childProcess.exec)(arg)
|
const out = await util.promisify(childProcess.exec)(arg)
|
||||||
console.log(out.stderr)
|
console.log(out.stderr)
|
||||||
return out.stdout
|
return minifyHTML(out.stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
const run = async () => {
|
const run = async () => {
|
||||||
|
@ -76,9 +76,6 @@ ul
|
|||||||
flex-wrap: wrap
|
flex-wrap: wrap
|
||||||
margin: -0.5rem
|
margin: -0.5rem
|
||||||
|
|
||||||
h4
|
|
||||||
margin: 0
|
|
||||||
|
|
||||||
.art
|
.art
|
||||||
flex: 1 1 0
|
flex: 1 1 0
|
||||||
display: flex
|
display: flex
|
||||||
|
Loading…
Reference in New Issue
Block a user