Initial commit
This commit is contained in:
commit
85ea81a41d
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glsl-lexer"
|
||||||
|
version = "0.1.0"
|
6
Cargo.toml
Normal file
6
Cargo.toml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[package]
|
||||||
|
name = "glsl-lexer"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
113
flake.lock
Normal file
113
flake.lock
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1709336216,
|
||||||
|
"narHash": "sha256-Dt/wOWeW6Sqm11Yh+2+t0dfEWxoMxGBvv3JpIocFl9E=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "f7b3c975cf067e56e7cda6cb098ebe3fb4d74ca2",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710806803,
|
||||||
|
"narHash": "sha256-qrxvLS888pNJFwJdK+hf1wpRCSQcqA6W5+Ox202NDa0=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "b06025f1533a1e07b6db3e75151caa155d1c7eb3",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-lib": {
|
||||||
|
"locked": {
|
||||||
|
"dir": "lib",
|
||||||
|
"lastModified": 1709237383,
|
||||||
|
"narHash": "sha256-cy6ArO4k5qTx+l5o+0mL9f5fa86tYUX3ozE1S+Txlds=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "1536926ef5621b09bba54035ae2bb6d806d72ac8",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"dir": "lib",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1708475490,
|
||||||
|
"narHash": "sha256-g1v0TsWBQPX97ziznfJdWhgMyMGtoBFs102xSYO4syU=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "0e74ca98a74bc7270d28838369593635a5db3260",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"systems": "systems",
|
||||||
|
"treefmt-nix": "treefmt-nix"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1,
|
||||||
|
"narHash": "sha256-8wkkYGr1dPSnX9oVMX8D6dTOROXKOYpBTKfriA0sEBI=",
|
||||||
|
"path": "/nix/store/z4by2bx56bm456s77isfwvr59mqg9brg-source/flake.systems.nix",
|
||||||
|
"type": "path"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"path": "/nix/store/z4by2bx56bm456s77isfwvr59mqg9brg-source/flake.systems.nix",
|
||||||
|
"type": "path"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"treefmt-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710781103,
|
||||||
|
"narHash": "sha256-nehQK/XTFxfa6rYKtbi8M1w+IU1v5twYhiyA4dg1vpg=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"rev": "7ee5aaac63c30d3c97a8c56efe89f3b2aa9ae564",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
79
flake.nix
Normal file
79
flake.nix
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
# Nix flake for reproducible builds & development environments.
|
||||||
|
# TL;DR:
|
||||||
|
# either `curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install`
|
||||||
|
# or https://github.com/DeterminateSystems/nix-installer
|
||||||
|
# and then `nix build` or `nix develop`
|
||||||
|
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||||
|
#systems.url = "github:nix-systems/default";
|
||||||
|
|
||||||
|
# Dev tools
|
||||||
|
treefmt-nix.url = "github:numtide/treefmt-nix";
|
||||||
|
};
|
||||||
|
inputs.systems.url = "./flake.systems.nix";
|
||||||
|
inputs.systems.flake = false;
|
||||||
|
|
||||||
|
outputs = inputs:
|
||||||
|
inputs.flake-parts.lib.mkFlake { inherit inputs; } {
|
||||||
|
systems = import inputs.systems;
|
||||||
|
imports = [
|
||||||
|
inputs.treefmt-nix.flakeModule
|
||||||
|
];
|
||||||
|
perSystem = { config, self', pkgs, lib, system, ... }:
|
||||||
|
let
|
||||||
|
cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
|
||||||
|
nonRustDeps = [
|
||||||
|
pkgs.pkg-config
|
||||||
|
];
|
||||||
|
rust-toolchain = pkgs.symlinkJoin {
|
||||||
|
name = "rust-toolchain";
|
||||||
|
paths = [ pkgs.rustc pkgs.cargo pkgs.cargo-watch pkgs.rust-analyzer pkgs.rustPlatform.rustcSrc ];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Rust package
|
||||||
|
packages.default = pkgs.rustPlatform.buildRustPackage {
|
||||||
|
inherit (cargoToml.package) name version;
|
||||||
|
nativeBuildInputs = nonRustDeps;
|
||||||
|
buildInputs = nonRustDeps;
|
||||||
|
src = ./.;
|
||||||
|
cargoLock.lockFile = ./Cargo.lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Rust dev environment
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
inputsFrom = [
|
||||||
|
config.treefmt.build.devShell
|
||||||
|
];
|
||||||
|
shellHook = ''
|
||||||
|
# For rust-analyzer 'hover' tooltips to work.
|
||||||
|
export RUST_SRC_PATH=${pkgs.rustPlatform.rustLibSrc}
|
||||||
|
echo $RUST_SRC_PATH
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Run 'just <recipe>' to get started"
|
||||||
|
just
|
||||||
|
'';
|
||||||
|
buildInputs = nonRustDeps;
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
just
|
||||||
|
rust-toolchain
|
||||||
|
];
|
||||||
|
RUST_BACKTRACE = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Add your auto-formatters here.
|
||||||
|
# cf. https://numtide.github.io/treefmt/
|
||||||
|
treefmt.config = {
|
||||||
|
projectRootFile = "flake.nix";
|
||||||
|
programs = {
|
||||||
|
nixpkgs-fmt.enable = true;
|
||||||
|
rustfmt.enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
4
flake.systems.nix
Normal file
4
flake.systems.nix
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
]
|
14
justfile
Normal file
14
justfile
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
default:
|
||||||
|
@just --list
|
||||||
|
|
||||||
|
# Auto-format the source tree
|
||||||
|
fmt:
|
||||||
|
treefmt
|
||||||
|
|
||||||
|
# Run 'cargo run' on the project
|
||||||
|
run *ARGS:
|
||||||
|
cargo run {{ARGS}}
|
||||||
|
|
||||||
|
# Run 'cargo watch' to run the project (auto-recompiles)
|
||||||
|
watch *ARGS:
|
||||||
|
cargo watch -x "run -- {{ARGS}}"
|
222
src/lib.rs
Normal file
222
src/lib.rs
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
// WIP THAT SHIT STILL WONKY AF
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum Token {
|
||||||
|
Identifier(String),
|
||||||
|
Keyword(String),
|
||||||
|
IntegerLiteral(i64),
|
||||||
|
FloatLiteral(f64),
|
||||||
|
Operator(String),
|
||||||
|
Symbol(char),
|
||||||
|
Whitespace,
|
||||||
|
Comment(String),
|
||||||
|
Unknown(char),
|
||||||
|
EndOfFile,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Lexer {
|
||||||
|
input: Vec<char>,
|
||||||
|
position: usize,
|
||||||
|
current_char: Option<char>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lexer {
|
||||||
|
pub fn new(input: &str) -> Self {
|
||||||
|
let mut lexer = Lexer {
|
||||||
|
input: input.chars().collect(),
|
||||||
|
position: 0,
|
||||||
|
current_char: None,
|
||||||
|
};
|
||||||
|
lexer.current_char = if lexer.position < lexer.input.len() {
|
||||||
|
Some(lexer.input[lexer.position])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
lexer
|
||||||
|
}
|
||||||
|
|
||||||
|
fn advance(&mut self) {
|
||||||
|
self.position += 1;
|
||||||
|
self.current_char = if self.position < self.input.len() {
|
||||||
|
Some(self.input[self.position])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_tokens(&mut self) -> Vec<Token> {
|
||||||
|
let mut tokens = Vec::new();
|
||||||
|
while let Some(c) = self.current_char {
|
||||||
|
if c.is_whitespace() {
|
||||||
|
self.consume_whitespace();
|
||||||
|
tokens.push(Token::Whitespace);
|
||||||
|
} else if c.is_alphabetic() || c == '_' {
|
||||||
|
tokens.push(self.consume_identifier_or_keyword());
|
||||||
|
} else if c.is_digit(10) {
|
||||||
|
tokens.push(self.consume_number());
|
||||||
|
} else if c == '/' && self.peek() == Some('/') {
|
||||||
|
tokens.push(self.consume_comment());
|
||||||
|
} else {
|
||||||
|
tokens.push(self.consume_symbol());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tokens.push(Token::EndOfFile);
|
||||||
|
tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek(&self) -> Option<char> {
|
||||||
|
if self.position + 1 < self.input.len() {
|
||||||
|
Some(self.input[self.position + 1])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lexer {
|
||||||
|
fn consume_whitespace(&mut self) {
|
||||||
|
while let Some(c) = self.current_char {
|
||||||
|
if !c.is_whitespace() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume_identifier_or_keyword(&mut self) -> Token {
|
||||||
|
let mut identifier = String::new();
|
||||||
|
while let Some(c) = self.current_char {
|
||||||
|
if c.is_alphanumeric() || c == '_' {
|
||||||
|
identifier.push(c);
|
||||||
|
self.advance();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if is_keyword(&identifier) {
|
||||||
|
Token::Keyword(identifier)
|
||||||
|
} else {
|
||||||
|
Token::Identifier(identifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume_number(&mut self) -> Token {
|
||||||
|
let mut number = String::new();
|
||||||
|
let mut is_float = false;
|
||||||
|
|
||||||
|
while let Some(c) = self.current_char {
|
||||||
|
if c.is_digit(10) {
|
||||||
|
number.push(c);
|
||||||
|
self.advance();
|
||||||
|
} else if c == '.' {
|
||||||
|
number.push(c);
|
||||||
|
is_float = true;
|
||||||
|
self.advance();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_float {
|
||||||
|
Token::FloatLiteral(number.parse().unwrap())
|
||||||
|
} else {
|
||||||
|
Token::IntegerLiteral(number.parse().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume_comment(&mut self) -> Token {
|
||||||
|
let mut comment = String::new();
|
||||||
|
while let Some(c) = self.current_char {
|
||||||
|
if c == '\n' {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
comment.push(c);
|
||||||
|
self.advance();
|
||||||
|
}
|
||||||
|
Token::Comment(comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume_symbol(&mut self) -> Token {
|
||||||
|
let symbol = self.current_char.unwrap();
|
||||||
|
self.advance();
|
||||||
|
Token::Symbol(symbol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_keyword(word: &str) -> bool {
|
||||||
|
matches!(
|
||||||
|
word,
|
||||||
|
"void"
|
||||||
|
| "int"
|
||||||
|
| "float"
|
||||||
|
| "bool"
|
||||||
|
| "if"
|
||||||
|
| "else"
|
||||||
|
| "for"
|
||||||
|
| "while"
|
||||||
|
| "return"
|
||||||
|
| "struct"
|
||||||
|
| "uniform"
|
||||||
|
| "varying"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lexer {
|
||||||
|
fn error(&self, message: &str) -> ! {
|
||||||
|
panic!("Lexer error at position {}: {}", self.position, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_whitespace() {
|
||||||
|
let source_code = " \t\n";
|
||||||
|
let mut lexer = Lexer::new(source_code);
|
||||||
|
let tokens = lexer.get_tokens();
|
||||||
|
assert_eq!(tokens, vec![Token::Whitespace, Token::EndOfFile]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_identifier() {
|
||||||
|
let source_code = "variableName";
|
||||||
|
let mut lexer = Lexer::new(source_code);
|
||||||
|
let tokens = lexer.get_tokens();
|
||||||
|
assert_eq!(
|
||||||
|
tokens,
|
||||||
|
vec![
|
||||||
|
Token::Identifier("variableName".to_string()),
|
||||||
|
Token::EndOfFile
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_keyword() {
|
||||||
|
let source_code = "uniform";
|
||||||
|
let mut lexer = Lexer::new(source_code);
|
||||||
|
let tokens = lexer.get_tokens();
|
||||||
|
assert_eq!(
|
||||||
|
tokens,
|
||||||
|
vec![Token::Keyword("uniform".to_string()), Token::EndOfFile]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_integer_literal() {
|
||||||
|
let source_code = "12345";
|
||||||
|
let mut lexer = Lexer::new(source_code);
|
||||||
|
let tokens = lexer.get_tokens();
|
||||||
|
assert_eq!(tokens, vec![Token::IntegerLiteral(12345), Token::EndOfFile]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_float_literal() {
|
||||||
|
let source_code = "123.45";
|
||||||
|
let mut lexer = Lexer::new(source_code);
|
||||||
|
let tokens = lexer.get_tokens();
|
||||||
|
assert_eq!(tokens, vec![Token::FloatLiteral(123.45), Token::EndOfFile]);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue