From 437dae8e4ad791e0616c5d4ac482d65a2c27eda7 Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 31 Jul 2024 03:38:38 +0200 Subject: [PATCH] Recoded everything --- Makefile | 28 +++- README.md | 41 +---- src/maeel.maeel | 422 ++++-------------------------------------------- src/maeel.rs | 116 ++++++------- tests.maeel | 55 +++---- 5 files changed, 137 insertions(+), 525 deletions(-) diff --git a/Makefile b/Makefile index a81274c..ef1fef0 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,29 @@ +CC=rustc +CCARGS=-Ctarget-feature=+crt-static -Copt-level=3 -Cstrip=symbols -v +SRC=src/maeel.rs +TESTS=tests.maeel +EXE=maeel + + all: build +fmt: + rustfmt $(SRC) + build: - rustc src/maeel.rs -Copt-level=3 -o maeel + $(CC) $(CCARGS) $(SRC) -o $(EXE) + nvim: - rm ~/.config/nvim/syntax/maeel.vim && cp editor_impls/vim/maeel.vim ~/.config/nvim/syntax + rm -f ~/.config/nvim/syntax/maeel.vim + cp editor_impls/vim/maeel.vim ~/.config/nvim/syntax vscode: - rm -rf ~/.vscode/extensions/maeel-syntax-highlighting 2>/dev/null && cp -r editor_impls/vscode ~/.vscode/extensions/maeel-syntax-highlighting + rm -rf ~/.vscode/extensions/maeel-syntax-highlighting 2>/dev/null + cp -r editor_impls/vscode ~/.vscode/extensions/maeel-syntax-highlighting + +test: build + ./$(EXE) $(TESTS) -test: - rustc src/maeel.rs -o maeel && ./maeel tests.maeel +bench: build + hyperfine --runs 1000 "./$(EXE) $(TESTS)" -bench: - rustc src/maeel.rs -o maeel && hyperfine --runs 1000 "./maeel tests.maeel" diff --git a/README.md b/README.md index 977be20..958e0d5 100644 --- a/README.md +++ b/README.md @@ -18,40 +18,11 @@ The interpreter is made of ~ 600 lines of code :3 Indeed a lot of maeel features `$ ./maeel program.maeel` -## Hello, world! +## Implemented in maeel -``` -"Hello, world" puts -``` +- Stack functions (drop, dup, rot, over, swap) +- Booleans functions (and, or, not) +- Conditions (ifelse, then) (s/o Turring) +- Loops (while, for) +- Lists definition (List ... end) -## Variables - -``` - ~ -``` - -``` -"hello" ~ hello -``` - -## Functions - -``` -fun name x y z (x y + z *) -``` - -maeel also supports inline functions: - -``` -fun inline name (+ *) -``` - -## Conditions - -``` - ? () -``` - -``` - () () ifelse -``` diff --git a/src/maeel.maeel b/src/maeel.maeel index db29e9f..ee8ef0e 100644 --- a/src/maeel.maeel +++ b/src/maeel.maeel @@ -1,418 +1,66 @@ -fun inline add (+) -fun inline sub (-) -fun inline mul (*) -fun inline div (/) -fun inline mod (%) +fun inline print (dup puts) -# Drop function -# Usage: a drop -# -# Removes the top element from the stack. -# -# Example: -# - ``` 1 2 3 drop ``` will result in the stack: 1 2 -fun inline drop __ () -fun inline drp __ () +fun inline println (print "\n" puts) +fun inline putsln (puts "\n" puts) + +fun inline drop __ () -# Dup function -# Usage: a dup -# -# Duplicates the top element of the stack. -# -# Examples: -# - ``` 1 dup ``` will result in the stack: 1 1 -# - ``` {1 2 3} dup ``` will result in the stack: {1 2 3} {1 2 3} fun inline dup __a (__a __a) -# Rot function -# Usage: a b c rot -# -# Rotates the top three elements of the stack. -# -# Example: -# - ``` 1 2 3 rot ``` will result in the stack: 3 1 2 fun inline rot __a __b __c (__c __a __b) -# Swap function -# Usage: a b swap -# -# Swaps the top two elements of the stack. -# -# Examples: -# - ``` 1 2 swap ``` will result in the stack: 2 1 -# - ``` {1 2} {3 4} swap ``` will result in the stack: {3 4} {1 2} fun inline swap __a __b (__b __a) -fun inline swp __a __b (__b __a) -# Over function -# Usage: a b over -# -# Copies the second element from the top of the stack to the top. -# -# Example: -# - ``` 1 2 over ``` will result in the stack: 1 2 1 fun inline over __a __b (__a __b __a) -fun inline ovr __a __b (__a __b __a) -# While function -# Usage: __fn __pr while -# -# Executes a loop block while a predicate is true. -# -# Example: -# - ``` (1-) (dup 0 >) while ``` will decrement the top of the stack until it is no longer greater than 0. -fun inline while __fn __pr ( - __pr! # execute the predicate function (should return 0 or 1) - ? ( # if predicate returns 1 - __fn! # execute loop block - __fn __pr while # recursive call - ) -) - -# For function -# Usage: xs __for_loop_fn for -# -# Iterates over a list and executes a function for each element. -# -# Example: -# - ``` {1 2 3} (1+) for ``` will result in the stack: {2 3 4} -fun inline for __xs __for_loop_fn ( - __xs len ~ _maeel_for_loop_xs_len # max index - - 0 ~ _maeel_for_loop_index # current index in list +fun inline true ((drop)) - ( - __xs _maeel_for_loop_index get # push list current element - __for_loop_fn! # execute loop block - _maeel_for_loop_index 1 +~ _maeel_for_loop_index # increment current index - ) +fun inline false ((swap drop)) - # make sure we aren't doing an "index out of range" [1] - (_maeel_for_loop_index _maeel_for_loop_xs_len <) +fun inline ifelse (rot rot!!) - while # continue until: index = len(xs) [1] -) - -# Match function -# Usage: value mapper predicate match -# -# Applies a predicate function to elements in a mapper and returns the first match. -# -# Example: -# - ``` 5 {1 3 5 7 9} (=) match ``` will return 5 if 5 is in the list. -fun matchp value mapper predicate ( - mapper len ~ mapper_len +fun inline then (() ifelse) - list 0 ( - dup 2 % 0= ? ( - mapper over get value predicate! - ? (mapper over 1+ get rot rot+ swap) - ) 1+ - ) (dup mapper_len <) while +fun inline bool_to_string (("true") ("false") ifelse) - drop -) +fun inline bool_to_int ((1) (0) ifelse) -fun inline match ((=) matchp 0 get) +fun inline not ((false) (true) ifelse) -# Reverse function -# Usage: xs reverse -# -# Reverses the elements of a list. -# -# Example: -# - ``` {1 2 3} reverse ``` will return {3 2 1} -fun reverse xs ( - list ~ ys - xs len (1- dup xs swap get ys +~ ys) (dup 1 < not) while drop ys +fun and p q ( + 1 0 p! 1= + (1 0 q! 1= (true) (false) ifelse) + (false) + ifelse ) -# Contains function -# Usage: xs x contains -# -# Checks if an element is in a list. -# -# Example: -# - ``` {1 2 3} 2 contains ``` will return 1 (true) -# - ``` {1 2 3} 4 contains ``` will return 0 (false) -fun contains xs x ( - xs len ~xs_len 0 ~i 0 (xs i get x = ? (drop 1) i 1+ ~i) (i xs_len <) while +fun or p q ( + 1 0 p! 1= + (true) + (1 0 q! 1= (true) (false) ifelse) + ifelse ) -"__maeel_list_start" ~ List -"__maeel_mapper_start" ~ Mapper +fun inline while __while_fn __pr (__pr! (__while_fn! __while_fn __pr while) then) -# End function -# Usage: end -# -# Processes a list or mapper and returns the result. -# -# Example: -# - ``` {1 2 3} end ``` will process the list and return the result. -fun end ( - list - List + - Mapper + +fun inline loop ((True) while) - ~ stop_keywords - - list ~ output - (output +~ output) (dup stop_keywords swap contains not) while - - list - List+ (output reverse)+ - - Mapper+ ( - 0 ~ i - output len ~ lst_len - list ~ mapper - output ( - i 2% 0= ? (list output i 1+ get+ output i get+ mapper +~ mapper) i 1 +~ i - ) (i lst_len <) while - - mapper - )+ - - match! +fun inline for __xs __for_fn ( + __xs len :__xs_len 0 :__idx + (__xs __idx get __for_fn! __idx 1+ :__idx) (__idx __xs_len <) + while ) -fun take1 a (List a end) - -fun take2 a b (List a b end) - -fun take3 a b c (List a b c end) - -fun take4 a b c d (List a b c d end) - -fun inline take (0 swap range list swap (drop +) for reverse) - - -# Count function -# Usage: xs x count -# -# Pushes the number of occurence of x in xs. -# -# Examples: -# - {1 2 3 3 4} 3 count will return 2 -# - {1 2 3 3 4} 4 count will return 1 -# - {1 2 3 3 4} 42 count will return 0 -fun count x (0 swap (x = pom ? (0) ? (1 swap) +) for) - -# Range function -# Usage: a b range -# -# Pushes an array with all the numbers N such that a-1 < N < b -# -# Example: -# - 1 10 range will return {1 2 3 4 5 6 7 8 9} -fun range b (list swap (dup rot+ swap 1+) (dup b<) while drop) +fun take1 (list+) +fun take2 (list++) +fun take3 (list+++) -# Last function -# Usage: xs last -# -# Pushes the last element of an array (drops the array) -# -# Examples: -# - {3 2 1} last will return 1 -# - {1 2 3} last will return 3 -# - {2} last will return 2 -fun inline last (dup len 1- get) +fun inline List ("__maeel_list_start") -# Merge function -# Usage: xs ys range -# -# Merges two arrays togather -# -# Examples: -# - {1 2 3} {4 5 6} merge will return {1 2 3 4 5 6} -# - {1} {1} merge will return {1 1} -fun inline merge (list rot rot (+) for swap (+) for) +fun inline end2 (list (+) (over List= not) while swap drop) -# Map function -# Usage xs f map -# -# Applies f(x) for each element x of xs -# -# Examples: -# - ``` {1 2 3} (1+) map ``` will return {2 3 4} -# - ``` {1 2 3} (4*) map ``` will return {4 8 12} -fun inline map (list rot swap (over! rot rot+ swap) for drop) +fun inline reverse (List swap () for end2) +fun inline end (list (+) (over List= not) while swap drop reverse) -# Reduce function -# Usage: xs __reduce_fn reduce -# -# Reduces a list using a provided function. -# -# Example: -# - ``` {1 2 3 4} (+) 0 reduce ``` will return 10 -fun inline reduce (swap ~ _reduce_fn swap (swap _reduce_fn!) for) -fun inline reduceR (swap ~ _reduce_fn swap reverse (swap _reduce_fn!) for) - -# Filter function -# Usage: xs pr filter -# -# Filters a list based on a predicate function. -# -# Example: -# - ``` {1 2 3 4} (2%) filter ``` will return {2 4} -fun filter pr (list ~ xs (dup pr! ? (xs swap +~ xs)) for xs) - -# Ifelse function -# Usage: a b c ifelse -# -# Executes one of two functions based on a condition. -# -# Example: -# - ``` 1 2 3 ifelse ``` will execute the second function if the top of the stack is 1, otherwise the third. -fun ifelse (1 rot 0 swap take4 match!) - -# And function -# Usage: a b and -# -# Performs a logical AND on the top two elements of the stack. -# -# Example: -# - ``` 1 1 and ``` will return 1 (true) -# - ``` 1 0 and ``` will return 0 (false) -fun inline and (*) - -# Or function -# Usage: a b or -# -# Performs a logical OR on the top two elements of the stack. -# -# Example: -# - ``` 1 0 or ``` will return 1 (true) -# - ``` 0 0 or ``` will return 0 (false) -fun inline or (+ 0= 1 swap-) - -# Not function -# Usage: a not -# -# Performs a logical NOT on the top element of the stack. -# -# Example: -# - ``` 1 not ``` will return 0 (false) -# - ``` 0 not ``` will return 1 (true) -fun inline not (1 swap-) - -# Xor function -# Usage: a b xor -# -# Performs a logical XOR on the top two elements of the stack. -# -# Example: -# - ``` 1 0 xor ``` will return 1 (true) -# - ``` 1 1 xor ``` will return 0 (false) -fun inline xor (+ 2 %) - -fun inline pom (dup not) - -fun inline mop (pom swap) - -# Fact function -# Usage: n fact -# -# Calculates the factorial of a number using recursion. -# -# Example: -# - ``` 5 fact ``` will return 120 -fun fact (dup (1- dup rot * swap) (dup 1 >) while drop) - -# Fact1 function -# Usage: n fact1 -# -# Calculates the factorial of a number using range and reduce. -# -# Example: -# - ``` 5 fact1 ``` will return 120 -fun fact1 (1+ 1 swap range (*) 1 reduce) - -# Sqrt function -# Usage: x sqrt -# -# Calculates the square root of a number using the Babylonian method. -# -# Example: -# - ``` 9 sqrt ``` will return 3.0 -fun sqrt x (x 0.5* 0 5 range (swap dup x swap/ swap+ 0.5* swap drop) for) - -# Log function -# Usage: base x log -# -# Calculates the logarithm of a number to a given base. -# -# Example: -# - ``` 10 100 log ``` will return 2 -fun log base x (0 (1+ x base /~ x) (x base < not) while) - -# Pow function -# Usage: base exp pow -# -# Calculates the power of a base raised to an exponent. -# -# Example: -# - ``` 2 3 pow ``` will return 8 -fun pow base exp (list 0 exp range (drop base+) for (*) 1 reduce ) - -# Print function -# Usage: x print -# -# Prints the top element of the stack. -# -# Example: -# - ``` 42 print ``` will output 42 -fun inline print (dup puts) - -# Puts_digit function -# Usage: x puts_digit -# -# Converts an integer to a character and prints it. -# -# Example: -# - ``` 4 puts_digit ``` will output '4' -fun inline puts_digit (int2char print drop) - -# Putsln function -# Usage: x putsln -# -# Prints the top element of the stack followed by a newline. -# -# Example: -# - ``` 42 putsln ``` will output 42 followed by a newline -fun inline putsln (puts "\n" puts) - -# Println function -# Usage: x println -# -# Prints the top element of the stack followed by a newline. -# -# Example: -# - ``` 42 println ``` will output 42 followed by a newline -fun inline println (print "\n" puts) - -fun format ( - "" ~ output - (dup ~ char "%" = (output swap) (output char) ifelse +~ output) for - output -) - -# Int function -# Usage: s int -# -# Converts a string to an integer. -# -# Example: -# - ``` "123" int ``` will return 123 -fun inline int ( - 0 swap ( - swap 10 * swap - List - "0" 0 "1" 1 "2" 2 "3" 3 "4" 4 - "5" 5 "6" 6 "7" 7 "8" 8 "9" 9 - end - match+ - ) for -) diff --git a/src/maeel.rs b/src/maeel.rs index 232cf09..01bbc7a 100644 --- a/src/maeel.rs +++ b/src/maeel.rs @@ -90,8 +90,8 @@ enum CordRepr { } /* Tokens used by the lexer */ -#[derive(Clone, Debug)] -pub enum Token { +#[derive(Clone, Debug, PartialEq)] +enum Token { Block(Stack), Str(String), Name(String), @@ -100,10 +100,34 @@ pub enum Token { Symbol(char), } +macro_rules! True { + ($l:expr, $f:expr) => { + Cord::Fun(( + [(Token::Name("drop".to_string()), $f, $l)] + .as_slice() + .into(), + true, + )) + }; +} + +macro_rules! False { + ($l:expr, $f:expr) => { + Cord::Fun(( + [ + (Token::Name("drop".to_string()), $f, $l), + (Token::Name("swap".to_string()), $f, $l), + ] + .as_slice() + .into(), + true, + )) + }; +} + /* Used for error messages */ #[derive(Debug)] -pub enum TokenRepr { - Block, +enum TokenRepr { Str, Name, } @@ -191,7 +215,6 @@ fn lex_into_tokens(code: &str, file: &str) -> Stack { assert_eq!(depth, 0); /* We need to parse differents code blocks now */ - /* TODO: rework on this algorithm */ let mut stack = Vec::default(); let mut output = Vec::default(); let mut temp_tokens = Vec::default(); @@ -257,31 +280,30 @@ impl BocchiVM { tokens.reverse(); } + let match_bool = |i: bool, l: u16, f: &String| match i { + false => False!(l, f.clone()), + true => True!(l, f.clone()), + }; + while let Some((token, file, line)) = tokens.pop() { match token { - Token::Str(_) | Token::Float(_) | Token::Int(_) => self.push(token.into()), - + Token::Str(_) | Token::Float(_) | Token::Int(_) => self.push(match token { + Token::Str(x) => Cord::Str(x), + Token::Int(x) => Cord::Int(x), + _ => unreachable!(), + }), Token::Symbol('+') => binop!(|a, b: Cord| b.add(a), self, &file, line), - Token::Symbol('-') => binop!(|a, b: Cord| b.sub(a), self, &file, line), - Token::Symbol('*') => binop!(|a, b: Cord| b.mul(a), self, &file, line), - Token::Symbol('/') => binop!(|a, b: Cord| b.div(a), self, &file, line), - Token::Symbol('%') => binop!(|a, b: Cord| b.rem(a), self, &file, line), - - Token::Symbol('=') => binop!(|a, b| Cord::Int((b == a) as i32), self, &file, line), - - Token::Symbol('<') => binop!(|a, b| Cord::Int((b < a) as i32), self, &file, line), - - Token::Symbol('>') => binop!(|a, b| Cord::Int((b > a) as i32), self, &file, line), - + Token::Symbol('=') => binop!(|a, b| match_bool(b == a, line, &file), self, &file, line), + Token::Symbol('<') => binop!(|a, b| match_bool(b < a, line, &file), self, &file, line), + Token::Symbol('>') => binop!(|a, b| match_bool(b > a, line, &file), self, &file, line), Token::Block(mut block) => { block.reverse(); /* meow */ self.push(Cord::Fun((block.as_slice().into(), true))) } - Token::Symbol('!') /* Manually call a function */ => { let (function_tokens, is_inline) = expect_stack!(Fun, self, file, line); @@ -292,46 +314,24 @@ impl BocchiVM { ) } } - - Token::Symbol('?') /* Basically "if" statement */ => { - let temp_tokens = expect_token!(Block, tokens, file, line); - - if expect_stack!(Int, self, file, line) /* The boolean (actually an integer, anyways :3) */ == 1 { - let temp_tokens_length = temp_tokens.len(); - - (0..temp_tokens_length).for_each(|index| { /* Pushing from end to start */ - let temp_token = temp_tokens - .get(temp_tokens_length - index - 1) - .unwrap(); - - tokens.push(temp_token.clone()) - }); - } - } - - Token::Symbol('~') /* Assign stack top value to next name */ => { + Token::Symbol(':') | Token::Symbol('~') /* Assign stack top value to next name */ => { let name = expect_token!(Name, tokens, file, line); let value = self.pop() .unwrap_or_else(|_| emit_error!(file, line, "stack is empty!")); variables.insert(name, value); } - Token::Symbol(char) => emit_error!(file, line, format!("unknown symbol: {char}.")), - Token::Name(name) => match name.as_str() { - "puts" => print!("{}", self.pop().unwrap() /* TODO: make an error message when stack is empty */), - + "puts" => print!("{}", self.pop().unwrap()), "list" => self.push(Cord::List(Vec::default())), - "fun" => { let mut function_name = expect_token!(Name, tokens, file, line); let mut function_tokens = Vec::default(); - let is_inline = function_name == "inline"; + if is_inline { function_name = expect_token!(Name, tokens, file, line) } - while let Some(temp_token) = tokens.pop() - { + while let Some(temp_token) = tokens.pop() { match temp_token.clone() { (Token::Block(temp_tokens), _, _) => { function_tokens.reverse(); /* uhm */ @@ -355,7 +355,6 @@ impl BocchiVM { functions.insert(function_name.clone(), (function_tokens.as_slice().into(), is_inline)); } - "len" => { let length = match self.pop() { Ok(Cord::Str(string)) => Cord::Int(string.len() as i32), @@ -366,7 +365,6 @@ impl BocchiVM { self.push(length) } - "get" => { let index = expect_stack!(Int, self, file, line) as usize; @@ -387,7 +385,6 @@ impl BocchiVM { _ => emit_error!(file, line, format!("unindexable: EOF")), } } - "include" /* This is bad */ => { let target = expect_token!(Str, tokens, file, line); let content = match target.clone().as_str() { @@ -405,14 +402,10 @@ impl BocchiVM { tokens.push(temp_tokens.get(temp_tokens_length - index - 1).unwrap().clone()) } } - name => { - if let Some(value) = variables.get(name) - { + if let Some(value) = variables.get(name) { self.push(value.clone()) - } - else if let Some((function_tokens, inline)) = functions.get(name) - { + } else if let Some((function_tokens, inline)) = functions.get(name) { match inline { true => function_tokens .iter() @@ -421,9 +414,7 @@ impl BocchiVM { &mut function_tokens.to_vec(), &mut variables.clone(), functions, false ) } - } - else - { + } else { emit_error!(file, line, format!("unknown name {name}")) } } @@ -500,6 +491,7 @@ impl PartialEq for Cord { (Self::Int(a), Self::Int(b)) => a == b, (Self::Float(a), Self::Float(b)) => a == b, (Self::Int(a), Self::Float(b)) | (Self::Float(b), Self::Int(a)) => (*a as f32) == *b, + (Self::Fun(a), Self::Fun(b)) => a == b, /* perf issue */ _ => false, } } @@ -568,16 +560,6 @@ impl Cord { } } -impl From for Cord { - fn from(val: Token) -> Self { - match val { - Token::Str(x) => Cord::Str(x), - Token::Int(x) => Cord::Int(x), - _ => unreachable!(), - } - } -} - fn main() { let file = args().nth(1).unwrap(); let file_content = &read_to_string(&file).unwrap(); diff --git a/tests.maeel b/tests.maeel index 3a65676..10b0a4e 100644 --- a/tests.maeel +++ b/tests.maeel @@ -1,36 +1,33 @@ include "maeel" fun test value supposed message ( - message " " + dup dup - 30 swap len -~ padding + message " " + dup dup + 30 swap len -: padding puts "." padding * puts - value supposed = (": SUCCESS" putsln) (": FAIL" putsln) ifelse -) - -List 1 2 3 end ~ lst -List 1 2 3 3 5 6 end ~ lst2 + value supposed = + (": SUCCESS" putsln) + (": FAIL -- " puts value putsln) + ifelse +) -3 2 + 5 "add" test -3 2 - 1 "sub" test -3 2 * 6 "mul" test -6 2 / 3 "div" test -2 3 drop 2 "drop" test -2 3 dup take3 2 3 3 take3 "dup" test -1 2 3 rot take3 3 1 2 take3 "rot" test -2 3 over take3 2 3 2 take3 "over" test -1 2 3 swap take3 1 3 2 take3 "swap" test -lst (1+) map 2 3 4 take3 "map" test -lst2 3 count 2 "count" test -4 fact 24 "factorial 4" test -5 fact 120 "factorial 5" test -6 fact 720 "factorial 6" test -7 fact 5040 "factorial 7" test -lst reverse list 3+ 2+ 1+ "reverse" test -list reverse list "empty reverse" test -1 4 range lst "range" test - -1 5 range (dup fact swap fact1 "second factorial" test) for -3 2 + 2 3 + "add commutativity" test -3 2 * 2 3 * "mul commutativity" test +3 2 + 5 "add" test +3 2 - 1 "sub" test +3 2 * 6 "mul" test +6 2 / 3 "div" test +2 3 drop 2 "drop" test +2 3 dup take3 2 3 3 take3 "dup" test +1 2 3 rot take3 3 1 2 take3 "rot" test +2 3 over take3 2 3 2 take3 "over" test +1 2 3 swap take3 1 3 2 take3 "swap" test +3 2 + 2 3 + "add commutativity" test +3 2 * 2 3 * "mul commutativity" test +true true and bool_to_string "true" "and 1" test +true false and bool_to_string "false" "and 2" test +false true and bool_to_string "false" "and 3" test +false false and bool_to_string "false" "and 4" test +true true or bool_to_string "true" "or 1" test +true false or bool_to_string "true" "or 2" test +false true or bool_to_string "true" "or 3" test +false false or bool_to_string "false" "or 4" test