From 15725c024f9cbebaeeeb9599d7a9956afa707b34 Mon Sep 17 00:00:00 2001
From: Stephen D <webmaster@scd31.com>
Date: Sun, 9 Mar 2025 11:40:11 -0400
Subject: [PATCH] parser

---
 src/expr.rs    |   8 ++
 src/main.rs    |  15 +++-
 src/parser.rs  | 194 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/scanner.rs |  11 +--
 src/token.rs   |   5 +-
 5 files changed, 221 insertions(+), 12 deletions(-)
 create mode 100644 src/expr.rs
 create mode 100644 src/parser.rs

diff --git a/src/expr.rs b/src/expr.rs
new file mode 100644
index 0000000..a8a8a8d
--- /dev/null
+++ b/src/expr.rs
@@ -0,0 +1,8 @@
+#[derive(Debug)]
+pub enum Expr {
+    Lambda(Box<Expr>),
+    Abs(Box<Expr>),
+    App(Box<Expr>, Box<Expr>),
+    Var(usize),
+    DefinitionName(String),
+}
diff --git a/src/main.rs b/src/main.rs
index 900fb56..4de6c3e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,8 @@
+use parser::Parser;
 use scanner::Scanner;
 
+mod expr;
+mod parser;
 mod scanner;
 mod token;
 
@@ -14,7 +17,15 @@ $true = λtf.t
 ",
     );
 
-    for t in scanner.scan_tokens().unwrap() {
-        dbg!(t.token_type);
+    let tokens = scanner.scan_tokens().unwrap();
+
+    for t in &tokens {
+        println!("{} {:?}", t.line, t.token_type);
     }
+
+    let mut parser = Parser::new(tokens);
+    let (funcs, main) = parser.parse().unwrap();
+
+    dbg!(funcs);
+    dbg!(main);
 }
diff --git a/src/parser.rs b/src/parser.rs
new file mode 100644
index 0000000..8dd05f8
--- /dev/null
+++ b/src/parser.rs
@@ -0,0 +1,194 @@
+use std::collections::HashMap;
+
+use crate::{
+    expr::Expr,
+    token::{Token, TokenType},
+};
+
+pub struct Parser {
+    tokens: Vec<Token>,
+    current: usize,
+    env: Vec<char>,
+}
+
+impl Parser {
+    pub fn new(tokens: Vec<Token>) -> Self {
+        Self {
+            tokens,
+            current: 0,
+            env: vec![],
+        }
+    }
+
+    pub fn parse(&mut self) -> Option<(HashMap<String, Expr>, Expr)> {
+        let mut definitions = HashMap::new();
+        let mut main_expr = None;
+        while !self.is_at_end() {
+            let t = self.peek().token_type.clone();
+
+            match t {
+                TokenType::DefinitionName(name) => {
+                    self.advance(); // consume definition name
+
+                    // function expr
+
+                    if self.peek().token_type != TokenType::Equals {
+                        self.print_error("Expected '=' after definition name");
+
+                        return None;
+                    }
+
+                    self.advance(); // consume equals
+
+                    let definition = self.expr()?;
+
+                    if definitions.contains_key(&name) {
+                        self.print_error(&format!("Duplicate definition for ${}", name));
+                        return None;
+                    }
+
+                    definitions.insert(name, definition);
+                }
+
+                // main expr
+                _ => {
+                    let expr = self.expr()?;
+
+                    if main_expr.is_some() {
+                        self.print_error("Duplicate primary expression");
+                        return None;
+                    }
+
+                    main_expr = Some(expr);
+                }
+            }
+        }
+
+        match main_expr {
+            Some(expr) => Some((definitions, expr)),
+            None => {
+                self.print_error("Missing primary expression");
+
+                None
+            }
+        }
+    }
+
+    fn expr(&mut self) -> Option<Expr> {
+        match &self.peek().token_type {
+            TokenType::Lambda => self.abstraction(),
+
+            TokenType::DefinitionName(name) => {
+                let name = name.clone();
+                self.advance();
+
+                Some(Expr::DefinitionName(name))
+            }
+
+            TokenType::LeftParen => self.application(),
+
+            TokenType::Variable(c) => {
+                let c = *c;
+                self.advance();
+
+                Some(Expr::Var(self.find_variable(c)?))
+            }
+
+            _ => {
+                self.print_error(
+                    "Unexpected token. Expected one of 'λ', '/', '\\', '$', '(', or a variable",
+                );
+                None
+            }
+        }
+    }
+
+    fn abstraction(&mut self) -> Option<Expr> {
+        self.advance(); // consume lambda
+
+        let mut abs_count = 0;
+        while let TokenType::Variable(v) = self.peek().token_type {
+            self.env.push(v);
+            abs_count += 1;
+            self.advance();
+        }
+
+        if self.advance().token_type != TokenType::Period {
+            self.print_error("Expected '.' after abstraction variable definitions");
+            return None;
+        }
+
+        let mut expr = self.expr()?;
+
+        for _ in 0..abs_count {
+            self.env.pop().unwrap();
+            expr = Expr::Lambda(Box::new(expr));
+        }
+
+        Some(expr)
+    }
+
+    fn application(&mut self) -> Option<Expr> {
+        self.advance(); // consume left paren
+
+        let left = self.expr()?;
+        let right = self.expr()?;
+
+        dbg!(&left, &right);
+        dbg!(&self.peek().token_type);
+
+        if self.peek().token_type != TokenType::RightParen {
+            self.print_error("Expected ')' after application");
+            return None;
+        }
+
+        self.advance(); // consume right paren
+
+        Some(Expr::App(Box::new(left), Box::new(right)))
+    }
+
+    fn find_variable(&self, name: char) -> Option<usize> {
+        let ret = self
+            .env
+            .iter()
+            .enumerate()
+            .find(|(_, x)| **x == name)
+            .map(|(i, _)| i);
+
+        match ret {
+            Some(x) => Some(x),
+            None => {
+                self.print_error(&format!("Unknown variable '{}'.", name));
+
+                None
+            }
+        }
+    }
+
+    fn peek(&self) -> &Token {
+        &self.tokens[self.current]
+    }
+
+    fn peek_next(&self) -> &TokenType {
+        if self.current + 1 == self.tokens.len() {
+            &TokenType::NoToken
+        } else {
+            &self.tokens[self.current + 1].token_type
+        }
+    }
+
+    fn advance(&mut self) -> &Token {
+        let t = &self.tokens[self.current];
+        self.current += 1;
+
+        t
+    }
+
+    fn is_at_end(&self) -> bool {
+        self.tokens.len() == self.current
+    }
+
+    fn print_error(&self, err: &str) {
+        eprintln!("Error on line [{}]: {err}", self.peek().line);
+    }
+}
diff --git a/src/scanner.rs b/src/scanner.rs
index d5fced3..20fc7b3 100644
--- a/src/scanner.rs
+++ b/src/scanner.rs
@@ -26,9 +26,6 @@ impl Scanner {
             self.scan_token()?;
         }
 
-        self.tokens
-            .push(Token::new(TokenType::Eof, "".to_string(), self.line));
-
         Ok(self.tokens)
     }
 
@@ -45,7 +42,7 @@ impl Scanner {
             '\\' | '/' | 'λ' => Lambda,
             _ if c.is_alphabetic() => Variable(c),
             '$' => {
-                self.function()?;
+                self.definition()?;
 
                 NoToken
             }
@@ -59,7 +56,7 @@ impl Scanner {
             }
 
             _ => {
-                self.print_error(&format!("Unexpected character `{}`.", c));
+                self.print_error(&format!("Unexpected character '{}'.", c));
 
                 return Err(());
             }
@@ -70,7 +67,7 @@ impl Scanner {
         Ok(())
     }
 
-    fn function(&mut self) -> Result<(), ()> {
+    fn definition(&mut self) -> Result<(), ()> {
         while self.peek().is_alphanumeric() {
             self.advance();
         }
@@ -81,7 +78,7 @@ impl Scanner {
             return Err(());
         }
 
-        self.add_token(TokenType::Function(
+        self.add_token(TokenType::DefinitionName(
             self.chars[(self.start + 1)..self.current].iter().collect(),
         ));
 
diff --git a/src/token.rs b/src/token.rs
index b02bbbf..8e2cd83 100644
--- a/src/token.rs
+++ b/src/token.rs
@@ -1,12 +1,11 @@
-#[derive(PartialEq, Eq, Debug)]
+#[derive(Clone, PartialEq, Eq, Debug)]
 pub enum TokenType {
     Variable(char),
-    Function(String),
+    DefinitionName(String),
     Lambda,
 
     Period,
     Equals,
-    Eof,
 
     LeftParen,
     RightParen,
-- 
GitLab