diff options
Diffstat (limited to 'awk')
37 files changed, 6301 insertions, 0 deletions
diff --git a/awk/2222.awk b/awk/2222.awk new file mode 100755 index 0000000..bafa5ac --- /dev/null +++ b/awk/2222.awk @@ -0,0 +1,6 @@ +#!/bin/bash + +file="2222.txt" + +# read each line from the file and sleep for 0.6039604 seconds +awk '{ system("clear"); print $0; system("sleep 0.6039604") }' "$file" diff --git a/awk/2222.txt b/awk/2222.txt new file mode 100644 index 0000000..e608ec7 --- /dev/null +++ b/awk/2222.txt @@ -0,0 +1,2222 @@ +ABET +ABLE +ABLY +ABUT +ACAI +ACED +ACES +ACHE +ACHY +ACID +ACME +ACNE +ACRE +ACTS +ADDS +AEON +AFAR +AFRO +AGED +AGES +AGOG +AHEM +AIDE +AILS +AIMS +AIRS +AIRY +AJAR +AKIN +ALAS +ALLY +ALMS +ALOE +ALSO +ALTO +ALUM +AMEN +AMID +AMMO +AMOK +AMPS +ANDS +ANEW +ANKH +ANON +ANTI +ANTS +APES +APEX +APPS +AQUA +ARCH +ARCS +AREA +ARIA +ARID +ARKS +ARMS +ARMY +ARSE +ARTS +ARTY +ASHY +ASKS +ATOM +ATOP +AUNT +AURA +AUTO +AVID +AVOW +AWAY +AWED +AWES +AWLS +AWRY +AXED +AXES +AXIS +AXLE +BABA +BABE +BABY +BACH +BACK +BADS +BAGS +BAIL +BAIT +BAKE +BALD +BALE +BALK +BALL +BALM +BAND +BANE +BANG +BANK +BANS +BARB +BARD +BARE +BARF +BARK +BARN +BARS +BASE +BASH +BASK +BASS +BAST +BATH +BATS +BAUD +BAWL +BAYS +BEAD +BEAK +BEAM +BEAN +BEAR +BEAT +BEAU +BECK +BEDS +BEEF +BEEN +BEEP +BEER +BEES +BEET +BEGS +BELL +BELT +BEND +BENT +BERG +BERM +BEST +BETA +BETS +BEVY +BEYS +BIAS +BIBS +BIDE +BIDS +BIFF +BIKE +BILE +BILK +BILL +BIND +BIOS +BIRD +BITE +BITS +BLAB +BLAH +BLEB +BLED +BLEW +BLIP +BLOB +BLOC +BLOG +BLOT +BLOW +BLUE +BLUR +BOAR +BOAS +BOAT +BOBS +BODE +BODS +BODY +BOGS +BOGY +BOIL +BOLA +BOLD +BOLO +BOLT +BOMB +BOND +BONE +BONG +BONK +BONY +BOOK +BOOM +BOON +BOOR +BOOS +BOOT +BORE +BORN +BOSS +BOTH +BOTS +BOUT +BOWL +BOWS +BOXY +BOYO +BOYS +BOZO +BRAG +BRAN +BRAS +BRAT +BRAY +BRED +BREW +BRIE +BRIG +BRIM +BRIS +BRIT +BROS +BROW +BUCK +BUDS +BUFF +BUGS +BULB +BULK +BULL +BUMP +BUMS +BUND +BUNG +BUNK +BUNS +BUNT +BUOY +BURB +BURG +BURL +BURN +BURP +BURR +BURY +BUSH +BUSK +BUST +BUSY +BUTT +BUYS +BUZZ +BYES +BYTE +CABS +CAFE +CAFF +CAGE +CAKE +CALF +CALL +CALM +CAME +CAMO +CAMP +CANE +CANS +CAPE +CAPO +CAPS +CARB +CARD +CARE +CARP +CARS +CART +CASE +CASH +CASK +CAST +CATS +CAVE +CAWS +CAYS +CEDE +CELL +CELS +CELT +CENT +CESS +CHAD +CHAI +CHAP +CHAR +CHAT +CHEF +CHEW +CHIC +CHIN +CHIP +CHIT +CHOP +CHOW +CHUB +CHUG +CHUM +CIAO +CITE +CITY +CLAD +CLAM +CLAN +CLAP +CLAW +CLAY +CLEF +CLIP +CLOD +CLOG +CLOP +CLOT +CLUB +CLUE +COAL +COAT +COAX +COBS +COCA +COCO +CODA +CODE +CODS +COED +COGS +COIL +COIN +COKE +COLA +COLD +COLE +COLT +COMA +COMB +COME +COMP +CONE +CONS +COOK +COOL +COOP +COOS +COPE +COPS +COPY +CORD +CORE +CORK +CORN +COST +COSY +COTS +COUP +COVE +COWL +COWS +COZY +CRAB +CRAG +CRAM +CRAP +CRAW +CREW +CRIB +CRIT +CROC +CROP +CROW +CRUD +CRUX +CUBE +CUBS +CUDS +CUED +CUES +CUFF +CULL +CULT +CUPS +CURB +CURD +CURE +CURL +CURT +CUSP +CUSS +CUTE +CUTS +CYAN +CYST +CZAR +DABS +DADA +DADS +DAFT +DAME +DAMN +DAMP +DAMS +DANK +DARE +DARK +DARN +DART +DASH +DATA +DATE +DAWN +DAYS +DAZE +DEAD +DEAF +DEAL +DEAN +DEAR +DEBT +DECK +DEED +DEEM +DEEP +DEER +DEFT +DEFY +DELI +DELL +DELT +DEMO +DENS +DENT +DERM +DESK +DEWS +DEWY +DIAL +DICE +DIED +DIES +DIET +DIGS +DILL +DIME +DIMS +DINE +DING +DINK +DINO +DINT +DIPS +DIRE +DIRT +DISC +DISH +DISK +DISS +DIVA +DIVE +DOCK +DOCS +DODO +DOER +DOES +DOFF +DOGS +DOJO +DOLE +DOLL +DOLT +DOME +DONE +DONG +DOOM +DOOR +DOPE +DORK +DORM +DORY +DOSE +DOTE +DOTH +DOTS +DOUR +DOVE +DOWN +DOXY +DOZE +DOZY +DRAB +DRAG +DRAM +DRAW +DRAY +DREW +DRIP +DROP +DRUG +DRUM +DRYS +DUAL +DUBS +DUCK +DUCT +DUDE +DUDS +DUEL +DUES +DUET +DUFF +DUKE +DULL +DULY +DUMB +DUMP +DUNE +DUNG +DUNK +DUNS +DUOS +DUPE +DUSK +DUST +DUTY +DYAD +DYED +DYER +DYES +EACH +EARL +EARN +EARS +EASE +EAST +EASY +EATS +EAVE +EBBS +ECHO +EDDY +EDGE +EDGY +EDIT +EELS +EGGS +EGGY +EGOS +ELKS +ELMS +ELSE +EMIT +EMMY +EMUS +ENDS +ENVY +EONS +EPEE +EPIC +ERAS +ERRS +ETCH +EURO +EVEN +EVER +EVES +EVIL +EWES +EXAM +EXEC +EXIT +EXON +EXPO +EYED +EYES +FABS +FACE +FACT +FADE +FADS +FAIL +FAIR +FAKE +FALL +FAME +FANG +FANS +FARE +FARM +FART +FAST +FATE +FATS +FAUN +FAVA +FAVE +FAWN +FAZE +FEAR +FEAT +FEED +FEEL +FEES +FEET +FELL +FELT +FEND +FENS +FERN +FESS +FEST +FETA +FETE +FEUD +FIAT +FIBS +FIFE +FIGS +FILE +FILL +FILM +FILO +FIND +FINE +FINK +FINS +FIRE +FIRM +FIRS +FISH +FIST +FITS +FIVE +FIZZ +FLAB +FLAG +FLAK +FLAM +FLAN +FLAP +FLAT +FLAW +FLAX +FLAY +FLEA +FLED +FLEE +FLEW +FLEX +FLIP +FLIT +FLOC +FLOE +FLOG +FLOP +FLOW +FLUB +FLUE +FLUS +FLUX +FOAL +FOAM +FOBS +FOCI +FOES +FOGS +FOGY +FOIL +FOLD +FOLK +FOND +FONT +FOOD +FOOL +FOOT +FOPS +FORD +FORE +FORK +FORM +FORT +FOUL +FOUR +FOWL +FOXY +FRAG +FRAT +FRAY +FREE +FRET +FRIG +FROG +FROM +FUEL +FUCK +FUGU +FULL +FUME +FUND +FUNK +FURL +FURS +FURY +FUSE +FUSS +FUTZ +FUZZ +GAFF +GAGS +GAIN +GAIT +GALA +GALE +GALL +GALS +GAME +GANG +GAPE +GAPS +GARB +GASH +GASP +GATE +GAVE +GAWK +GAYS +GAZE +GEAR +GEEK +GEES +GELS +GEMS +GENE +GENS +GENT +GERM +GETS +GHAT +GHEE +GIFT +GIGS +GILD +GILL +GILT +GINS +GIRD +GIRL +GIST +GITS +GIVE +GLAD +GLAM +GLEE +GLEN +GLIB +GLOB +GLOM +GLOP +GLOW +GLUE +GLUG +GLUM +GLUT +GNAR +GNAT +GNAW +GOAD +GOAL +GOAT +GOBS +GOBY +GODS +GOER +GOES +GOLD +GOLF +GONE +GONG +GOOD +GOOF +GOON +GOOP +GOOS +GORE +GORY +GOSH +GOTH +GOUT +GOWN +GRAB +GRAD +GRAM +GRAN +GRAY +GREW +GREY +GRID +GRIM +GRIN +GRIP +GRIT +GROG +GROW +GRUB +GUFF +GULF +GULL +GULP +GUMS +GUNK +GUNS +GURU +GUSH +GUST +GUTS +GUYS +GYMS +GYRE +GYRO +HACK +HAGS +HAIL +HAIR +HALF +HALL +HALO +HALT +HAMS +HAND +HANG +HANK +HAPS +HARD +HARE +HARK +HARM +HARP +HART +HASH +HATE +HATH +HATS +HAUL +HAVE +HAWK +HAWS +HAYS +HAZE +HAZY +HEAD +HEAL +HEAP +HEAR +HEAT +HECK +HEED +HEEL +HEFT +HEIR +HELD +HELL +HELM +HELP +HEMP +HEMS +HENS +HERB +HERD +HERE +HERO +HERS +HEST +HEWN +HEWS +HICK +HIDE +HIGH +HIKE +HILL +HILT +HIND +HINT +HIPS +HIRE +HISS +HITS +HIVE +HIYA +HOAR +HOAX +HOBO +HOCK +HOED +HOES +HOGS +HOLD +HOLE +HOLO +HOLT +HOLY +HOME +HONE +HONK +HOOD +HOOF +HOOK +HOOP +HOOT +HOPE +HOPS +HORN +HOSE +HOST +HOUR +HOVE +HOWL +HUBS +HUCK +HUED +HUES +HUFF +HUGE +HUGS +HULA +HULK +HULL +HUMP +HUMS +HUNG +HUNK +HUNT +HURL +HURT +HUSH +HUSK +HUTS +HYMN +HYPE +HYPO +ICED +ICES +ICKY +ICON +IDEA +IDLE +IDLY +IDOL +IFFY +ILLS +IMPS +INCH +INFO +INKS +INKY +INNS +INTO +IONS +IOTA +IRIS +IRKS +IRON +ISLE +ISMS +ITCH +ITEM +JABS +JACK +JADE +JAIL +JAKE +JAMB +JAMS +JAPE +JARS +JAVA +JAWA +JAWS +JAYS +JAZZ +JEAN +JEEP +JEER +JEEZ +JELL +JERK +JEST +JETS +JIBE +JIBS +JIGS +JILT +JINK +JINX +JIVE +JOBS +JOCK +JOGS +JOIN +JOKE +JOLT +JOTS +JOWL +JOYS +JUDO +JUGS +JUJU +JUKE +JUMP +JUNK +JURY +JUST +JUTE +JUTS +KALE +KEEL +KEEN +KEEP +KEGS +KELP +KEPT +KETO +KEYS +KHAN +KHAT +KICK +KIDS +KILL +KILN +KILO +KILT +KIND +KING +KIPS +KISS +KITE +KITS +KIWI +KNEE +KNEW +KNIT +KNOB +KNOT +KNOW +KOAN +KOOK +KOTO +LABS +LACE +LACK +LACY +LADS +LADY +LAGS +LAID +LAIN +LAIR +LAKE +LAMA +LAMB +LAME +LAMP +LAND +LANE +LAPS +LARD +LARK +LASH +LASS +LAST +LATE +LAUD +LAVA +LAWN +LAWS +LAYS +LAZE +LAZY +LEAD +LEAF +LEAK +LEAN +LEAP +LEAS +LEEK +LEER +LEET +LEFT +LEGS +LEND +LENS +LENT +LESS +LEST +LEWD +LIAR +LIBS +LICE +LICK +LIDS +LIED +LIEN +LIER +LIES +LIEU +LIFE +LIFT +LIKE +LILT +LILY +LIMA +LIMB +LIME +LIMO +LIMP +LINE +LING +LINK +LINT +LION +LIPS +LISP +LIST +LITE +LIVE +LOAD +LOAF +LOAM +LOAN +LOBE +LOBS +LOCH +LOCK +LOCO +LODE +LOFT +LOGE +LOGO +LOGS +LOIN +LONE +LONG +LOOK +LOOM +LOON +LOOP +LOOS +LOOT +LOPS +LORD +LORE +LOSE +LOSS +LOST +LOTH +LOTS +LOUD +LOUT +LOVE +LOWS +LUBE +LUCK +LUFF +LUGS +LULL +LUMP +LUNE +LUNG +LURE +LURK +LUSH +LUST +LUTE +LUVS +LYNX +LYRE +MACE +MACH +MACK +MACS +MADE +MAGE +MAGI +MAGS +MAID +MAIL +MAIM +MAIN +MAKE +MAKI +MALE +MALL +MALT +MAMA +MANE +MANS +MANY +MAPS +MARE +MARK +MARS +MART +MASH +MASK +MASS +MAST +MATE +MATH +MATS +MAUL +MAWS +MAXI +MAYO +MAZE +MAZY +MEAD +MEAL +MEAN +MEAT +MEEK +MEET +MEGA +MELD +MELT +MEMO +MEND +MENU +MEOW +MERE +MESA +MESH +MESS +META +METE +MEWL +MEWS +MICA +MICE +MICS +MIDS +MILD +MILE +MILK +MILL +MIME +MIND +MINE +MINI +MINK +MINT +MINX +MIRE +MISO +MISS +MIST +MITE +MOAN +MOAT +MOBS +MOCK +MODE +MODS +MOJO +MOLD +MOLE +MOLT +MOMS +MONK +MONO +MOOD +MOON +MOOR +MOOS +MOOT +MOPE +MOPS +MORE +MORN +MOSH +MOSS +MOST +MOTE +MOTH +MOVE +MOWS +MUCH +MUCK +MUDS +MUFF +MUGS +MULE +MULL +MUMS +MUON +MURK +MUSE +MUSH +MUSK +MUST +MUTE +MUTT +MYTH +NAAN +NABS +NAGS +NAIL +NAME +NANA +NANS +NAPE +NAPS +NARC +NARD +NARY +NAVY +NAYS +NEAR +NEAT +NECK +NEED +NEEM +NEON +NERD +NESS +NEST +NETS +NEWS +NEWT +NEXT +NIBS +NICE +NICK +NIGH +NINE +NITE +NOBS +NODE +NODS +NOEL +NOIR +NONE +NOOK +NOON +NOPE +NORI +NORM +NOSE +NOSH +NOSY +NOTE +NOUN +NOVA +NUBS +NUDE +NUKE +NULL +NUMB +NUNS +NUTS +OAFS +OAKS +OAKY +OARS +OATH +OATS +OBEY +OBIT +OBOE +ODDS +ODES +ODOR +OGLE +OGRE +OILS +OILY +OINK +OKAY +OKRA +OLDS +OMEN +OMIT +ONCE +ONES +ONLY +ONTO +ONUS +ONYX +OOPS +OOZE +OOZY +OPAL +OPEN +OPTS +OPUS +ORAL +ORBS +ORCA +ORES +ORGY +ORZO +OUCH +OURS +OUST +OUTS +OVAL +OVEN +OVER +OVUM +OWED +OWES +OWLS +OWNS +PACE +PACK +PACT +PADS +PAGE +PAID +PAIL +PAIN +PAIR +PALE +PALL +PALM +PALP +PALS +PANE +PANG +PANS +PANT +PAPA +PAPS +PARA +PARE +PARK +PARS +PART +PASS +PAST +PATE +PATH +PATS +PAVE +PAWN +PAWS +PAYS +PEAK +PEAL +PEAR +PEAS +PEAT +PECK +PECS +PEED +PEEK +PEEL +PEEP +PEER +PEES +PEGS +PELT +PENS +PENT +PEON +PERK +PERM +PERP +PESO +PEST +PETS +PEWS +PHEW +PICK +PICS +PIED +PIER +PIES +PIGS +PIKA +PIKE +PILE +PILL +PINE +PING +PINK +PINS +PINT +PIPE +PISS +PITA +PITH +PITS +PITY +PLAN +PLAT +PLAY +PLEA +PLEB +PLED +PLEX +PLOD +PLOP +PLOT +PLOW +PLOY +PLUG +PLUM +PLUS +POCK +PODS +POEM +POET +POKE +POKY +POLE +POLL +POLO +POLY +POMP +POND +PONG +PONY +POOF +POOL +POOP +POOR +POPE +POPS +PORE +PORK +PORT +POSE +POSH +POSY +POTS +POUF +POUR +POUT +PRAM +PRAY +PREP +PREY +PREZ +PRIG +PRIM +PROB +PROD +PROF +PROG +PROM +PROP +PROS +PROW +PUBS +PUCK +PUDS +PUFF +PUGS +PUKE +PULL +PULP +PUMA +PUMP +PUNK +PUNS +PUNT +PUNY +PUPA +PUPS +PURE +PURL +PURR +PUSH +PUSS +PUTS +PUTT +PUTZ +PYRE +PYRO +QUAD +QUID +QUIP +QUIT +QUIZ +RACE +RACK +RACY +RADS +RAFT +RAGA +RAGE +RAGS +RAID +RAIL +RAIN +RAJA +RAKE +RAKU +RALE +RAMP +RAMS +RANG +RANK +RANT +RAPS +RAPT +RARE +RASH +RASP +RATE +RATH +RATS +RAVE +RAYS +RAZE +RAZZ +READ +REAL +REAM +REAP +REAR +REDO +REED +REEF +REEK +REEL +REFS +REIN +RELY +REMS +REND +RENT +REPO +REPP +REPS +REST +RIBS +RICE +RICH +RIDE +RIDS +RIFE +RIFF +RIFT +RIGS +RILE +RILL +RIME +RIMS +RIND +RING +RINK +RIOT +RIPE +RIPS +RISE +RISK +RITE +RITZ +ROAD +ROAM +ROAN +ROAR +ROBE +ROBS +ROCK +RODE +RODS +ROES +ROIL +ROLE +ROLL +ROMP +ROMS +ROOF +ROOK +ROOM +ROOS +ROOT +ROPE +ROPY +ROSE +ROSY +ROTE +ROTI +ROTO +ROTS +ROUT +ROUX +ROVE +ROWS +RUBE +RUBS +RUBY +RUCK +RUDE +RUED +RUES +RUFF +RUGS +RUIN +RULE +RUMP +RUMS +RUNE +RUNG +RUNS +RUNT +RUSE +RUSH +RUST +RUTS +SACK +SACS +SAFE +SAGA +SAGE +SAGO +SAGS +SAID +SAIL +SAKE +SAKI +SALE +SALT +SAME +SAND +SANE +SANG +SANK +SAPS +SARI +SASH +SASS +SATE +SAVE +SAWN +SAWS +SAYS +SCAB +SCAD +SCAM +SCAN +SCAR +SCAT +SCOT +SCUD +SCUM +SEAL +SEAM +SEAR +SEAS +SEAT +SECT +SEED +SEEK +SEEM +SEEN +SEEP +SEER +SEES +SELF +SELL +SEMI +SEND +SENT +SEPT +SETS +SEWN +SEWS +SHAM +SHED +SHEW +SHIM +SHIN +SHIP +SHIT +SHIV +SHOD +SHOE +SHOO +SHOP +SHOT +SHOW +SHUN +SHUT +SIBS +SICK +SIDE +SIFT +SIGH +SIGN +SILK +SILL +SILO +SILT +SINE +SING +SINK +SINS +SIPS +SIRE +SIRS +SITE +SITS +SIZE +SKEW +SKID +SKIM +SKIN +SKIP +SKIS +SKIT +SLAB +SLAG +SLAM +SLAP +SLAT +SLAW +SLAY +SLED +SLEW +SLID +SLIM +SLIP +SLIT +SLOB +SLOE +SLOG +SLOP +SLOT +SLOW +SLUG +SLUM +SMOG +SMUG +SNAG +SNAP +SNIP +SNIT +SNOB +SNOT +SNOW +SNUB +SNUG +SOAK +SOAP +SOAR +SOBA +SOBS +SOCK +SODA +SODS +SOFA +SOFT +SOIL +SOLD +SOLE +SOLO +SOME +SONG +SONS +SOON +SOOT +SOPS +SORE +SORT +SOUL +SOUP +SOUR +SOWN +SOWS +SOYA +SPAM +SPAN +SPAR +SPAS +SPAT +SPAY +SPEC +SPED +SPEW +SPIN +SPIT +SPOT +SPRY +SPUD +SPUN +SPUR +STAB +STAG +STAR +STAT +STAY +STEM +STEP +STEW +STIR +STOP +STOW +STUB +STUD +STUN +STYE +SUBS +SUCH +SUCK +SUED +SUES +SUET +SUIT +SULK +SUMO +SUMP +SUMS +SUNG +SUNK +SUNS +SUPS +SURE +SURF +SUSS +SWAB +SWAG +SWAM +SWAN +SWAP +SWAT +SWAY +SWIG +SWIM +SWUM +SYNC +TABS +TACH +TACK +TACO +TACT +TAGS +TAIL +TAKE +TALC +TALE +TALK +TALL +TAME +TAMP +TANG +TANK +TANS +TAPE +TAPS +TARE +TARN +TARO +TARP +TARS +TART +TASK +TAUT +TAXI +TEAK +TEAL +TEAM +TEAR +TEAS +TEAT +TECH +TEEM +TEEN +TEES +TEFF +TELE +TELL +TEMP +TEND +TENT +TERM +TERN +TEST +TEXT +THAN +THAT +THAW +THEE +THEM +THEN +THEY +THIN +THIS +THOU +THRU +THUD +THUG +THUS +TICK +TIDE +TIDY +TIED +TIER +TIES +TIFF +TIKI +TILE +TILL +TILT +TIME +TINE +TING +TINS +TINT +TINY +TIPS +TIRE +TITS +TOAD +TODE +TOED +TOES +TOFU +TOGA +TOIL +TOKE +TOLD +TOLE +TOLL +TOMB +TOME +TONE +TONG +TOOK +TOOL +TOON +TOOT +TOPS +TORE +TORN +TORT +TORY +TOSS +TOTE +TOTS +TOUR +TOUT +TOWN +TOWS +TOYS +TRAM +TRAP +TRAY +TREE +TREK +TREY +TRIG +TRIM +TRIO +TRIP +TROD +TROT +TSAR +TUBA +TUBE +TUBS +TUCK +TUFF +TUFT +TUGS +TUMS +TUNA +TUNE +TURD +TURF +TURK +TURN +TUSH +TUSK +TUTS +TUTU +TWEE +TWIG +TWIN +TWIT +TWOS +TYKE +TYPE +TYPO +UDON +UGLY +UMPS +UNDO +UNIT +UNTO +UPON +URGE +URNS +USED +USER +USES +VAIL +VAIN +VAMP +VANE +VANS +VARY +VASE +VAST +VATS +VEAL +VEER +VEIL +VEIN +VEND +VENT +VERB +VERT +VERY +VEST +VETO +VETS +VIAL +VIBE +VICE +VIDS +VIED +VIES +VIEW +VILE +VIMS +VINE +VINO +VISA +VISE +VITA +VIVA +VOID +VOLE +VOLT +VOTE +VOWS +WACK +WADE +WADS +WAFT +WAGE +WAGS +WAIF +WAIL +WAIT +WAKE +WALK +WALL +WAND +WANE +WANT +WARD +WARE +WARM +WARN +WARP +WARS +WART +WARY +WASH +WASP +WATT +WAVE +WAVY +WAXY +WAYS +WEAK +WEAN +WEAR +WEBS +WEDS +WEED +WEEK +WEEN +WEEP +WELL +WENT +WERE +WEST +WHAT +WHEN +WHOM +WIDE +WIFE +WILD +WILL +WILT +WIND +WINE +WING +WINS +WIPE +WIRE +WISE +WISH +WITH +WOLF +WOOD +WOOL +WORD +WORE +WORK +WORM +WORN +WRAP +YARD +YARN +YEAH +YEAR +YOGA +YOUR +ZERO +ZINC +ZONE +ZOOM \ No newline at end of file diff --git a/awk/forth/f.awk b/awk/forth/f.awk new file mode 100755 index 0000000..16de171 --- /dev/null +++ b/awk/forth/f.awk @@ -0,0 +1,369 @@ +#!/usr/bin/awk -f + +# I wanted to implement something non-trivial using awk. +# If I was clever I wouldn’t implement forth directly in awk, +# instead I’d implement a simple virtual machine using awk, +# and then implement the forth using the virtual machine’s byte code +# ...but there is only so much brain power I can exert on such a silly project. + +BEGIN { + # Initialize stacks and dictionaries + stack_ptr = 0 + dict_size = 0 + + # Built-in words, and some documentation (I could use stack comments, + # but I find those sort of unintuitive) + dict["+"] = "+ : Adds the top two numbers on the stack." + dict["-"] = "- : Subtracts the top number from the second top number on the stack." + dict["*"] = "* : Multiplies the top two numbers on the stack." + dict["/"] = "/ : Divides the second top number by the top number on the stack." + dict["."] = ". : Prints the top of the stack." + dict[".s"] = ".s : Shows all values on the stack." + dict["dup"] = "dup : Duplicates the top value on the stack." + dict["drop"] = "drop : Removes the top value from the stack." + dict["swap"] = "swap : Swaps the top two values on the stack." + dict["over"] = "over : Copies the second top value to the top of the stack." + dict["rot"] = "rot : Rotates the top three values on the stack." + dict["="] = "= : Compares the top two values for equality." + dict["<"] = "< : Checks if the second top value is less than the top value." + dict[">"] = "> : Checks if the second top value is greater than the top value." + dict["bye"] = "bye : Exits the interpreter." + dict["words"] = "words : Lists all available words and their documentation." + + # State flags + compiling = 0 + current_def = "" + def_name = "" + + # If an input file isn't specified, enter REPL mode + if (ARGC == 1) { + repl() + } +} + +# Handle file input +{ + if (FILENAME ~ /\.forth$/) { + interpret($0) + } +} + +function repl() { + print "f.awk! A forth interpreter.\nUse 'bye' to exit.\nUse 'words' to list all available words.\n" + while (1) { + printf "f> " + if (getline input < "/dev/tty" <= 0) break + interpret(input) + } +} + +function interpret(line) { + gsub(/\(.*\)/, "", line) # Remove everything from ( to ) + gsub(/\\.*$/, "", line) # Remove backslash comments, too + + n = split(line, words, /[ \t]+/) + + for (i = 1; i <= n; i++) { + word = words[i] + if (word == "") continue + + # print "Processing word: " word + + if (word == ":") { + compiling = 1 + i++ + def_name = words[i] + current_def = "" + continue + } + + if (compiling) { + if (word == ";") { + # Store user-defined word with its name and definition + dict[def_name] = "word " current_def + compiling = 0 + continue + } + current_def = current_def " " word + continue + } + + # Execute the word and skip further processing if it's .s + if (word == ".s") { + execute_word(word) + break # Exit the loop after executing .s + } + + execute_word(word) + } +} + +function execute_word(word) { + if (word ~ /^-?[0-9]+$/) { + push(word + 0) + } else if (word in dict) { + if (dict[word] ~ /^word /) { + # User-defined word + sequence = substr(dict[word], 6) + split(sequence, subwords, " ") + for (sw in subwords) { + if (subwords[sw] != "") { + execute_word(subwords[sw]) + } + } + } else { + # Built-in words + if (word == "+") math_add() + else if (word == "-") math_sub() + else if (word == "*") math_mul() + else if (word == "/") math_div() + else if (word == ".") stack_print() + else if (word == ".s") { + # print "Executing .s command" + stack_show() + } + else if (word == "dup") stack_dup() + else if (word == "drop") stack_drop() + else if (word == "swap") stack_swap() + else if (word == "over") stack_over() + else if (word == "rot") stack_rot() + else if (word == "=") compare_eq() + else if (word == "<") compare_lt() + else if (word == ">") compare_gt() + else if (word == "bye") exit_program() + else if (word == "words") list_words() + else if (word == "if") { + # Handle the if statement + if_condition = pop() + if (if_condition == 0) { + # Skip to the next part until we find 'then' or 'else' + skip_if = 1 + } + } + else if (word == "else") { + # Handle the else statement + if (skip_if) { + skip_if = 0 # Reset the skip flag + } else { + # Skip to the next part until we find 'then' + skip_else = 1 + } + } + else if (word == "then") { + # End of the conditional + skip_if = 0 + skip_else = 0 + } + } + } else { + print "Error: Unknown word '" word "'" + } +} + +function push(val) { + stack[stack_ptr++] = val +} + +function pop() { + if (stack_ptr <= 0) { + print "Error: Stack underflow" + return 0 + } + return stack[--stack_ptr] +} + +function math_add() { + if (stack_ptr < 2) { + print "Error: Stack underflow" + return + } + b = pop() + a = pop() + push(a + b) +} + +function math_sub() { + if (stack_ptr < 2) { + print "Error: Stack underflow" + return + } + b = pop() + a = pop() + push(a - b) +} + +function math_mul() { + if (stack_ptr < 2) { + print "Error: Stack underflow" + return + } + b = pop() + a = pop() + push(a * b) +} + +function math_div() { + if (stack_ptr < 2) { + print "Error: Stack underflow" + return + } + b = pop() + if (b == 0) { + print "Error: Division by zero" + return + } + a = pop() + push(int(a / b)) +} + +function stack_print() { + if (stack_ptr < 1) { + print "Error: Stack underflow" + return + } + print pop() +} + +function stack_show() { + print "<", stack_ptr, "> " + for (i = 0; i < stack_ptr; i++) { + printf "%s ", stack[i] + } + print "" + # print "Stack state after .s: " + # for (i = 0; i < stack_ptr; i++) { + # print stack[i] + # } + # print "" +} + +function stack_dup() { + if (stack_ptr < 1) { + print "Error: Stack underflow" + return + } + val = stack[stack_ptr - 1] + push(val) +} + +function stack_drop() { + if (stack_ptr < 1) { + print "Error: Stack underflow" + return + } + pop() +} + +function stack_swap() { + if (stack_ptr < 2) { + print "Error: Stack underflow" + return + } + b = pop() + a = pop() + push(b) + push(a) +} + +function stack_over() { + if (stack_ptr < 2) { + print "Error: Stack underflow" + return + } + b = pop() + a = pop() + push(a) + push(b) + push(a) +} + +function stack_rot() { + if (stack_ptr < 3) { + print "Error: Stack underflow" + return + } + c = pop() + b = pop() + a = pop() + push(b) + push(c) + push(a) +} + +function compare_eq() { + if (stack_ptr < 2) { + print "Error: Stack underflow" + return + } + b = pop() + a = pop() + push(a == b ? -1 : 0) +} + +function compare_lt() { + if (stack_ptr < 2) { + print "Error: Stack underflow" + return + } + b = pop() + a = pop() + push(a < b ? -1 : 0) +} + +function compare_gt() { + if (stack_ptr < 2) { + print "Error: Stack underflow" + return + } + b = pop() + a = pop() + push(a > b ? -1 : 0) +} + +function exit_program() { + print "Exiting program." + exit 0 +} + +function list_words() { + print "Available words:" + + # Separate arrays to hold built-in and user-defined words + split("", built_in_words) + split("", user_defined_words) + + for (w in dict) { + split(dict[w], parts, ": ") + if (parts[1] ~ /^word /) { + user_defined_words[w] = parts[2] + } else { + built_in_words[w] = parts[2] + } + } + + # Sort built-in words manually because I'm picky + n = 0 + for (w in built_in_words) { + sorted_words[n++] = w + } + + for (i = 0; i < n; i++) { + for (j = i + 1; j < n; j++) { + if (sorted_words[i] > sorted_words[j]) { + temp = sorted_words[i] + sorted_words[i] = sorted_words[j] + sorted_words[j] = temp + } + } + } + + # First print the built-in words + for (i = 0; i < n; i++) { + print sorted_words[i] ": " built_in_words[sorted_words[i]] + } + + # Then print the user-defined words + for (w in user_defined_words) { + print w ": " user_defined_words[w] " ( User-defined )" + } +} \ No newline at end of file diff --git a/awk/forth/old/f.awk b/awk/forth/old/f.awk new file mode 100755 index 0000000..eed9774 --- /dev/null +++ b/awk/forth/old/f.awk @@ -0,0 +1,344 @@ +#!/usr/bin/awk -f + +# Forth interpreter in AWK + +BEGIN { + print "Welcome to the AWK Forth Interpreter!" + print "Type your commands below. Use 'bye' to quit." + # Initialize variables + top = -1 # Initialize stack pointer + + # Initialize the dictionary with basic words + words["+"] = "+" + words["-"] = "-" + words["*"] = "*" + words["/"] = "/" + words["dup"] = "dup" + words["over"] = "over" + words["swap"] = "swap" + words["."] = "." + words["bye"] = "bye" + words["rot"] = "rot" + words["drop"] = "drop" + words["nip"] = "nip" + words["tuck"] = "tuck" + words["roll"] = "roll" + words["pick"] = "pick" + words["negate"] = "negate" + words["abs"] = "abs" + words["max"] = "max" + words["min"] = "min" + words["mod"] = "mod" + words["="] = "=" + words["see"] = "see" + words["if"] = "if" + words["then"] = "then" + words["else"] = "else" + words[">"] = ">" + words["<"] = "<" + + # Add handlers for all words + handlers["+"] = "add" + handlers["-"] = "subtract" + handlers["*"] = "multiply" + handlers["/"] = "divide" + handlers["dup"] = "dup" + handlers["over"] = "over" + handlers["swap"] = "swap" + handlers["."] = "print_top" + handlers["<"] = "less_than" + handlers[">"] = "greater_than" + handlers["rot"] = "rot" + handlers["drop"] = "drop" + handlers["nip"] = "nip" + handlers["tuck"] = "tuck" + handlers["roll"] = "roll" + handlers["pick"] = "pick" + handlers["negate"] = "negate" + handlers["abs"] = "abs" + handlers["max"] = "max" + handlers["min"] = "min" + handlers["mod"] = "mod" + handlers["="] = "equals" + handlers["if"] = "handle_if" + handlers["then"] = "handle_then" + handlers["else"] = "handle_else" + handlers["bye"] = "bye" + handlers["see"] = "see" + + # Add descriptions for words + desc["+"] = "( n1 n2 -- sum ) Add top two numbers" + desc["-"] = "( n1 n2 -- diff ) Subtract top number from second" + desc["*"] = "( n1 n2 -- prod ) Multiply top two numbers" + desc["/"] = "( n1 n2 -- quot ) Divide second by top" + desc["dup"] = "( n -- n n ) Duplicate top of stack" + desc["over"] = "( n1 n2 -- n1 n2 n1 ) Copy second item to top" + desc["swap"] = "( n1 n2 -- n2 n1 ) Swap top two items" + desc["rot"] = "( n1 n2 n3 -- n2 n3 n1 ) Rotate top three items" + desc["drop"] = "( n -- ) Discard top item" + desc["nip"] = "( n1 n2 -- n2 ) Remove second item" + desc["tuck"] = "( n1 n2 -- n2 n1 n2 ) Copy top item below second" + desc["roll"] = "( nk ... n1 n0 k -- nk-1 ... n1 n0 nk ) Move kth item to top" + desc["pick"] = "( nk ... n1 n0 k -- nk ... n1 n0 nk ) Copy kth item to top" + desc["negate"] = "( n -- -n ) Negate number" + desc["abs"] = "( n -- |n| ) Absolute value" + desc["max"] = "( n1 n2 -- max ) Maximum of top two numbers" + desc["min"] = "( n1 n2 -- min ) Minimum of top two numbers" + desc["mod"] = "( n1 n2 -- rem ) Remainder of n1/n2" + desc["="] = "( n1 n2 -- flag ) Test if equal, leaves 1 if true, 0 if false" + desc["if"] = "( flag -- ) Begin conditional execution" + desc["then"] = "( -- ) End conditional execution" + desc["else"] = "( -- ) Execute if previous condition was false" + desc[">"] = "( n1 n2 -- flag ) Returns true if n1 is greater than n2" + desc["<"] = "( n1 n2 -- flag ) Returns true if n1 is less than n2" + desc["bye"] = "( -- ) Exit the interpreter" + desc["see"] = "( -- ) Show definition of a word" + + # Initialize condition stack + cond_top = -1 + + # Mark these as compile-only words + compile_only["if"] = 1 + compile_only["then"] = 1 + compile_only["else"] = 1 +} + +# Stack operations +function push(value) { + stack[++top] = value +} + +function pop() { + if (top < 0) { + print "Error: Stack underflow" + return 0 + } + return stack[top--] +} + +function check_stack(min_items, error_msg) { + if (top < min_items - 1) { + print error_msg ? error_msg : "Error: Not enough values on stack" + return 0 + } + return 1 +} + +# Binary operations +function binary_op(operation) { + if (!check_stack(2)) return + second = pop() + first = pop() + if (operation == "+") push(first + second) + else if (operation == "-") push(first - second) + else if (operation == "*") push(first * second) + else if (operation == "/") { + if (second == 0) { + print "Error: Division by zero" + push(first) + push(second) + return + } + push(first / second) + } + else if (operation == "mod") push(first % second) + else if (operation == "=") push(first == second ? 1 : 0) + else if (operation == "<") push(first < second ? 1 : 0) + else if (operation == ">") push(first > second ? 1 : 0) +} + +# Handler functions +function add() { binary_op("+") } +function subtract() { binary_op("-") } +function multiply() { binary_op("*") } +function divide() { binary_op("/") } +function mod() { binary_op("mod") } +function equals() { binary_op("=") } +function less_than() { binary_op("<") } +function greater_than() { binary_op(">") } + +function dup() { + if (!check_stack(1)) return + push(stack[top]) +} + +function over() { + if (!check_stack(2)) return + push(stack[top - 1]) +} + +function swap() { + if (!check_stack(2)) return + temp = pop() + second = pop() + push(temp) + push(second) +} + +function rot() { + if (!check_stack(3)) return + third = pop() + second = pop() + first = pop() + push(second) + push(third) + push(first) +} + +function drop() { + if (!check_stack(1)) return + top-- +} + +function nip() { + if (!check_stack(2)) return + temp = stack[top] + drop() + drop() + push(temp) +} + +function tuck() { + if (!check_stack(2)) return + temp = pop() + second = pop() + push(temp) + push(second) + push(temp) +} + +function roll() { + if (!check_stack(1)) return + n = int(pop()) + if (!check_stack(n)) return + if (n <= 0) return + + temp = stack[top - n + 1] + for (i = top - n + 1; i < top; i++) { + stack[i] = stack[i + 1] + } + stack[top] = temp +} + +function pick() { + if (!check_stack(1)) return + n = int(pop()) + if (!check_stack(n)) return + if (n < 0) return + push(stack[top - n]) +} + +function negate() { + if (!check_stack(1)) return + push(-pop()) +} + +function abs() { + if (!check_stack(1)) return + n = pop() + push(n < 0 ? -n : n) +} + +function max() { + if (!check_stack(2)) return + b = pop() + a = pop() + push(a > b ? a : b) +} + +function min() { + if (!check_stack(2)) return + b = pop() + a = pop() + push(a < b ? a : b) +} + +function print_top() { + if (!check_stack(1)) return + print stack[top] + drop() +} + +function bye() { + exit +} + +function see(word) { + if (!(word in words)) { + print "Error: Word '" word "' not found" + return + } + if (word in desc) { + print desc[word] + } + if (word in raw_definitions) { + print ": " word " " raw_definitions[word] " ;" + } +} + +# Main processing function +function execute_word(word) { + if (word in handlers) { + handler = handlers[word] + if (handler == "bye") exit + else if (handler == "see") { + if (i + 1 <= NF) see($(++i)) + else print "Error: see requires a word name" + } + else if (handler == "add") add() + else if (handler == "subtract") subtract() + else if (handler == "multiply") multiply() + else if (handler == "divide") divide() + else if (handler == "dup") dup() + else if (handler == "over") over() + else if (handler == "swap") swap() + else if (handler == "print_top") print_top() + else if (handler == "less_than") less_than() + else if (handler == "greater_than") greater_than() + else if (handler == "rot") rot() + else if (handler == "drop") drop() + else if (handler == "nip") nip() + else if (handler == "tuck") tuck() + else if (handler == "roll") roll() + else if (handler == "pick") pick() + else if (handler == "negate") negate() + else if (handler == "abs") abs() + else if (handler == "max") max() + else if (handler == "min") min() + else if (handler == "mod") mod() + else if (handler == "equals") equals() + else if (handler == "handle_if") handle_if() + else if (handler == "handle_then") handle_then() + else if (handler == "handle_else") handle_else() + else { + print "Error: Handler '" handler "' not implemented" + return 0 + } + return 1 + } + return 0 +} + +# Process each line of input +{ + if (NF > 0) { + # Remove comments and normalize whitespace + gsub(/\(.*\)/, "") + gsub(/^[[:space:]]+/, "") + gsub(/[[:space:]]+$/, "") + gsub(/[[:space:]]+/, " ") + + # Process each token + for (i = 1; i <= NF; i++) { + if ($i ~ /^-?[0-9]+$/) { + push($i) + } else if ($i in words) { + if (!execute_word($i)) { + print "Error: Failed to execute word '" $i "'" + } + } else { + print "Error: Unknown word '" $i "'" + } + } + } +} \ No newline at end of file diff --git a/awk/forth/old/test.forth b/awk/forth/old/test.forth new file mode 100644 index 0000000..a1f4f50 --- /dev/null +++ b/awk/forth/old/test.forth @@ -0,0 +1,44 @@ +( Basic arithmetic operations ) +2 3 + . ( expect: 5 ) +10 3 - . ( expect: 7 ) +4 5 * . ( expect: 20 ) +20 4 / . ( expect: 5 ) +7 3 mod . ( expect: 1 ) + +( Stack manipulation operations ) +5 dup . . ( expect: 5 5 ) +1 2 swap . . ( expect: 2 1 ) +1 2 over . . . ( expect: 1 2 1 ) +1 2 3 rot . . . ( expect: 2 3 1 ) +1 2 3 4 2 roll . . . . ( expect: 1 3 4 2 ) +5 drop +1 2 nip . ( expect: 2 ) +1 2 tuck . . . ( expect: 2 1 2 ) + +( Comparison operations ) +5 3 > . ( expect: 1 ) +3 5 < . ( expect: 1 ) +4 4 = . ( expect: 1 ) +5 3 < . ( expect: 0 ) +3 5 > . ( expect: 0 ) +4 5 = . ( expect: 0 ) + +( Math operations ) +5 negate . ( expect: -5 ) +-7 abs . ( expect: 7 ) +5 2 max . ( expect: 5 ) +5 2 min . ( expect: 2 ) + +( Complex stack manipulations ) +1 2 3 4 5 \ Put 5 numbers on stack +3 pick . ( expect: 2 ) +2 roll . ( expect: 4 ) +. . . . ( expect: 5 3 1 ) + +( Error handling tests ) +drop drop drop drop drop \ Clear stack +drop ( expect: Error: Stack underflow ) +. ( expect: Error: Stack underflow ) +5 0 / ( expect: Error: Division by zero ) + +bye \ No newline at end of file diff --git a/awk/forth/test.forth b/awk/forth/test.forth new file mode 100644 index 0000000..daa6943 --- /dev/null +++ b/awk/forth/test.forth @@ -0,0 +1,34 @@ +\ Test arithmetic operations +10 5 + . \ Should print 15 +10 5 - . \ Should print 5 +10 5 * . \ Should print 50 +10 5 / . \ Should print 2 + +\ Test stack manipulation +1 2 3 .s \ Should show 3 values: 1 2 3 +dup . \ Should print 3 again +drop . \ Should print 2 +swap .s \ Should show 2 1 +over .s \ Should show 2 1 2 +rot .s \ Should show 1 2 3 + +\ Test comparisons +5 5 = . \ Should print -1 (true) +5 3 < . \ Should print 0 (false) +3 5 > . \ Should print 0 (false) + +\ Test conditionals within user-defined words +: test_if 10 20 if .s then ; \ Should print 1 2 (since the condition is true) +: test_else 10 5 if .s else 1 then ; \ Should print 1 (since the condition is false) + +\ Test user-defined words +: square dup * ; \ Define a word to square a number +4 square . \ Should print 16 + +: add_three 1 2 + + ; \ Define a word to add three numbers +1 2 add_three . \ Should print 6 + +\ List all words +words \ Should list all available words + +bye \ Exit the interpreter \ No newline at end of file diff --git a/awk/retro/retro.awk b/awk/retro/retro.awk new file mode 100755 index 0000000..2a14ff0 --- /dev/null +++ b/awk/retro/retro.awk @@ -0,0 +1,250 @@ +#!/usr/bin/awk -f + +# Constants and VM setup +BEGIN { + IMAGE_SIZE = 524288 # Amount of simulated RAM + DATA_DEPTH = 8192 # Depth of data stack + ADDRESS_DEPTH = 32768 # Depth of the stacks + + # Initialize stacks + data_sp = 0 + addr_sp = 0 + + # VM state + ip = 0 + + # Opcode definitions + OP_NOP = 0 + OP_LIT = 1 + OP_DUP = 2 + OP_DROP = 3 + OP_SWAP = 4 + OP_PUSH = 5 + OP_POP = 6 + OP_JUMP = 7 + OP_CALL = 8 + OP_CCALL = 9 + OP_RETURN = 10 + OP_EQ = 11 + OP_NEQ = 12 + OP_LT = 13 + OP_GT = 14 + OP_FETCH = 15 + OP_STORE = 16 + OP_ADD = 17 + OP_SUB = 18 + OP_MUL = 19 + OP_DIVMOD = 20 + OP_AND = 21 + OP_OR = 22 + OP_XOR = 23 + OP_SHIFT = 24 + OP_ZERO_EXIT = 25 + OP_HALT = 26 + + # Initialize VM + prepare_vm() + + # Load and run test program + load_test_program() + execute(0) + + # Print results + print "Stack contents after execution:" + print_stack() +} + +# Stack operations +function stack_push(stack_name, value) { + if (stack_name == "data") { + data_sp++ + data_stack[data_sp] = value + } else if (stack_name == "addr") { + addr_sp++ + addr_stack[addr_sp] = value + } +} + +function stack_pop(stack_name) { + if (stack_name == "data") { + if (data_sp > 0) { + value = data_stack[data_sp] + data_sp-- + return value + } + } else if (stack_name == "addr") { + if (addr_sp > 0) { + value = addr_stack[addr_sp] + addr_sp-- + return value + } + } + return 0 +} + +function stack_tos(stack_name) { + if (stack_name == "data" && data_sp > 0) { + return data_stack[data_sp] + } + return 0 +} + +function stack_nos(stack_name) { + if (stack_name == "data" && data_sp > 1) { + return data_stack[data_sp - 1] + } + return 0 +} + +# Bitwise operations +function bitwise_and(x, y, i, result, a, b) { + result = 0 + for (i = 0; i < 32; i++) { + a = int(x / (2 ^ i)) % 2 + b = int(y / (2 ^ i)) % 2 + if (a == 1 && b == 1) + result += 2 ^ i + } + return result +} + +function bitwise_or(x, y, i, result, a, b) { + result = 0 + for (i = 0; i < 32; i++) { + a = int(x / (2 ^ i)) % 2 + b = int(y / (2 ^ i)) % 2 + if (a == 1 || b == 1) + result += 2 ^ i + } + return result +} + +function bitwise_xor(x, y, i, result, a, b) { + result = 0 + for (i = 0; i < 32; i++) { + a = int(x / (2 ^ i)) % 2 + b = int(y / (2 ^ i)) % 2 + if (a != b) + result += 2 ^ i + } + return result +} + +# Helper functions +function abs(x) { + return x < 0 ? -x : x +} + +function lshift(x, n) { + return int(x * (2 ^ n)) +} + +function rshift(x, n) { + return int(x / (2 ^ n)) +} + +# VM core functions +function process_opcode(opcode) { + if (opcode == OP_NOP) { + return + } + else if (opcode == OP_LIT) { + ip++ + stack_push("data", image[ip]) + } + else if (opcode == OP_DUP) { + stack_push("data", stack_tos("data")) + } + else if (opcode == OP_DROP) { + stack_pop("data") + } + else if (opcode == OP_SWAP) { + temp = stack_pop("data") + temp2 = stack_pop("data") + stack_push("data", temp) + stack_push("data", temp2) + } + else if (opcode == OP_ADD) { + x = stack_pop("data") + y = stack_pop("data") + stack_push("data", x + y) + } + else if (opcode == OP_SUB) { + x = stack_pop("data") + y = stack_pop("data") + stack_push("data", y - x) + } + else if (opcode == OP_MUL) { + x = stack_pop("data") + y = stack_pop("data") + stack_push("data", x * y) + } + else if (opcode == OP_HALT) { + ip = IMAGE_SIZE + } +} + +function check_stack() { + if (data_sp < 0 || addr_sp < 0 || + data_sp > DATA_DEPTH || addr_sp > ADDRESS_DEPTH) { + ip = 0 + data_sp = 0 + addr_sp = 0 + } +} + +function process_packed_opcodes(packed) { + ops[0] = bitwise_and(packed, 255) + ops[1] = bitwise_and(rshift(packed, 8), 255) + ops[2] = bitwise_and(rshift(packed, 16), 255) + ops[3] = bitwise_and(rshift(packed, 24), 255) + + for (i = 0; i < 4; i++) { + if (ops[i] != 0) { + process_opcode(ops[i]) + } + } +} + +function execute(offset) { + addr_sp = 1 + ip = offset + + while (ip < IMAGE_SIZE) { + opcode = image[ip] + process_packed_opcodes(opcode) + + if (addr_sp == 0) + ip = IMAGE_SIZE + + ip++ + } +} + +function prepare_vm() { + ip = 0 + data_sp = 0 + addr_sp = 0 +} + +# Test program loader +function pack_opcodes(op1, op2, op3, op4) { + return op1 + (op2 * 256) + (op3 * 65536) + (op4 * 16777216) +} + +function load_test_program() { + # Simple test program that adds 10 and 5 + image[0] = pack_opcodes(OP_LIT, 0, 0, 0) # Push literal + image[1] = 10 # Value 10 + image[2] = pack_opcodes(OP_LIT, 0, 0, 0) # Push literal + image[3] = 5 # Value 5 + image[4] = pack_opcodes(OP_ADD, 0, 0, 0) # Add them + image[5] = pack_opcodes(OP_HALT, 0, 0, 0) # Halt +} + +# Debug helper +function print_stack() { + for (i = 1; i <= data_sp; i++) { + print "Item", i ":", data_stack[i] + } +} \ No newline at end of file diff --git a/awk/retro/test.awk b/awk/retro/test.awk new file mode 100755 index 0000000..191fa5b --- /dev/null +++ b/awk/retro/test.awk @@ -0,0 +1,52 @@ +#!/usr/bin/awk -f + +@include "vm.awk" + +# Complex test program +BEGIN { + # Test program to calculate factorial of 5 + i = 0 + + # Push 5 onto stack + image[i++] = pack_opcodes(OP_LIT, 0, 0, 0) + image[i++] = 5 + + # Push 1 onto stack (accumulator) + image[i++] = pack_opcodes(OP_LIT, 0, 0, 0) + image[i++] = 1 + + # Start of multiplication loop + loop_start = i + + # Duplicate top number (counter) + image[i++] = pack_opcodes(OP_DUP, 0, 0, 0) + + # Test if counter is zero + image[i++] = pack_opcodes(OP_ZERO_EXIT, 0, 0, 0) + + # Multiply accumulator by counter + image[i++] = pack_opcodes(OP_MUL, 0, 0, 0) + + # Decrement counter + image[i++] = pack_opcodes(OP_LIT, 0, 0, 0) + image[i++] = 1 + image[i++] = pack_opcodes(OP_SUB, 0, 0, 0) + + # Jump back to start of loop + image[i++] = pack_opcodes(OP_LIT, 0, 0, 0) + image[i++] = loop_start + image[i++] = pack_opcodes(OP_JUMP, 0, 0, 0) + + # Halt + image[i++] = pack_opcodes(OP_HALT, 0, 0, 0) + + # Execute program + execute(0) + + # Print result (should be 120 - factorial of 5) + print "Factorial of 5:", stack_tos("data") +} + +function pack_opcodes(op1, op2, op3, op4) { + return op1 + (op2 * 256) + (op3 * 65536) + (op4 * 16777216) +} \ No newline at end of file diff --git a/awk/retro/vm.awk b/awk/retro/vm.awk new file mode 100755 index 0000000..cd894c5 --- /dev/null +++ b/awk/retro/vm.awk @@ -0,0 +1,364 @@ +#!/usr/bin/awk -f + +# Constants +BEGIN { + IMAGE_SIZE = 524288 # Amount of simulated RAM + DATA_DEPTH = 8192 # Depth of data stack + ADDRESS_DEPTH = 32768 # Depth of the stacks + + # Initialize stacks + data_sp = 0 + addr_sp = 0 + + # VM state + ip = 0 + + # Opcode definitions + OP_NOP = 0 + OP_LIT = 1 + OP_DUP = 2 + OP_DROP = 3 + OP_SWAP = 4 + OP_PUSH = 5 + OP_POP = 6 + OP_JUMP = 7 + OP_CALL = 8 + OP_CCALL = 9 + OP_RETURN = 10 + OP_EQ = 11 + OP_NEQ = 12 + OP_LT = 13 + OP_GT = 14 + OP_FETCH = 15 + OP_STORE = 16 + OP_ADD = 17 + OP_SUB = 18 + OP_MUL = 19 + OP_DIVMOD = 20 + OP_AND = 21 + OP_OR = 22 + OP_XOR = 23 + OP_SHIFT = 24 + OP_ZERO_EXIT = 25 + OP_HALT = 26 + OP_IE = 27 + OP_IQ = 28 + OP_II = 29 +} + +# Stack operations +function stack_push(stack_name, value) { + if (stack_name == "data") { + data_sp++ + data_stack[data_sp] = value + } else if (stack_name == "addr") { + addr_sp++ + addr_stack[addr_sp] = value + } +} + +function stack_pop(stack_name) { + if (stack_name == "data") { + if (data_sp > 0) { + value = data_stack[data_sp] + data_sp-- + return value + } + } else if (stack_name == "addr") { + if (addr_sp > 0) { + value = addr_stack[addr_sp] + addr_sp-- + return value + } + } + return 0 +} + +function stack_tos(stack_name) { + if (stack_name == "data" && data_sp > 0) { + return data_stack[data_sp] + } + return 0 +} + +function stack_nos(stack_name) { + if (stack_name == "data" && data_sp > 1) { + return data_stack[data_sp - 1] + } + return 0 +} + +# Bitwise operation implementations +function bitwise_and(x, y, i, result, a, b) { + result = 0 + for (i = 0; i < 32; i++) { + a = int(x / (2 ^ i)) % 2 + b = int(y / (2 ^ i)) % 2 + if (a == 1 && b == 1) + result += 2 ^ i + } + return result +} + +function bitwise_or(x, y, i, result, a, b) { + result = 0 + for (i = 0; i < 32; i++) { + a = int(x / (2 ^ i)) % 2 + b = int(y / (2 ^ i)) % 2 + if (a == 1 || b == 1) + result += 2 ^ i + } + return result +} + +function bitwise_xor(x, y, i, result, a, b) { + result = 0 + for (i = 0; i < 32; i++) { + a = int(x / (2 ^ i)) % 2 + b = int(y / (2 ^ i)) % 2 + if (a != b) + result += 2 ^ i + } + return result +} + +function lshift(x, n) { + return int(x * (2 ^ n)) +} + +function rshift(x, n) { + return int(x / (2 ^ n)) +} + +# VM instruction implementations +function process_opcode(opcode) { + if (opcode == OP_NOP) { + return + } + else if (opcode == OP_LIT) { + ip++ + stack_push("data", image[ip]) + } + else if (opcode == OP_DUP) { + stack_push("data", stack_tos("data")) + } + else if (opcode == OP_DROP) { + stack_pop("data") + } + else if (opcode == OP_SWAP) { + temp = stack_pop("data") + temp2 = stack_pop("data") + stack_push("data", temp) + stack_push("data", temp2) + } + else if (opcode == OP_PUSH) { + stack_push("addr", stack_pop("data")) + } + else if (opcode == OP_POP) { + stack_push("data", stack_pop("addr")) + } + else if (opcode == OP_JUMP) { + ip = stack_pop("data") - 1 + } + else if (opcode == OP_CALL) { + stack_push("addr", ip) + ip = stack_pop("data") - 1 + } + else if (opcode == OP_CCALL) { + a = stack_pop("data") + b = stack_pop("data") + if (b != 0) { + stack_push("addr", ip) + ip = a - 1 + } + } + else if (opcode == OP_RETURN) { + ip = stack_pop("addr") + } + else if (opcode == OP_EQ) { + a = stack_pop("data") + b = stack_pop("data") + stack_push("data", (b == a) ? -1 : 0) + } + else if (opcode == OP_NEQ) { + a = stack_pop("data") + b = stack_pop("data") + stack_push("data", (b != a) ? -1 : 0) + } + else if (opcode == OP_LT) { + a = stack_pop("data") + b = stack_pop("data") + stack_push("data", (b < a) ? -1 : 0) + } + else if (opcode == OP_GT) { + a = stack_pop("data") + b = stack_pop("data") + stack_push("data", (b > a) ? -1 : 0) + } + else if (opcode == OP_FETCH) { + x = stack_pop("data") + if (x == -1) + stack_push("data", data_sp) + else if (x == -2) + stack_push("data", addr_sp) + else if (x == -3) + stack_push("data", IMAGE_SIZE) + else if (x == -4) + stack_push("data", -2147483648) + else if (x == -5) + stack_push("data", 2147483647) + else + stack_push("data", image[x]) + } + else if (opcode == OP_STORE) { + addr = stack_pop("data") + value = stack_pop("data") + image[addr] = value + } + else if (opcode == OP_ADD) { + x = stack_pop("data") + y = stack_pop("data") + stack_push("data", x + y) + } + else if (opcode == OP_SUB) { + x = stack_pop("data") + y = stack_pop("data") + stack_push("data", y - x) + } + else if (opcode == OP_MUL) { + x = stack_pop("data") + y = stack_pop("data") + stack_push("data", y * x) + } + else if (opcode == OP_DIVMOD) { + b = stack_pop("data") + a = stack_pop("data") + if (b == 0) { + ip = 0 + data_sp = 0 + addr_sp = 0 + } else { + x = abs(b) + y = abs(a) + q = int(y / x) + r = y % x + if (a < 0 && b < 0) + r = r * -1 + if (a > 0 && b < 0) + q = q * -1 + if (a < 0 && b > 0) { + r = r * -1 + q = q * -1 + } + stack_push("data", r) + stack_push("data", q) + } + } + else if (opcode == OP_AND) { + x = stack_pop("data") + y = stack_pop("data") + stack_push("data", bitwise_and(x, y)) + } + else if (opcode == OP_OR) { + x = stack_pop("data") + y = stack_pop("data") + stack_push("data", bitwise_or(x, y)) + } + else if (opcode == OP_XOR) { + x = stack_pop("data") + y = stack_pop("data") + stack_push("data", bitwise_xor(x, y)) + } + else if (opcode == OP_SHIFT) { + x = stack_pop("data") + y = stack_pop("data") + if (x < 0) + stack_push("data", lshift(y, -x)) + else + stack_push("data", rshift(y, x)) + } + else if (opcode == OP_ZERO_EXIT) { + if (stack_tos("data") == 0) { + stack_pop("data") + ip = stack_pop("addr") + } + } + else if (opcode == OP_HALT) { + ip = IMAGE_SIZE + } + + check_stack() +} + +# Helper functions +function abs(x) { + return x < 0 ? -x : x +} + +function check_stack() { + if (data_sp < 0 || addr_sp < 0 || + data_sp > DATA_DEPTH || addr_sp > ADDRESS_DEPTH) { + ip = 0 + data_sp = 0 + addr_sp = 0 + } +} + +function process_packed_opcodes(packed) { + ops[0] = bitwise_and(packed, 255) + ops[1] = bitwise_and(rshift(packed, 8), 255) + ops[2] = bitwise_and(rshift(packed, 16), 255) + ops[3] = bitwise_and(rshift(packed, 24), 255) + + for (i = 0; i < 4; i++) { + if (ops[i] != 0) { + process_opcode(ops[i]) + } + } +} + +# Main execution function +function execute(offset) { + addr_sp = 1 + ip = offset + + while (ip < IMAGE_SIZE) { + opcode = image[ip] + process_packed_opcodes(opcode) + + if (addr_sp == 0) + ip = IMAGE_SIZE + + ip++ + } +} + +# String handling functions +function string_inject(str, buffer, i, len) { + len = length(str) + for (i = 1; i <= len; i++) { + image[buffer + i - 1] = ord(substr(str, i, 1)) + image[buffer + i] = 0 + } +} + +function string_extract(at, str, i) { + str = "" + i = at + while (image[i] != 0) { + str = str chr(image[i]) + i++ + } + return str +} + +# Initialize VM +BEGIN { + prepare_vm() +} + +function prepare_vm() { + ip = 0 + data_sp = 0 + addr_sp = 0 +} \ No newline at end of file diff --git a/awk/scheme/s.awk b/awk/scheme/s.awk new file mode 100755 index 0000000..7c8bba6 --- /dev/null +++ b/awk/scheme/s.awk @@ -0,0 +1,139 @@ +#!/usr/bin/awk -f + +# Set debug mode +DEBUG = 1 # Change to 0 to disable debug output + +# Environment to store variable bindings +BEGIN { + print "Welcome to the AWK Scheme Interpreter!" + print "Type your Scheme expressions below (type 'exit' to quit):" + while (1) { + printf "> " + if (getline input <= 0) { + print "Error reading input. Exiting." + break + } + if (input == "exit") { + print "Exiting the interpreter." + exit + } + if (input == "") { + print "Empty input received, continuing..." + continue + } + + print "Input received: " input # Echo the input + ast = parse(input) # Parse the input + + # Print the entire AST for debugging + for (i = 1; i <= length(ast); i++) { + print "AST[" i "] = " ast[i] + } + + # Evaluate the AST + if (length(ast) > 0) { + result = eval(ast) # Evaluate the AST + print "Result: " result # Print the result + } else { + print "Parsed AST is empty." + } + } +} + +# Function to parse input into an AST +function parse(input) { + # Remove outer whitespace + gsub(/^\s+|\s+$/, "", input) + + # Check if input is empty after trimming + if (input == "") { + print "Input is empty after trimming" + return "" + } + + # Debugging: Print input before processing + print "Debug: Raw input for parsing: " input + + # Remove parentheses at start and end + if (substr(input, 1, 1) == "(") { + input = substr(input, 2) + } + if (substr(input, length(input), 1) == ")") { + input = substr(input, 1, length(input) - 1) + } + + # Debugging: Print input after removing outer parentheses + print "Debug: Input after removing outer parentheses: " input + + # Split the input into tokens + gsub(/\(/, " ( ", input) + gsub(/\)/, " ) ", input) + gsub(/\s+/, " ", input) # normalize whitespace + gsub(/^\s+|\s+$/, "", input) # trim + + # Debugging: Print input after tokenization + print "Debug: Input after tokenization: " input + + n = split(input, ast, " ") + + # Debugging: Print the number of tokens + print "Debug: Number of tokens: " n + + return ast +} + +# Function to evaluate the AST +function eval(ast, i, result) { + # Debugging: Print the current AST being evaluated + print "Debug: Evaluating AST: " ast[1] " " ast[2] " " ast[3] + + # Handle numbers directly + if (ast[1] ~ /^[+-]?[0-9]+$/) { + print "Debug: Returning number: " ast[1] + return ast[1] + 0 # Convert string to number + } + + # Handle addition + if (ast[1] == "+") { + result = 0 + for (i = 2; i <= length(ast); i++) { + print "Debug: Adding operand: " ast[i] + result += eval(ast[i]) # Recursively evaluate operands + } + return result + } + + # Handle subtraction + if (ast[1] == "-") { + result = eval(ast[2]) # Start with the first operand + for (i = 3; i <= length(ast); i++) { + print "Debug: Subtracting operand: " ast[i] + result -= eval(ast[i]) # Subtract subsequent operands + } + return result + } + + # Handle multiplication + if (ast[1] == "*") { + result = 1 + for (i = 2; i <= length(ast); i++) { + print "Debug: Multiplying operand: " ast[i] + result *= eval(ast[i]) # Multiply operands + } + return result + } + + # Handle division + if (ast[1] == "/") { + result = eval(ast[2]) # Start with the first operand + for (i = 3; i <= length(ast); i++) { + print "Debug: Dividing by operand: " ast[i] + result /= eval(ast[i]) # Divide by subsequent operands + } + return result + } + + # If we reach here, the operation is not recognized + return "Error: Unknown operation " ast[1] +} + diff --git a/awk/scheme/scheme/README.md b/awk/scheme/scheme/README.md new file mode 100644 index 0000000..7711442 --- /dev/null +++ b/awk/scheme/scheme/README.md @@ -0,0 +1,186 @@ +# Awk-Scheme + +## Overview +A Scheme interpreter implemented in AWK, featuring: +- A compiler that translates Scheme expressions to stack-based VM instructions +- A virtual machine that executes the compiled code +- Support for basic arithmetic, list operations, functions, and variable bindings +- Persistent global state between REPL sessions + +## Architecture + +### Components +1. **Compiler** (`bin/compiler.awk`): + - Lexical analyzer for tokenizing input + - Recursive descent parser for Scheme expressions + - Code generator that produces stack-based VM instructions + - Handles nested expressions and proper scoping + +2. **Virtual Machine** (`bin/vm.awk`): + - Stack-based execution model + - Typed value system with runtime type checking + - Environment-based variable binding + - Function call/return mechanism + - Heap memory for cons cells + - State persistence for globals and functions + +3. **REPL** (`bin/repl`): + - Interactive command line interface + - Multi-line input support + - State persistence between sessions + - Debug mode support + +### Data Types +All values are tagged with their type: +- `N:value` - Numbers (integers) +- `B:value` - Booleans (0/1) +- `P:index` - Pairs (cons cells) +- `F:name` - Functions +- `NIL:` - Empty list +- `S:name` - Symbols + +## Usage + +### Running the REPL +```bash +# Start interactive REPL +./scheme + +# Run a Scheme file +./scheme myprogram.scm + +# Enable debug output +DEBUG=1 ./scheme +``` + +### Basic Operations + +1. **Arithmetic**: +```scheme +scheme> (+ 1 2 3) +N:6 +scheme> (* 4 (- 10 5)) +N:20 +``` + +2. **Comparisons**: +```scheme +scheme> (< 1 2) +B:1 +scheme> (= 42 42) +B:1 +``` + +3. **Lists**: +```scheme +scheme> (cons 1 (cons 2 nil)) +P:1 +scheme> (car (cons 1 2)) +N:1 +``` + +4. **Variables**: +```scheme +scheme> (define x 42) +N:42 +scheme> x +N:42 +``` + +5. **Functions**: +```scheme +scheme> (define add (x y) (+ x y)) +scheme> (add 2 3) +N:5 +``` + +6. **Let Expressions**: +```scheme +scheme> (let ((x 10) (y 20)) (+ x y)) +N:30 +``` + +## Implementation Details + +### Compiler Pipeline +1. **Lexical Analysis**: + - Tokenizes input into numbers, symbols, and parentheses + - Handles whitespace and basic comments + +2. **Parsing**: + - Builds expression tree from tokens + - Validates syntax and expression structure + +3. **Code Generation**: + - Translates expressions to VM instructions + - Manages scope and variable bindings + - Handles function definitions + +### Virtual Machine +1. **Stack Operations**: + - PUSH_CONST: Push constant value + - POP: Remove top value + - STORE: Store variable binding + - LOOKUP: Retrieve variable value + +2. **Function Calls**: + - CALL: Invoke function + - RETURN: Return from function + - Environment frame management + +3. **Memory Management**: + - Stack-based evaluation + - Simple heap for cons cells + - Basic reference counting (not fully implemented) + +### State Persistence +- Global variables and functions persist between sessions +- State stored in `/tmp/scheme_vm.state` and `/tmp/scheme_vm.env` +- Automatic state loading/saving + +## Extending the System + +### Adding New Special Forms +1. Add parsing in `compile_expr()` +2. Implement code generation function +3. Add corresponding VM instructions if needed + +### Adding New Data Types +1. Define new type tag in VM +2. Add type checking predicates +3. Implement value construction/access +4. Update relevant operations + +### Adding VM Instructions +1. Add instruction handling in `execute()` +2. Implement instruction logic +3. Update compiler to generate new instruction + +### Debugging +- Enable debug output: `DEBUG=1 ./scheme` +- Debug messages show: + - Lexical analysis + - Parsing steps + - Code generation + - VM execution + - Stack operations + - Environment changes + +## Limitations +Current implementation does not support: +- Floating point numbers +- Strings +- Proper tail recursion +- Garbage collection +- Error recovery in REPL +- Full numeric tower +- Macros +- Standard library + +## Future Enhancements +1. Proper tail call optimization +2. Garbage collection +3. Error recovery +4. More data types +5. Standard library +6. Better debugging tools \ No newline at end of file diff --git a/awk/scheme/scheme/TODO.txt b/awk/scheme/scheme/TODO.txt new file mode 100644 index 0000000..31723a4 --- /dev/null +++ b/awk/scheme/scheme/TODO.txt @@ -0,0 +1,69 @@ +Scheme Interpreter TODO +===================== + +Current State: +------------- +- Basic arithmetic operations (+,-,*,/) are working +- Let expressions with simple bindings are working +- Function definitions (define) and lambda expressions are partially implemented +- Stack-based virtual machine with environment for variable bindings + +Recent Changes: +-------------- +1. Fixed function table array naming conflicts (func_name -> func_def_names) +2. Modified vm_get_value to handle function names correctly +3. Attempted to fix argument handling in function calls +4. Modified lambda compilation to avoid pushing function name twice + +Current Issues: +-------------- +1. Function Definitions (define): + - Function calls are failing with "ADD requires numeric operands" + - Arguments may not be properly passed to functions + - Function environment may not be properly set up + +2. Lambda Expressions: + - Direct lambda calls are failing + - Lambda bindings in let expressions are failing + - Function environment for lambda parameters may not be correct + +Next Steps: +----------- +1. Debug Function Calls: + - Add detailed debug logging in vm_call_function + - Verify argument handling and environment setup + - Check if function code is being properly stored and retrieved + +2. Fix Function Environment: + - Review how parameters are stored in the environment + - Ensure environment is properly restored after function calls + - Verify parameter values are correctly passed to functions + +3. Improve Lambda Support: + - Review lambda compilation process + - Ensure lambda functions are properly stored and called + - Fix environment handling for lambda parameters + +4. Testing Strategy: + - Create test cases for each type of function call + - Add debug output to track stack and environment state + - Verify each step of function compilation and execution + +5. Code Cleanup: + - Review and document function handling code + - Ensure consistent naming and error handling + - Add comments explaining complex operations + +Technical Notes: +--------------- +- Function calls use a combination of LOOKUP, GET_VALUE, and CALL instructions +- Environment stack handles variable bindings and function parameters +- Function code is stored in FUNCTIONS table with unique names +- Lambda functions use __lambda_N naming scheme + +Debugging Tips: +-------------- +1. Enable DEBUG=1 to see detailed execution logs +2. Check stack contents before and after each operation +3. Verify environment state during function calls +4. Monitor function code storage and retrieval \ No newline at end of file diff --git a/awk/scheme/scheme/bin/compiler.awk b/awk/scheme/scheme/bin/compiler.awk new file mode 100755 index 0000000..debaa2c --- /dev/null +++ b/awk/scheme/scheme/bin/compiler.awk @@ -0,0 +1,594 @@ +#!/usr/bin/awk -f + +# This is a Scheme-to-Assembly compiler implemented in AWK +# It takes Scheme expressions as input and outputs assembly instructions +# for a custom stack-based virtual machine + +BEGIN { + # Compiler maintains internal state for code generation + curr_token = "" # Current token being processed by lexer + input_buffer = "" # Buffer for input text being tokenized + next_label = 0 # Counter for generating unique labels + program = "" # Accumulates the full program text + + # Debug mode disabled by default, can be enabled via DEBUG=1 environment variable + DEBUG = (ENVIRON["DEBUG"] == "1") ? 1 : 0 +} + +# Debug logging helper function +function debug(msg) { + if (DEBUG) printf("[DEBUG] %s\n", msg) > "/dev/stderr" +} + +# Each line of input is accumulated into the program string +# This allows handling multi-line expressions +{ + if (program != "") program = program "\n" + program = program $0 +} + +# Main compiler entry point - called after all input is read +END { + debug("Raw program:\n" program) + if (program == "") exit + + # Parse and compile each expression in the program + split_expressions(program) +} + +# Splits input into individual Scheme expressions +# Handles nested parentheses and comments +function split_expressions(prog, current, paren_count, i, c, expr, cleaned) { + current = "" + paren_count = 0 + + # Clean up the input: + # 1. Remove comments (text between ; and next opening paren) + # 2. Normalize whitespace + # 3. Trim leading/trailing whitespace + cleaned = prog + gsub(/;[^(]*\(/, "(", cleaned) # Remove comments before expressions + gsub(/\)[^)]*;/, ")", cleaned) # Remove comments after expressions + gsub(/[ \t\n]+/, " ", cleaned) # Normalize whitespace to single spaces + sub(/^[ \t\n]+/, "", cleaned) # Trim leading whitespace + sub(/[ \t\n]+$/, "", cleaned) # Trim trailing whitespace + + debug("Cleaned program: [" cleaned "]") + + if (cleaned == "") return + + # Parse expressions by tracking parenthesis nesting + for (i = 1; i <= length(cleaned); i++) { + c = substr(cleaned, i, 1) + + if (c == "(") { + if (paren_count == 0) current = "" + paren_count++ + } + + current = current c + + if (c == ")") { + paren_count-- + if (paren_count == 0) { + # Complete expression found - compile it + expr = current + sub(/^\s+/, "", expr) + sub(/\s+$/, "", expr) + + debug("Processing expression: [" expr "]") + program = expr # Set for parser + expr = parse_expr() + compile_expr(expr) + current = "" + } + } + } + + # Add final HALT instruction + print "HALT" +} + +# Lexer helper functions for character classification +function is_digit(c) { return c >= "0" && c <= "9" } +function is_whitespace(c) { return c == " " || c == "\t" || c == "\n" } + +# Lexical analyzer - converts input into tokens +# Handles numbers, symbols, and parentheses +function next_token() { + # Initialize input buffer on first call + if (input_buffer == "") input_buffer = program + + # Skip whitespace between tokens + while (length(input_buffer) > 0 && is_whitespace(substr(input_buffer, 1, 1))) + input_buffer = substr(input_buffer, 2) + + if (length(input_buffer) == 0) return "EOF" + + # Handle parentheses as single-character tokens + c = substr(input_buffer, 1, 1) + if (c == "(" || c == ")") { + input_buffer = substr(input_buffer, 2) + return c + } + + # Handle numbers (including negative numbers) + if (is_digit(c) || c == "-" && length(input_buffer) > 1 && is_digit(substr(input_buffer, 2, 1))) { + num = "" + while (length(input_buffer) > 0) { + c = substr(input_buffer, 1, 1) + if (!is_digit(c) && c != "-") break + num = num c + input_buffer = substr(input_buffer, 2) + } + return num + } + + # Handle symbols (identifiers and operators) + sym = "" + while (length(input_buffer) > 0) { + c = substr(input_buffer, 1, 1) + if (is_whitespace(c) || c == "(" || c == ")") break + sym = sym c + input_buffer = substr(input_buffer, 2) + } + return sym +} + +# Recursive descent parser for Scheme expressions +# Returns parsed expression as a string +function parse_expr(token, result) { + token = next_token() + if (token == "EOF") return "" + + if (token == "(") { + result = parse_list() + debug("Parsed list: " result) + return result + } + + debug("Parsed token: " token) + return token +} + +# Parses a list expression (anything in parentheses) +function parse_list(result, expr) { + result = "" + + while (1) { + expr = parse_expr() + if (expr == "" || expr == ")") break + + if (result != "") result = result " " + result = result expr + } + + if (expr == "") error("Unexpected end of input in list") + return "(" result ")" +} + +# Splits an expression into operator and arguments +# Handles nested expressions correctly +function split_expr(expr, i, len, c, op, args, paren_count) { + len = length(expr) + paren_count = 0 + + for (i = 1; i <= len; i++) { + c = substr(expr, i, 1) + if (c == " " && paren_count == 0) { + op = substr(expr, 1, i - 1) + args = substr(expr, i + 1) + break + } + if (c == "(") paren_count++ + if (c == ")") paren_count-- + } + + if (!op) { + op = expr + args = "" + } + + debug("Split expr: op=" op " args=" args) + return op SUBSEP args +} + +# Splits argument list handling nested parentheses +function split_args(args, arg_array, len, i, c, current, paren_count, arg_count) { + len = length(args) + current = "" + paren_count = 0 + arg_count = 0 + + for (i = 1; i <= len; i++) { + c = substr(args, i, 1) + + if (c == "(") paren_count++ + if (c == ")") paren_count-- + + if (c == " " && paren_count == 0 && current != "") { + arg_array[++arg_count] = current + current = "" + } else if (c != " " || paren_count > 0) { + current = current c + } + } + + if (current != "") { + arg_array[++arg_count] = current + } + + return arg_count +} + +# Code generation for numeric literals +function compile_number(num) { + debug("Compiling number: " num) + print "PUSH_CONST N:" num +} + +# Code generation for primitive operations (+, -, *, cons, etc) +function compile_primitive_call(op, args, arg_array, nargs, i) { + debug("Primitive call: op=" op " args=" args) + nargs = split_args(args, arg_array) + + # Check if this is a lambda function call + if (substr(op, 1, 1) == "(") { + # This is a lambda function call + # First compile the lambda function + compile_expr(op) + + # Then compile all arguments + for (i = 1; i <= nargs; i++) { + compile_expr(arg_array[i]) + } + + # Call the function + print "CALL __lambda_" (next_label - 1) + return + } + + # Then emit appropriate operation + if (op == "+") { + # Compile arguments + for (i = 1; i <= nargs; i++) { + compile_expr(arg_array[i]) + } + for (i = 1; i < nargs; i++) + print "ADD" + } + else if (op == "-") { + # Compile arguments + for (i = 1; i <= nargs; i++) { + compile_expr(arg_array[i]) + } + if (nargs == 1) { + print "PUSH_CONST N:0" + print "SWAP" + } + for (i = 1; i < nargs; i++) + print "SUB" + } + else if (op == "*") { + # Compile arguments + for (i = 1; i <= nargs; i++) { + compile_expr(arg_array[i]) + } + for (i = 1; i < nargs; i++) + print "MUL" + } + else if (op == "cons") { + if (nargs != 2) error("cons requires 2 arguments") + # Compile arguments + for (i = 1; i <= nargs; i++) { + compile_expr(arg_array[i]) + } + print "CONS" + } + else if (op == "car") { + if (nargs != 1) error("car requires 1 argument") + # Compile argument + compile_expr(arg_array[1]) + print "CAR" + } + else if (op == "cdr") { + if (nargs != 1) error("cdr requires 1 argument") + # Compile argument + compile_expr(arg_array[1]) + print "CDR" + } + else if (op == "<") { + if (nargs != 2) error("< requires 2 arguments") + # Compile arguments + for (i = 1; i <= nargs; i++) { + compile_expr(arg_array[i]) + } + print "LT" + } + else if (op == "=") { + if (nargs != 2) error("= requires 2 arguments") + # Compile arguments + for (i = 1; i <= nargs; i++) { + compile_expr(arg_array[i]) + } + print "EQ" + } + else { + # Function call for user-defined functions + debug("Function call: " op) + # Look up the function name + print "LOOKUP " op + # Get the actual function name + print "GET_VALUE" + # Then compile arguments + for (i = 1; i <= nargs; i++) { + compile_expr(arg_array[i]) + } + # Call the function + print "CALL" + } +} + +# Splits let bindings into individual variable/value pairs +function split_bindings(bindings, binding_array, count, current, paren_count, i, c, in_lambda) { + count = 0 + current = "" + paren_count = 0 + in_lambda = 0 + + for (i = 1; i <= length(bindings); i++) { + c = substr(bindings, i, 1) + + # Track nested parentheses + if (c == "(") { + paren_count++ + if (paren_count == 1 && !in_lambda) { + current = "" # Start new binding + continue + } + } + if (c == ")") { + paren_count-- + if (paren_count == 0 && !in_lambda) { + # End of binding + binding_array[++count] = current + current = "" + continue + } + } + + # Track if we're inside a lambda expression + if (substr(bindings, i, 7) == "lambda ") { + in_lambda = 1 + } + + # Only add character if we're inside a binding + if (paren_count > 0) { + current = current c + } + } + + return count +} + +# Compiles let expressions (local variable bindings) +function compile_let(args, bindings, body, binding_array, nbindings, i, var, val, binding_parts) { + # Split into bindings and body + if (substr(args, 1, 1) != "(") error("Malformed let expression") + + # Find matching closing parenthesis for bindings + paren_count = 1 + i = 2 + while (paren_count > 0 && i <= length(args)) { + if (substr(args, i, 1) == "(") paren_count++ + if (substr(args, i, 1) == ")") paren_count-- + i++ + } + if (paren_count > 0) error("Unmatched parenthesis in let bindings") + + bindings = substr(args, 2, i - 3) # Remove outer parentheses + body = substr(args, i) + + # Trim whitespace from body + sub(/^[ \t\n]+/, "", body) + sub(/[ \t\n]+$/, "", body) + + debug("Let bindings: " bindings) + debug("Let body: " body) + + # Compile each binding + nbindings = split_bindings(bindings, binding_array) + for (i = 1; i <= nbindings; i++) { + debug("Processing binding: " binding_array[i]) + + # Find the variable name (everything up to the first space) + var = binding_array[i] + sub(/ .*$/, "", var) + + # Find the value (everything after the first space) + val = binding_array[i] + sub(/^[^ ]+ /, "", val) + + debug("Binding var: " var " val: " val) + + # Compile the value + if (substr(val, 1, 1) == "(") { + # Handle lambda or other compound expressions + if (substr(val, 2, 6) == "lambda") { + # This is a lambda expression + # First compile the lambda + compile_lambda(substr(val, 8)) # Skip "(lambda " + # Store the function name in the environment + print "STORE " var + } else { + compile_expr(val) + print "STORE " var + } + } else { + compile_expr(val) + print "STORE " var + } + } + + # Compile the body + compile_expr(body) + + # Clean up bindings AFTER evaluating body + for (i = nbindings; i >= 1; i--) { + print "POP_ENV" + } +} + +# Compiles define expressions (function/variable definitions) +function compile_define(args, name, params, body, param_array, nparams, i, paren_start, paren_end) { + # Set flag for global definition + print "PUSH_CONST B:1" + print "STORE from_define" # Must match exactly what vm_store checks for + + # Find the function name (everything up to the first space) + i = index(args, " ") + if (i == 0) error("Malformed define expression") + name = substr(args, 1, i - 1) + args = substr(args, i + 1) + + # Check if it's a function or variable definition + if (substr(args, 1, 1) == "(") { + # It's a function definition + paren_count = 1 + i = 2 + while (paren_count > 0 && i <= length(args)) { + if (substr(args, i, 1) == "(") paren_count++ + if (substr(args, i, 1) == ")") paren_count-- + i++ + } + if (paren_count > 0) error("Unmatched parenthesis in parameter list") + + params = substr(args, 2, i - 3) # Remove parentheses + body = substr(args, i + 1) + + # Create function label + print "LABEL " name + + # Process parameters + nparams = split(params, param_array, " ") + for (i = 1; i <= nparams; i++) { + print "STORE " param_array[i] + } + + # Compile function body + compile_expr(body) + + # Clean up parameters and return + for (i = nparams; i >= 1; i--) { + print "POP_ENV" + } + print "RETURN" + } else { + # Variable definition + debug("Defining variable: " name " with value: " args) + compile_expr(args) # Compile the value + print "STORE " name # Store the variable + } +} + +# Compiles lambda expressions (anonymous functions) +function compile_lambda(args, params, body, param_array, nparams, i, lambda_name) { + # Generate a unique name for the lambda function + lambda_name = "__lambda_" next_label++ + + # Split into parameters and body + if (substr(args, 1, 1) != "(") error("Malformed lambda expression") + + # Find matching closing parenthesis for parameters + paren_count = 1 + i = 2 + while (paren_count > 0 && i <= length(args)) { + if (substr(args, i, 1) == "(") paren_count++ + if (substr(args, i, 1) == ")") paren_count-- + i++ + } + if (paren_count > 0) error("Unmatched parenthesis in lambda parameters") + + params = substr(args, 2, i - 3) # Remove parentheses + body = substr(args, i) + + # Trim whitespace from body + sub(/^[ \t\n]+/, "", body) + sub(/[ \t\n]+$/, "", body) + + debug("Lambda parameters: " params) + debug("Lambda body: " body) + + # Create function label + print "LABEL " lambda_name + + # Process parameters + nparams = split(params, param_array, " ") + for (i = 1; i <= nparams; i++) { + print "STORE " param_array[i] + } + + # Compile function body + compile_expr(body) + + # Clean up parameters and return + for (i = nparams; i >= 1; i--) { + print "POP_ENV" + } + print "RETURN" + + # Only push the function name if we're not in a direct call + if (!(args ~ /^\([^)]*\)[^(]*$/)) { + print "PUSH_CONST S:" lambda_name + } +} + +# Main expression compiler - dispatches based on expression type +function compile_expr(expr, split_result, op, args) { + debug("Compiling expression: " expr) + + # Handle numeric literals + if (expr ~ /^-?[0-9]+$/) { + compile_number(expr) + return + } + + # Handle nil constant + if (expr == "nil") { + print "PUSH_CONST NIL:" + return + } + + # Handle variable lookup + if (expr ~ /^[a-zA-Z_][a-zA-Z0-9_]*$/) { + print "LOOKUP " expr + return + } + + # Handle compound expressions (lists) + if (substr(expr, 1, 1) == "(") { + expr = substr(expr, 2, length(expr) - 2) + split_result = split_expr(expr) + op = substr(split_result, 1, index(split_result, SUBSEP) - 1) + args = substr(split_result, index(split_result, SUBSEP) + 1) + + if (op == "define") { + compile_define(args) + } else if (op == "let") { + compile_let(args) + } else if (op == "lambda") { + compile_lambda(args) + } else { + compile_primitive_call(op, args) + } + return + } + + error("Unknown expression type: " expr) +} + +# Error reporting helper +function error(msg) { + print "Error: " msg > "/dev/stderr" + exit 1 +} \ No newline at end of file diff --git a/awk/scheme/scheme/bin/repl b/awk/scheme/scheme/bin/repl new file mode 100755 index 0000000..14a10cf --- /dev/null +++ b/awk/scheme/scheme/bin/repl @@ -0,0 +1,147 @@ +#!/bin/bash + +# Enable debug tracing +DEBUG=0 + +debug() { + if [ "$DEBUG" = "1" ]; then + echo "[DEBUG] $*" >&2 + fi +} + +# Find the directory containing this script and the components +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +COMPILER="$DIR/compiler.awk" +VM="$DIR/vm.awk" + +debug "Using compiler: $COMPILER" +debug "Using VM: $VM" + +# Verify components exist and are executable +for component in "$COMPILER" "$VM"; do + if [ ! -f "$component" ]; then + echo "Error: Cannot find $component" >&2 + echo "Please ensure all components are present" >&2 + exit 1 + fi + chmod +x "$component" +done + +# Set up temporary files and state +TMPDIR=$(mktemp -d) +debug "Created temp dir: $TMPDIR" +STATE_FILE="/tmp/scheme_vm.state" + +cleanup() { + debug "Cleaning up temp dir: $TMPDIR" + rm -rf "$TMPDIR" + if [ "$1" != "keep_state" ]; then + rm -f "$STATE_FILE" + rm -f "/tmp/scheme_vm.env" + fi +} +trap "cleanup" EXIT + +# Set up temporary files +INPUT_FILE="$TMPDIR/input.scm" +ASM_FILE="$TMPDIR/output.asm" +DEBUG_FILE="$TMPDIR/debug.out" + +# Initialize/clear state files at REPL start +if [ "$#" -eq 0 ]; then # Only for interactive mode + : > "/tmp/scheme_vm.state" + : > "/tmp/scheme_vm.env" +fi + +# Function to handle evaluation +evaluate_expression() { + local input="$1" + local result + + # Skip empty lines + if [ -z "$input" ]; then + return 0 + fi + + debug "Evaluating expression: $input" + echo "$input" > "$INPUT_FILE" + debug "Input file contents:" + cat "$INPUT_FILE" >&2 + + # Show compilation output even if it fails + debug "Running compiler..." + if awk -f "$COMPILER" "$INPUT_FILE" > "$ASM_FILE" 2> "$DEBUG_FILE"; then + debug "Compilation successful. Debug output:" + cat "$DEBUG_FILE" >&2 + debug "Generated assembly:" + cat "$ASM_FILE" >&2 + + debug "Running VM..." + # Use persistent VM state + result=$(awk -v PERSIST=1 -f "$VM" "$ASM_FILE" 2>&1) + debug "VM output: $result" + if [ -n "$result" ]; then + echo "$result" + fi + return 0 + else + echo "Compilation error" >&2 + debug "Compiler output:" + cat "$DEBUG_FILE" >&2 + return 1 + fi +} + +# Check if a file argument is provided +if [ "$#" -gt 0 ]; then + if [ ! -f "$1" ]; then + echo "Error: File not found: $1" >&2 + exit 1 + fi + debug "Reading file: $1" + file_content=$(cat "$1" | tr '\n' ' ') + debug "File content: $file_content" + evaluate_expression "$file_content" + cleanup "keep_state" # Keep state after file execution + exit 0 +fi + +# REPL state +paren_count=0 +current_input="" + +# Print welcome message +echo "Scheme REPL (Press Ctrl+D to exit)" +echo + +# Main REPL loop +while true; do + if [ $paren_count -eq 0 ]; then + printf "scheme> " + else + printf "... " + fi + + read -r line || exit 0 + + # Skip empty lines + if [ -z "$line" ]; then + continue + fi + + # Count parentheses + open_parens=$(echo "$line" | tr -cd '(' | wc -c) + close_parens=$(echo "$line" | tr -cd ')' | wc -c) + paren_count=$((paren_count + open_parens - close_parens)) + + if [ -n "$current_input" ]; then + current_input="$current_input $line" + else + current_input="$line" + fi + + if [ $paren_count -eq 0 ]; then + evaluate_expression "$current_input" + current_input="" + fi +done \ No newline at end of file diff --git a/awk/scheme/scheme/bin/vm.awk b/awk/scheme/scheme/bin/vm.awk new file mode 100755 index 0000000..4e7d2c7 --- /dev/null +++ b/awk/scheme/scheme/bin/vm.awk @@ -0,0 +1,707 @@ +#!/usr/bin/awk -f + +# This is a stack-based virtual machine for executing compiled Scheme code +# It implements a simple instruction set with support for: +# - Basic arithmetic operations +# - Function calls and returns +# - Variable bindings and lookups +# - Cons cells and list operations + +BEGIN { + # Type system tags for runtime type checking + T_NUMBER = "N" # Numbers (integers) + T_BOOLEAN = "B" # Booleans (0/1) + T_SYMBOL = "S" # Symbols (identifiers) + T_PAIR = "P" # Cons cells (pairs) + T_FUNCTION = "F" # Function references + T_NIL = "NIL" # Empty list marker + + # Virtual machine registers + stack_ptr = 0 # Points to top of evaluation stack + heap_ptr = 0 # Points to next free heap location + pc = 0 # Program counter for instruction fetch + + # Debug mode disabled by default, can be enabled via DEBUG=1 environment variable + DEBUG = (ENVIRON["DEBUG"] == "1") ? 1 : 0 + + # Environment for variable bindings + env_size = 0 # Current size of environment stack + + # Function table for storing defined functions + delete func_def_names # Function names + delete func_def_pc # Entry points + delete func_def_code # Function bodies + func_def_size = 0 # Number of defined functions + + # Call stack for function returns + call_stack_ptr = 0 + + # State persistence configuration + STATE_FILE = "/tmp/scheme_vm.state" + if (PERSIST) { + debug("Loading state from: " STATE_FILE) + if ((getline line < STATE_FILE) >= 0) { # Check if file exists and is readable + do { + if (line ~ /^FUNC /) { + # Parse and load function definition + sub(/^FUNC /, "", line) + name = line + sub(/ .*$/, "", name) + code = line + sub(/^[^ ]+ /, "", code) + + debug("Loaded function: " name) + debug("Code: " code) + + # Store function in function table + func_def_names[func_def_size] = name + func_def_code[func_def_size] = code + func_def_size++ + } + } while ((getline line < STATE_FILE) > 0) + close(STATE_FILE) + } + } + + # Function environment storage + delete func_env_names # Variable names in function scope + delete func_env_vals # Variable values in function scope + delete func_env_sizes # Size of each function's environment + + # Global function registry + delete FUNCTIONS # Maps function names to implementations + + # Environment persistence configuration + ENV_STATE_FILE = "/tmp/scheme_vm.env" + if (PERSIST) { + debug("Loading environment state from: " ENV_STATE_FILE) + if ((getline line < ENV_STATE_FILE) >= 0) { + do { + if (line ~ /^ENV /) { + # Parse and load environment binding + sub(/^ENV /, "", line) + name = line + sub(/ .*$/, "", name) + val = line + sub(/^[^ ]+ /, "", val) + + debug("Loaded env var: " name " = " val) + + # Store in environment + env_name[env_size] = name + env_val[env_size] = val + env_size++ + } + } while ((getline line < ENV_STATE_FILE) > 0) + close(ENV_STATE_FILE) + } + } + + # Register built-in functions + FUNCTIONS["+"] = "add" + FUNCTIONS["-"] = "subtract" + FUNCTIONS["*"] = "multiply" + FUNCTIONS["/"] = "divide" + FUNCTIONS["="] = "equals" + FUNCTIONS["<"] = "less_than" + FUNCTIONS[">"] = "greater_than" + FUNCTIONS["add1"] = "add_one" + + # Track if VM halted normally (vs error) + normal_exit = 0 +} + +# Debug output helper +function debug(msg) { + if (DEBUG) printf("[DEBUG] %s\n", msg) > "/dev/stderr" +} + +# Value constructors and accessors +# Values are stored as type:value pairs for runtime type checking +function makeValue(type, val) { + return type ":" val +} + +function getType(val) { + type = substr(val, 1, index(val, ":") - 1) + debug("Get type: " type " from " val) + return type +} + +function getValue(val) { + value = substr(val, index(val, ":") + 1) + debug("Get value: " value " from " val) + return value +} + +# Type checking predicates +function isNumber(val) { return getType(val) == T_NUMBER } +function isBoolean(val) { return getType(val) == T_BOOLEAN } +function isSymbol(val) { return getType(val) == T_SYMBOL } +function isPair(val) { return getType(val) == T_PAIR } +function isFunction(val) { return getType(val) == T_FUNCTION } +function isNil(val) { return getType(val) == T_NIL } + +# Stack operations +function push(val) { + stack[++stack_ptr] = val + debug("Push: " val " (SP: " stack_ptr ")") +} + +function pop() { + if (stack_ptr < 1) error("Stack underflow") + val = stack[stack_ptr--] + debug("Pop: " val " (SP: " stack_ptr ")") + return val +} + +function peek() { + if (stack_ptr < 1) error("Stack empty") + debug("Peek: " stack[stack_ptr]) + return stack[stack_ptr] +} + +# Heap operations for cons cells +function allocate(val) { + heap[++heap_ptr] = val + refs[heap_ptr] = 1 # Reference counting (not fully implemented) + debug("Allocate: " val " at " heap_ptr) + return heap_ptr +} + +function getHeap(idx) { + if (!(idx in heap)) { + error("Invalid heap access: " idx) + return "" + } + return heap[idx] +} + +# Error handling +function error(msg) { + print "Error at PC " pc ": " msg > "/dev/stderr" + exit 1 +} + +# Arithmetic instruction implementations +function vm_add() { + if (stack_ptr < 2) error("ADD requires two operands") + val2 = pop() + val1 = pop() + if (!isNumber(val1) || !isNumber(val2)) + error("ADD requires numeric operands") + result = getValue(val1) + getValue(val2) + push(makeValue(T_NUMBER, result)) +} + +function vm_subtract() { + if (stack_ptr < 2) error("SUB requires two operands") + val2 = pop() + val1 = pop() + if (!isNumber(val1) || !isNumber(val2)) + error("SUB requires numeric operands") + result = getValue(val1) - getValue(val2) + push(makeValue(T_NUMBER, result)) +} + +function vm_multiply() { + if (stack_ptr < 2) error("MUL requires two operands") + val2 = pop() + val1 = pop() + if (!isNumber(val1) || !isNumber(val2)) + error("MUL requires numeric operands") + result = getValue(val1) * getValue(val2) + push(makeValue(T_NUMBER, result)) +} + +function vm_divide() { + if (stack_ptr < 2) error("DIV requires two operands") + val2 = pop() + val1 = pop() + if (!isNumber(val1) || !isNumber(val2)) + error("DIV requires numeric operands") + if (getValue(val2) == 0) + error("Division by zero") + result = getValue(val1) / getValue(val2) + push(makeValue(T_NUMBER, result)) +} + +# List operation implementations +function vm_cons() { + if (stack_ptr < 2) error("CONS requires two operands") + val2 = pop() + val1 = pop() + pair_val = val1 "," val2 + pair_idx = allocate(pair_val) + push(makeValue(T_PAIR, pair_idx)) +} + +function vm_car() { + if (stack_ptr < 1) error("CAR requires one operand") + val = pop() + if (!isPair(val)) error("CAR requires pair operand") + pair_idx = getValue(val) + pair = getHeap(pair_idx) + car_val = substr(pair, 1, index(pair, ",") - 1) + push(car_val) +} + +function vm_cdr() { + if (stack_ptr < 1) error("CDR requires one operand") + val = pop() + if (!isPair(val)) error("CDR requires pair operand") + pair_idx = getValue(val) + pair = getHeap(pair_idx) + cdr_val = substr(pair, index(pair, ",") + 1) + push(cdr_val) +} + +# Comparison operations +function vm_equal() { + if (stack_ptr < 2) error("EQ requires two operands") + val2 = pop() + val1 = pop() + result = (val1 == val2) ? "1" : "0" + debug("Equal comparison: " val1 " == " val2 " -> " result) + push(makeValue(T_BOOLEAN, result)) +} + +function vm_less_than() { + if (stack_ptr < 2) error("LT requires two operands") + val2 = pop() + val1 = pop() + if (!isNumber(val1) || !isNumber(val2)) + error("LT requires numeric operands") + result = (getValue(val1) < getValue(val2)) ? "1" : "0" + debug("Less than comparison: " val1 " < " val2 " -> " result) + push(makeValue(T_BOOLEAN, result)) +} + +# Main instruction execution loop +function execute(instr) { + split(instr, parts, " ") + op = parts[1] + debug("Execute: " instr) + + # Dispatch based on instruction opcode + if (op == "PUSH_CONST") { + push(parts[2]) + } + else if (op == "POP") { + pop() + } + else if (op == "DUP") { + val = peek() + push(val) + } + else if (op == "SWAP") { + if (stack_ptr < 2) error("SWAP requires two operands") + val2 = pop() + val1 = pop() + push(val2) + push(val1) + } + else if (op == "ADD") { + vm_add() + } + else if (op == "SUB") { + vm_subtract() + } + else if (op == "MUL") { + vm_multiply() + } + else if (op == "DIV") { + vm_divide() + } + else if (op == "CONS") { + vm_cons() + } + else if (op == "CAR") { + vm_car() + } + else if (op == "CDR") { + vm_cdr() + } + else if (op == "EQ") { + vm_equal() + } + else if (op == "LT") { + vm_less_than() + } + else if (op == "PRINT") { + if (stack_ptr < 1) error("PRINT requires one operand") + print peek() + } + else if (op == "HALT") { + normal_exit = 1 + if (stack_ptr > 0) { + result = peek() + } + if (PERSIST) { + save_state() + } + if (result) { + print result + } + exit(0) + } + else if (op == "STORE") { + vm_store(parts[2]) + } + else if (op == "POP_ENV") { + vm_pop_env() + } + else if (op == "LOOKUP") { + vm_lookup(parts[2]) + } + else if (op == "LABEL") { + vm_define_function(parts[2], pc) + } + else if (op == "CALL") { + vm_call_function(parts[2]) + } + else if (op == "RETURN") { + vm_return() + } + else if (op == "GET_VALUE") { + vm_get_value() + } + else { + error("Unknown instruction: " op) + } +} + +# Load program instructions +{ + program[NR-1] = $0 +} + +# Main execution loop +END { + while (pc < length(program)) { + execute(program[pc++]) + } + + # Save state if we didn't halt normally + if (!normal_exit && PERSIST) { + save_state() + } +} + +# Variable binding implementation +function vm_store(name) { + debug("Storing " peek() " as " name " at env_size: " env_size) + + # Handle global definitions specially + if (lookup_no_error("from_define")) { + name = "__global_" name + # Clear the define flag + for (i = env_size - 1; i >= 0; i--) { + if (env_name[i] == "from_define") { + env_size-- + break + } + } + + # Remove any previous definition of this global + for (i = env_size - 1; i >= 0; i--) { + if (env_name[i] == name) { + # Shift everything down + for (j = i; j < env_size - 1; j++) { + env_name[j] = env_name[j + 1] + env_val[j] = env_val[j + 1] + } + env_size-- + break + } + } + } + + # Handle lambda functions + val = peek() + if (isSymbol(val)) { + func_name = getValue(val) + if (func_name ~ /^__lambda_/) { + # Store the function code under the new name + FUNCTIONS[name] = FUNCTIONS[func_name] + # Store the new name in the environment + env_name[env_size] = name + env_val[env_size] = makeValue(T_SYMBOL, name) + env_size++ + return + } + } + + # Add to environment + env_name[env_size] = name + env_val[env_size] = peek() + env_size++ + + debug("Environment after store:") + dump_env() +} + +# Remove top binding from environment +function vm_pop_env() { + if (env_size <= 0) error("Environment underflow") + debug("Popping environment at size: " env_size) + + # Don't pop globals + if (env_name[env_size-1] ~ /^__global_/) { + debug("Keeping global definition: " env_name[env_size-1]) + return + } + + debug("Removing: " env_name[env_size-1] " = " env_val[env_size-1]) + env_size-- +} + +# Variable lookup implementation +function vm_lookup(name, i, global_name, val) { + debug("Looking up " name " in environment of size: " env_size) + dump_env() + + # Check if it's a function (built-in or user-defined) + if (name in FUNCTIONS) { + debug("Found function: " name) + push(makeValue(T_SYMBOL, name)) + return + } + + # Try global name first, then local + global_name = "__global_" name + for (i = env_size - 1; i >= 0; i--) { + if (env_name[i] == global_name || env_name[i] == name) { + debug("Found " name " = " env_val[i] " at position " i) + push(env_val[i]) + return + } + } + error("Undefined variable: " name) +} + +# Function definition implementation +function vm_define_function(name, start_pc) { + debug("Defining function: " name " at " start_pc) + + # Build function code + code = "" + i = start_pc + while (i < length(program) && program[i] != "RETURN") { + if (code != "") code = code "\n" + code = code program[i] + i++ + } + code = code "\nRETURN" + + # Store function + debug("Storing function: " name " = " code) + FUNCTIONS[name] = code + + pc = i + 1 +} + +# Function call implementation +function vm_call_function(func_name, code_lines, j, saved_pc, saved_env_size, arg, param_name) { + debug("Calling function: " func_name) + + # If name is a symbol, get its value + if (isSymbol(func_name)) { + func_name = getValue(func_name) + } + + # Handle anonymous functions + if (func_name ~ /^__lambda_/) { + if (!(func_name in FUNCTIONS)) { + error("Undefined lambda function: " func_name) + } + } else if (!(func_name in FUNCTIONS)) { + error("Undefined function: " func_name) + } + + saved_pc = pc + saved_env_size = env_size + + # Split function code into lines + split(FUNCTIONS[func_name], code_lines, "\n") + + # Add function code to program at current position + for (j in code_lines) { + program[pc + j - 1] = code_lines[j] + } + + # Check if this is a parameterized function + if (code_lines[1] ~ /^STORE /) { + # This is a parameterized function (lambda) + # Get parameter name from STORE instruction + param_name = substr(code_lines[1], 7) + debug("Found parameter name: " param_name) + + # Get argument from stack + arg = pop() + debug("Function argument: " arg) + + # Create new environment frame + debug("Creating new environment frame at size: " env_size) + env_name[env_size] = param_name + env_val[env_size] = arg + env_size++ + } else { + # This is a built-in function or non-parameterized function + debug("Calling non-parameterized function: " func_name) + } + + # Save return info and jump to function + call_stack[++call_stack_ptr] = saved_pc + env_stack[call_stack_ptr] = saved_env_size + + debug("Function found, jumping to PC: " pc " with env_size: " saved_env_size) + dump_env() +} + +# Function return implementation +function vm_return() { + if (call_stack_ptr > 0) { + # Save return value + ret_val = pop() + + # Restore environment + while (env_size > env_stack[call_stack_ptr]) { + debug("Popping environment at size: " env_size) + vm_pop_env() + } + + # Restore program counter + pc = call_stack[call_stack_ptr--] + + # Push return value + push(ret_val) + + debug("Returned with value: " ret_val " and env_size: " env_size) + } +} + +# Debug helper to dump environment contents +function dump_env( i) { + debug("Environment dump:") + for (i = 0; i < env_size; i++) { + debug(sprintf(" %d: %s = %s", i, env_name[i], env_val[i])) + } +} + +# Helper for checking variable existence without error +function lookup_no_error(name, i) { + for (i = env_size - 1; i >= 0; i--) { + if (env_name[i] == name) { + return 1 + } + } + return 0 +} + +# State persistence implementation +function save_state() { + debug("Saving state to: " STATE_FILE) + for (i = 0; i < func_def_size; i++) { + debug("Saving function: " func_def_names[i]) + print "FUNC " func_def_names[i] " " func_def_code[i] > STATE_FILE + } + close(STATE_FILE) + + # Save environment state + debug("Saving environment state to: " ENV_STATE_FILE) + for (i = 0; i < env_size; i++) { + if (env_name[i] ~ /^__global_/) { # Only save globals + debug("Saving env var: " env_name[i] " = " env_val[i]) + print "ENV " env_name[i] " " env_val[i] > ENV_STATE_FILE + } + } + close(ENV_STATE_FILE) +} + +# Built-in function implementations +function equals() { + if (stack_ptr < 2) error("= requires two operands") + val2 = pop() + val1 = pop() + if (!isNumber(val1) || !isNumber(val2)) error("= requires numeric operands") + result = (getValue(val1) == getValue(val2)) ? 1 : 0 + push(makeValue(T_BOOLEAN, result)) +} + +function less_than() { + if (stack_ptr < 2) error("< requires two operands") + val2 = pop() + val1 = pop() + if (!isNumber(val1) || !isNumber(val2)) error("< requires numeric operands") + result = (getValue(val1) < getValue(val2)) ? 1 : 0 + push(makeValue(T_BOOLEAN, result)) +} + +function greater_than() { + if (stack_ptr < 2) error("> requires two operands") + val2 = pop() + val1 = pop() + if (!isNumber(val1) || !isNumber(val2)) error("> requires numeric operands") + result = (getValue(val1) > getValue(val2)) ? 1 : 0 + push(makeValue(T_BOOLEAN, result)) +} + +function add() { + if (stack_ptr < 2) error("+ requires two operands") + val2 = pop() + val1 = pop() + if (!isNumber(val1) || !isNumber(val2)) error("+ requires numeric operands") + result = getValue(val1) + getValue(val2) + push(makeValue(T_NUMBER, result)) +} + +function subtract() { + if (stack_ptr < 2) error("- requires two operands") + val2 = pop() + val1 = pop() + if (!isNumber(val1) || !isNumber(val2)) error("- requires numeric operands") + result = getValue(val1) - getValue(val2) + push(makeValue(T_NUMBER, result)) +} + +function multiply() { + if (stack_ptr < 2) error("* requires two operands") + val2 = pop() + val1 = pop() + if (!isNumber(val1) || !isNumber(val2)) error("* requires numeric operands") + result = getValue(val1) * getValue(val2) + push(makeValue(T_NUMBER, result)) +} + +function divide() { + if (stack_ptr < 2) error("/ requires two operands") + val2 = pop() + val1 = pop() + if (!isNumber(val1) || !isNumber(val2)) error("/ requires numeric operands") + if (getValue(val2) == 0) error("Division by zero") + result = getValue(val1) / getValue(val2) + push(makeValue(T_NUMBER, result)) +} + +function add_one() { + if (stack_ptr < 1) error("add1 requires one operand") + val = pop() + if (!isNumber(val)) error("add1 requires numeric operand") + result = getValue(val) + 1 + push(makeValue(T_NUMBER, result)) +} + +# Get value from top of stack +function vm_get_value() { + val = peek() + if (isSymbol(val)) { + name = getValue(val) + # If it's a function name, just push the name directly + if (name in FUNCTIONS) { + push(name) + } else { + push(makeValue(T_SYMBOL, name)) + } + } +} \ No newline at end of file diff --git a/awk/scheme/scheme/diagram.md b/awk/scheme/scheme/diagram.md new file mode 100644 index 0000000..4a719b4 --- /dev/null +++ b/awk/scheme/scheme/diagram.md @@ -0,0 +1,49 @@ +# Awk-Scheme Architecture +## Component Interaction Diagram + +``` ++----------------+ Scheme Code +----------------+ Assembly +----------------+ +| | -----------------> | | -------------> | | +| REPL | "(+ 1 2)" | Compiler | "PUSH_CONST | VM | +| (bin/repl) | | compiler.awk | N:1 | vm.awk | +| | | | PUSH_CONST | | +| - Multi-line | | - Lexer | N:2 | - Stack-based | +| - Debug mode | | - Parser | ADD | - Type system | +| - File input | | - Code gen | HALT" | - Environment | +| | <----------------- | | <------------- | | +| | Output: "N:3" | | Result | | ++----------------+ +----------------+ +----------------+ + ^ | + | v + | +----------------+ + | | Persistence | + | | /tmp files: | + +--------------------------------------------------------------+ - vm.state | + | - vm.env | + +----------------+ + +Debug Flow (when DEBUG=1): ++----------------+ Debug Info +----------------+ Debug Info +----------------+ +| REPL | -----------------> | Compiler | -------------> | VM | +| | [Input received] | | [Tokens found] | | +| [Debug output] | | [Parsing tree] | [Stack ops] | [Stack state] | +| stderr | <----------------- | [Gen code] | <------------- | [Environment] | ++----------------+ +----------------+ +----------------+ + +Execution Flow Example: +┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ +│ Input: │ │ Lexer/ │ │ Code Gen │ │ VM │ +│ (+ 1 2) │ ------> │ Parser: │ ------> │ PUSH_CONST │ ------> │ Stack: │ +│ │ │ (+ │ │ N:1 │ │ [N:1] │ +│ │ │ 1 │ │ PUSH_CONST │ │ [N:1,N:2] │ +│ │ │ 2) │ │ N:2 │ │ [N:3] │ +│ │ │ │ │ ADD │ │ │ +└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ + +State Management: +┌─────────────┐ ┌─────────────┐ ┌─────────────┐ +│ Global │ │ Environment │ │ Function │ +│ Variables │ ------> │ Stack │ ------> │ Calls │ +│ (persist) │ │ (frames) │ │ (stack) │ +└─────────────┘ └─────────────┘ └─────────────┘ +``` \ No newline at end of file diff --git a/awk/scheme/scheme/examples/cons.test.scm b/awk/scheme/scheme/examples/cons.test.scm new file mode 100644 index 0000000..d1e3847 --- /dev/null +++ b/awk/scheme/scheme/examples/cons.test.scm @@ -0,0 +1,3 @@ +(cons (+ 1 2) + (cons (* 3 4) + nil)) diff --git a/awk/scheme/scheme/examples/define.test.scm b/awk/scheme/scheme/examples/define.test.scm new file mode 100644 index 0000000..ec66b04 --- /dev/null +++ b/awk/scheme/scheme/examples/define.test.scm @@ -0,0 +1,2 @@ +(define add2 (x) (+ x 2)) +(add2 40) \ No newline at end of file diff --git a/awk/scheme/scheme/examples/lambda.test.scm b/awk/scheme/scheme/examples/lambda.test.scm new file mode 100644 index 0000000..1f2bb09 --- /dev/null +++ b/awk/scheme/scheme/examples/lambda.test.scm @@ -0,0 +1,12 @@ +; Test lambda function support +((lambda (x) (+ x 1)) 41) ; Should return 42 + +; Test lambda with multiple parameters +((lambda (x y) (+ x y)) 20 22) ; Should return 42 + +; Test lambda in let expression +(let ((add1 (lambda (x) (+ x 1)))) + (add1 41)) ; Should return 42 + +; Test nested lambda +((lambda (x) ((lambda (y) (+ x y)) 1)) 41) ; Should return 42 \ No newline at end of file diff --git a/awk/scheme/scheme/examples/let-and-define.test.scm b/awk/scheme/scheme/examples/let-and-define.test.scm new file mode 100644 index 0000000..fade30b --- /dev/null +++ b/awk/scheme/scheme/examples/let-and-define.test.scm @@ -0,0 +1,9 @@ +; Let expression example +(let ((x 5) (y 3)) + (+ x y)) + +; Function definition example +(define add2 (x) + (+ x 2)) + +(add2 40) ; Returns 42 \ No newline at end of file diff --git a/awk/scheme/scheme/examples/let.test.scm b/awk/scheme/scheme/examples/let.test.scm new file mode 100644 index 0000000..2cdc3b8 --- /dev/null +++ b/awk/scheme/scheme/examples/let.test.scm @@ -0,0 +1,2 @@ +(let ((x 5)) + (+ x 2)) diff --git a/awk/scheme/scheme/scheme b/awk/scheme/scheme/scheme new file mode 100755 index 0000000..cec35d1 --- /dev/null +++ b/awk/scheme/scheme/scheme @@ -0,0 +1,3 @@ +#!/bin/bash +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +exec "$DIR/bin/repl" "$@" diff --git a/awk/scheme/scheme/scratch/complex_test.scm.asm b/awk/scheme/scheme/scratch/complex_test.scm.asm new file mode 100644 index 0000000..67870c3 --- /dev/null +++ b/awk/scheme/scheme/scratch/complex_test.scm.asm @@ -0,0 +1,44 @@ +# Test proper list construction (3 2 1) +# Building the list in proper order: car points to value, cdr points to next pair + +# Start with empty list +PUSH_CONST NIL: # [nil] +PRINT # Print nil + +# Build (1 . nil) +PUSH_CONST NIL: # [nil] +PUSH_CONST N:1 # [nil 1] +SWAP # [1 nil] +CONS # [(1 . nil)] +DUP +PRINT # Print (1 . nil) + +# Build (2 . (1 . nil)) +PUSH_CONST N:2 # [(1.nil) 2] +SWAP # [2 (1.nil)] +CONS # [(2 . (1.nil))] +DUP +PRINT # Print (2 . (1.nil)) + +# Build (3 . (2 . (1 . nil))) +PUSH_CONST N:3 # [(2.(1.nil)) 3] +SWAP # [3 (2.(1.nil))] +CONS # [(3 . (2.(1.nil)))] +DUP +PRINT # Print full structure + +# Test CAR/CDR operations +DUP # Keep a copy of the list for later +DUP # Another copy for CAR +CAR # Get first element (3) +PRINT # Should print 3 + +SWAP # Bring back our spare list copy +CDR # Get rest of list ((2 . (1 . nil))) +DUP +PRINT # Print rest of list + +CAR # Get first of rest (2) +PRINT # Should print 2 + +HALT \ No newline at end of file diff --git a/awk/scheme/scheme/scratch/run.sh b/awk/scheme/scheme/scratch/run.sh new file mode 100755 index 0000000..0afdb41 --- /dev/null +++ b/awk/scheme/scheme/scratch/run.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# Compile Scheme to VM instructions +awk -f compiler.awk test.scm > test.asm +# Run VM instructions +awk -f vm.awk test.asm \ No newline at end of file diff --git a/awk/scheme/scheme/scratch/test.asm b/awk/scheme/scheme/scratch/test.asm new file mode 100644 index 0000000..8e7d8df --- /dev/null +++ b/awk/scheme/scheme/scratch/test.asm @@ -0,0 +1,16 @@ +PUSH_CONST N:1 +PUSH_CONST N:2 +ADD +PUSH_CONST N:3 +PUSH_CONST N:4 +MUL +PUSH_CONST N:10 +PUSH_CONST N:2 +PUSH_CONST N:3 +ADD +SUB +PUSH_CONST NIL: +CONS +CONS +CONS +HALT diff --git a/awk/scheme/scheme/scratch/test.scm b/awk/scheme/scheme/scratch/test.scm new file mode 100644 index 0000000..a01b174 --- /dev/null +++ b/awk/scheme/scheme/scratch/test.scm @@ -0,0 +1,8 @@ +;; Build a list of calculated values +(cons (+ 1 2) ; First element: 1 + 2 = 3 + (cons (* 3 4) ; Second element: 3 * 4 = 12 + (cons (- 10 + (+ 2 3)) ; Third element: 10 - (2 + 3) = 5 + nil))) ; End of list + +;; This should create a list: (3 12 5) \ No newline at end of file diff --git a/awk/scheme/scheme/scratch/test.scm.asm b/awk/scheme/scheme/scratch/test.scm.asm new file mode 100644 index 0000000..526e2b1 --- /dev/null +++ b/awk/scheme/scheme/scratch/test.scm.asm @@ -0,0 +1,7 @@ +PUSH_CONST N:5 +PUSH_CONST N:3 +ADD +PUSH_CONST N:2 +MUL +PRINT # should output N:16 +HALT \ No newline at end of file diff --git a/awk/vm/README.md b/awk/vm/README.md new file mode 100644 index 0000000..83a35fd --- /dev/null +++ b/awk/vm/README.md @@ -0,0 +1,91 @@ +# Stack-Based Virtual Machine in AWK + +A simple stack-based virtual machine implementation in AWK, inspired by Forth. The VM provides basic stack manipulation, arithmetic operations, register access, and memory operations. + +## Architecture + +The VM consists of: +- A data stack (100 elements) +- Main memory (1000 cells) +- Three registers: + - A: General purpose register + - B: Often used as memory pointer + - P: Program pointer, used for sequential memory operations + +## Instruction Set + +### Stack Operations +- `DROP` - Remove top item from stack +- `DUP` - Duplicate top stack item +- `OVER` - Copy second item to top of stack +- `SWAP` - Exchange top two stack items +- Numbers are automatically pushed onto the stack + +### Arithmetic Operations +- `+` - Add top two stack items (a b -- a+b) +- `*` - Multiply top two stack items (a b -- a*b) +- `AND` - Bitwise AND of top two items +- `XOR` - Bitwise XOR of top two items +- `NOT` - Bitwise NOT of top item +- `2*` - Multiply top item by 2 (shift left) +- `2/` - Divide top item by 2 (shift right) + +### Register Operations +- `A` - Push value of A register onto stack +- `A!` - Store top stack value into A register +- `B!` - Store top stack value into B register + +### Memory Operations +- `@` - Fetch from memory address on stack (addr -- value) +- `!` - Store to memory address on stack (value addr --) +- `@+` - Fetch from memory at P, then increment P +- `!+` - Store to memory at P, then increment P +- `@B` - Fetch from memory address in B register +- `!B` - Store to memory address in B register +- `@P` - Fetch from memory address in P register +- `!P` - Store to memory address in P register + +### Debug & Control +- `.` - NO-OP (does nothing) +- `BYE` - Exit program +- `SHOW` - Display current machine state (stack, registers, memory) + +## Memory Model + +- Memory is zero-based +- Each cell can hold a numeric value +- Memory is accessed either directly (using @ and !) or through registers +- P register is typically used for sequential memory operations +- B register is typically used as a memory pointer + +## Example Programs + +### Store and retrieve a value +``` +5 DUP 0 ! # Store 5 at address 0 +3 DUP 1 ! # Store 3 at address 1 +0 @ 1 @ + # Load both values and add them +2 ! # Store result at address 2 +``` + +### Using registers +``` +42 A! # Store 42 in A register +A # Push A's value onto stack +100 B! # Set B to address 100 +42 !B # Store 42 at address 100 +@B # Read from address 100 +``` + +## Usage + +```bash +# Run a program directly +echo "5 3 + SHOW" | awk -f vm.awk + +# Compile and run a program +./compiler.py program.coffee | awk -f vm.awk + +# Run test suite +./vm_tests.sh +``` \ No newline at end of file diff --git a/awk/vm/compiler.py b/awk/vm/compiler.py new file mode 100755 index 0000000..a406779 --- /dev/null +++ b/awk/vm/compiler.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 + +""" +A simple compiler that translates CoffeeScript-like syntax to VM instructions. +Example input: + + # Simple arithmetic + x = 5 + y = 3 + z = x + y + + # Using memory + array = [] + array[0] = 42 + array[1] = array[0] * 2 + +Will compile to VM instructions like: + + 5 A! # store 5 in A register + 3 B! # store 3 in B register + A @B + # add them +""" + +import sys +import re + +class Compiler: + def __init__(self): + self.variables = {} # Maps variable names to memory locations + self.next_memory = 0 # Next available memory location + + def allocate_variable(self, name): + """Allocate memory location for a variable""" + if name not in self.variables: + self.variables[name] = self.next_memory + self.next_memory += 1 + return self.variables[name] + + def compile_assignment(self, line): + """Compile assignment statements like 'x = 5' or 'x = y + z'""" + # Remove any comments from the line + line = line.split('#')[0].strip() + + match = re.match(r'(\w+)\s*=\s*(.+)', line) + if not match: + return None + + var_name = match.group(1) + expression = match.group(2) + + print(f"# Compiling assignment: {var_name} = {expression}", file=sys.stderr) + + # First get the memory location + mem_loc = self.allocate_variable(var_name) + + # Then compile the expression + expr_code = self.compile_expression(expression) + if not expr_code: + print(f"# Error: Failed to compile expression: {expression}", file=sys.stderr) + return None + + # Generate code that: + # 1. Evaluates the expression + # 2. Duplicates the result (for storing and leaving on stack) + # 3. Stores at memory location + vm_code = [] + vm_code.extend(expr_code) # Evaluate expression + vm_code.append("DUP") # Make a copy + vm_code.append(str(mem_loc)) # Push memory location + vm_code.append("@") # Read current value (for debugging) + vm_code.append("DROP") # Drop the old value + vm_code.append("!") # Store new value + + return vm_code + + def compile_expression(self, expr): + """Compile expressions like '5', 'x + y', etc.""" + vm_code = [] + + # Remove any comments from the expression + expr = expr.split('#')[0].strip() + + # Handle simple number + if expr.isdigit(): + vm_code.append(expr) + return vm_code + + # Handle variable reference + if expr in self.variables: + vm_code.append(str(self.variables[expr])) + vm_code.append("@") + return vm_code + + # Handle binary operations + ops = { + '+': '+', + '*': '*', + '-': 'NOT +', + } + + # Try each operator + for op in ops: + if op in expr: + parts = expr.split(op, 1) + if len(parts) == 2: + left = parts[0].strip() + right = parts[1].strip() + + print(f"# Debug: left={left}, right={right}", file=sys.stderr) + + # Generate code for left operand + left_code = self.compile_expression(left) + if not left_code: + continue + vm_code.extend(left_code) + + # Generate code for right operand + right_code = self.compile_expression(right) + if not right_code: + continue + vm_code.extend(right_code) + + # Add the operation + vm_code.append(ops[op]) + return vm_code + + return vm_code + + def compile(self, source): + """Compile source code to VM instructions""" + output = [] + debug_output = [] + + for line in source.split('\n'): + line = line.strip() + if not line or line.startswith('#'): + continue + + if line == "SHOW": + output.append("SHOW") + continue + + if '=' in line: + vm_code = self.compile_assignment(line) + if vm_code: + output.extend(vm_code) + debug_output.append(f"{' '.join(vm_code)} # {line}") + if not line.startswith('result ='): # If not final result + output.append("DROP") # Drop the duplicate we left on stack + continue + + print("# Generated VM code:", file=sys.stderr) + for line in debug_output: + print(f"# {line}", file=sys.stderr) + + # Add final SHOW to see the result + output.append("SHOW") + return ' '.join(output) + +def main(): + if len(sys.argv) > 1: + with open(sys.argv[1]) as f: + source = f.read() + else: + source = sys.stdin.read() + + compiler = Compiler() + vm_code = compiler.compile(source) + print(vm_code) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/awk/vm/debug.coffee b/awk/vm/debug.coffee new file mode 100644 index 0000000..0663cec --- /dev/null +++ b/awk/vm/debug.coffee @@ -0,0 +1,6 @@ +x = 5 +SHOW +y = 3 +SHOW +z = x + y +SHOW \ No newline at end of file diff --git a/awk/vm/simple.coffee b/awk/vm/simple.coffee new file mode 100644 index 0000000..0a596a7 --- /dev/null +++ b/awk/vm/simple.coffee @@ -0,0 +1,4 @@ +x = 5 +y = 3 +z = x + y +result = z * 2 \ No newline at end of file diff --git a/awk/vm/simple_test.coffee b/awk/vm/simple_test.coffee new file mode 100644 index 0000000..8fec5ba --- /dev/null +++ b/awk/vm/simple_test.coffee @@ -0,0 +1,8 @@ +x = 5 +SHOW +y = 3 +SHOW +z = x + y # Should be 8 +SHOW +result = z * 2 # Should be 16 +SHOW \ No newline at end of file diff --git a/awk/vm/stack_test.coffee b/awk/vm/stack_test.coffee new file mode 100644 index 0000000..ab29e63 --- /dev/null +++ b/awk/vm/stack_test.coffee @@ -0,0 +1,15 @@ +# First store 5 in memory location 0 +x = 5 +SHOW + +# Then store 3 in memory location 1 +y = 3 +SHOW + +# Add them: load 5, load 3, add them +z = x + y +SHOW + +# Double z: load 8, multiply by 2 +result = z * 2 +SHOW \ No newline at end of file diff --git a/awk/vm/test.coffee b/awk/vm/test.coffee new file mode 100644 index 0000000..aecda14 --- /dev/null +++ b/awk/vm/test.coffee @@ -0,0 +1,7 @@ +# Calculate sum +x = 5 +y = 3 +z = x + y + +# Double it +result = z * 2 diff --git a/awk/vm/test_steps.coffee b/awk/vm/test_steps.coffee new file mode 100644 index 0000000..f1d0415 --- /dev/null +++ b/awk/vm/test_steps.coffee @@ -0,0 +1,15 @@ +# Step 1: Initialize x +x = 5 +SHOW + +# Step 2: Initialize y +y = 3 +SHOW + +# Step 3: Add x and y +z = x + y +SHOW + +# Step 4: Double z +result = z * 2 +SHOW \ No newline at end of file diff --git a/awk/vm/vm.awk b/awk/vm/vm.awk new file mode 100755 index 0000000..67da3e7 --- /dev/null +++ b/awk/vm/vm.awk @@ -0,0 +1,254 @@ +#!/usr/bin/awk -f + + +# Stack: DROP, DUP, OVER, PUSH, POP +# Math: + AND XOR NOT 2* 2/ multiply-step +# Call: JUMP CALL RETURN IF -IF +# Loop: NEXT UNEXT +# Register: A A! B! +# Memory: @ ! @+ !+ @B !B @P !P +# NO-OP: . + + +BEGIN { + # Initialize VM state + stack_pointer = 0 # Points to next free position + pc = 0 # Program counter + MAX_STACK = 100 # Maximum stack size + MAX_MEM = 1000 # Memory size + + # Initialize registers + A = 0 # A register + B = 0 # B register + P = 0 # P register (auxiliary pointer) + + # Stack operations + split("", stack) # Initialize stack array + split("", memory) # Initialize memory array +} + +# Stack operations +function push(value) { + if (stack_pointer >= MAX_STACK) { + print "Stack overflow!" > "/dev/stderr" + exit 1 + } + printf "# DEBUG: Pushing %d onto stack\n", value > "/dev/stderr" + stack[stack_pointer++] = value +} + +function pop() { + if (stack_pointer <= 0) { + print "Stack underflow!" > "/dev/stderr" + exit 1 + } + value = stack[--stack_pointer] + printf "# DEBUG: Popping %d from stack\n", value > "/dev/stderr" + return value +} + +# Basic stack manipulation +function op_drop() { + pop() +} + +function op_dup() { + if (stack_pointer <= 0) { + print "Stack underflow on DUP!" > "/dev/stderr" + exit 1 + } + push(stack[stack_pointer - 1]) +} + +function op_over() { + if (stack_pointer <= 1) { + print "Stack underflow on OVER!" > "/dev/stderr" + exit 1 + } + push(stack[stack_pointer - 2]) +} + +# Basic arithmetic operations +function op_add() { + b = pop() + a = pop() + result = a + b + printf "# DEBUG: Adding %d + %d = %d\n", a, b, result > "/dev/stderr" + push(result) +} + +function op_and() { + b = pop() + a = pop() + # For now, we'll just multiply as a placeholder + # In a real implementation, we'd need to implement proper bitwise AND + push(a * b) +} + +function op_xor() { + b = pop() + a = pop() + # For now, we'll just add as a placeholder + # In a real implementation, we'd need to implement proper bitwise XOR + push(a + b) +} + +function op_not() { + a = pop() + # For now, we'll just negate as a placeholder + # In a real implementation, we'd need to implement proper bitwise NOT + push(-a - 1) +} + +function op_2times() { + a = pop() + push(a * 2) +} + +function op_2div() { + a = pop() + push(int(a / 2)) +} + +function op_multiply() { + b = pop() + a = pop() + result = a * b + printf "# DEBUG: Multiplying %d * %d = %d\n", a, b, result > "/dev/stderr" + push(result) +} + +# Register operations +function op_a() { + push(A) +} + +function op_astore() { + A = pop() +} + +function op_bstore() { + B = pop() +} + +# Memory operations +function op_fetch() { + addr = pop() + value = memory[addr] + printf "# DEBUG: Fetching %d from memory[%d]\n", value, addr > "/dev/stderr" + push(value) +} + +function op_store() { + addr = pop() # First pop the address + value = pop() # Then pop the value + printf "# DEBUG: Storing %d at memory[%d]\n", value, addr > "/dev/stderr" + memory[addr] = value +} + +function op_fetchplus() { + push(memory[P++]) +} + +function op_storeplus() { + memory[P++] = pop() +} + +function op_fetchb() { + push(memory[B]) +} + +function op_storeb() { + memory[B] = pop() +} + +function op_fetchp() { + push(memory[P]) +} + +function op_storep() { + memory[P] = pop() +} + +function print_stack() { + printf "Stack: " + for (i = 0; i < stack_pointer; i++) { + printf "%d ", stack[i] + } + printf "\n" +} + +function print_state() { + print_stack() + printf "Registers: A=%d B=%d P=%d\n", A, B, P + printf "Memory[P]=%d Memory[B]=%d\n", memory[P], memory[B] + printf "Memory: " + for (i = 0; i < 4; i++) { # Show first 4 memory locations + printf "[%d]=%d ", i, memory[i] + } + printf "\n-------------------\n" +} + +function op_swap() { + if (stack_pointer < 2) { + print "Stack underflow on SWAP!" > "/dev/stderr" + exit 1 + } + # Swap the top two elements on the stack + temp = stack[stack_pointer - 1] + stack[stack_pointer - 1] = stack[stack_pointer - 2] + stack[stack_pointer - 2] = temp + printf "# DEBUG: Swapping top two stack elements\n" > "/dev/stderr" +} + +function execute_instruction(inst) { + if (inst ~ /^[0-9]+$/) { + # Numbers are pushed onto the stack + push(int(inst)) + return + } + + if (inst == "BYE") { exit 0 } # not really in the minimal spec as set out by Chuck Moore, but useful for a graceful exit. + if (inst == "DROP") { op_drop(); return } + if (inst == "DUP") { op_dup(); return } + if (inst == "OVER") { op_over(); return } # copy second item to top of stack + if (inst == "SWAP") { op_swap(); return } # swap top two stack items + if (inst == "+") { op_add(); return } + if (inst == "AND") { op_and(); return } + if (inst == "XOR") { op_xor(); return } + if (inst == "NOT") { op_not(); return } + if (inst == "2*") { op_2times(); return } # multiply-step + if (inst == "2/") { op_2div(); return } # divide-step + if (inst == "*") { op_multiply(); return } + if (inst == "A") { op_a(); return } # push A register + if (inst == "A!") { op_astore(); return } # store A register + if (inst == "B!") { op_bstore(); return } # store B register + if (inst == "@") { op_fetch(); return } # fetch from memory + if (inst == "!") { op_store(); return } # store to memory + if (inst == "@+") { op_fetchplus(); return } # fetch from memory at P+ + if (inst == "!+") { op_storeplus(); return } # store to memory at P+ + if (inst == "@B") { op_fetchb(); return } # fetch from memory at B + if (inst == "!B") { op_storeb(); return } # store to memory at B + if (inst == "@P") { op_fetchp(); return } # fetch from memory at P + if (inst == "!P") { op_storep(); return } # store to memory at P + if (inst == ".") { return } # NO-OP + if (inst == "SHOW") { print_state(); return } # show state info + + print "Unknown instruction: " inst > "/dev/stderr" + exit 1 +} + +# Main execution loop +{ + # Remove comments (everything after #) + sub(/#.*$/, "") + + # Skip empty lines after comment removal + if (NF == 0) next + + # Split the input line into words + n = split($0, words) + for (i = 1; i <= n; i++) { + execute_instruction(words[i]) + } +} diff --git a/awk/vm/vm_tests.sh b/awk/vm/vm_tests.sh new file mode 100755 index 0000000..6244c51 --- /dev/null +++ b/awk/vm/vm_tests.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +echo "Running VM tests..." +echo + +echo "Test 1: Basic register A operations" +echo "42 A! A A # Store 42 in A, then read A twice" | awk -f vm.awk +echo + +echo "Test 2: Register A and B with memory operations" +echo "100 A! 200 B! 42 @B # Store 100 in A, 200 in B, read from B's address" | awk -f vm.awk +echo + +echo "Test 3: Sequential memory operations using P register" +echo "0 !P 1 !+ 2 !+ 3 !+ 4 !+ 5 !+ # Store 1-5 sequentially using P" | awk -f vm.awk +echo "0 !P @+ @+ @+ @+ @+ # Read back values using P" | awk -f vm.awk +echo + +echo "Test 4: Complex register manipulation" +echo "42 A! A DUP B! @B # Store 42 in A, copy to B, read via B" | awk -f vm.awk +echo + +echo "Test 5: Register arithmetic" +echo "5 A! 3 B! A @B + # Store 5 in A, 3 in B, add A + mem[B]" | awk -f vm.awk +echo + +echo "Test 6: Memory pointer operations" +echo "42 0 ! 1 !P @P # Store 42 at addr 0, point P to 1, read P" | awk -f vm.awk +echo + +echo "Test 7: Register and memory interaction" +echo "10 A! 20 B! A @B ! # Store A's value at B's address" | awk -f vm.awk +echo "@B # Read from memory at B's address" | awk -f vm.awk +echo + +echo "Test 8: Demonstrating B! vs !B" +echo "100 B! # Set B register to 100" | awk -f vm.awk +echo "42 !B # Store 42 at memory location 100" | awk -f vm.awk +echo "@B # Read from memory location 100" | awk -f vm.awk +echo + +echo "Tests completed." \ No newline at end of file |