Skip to content

Latest commit

 

History

27 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Nimic - TypeScript

PI5003 Programmiersprachen: Konzepte und Realisation (PKR) | Hochschule: THM (Technische Hochschule Mittelhessen) | Jahr: 2026

Dieses Projekt ist eine TypeScript Version vom Nimic, welches eine free-form macro language beschreibt und Befehle in die Tiefe parst.

Aufgabenstellung und Herausforderungen

  • Aufgabenstellung: das Rewriting System Nimic in TypeScript umzusetzen und Namespaces einführen.
  • Herausforderungen: Implementierung anhand des BlogPosts und Haskell-Codes

Lösung/Ergebnis

  • Eine Webseite/Web-App, mit dem Nutzer*innen Macro Befehle sowie Namespaces erstellen können und daraus Regeln speichern, um Befehle schrittweise zu reduzieren.

Architektur & Design

Die Anwendung folgt einer klaren Trennung von Parsing, Logik und UI:

  • Parsing (parser/): Konvertiert den Code in strukturierte abstrakte Syntaxbäume (ASTs) bzw. symbolische Terme.
  • Logik (engine/):
    • macro-logics.ts: Verwaltet Muster und deren Ersetzungsregeln.
    • reduction.ts: Implementiert die Reduktionsschritte (Step-Funktion) basierend auf Pattern-Matching. Unterstützt Definitionen von Namespaces, um Namenskonflikte zu vermeiden.
  • Typen (types/): Stark typisierte Datenstrukturen für eine saubere Datenhaltung zwischen den Parser und Engine.
  • UI (components/ und hooks/): Binden die Engine an die React-Oberfläche an, um den schrittweisen Evaluierungsprozess visuell darzustellen.

Testing:

Integration-Tests im Ordner tests, um das Zusammenspiel zwischen den Funktionen zu testen

  • macros.test.ts prüft, wie Macros durch verschiedene Funktionen geparst werden
  • namespaces.test.ts prüft, wie Namespaces mit Macros evaluiert werden
  • mocks.ts enthält die benötigten Mockdaten für beide Testdateien Tests ausführen:
cd app
npm run test

Probleme/Schwierigkeiten

  • Mehrere Befehle gleichzeitig zu speichern -> Lösung durch Hilfsmethoden
  • Code wie BlogPost zu formatieren -> blieb offen

Neue fachliche Erfahrungen:

  • Vertiefung im Bereich Compiler-Design sowie Traversierung von Syntaxbäumen
  • Framework Shadcn UI kennengelernt, welches Zeit bei Komponentenerstellung deutlich reduziert
  • Framework Parsimmon für einfaches Parsing

Verwendete Frameworks

Schritt 1: Installation

cd app
npm install

Schritt 2: .ts kompilieren

cd app
npm run build

Schritt 3: Projekt starten

cd app
npm run preview

Anmerkung: Step 2 muss vor Step 3 laufen, immer wenn Quellcode verändert wird.

Step 4: Usage

  • Nacdem das Projekt erfolgreich gestartet wurde, kann das URL aus dem Terminal im Browser geöffnet werden.

  • Beispiele: Im Input-Feld folgende Befehle eingeben und dann mit dem Submit-Button speichern und dem Step-Button die Befehle reduzieren, bis zum Ende:

; Drop the top element (discard #x, keep #y and #x)
(macro (drop #x #y #z) -> #y #z)

; Swap the top three elements (#x, #y and #z)
(macro (swap #x #y #z) -> #z #y #x)

; Rotate elements (#x, #y, #z -> #y, #z, #x)
(macro (rot #x #y #z) -> #y #z #x)

Befehle auch mit den Submit- und Step-Buttons evaluieren:

drop 1 2 3

Hierbei ist das Ergebnis (2 3)

swap 1 2 3

Hierbei ist das Ergebnis (3 2 1)

rot 1 2 3

Hierbei ist das Ergebnis (2 3 1)

Namespaces einführen

Mit Namespaces können benutzerdefinierten Makros in isolierten Bereichen organisieren, anstatt die globale Umgebung zu überladen.

  • Beispiele:
namespace MathA
(macro (op #x) -> (add #x 1))
  • Ein weiteres Namespace einfügen:
namespace MathB
(macro (op #x) -> (mul #x 2))

Macros einfügen, um das Matching zu evaluieren:

; match #val of ( #pat -> #res ; #rest )
  ----------
  if (judge #val #pat) then #res else (match #val of #rest)

; match #val of ( #pat -> #res )
  ----------
  if (judge #val #pat) then #res else (failed match)

Macro-Defintion einfügen, um Namespaces zu evaluieren:

(macro
 (ns-run #ns . #op (#x)) -> 
 (match #ns of (
   MathA -> (match #op of (
     op -> (add #x 1) ; 
     #other -> (failed match)
   )) ; 
   MathB -> (match #op of (
     op -> (mul #x 2) ; 
     #other -> (failed match)
   )) ; 
   #other -> (failed match)
 )))

Namespaces evaluieren:

ns-run MathA . op (5)
ns-run MathB . op (2)

Bei den ns-run MathA . op (5) hört das Programm bei (add 5 1) und bei ns-run MathB . op (2) endet das Program bei (mull 2 1).

Alternativ können namespaces frei ohne Macro Regeln verwendet werden, jedoch müssen dann die Variablen innerhalb der Namespaces eindeutig sein:

namespace MathA
(macro (opA #x) -> (add #x 1))
namespace MathB
(macro (opB #x) -> (mul #x 2))

Evalution mit den Befehlen:

opA (5)
opB (2)

Doc-Generation and Preview

cd app
npm run docs
npm run docs:serve

Screenshots

Macro Befehle erstellen

macro_rules

Macros evaluieren: drop rot swap

Namespaces einführen und evaluieren: namespace_MathA namespace_MathB namespace_MathA_evaluation namespace_MathB_evaluation

Namespaces mit Macros evaluieren: namespace_MathA_with_macro namespace_MathB_with_macro

Note

1-2, da die meisten Anforderungen erfüllt sind.

Sources

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages