Hopp til hovedinnhold

Bakgrunn

Et av mine favorittprogrammer er IPython, et alternativt REPL for Python med snacks som autocomplete og historikk. Jeg bruker IPython mest som kalkulator i kommandolinja, men opplever stadig at det knekker når jeg bytter mellom ulike Python-versjoner på prosjekter.

I stedet for å finne en mer fornuftig måte å håndtere disse versjonene på, bestemte jeg meg heller for å lage min egen kalkulator – og slik ble Abacus født. Hvor vanskelig kunne det egentlig være?

Hvordan skal dette bli

Jeg ønsket å evaluere matematiske uttrykk, lagre resultater i variabler, og bruke variabler videre i nye uttrykk. Det gikk fort opp for meg at jeg i praksis var i ferd med å implementere et lite programmeringsspråk. Og hva er et programmeringsspråk uten funksjoner?

De siste årene har jeg latt meg begeistre av Rust. En av de mest elegante funksjonene i Rust er pattern matching, som er tydelig inspirert av Haskell. I Haskell kan man til og med bruke pattern matching direkte i funksjonssignaturer, noe som eliminerer behovet for mange tradisjonelle kontrollstrukturer. Dette ønsket jeg å ta med inn i Abacus.

Rust

let x = 1;

match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("anything"),
}

Haskell

factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n-1)

Kjernefunksjonalitet

Jeg endte opp med et minimalt, men målrettet sett med funksjonalitet:

  • Evaluering av uttrykk
  • Variabler
  • Funksjoner med pattern matching i signaturer

Fra et tidligere fag hadde jeg erfaring med å lage en Pascal-kompilator, så kompilatorteknikk var ikke helt fremmed. Først må man bryte kildekoden ned i tokens i en prosess kalt lexing. Deretter bygges et syntakstre via parsing, før treet til slutt evalueres.

Input leses tegn for tegn og gjøres om til tokens. Deretter bygger jeg syntakstreet med Pratt-parsing, som gir en fleksibel måte å håndtere operatorpresedens på. Til slutt evalueres treet rekursivt, og resultatet spyttes ut.

Det var viktig for meg at REPL-et skulle være fargerikt og ergonomisk å bruke. Jeg landet på rustyline for håndtering av input og kommandohistorikk, miette for ryddige og fargerike feilmeldinger, og clap til å parse kommandolinjeargumenter.

Abacus kommer neppe til å revolusjonere verden, men det har vært et gøy og lærerikt prosjekt. Koden ligger på GitHub under MIT-lisens, og du kan installere Abacus via cargo. Jeg kommer til å fortsette utviklingen, så gi gjerne en stjerne om du vil, eller meld inn issues hvis du finner bugs eller har feature-ønsker.

Ting jeg ønsker å legge til i fremtiden

  • Range-typer: 0..10, 0..=100
  • Wildcard-pattern _
  • Flere integer-literal-formater (binary, hex, octal)
  • Strings og chars
  • Et minimalt standardbibliotek

Eksempler

Hvordan installere Abacus

cargo install abacus

REPL-eksempel

[ABACUS - Calculator REPL]
Type expressions or 'quit' to exit

[0x00]: 2 + 2
[0x00]: 4

[0x01]: factorial(n) = n * factorial(n - 1)

[0x02]: factorial(0) = 1

[0x03]: factorial(5)
[0x03]: 120

[0x04]:

Liker du innlegget?

Del gjerne med kollegaer og venner