From fc2844ae88c85f082b47630b92a55bca3ff285b1 Mon Sep 17 00:00:00 2001 From: Elizabeth Alexander Hunt Date: Mon, 3 Aug 2026 09:18:06 -0700 Subject: Init --- .gitignore | 1 + Cargo.lock | 7 +++ Cargo.toml | 6 ++ gock.ts | 42 ++++++++++++++ grammar.txt | 29 ++++++++++ src/main.rs | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 275 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 gock.ts create mode 100644 grammar.txt create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..0471cf1 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "gock" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..39565c0 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "gock" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/gock.ts b/gock.ts new file mode 100644 index 0000000..e8d1c53 --- /dev/null +++ b/gock.ts @@ -0,0 +1,42 @@ +type Int = "Int"; +type Bool = "Bool"; +type Atom = Int | Bool; +type SimpleType = Arrow | Atom; +type Arrow = { + left: SimpleType; + right: SimpleType; +}; + +type Identifier = string; + +type Binding = { + name: Identifier; + type?: SimpleType; +}; + +type Abstraction = { + binding: Binding; + body: ASTNode; +}; + +type ASTNode = Abstraction | Application | Identifier; + +// + +class Environment { + constructor(private readonly _captures: Map, private readonly _parent?: Environment) {} + + public add(identifier: Identifier, substitution: T): Environment { + const captures = new HashMap(); captures.put(identifier, substitution); + return new Environment<>(captures, this); + } + + public get(identifier: Identifier): T? { + if (this._captures.has(identifier)) return this._captures.get(identifier); + return this._parent?.get(identifier); + } +} + +//const tokenize +// const parse +// const check diff --git a/grammar.txt b/grammar.txt new file mode 100644 index 0000000..84b8123 --- /dev/null +++ b/grammar.txt @@ -0,0 +1,29 @@ +let x = 2; + y = 3; in + (x y) + +let x: (Int) = 2; + y: (Int) = 3; + f: ((Int -> Int) -> Int) = [a, b] { ((plus a) b) }; in + ((f x) y) +--- + + +LetStatement: "let" [Assignment ";"]+ "in" Body +Binding: Ident (":" TypeDecl)? +Assignment: Binding "=" Computation +Computation: Identifier | Application | Abstraction +Application: "(" Computation Computation ")" +Abstraction: "[" UncurriedArguments "]" "{" Computation "}" +UncurriedArguments: Binding ("," Binding)+ + +LetStatement: "let" Ident (":" TypeDecl)? "=" Computation in + +BaseType: Identifier +Arrow: TypeDecl "->" TypeDecl +TypeDecl: "(" BaseType | Arrow ")" + +Binding: Identifier (":" TypeDecl)? +UncurriedArguments: Identifier +Abstraction: UncurriedArguments "{" Binding "=>" Computation "}" + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..7ec318a --- /dev/null +++ b/src/main.rs @@ -0,0 +1,190 @@ +#[derive(Debug, Clone, PartialEq)] +enum TokenType { + Lparen, + Rparen, + Lbrace, + Rbrace, + Lbracket, + Rbracket, + Colon, + Identifier(String), + Eof, +} + +#[derive(Debug, Clone, Copy)] +struct Blame { + line: usize, + char: usize, +} + +#[derive(Debug)] +struct Token { + token: TokenType, + blame: Blame, +} + +struct Lexer; + +impl Lexer { + pub fn analyze(program: &str) -> Vec { + let mut tokens: Vec = program + .lines() + .enumerate() + .flat_map(|(line, text)| Self::lex_line(line, text)) + .collect(); + + tokens.push(Token { + token: TokenType::Eof, + blame: Blame { + line: tokens.len(), + char: 0, + }, + }); + tokens + } + + fn lex_line(line: usize, text: &str) -> Vec { + let mut tokens = Vec::new(); + let mut ident_start: Option = None; + + let flush = |tokens: &mut Vec, ident_start: &mut Option, end: usize| { + if let Some(start) = ident_start.take() { + if end > start { + tokens.push(Token { + token: TokenType::Identifier(text[start..end].to_string()), + blame: Blame { line, char: start }, + }); + } + } + }; + + for (i, c) in text.char_indices() { + let singlechar = match c { + '(' => Some(TokenType::Lparen), + ')' => Some(TokenType::Rparen), + '[' => Some(TokenType::Lbracket), + ']' => Some(TokenType::Rbracket), + '{' => Some(TokenType::Lbrace), + '}' => Some(TokenType::Rbrace), + ':' => Some(TokenType::Colon), + _ => None, + }; + + if c == ' ' || c == '\t' { + flush(&mut tokens, &mut ident_start, i); + continue; + } + + if let Some(tt) = singlechar { + flush(&mut tokens, &mut ident_start, i); + tokens.push(Token { + token: tt, + blame: Blame { line, char: i }, + }); + continue; + } + + if ident_start.is_none() { + ident_start = Some(i); + } + } + + flush(&mut tokens, &mut ident_start, text.len()); + tokens + } +} + +enum Atomic { + Unit, + Bool, + Int, +} + +enum SimpleType { + Arrow((Box, Box)), + Atom(Atomic), +} + +type Identifier = String; + +struct Binding { + name: Identifier, + arg_type: Option, +} + +enum ASTNode { + Abstraction { + binding: Binding, + body: Box, + }, + Application((Box, Box)), + Identifier(Identifier), +} + +pub enum StepResult { + Terminal(T), + Continue, +} + +pub trait Steppable { + fn small_step(&mut self) -> StepResult; +} + +pub struct Environment<'a, T> { + parent_scope: Option<&'a Environment<'a, T>>, + capture: Identifier, + substitution: &'a T, +} + +impl Environment<'_, T> { + fn add<'a>(&'a self, identifier: Identifier, substitution: &'a T) -> Environment<'a, T> { + return Environment { + parent_scope: Some(&self), + capture: identifier, + substitution: substitution, + }; + } + + fn get<'a>(&'a self, identifier: Identifier) -> Option<&'a T> { + if *identifier == *(self.capture) { + return Some(self.substitution); + } + return match self.parent_scope { + Some(scope) => (*scope).get(identifier), + None => None, + }; + } + + fn root<'a>(identifier: Identifier, substitution: &'a T) -> Environment<'a, T> { + return Environment { + parent_scope: None, + capture: identifier, + substitution: substitution, + }; + } +} + +fn main() { + println!("Hello, world!"); +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_env() { + let ident = Identifier::from("ident"); + let ident2 = Identifier::from("ident2"); + let val: i32 = 1; + let val2: i32 = 2; + let val3: i32 = 3; + let env = Environment::root(ident.clone(), &val); + assert_eq!(env.get(ident.clone()), Some(&val)); + let env2 = env.add(ident2.clone(), &val2); + assert_eq!(env2.get(ident.clone()), Some(&val2)); + let env3 = env2.add(ident.clone(), &val3); + assert_eq!(env3.get(ident2.clone()), Some(&val2)); + assert_eq!(env3.get(ident.clone()), Some(&val3)); + assert_eq!(env.get(ident.clone()), Some(&val)); + } +} -- cgit v1.3