├── .ccls ├── .github ├── ISSUE_SLOWNESS.md └── development │ ├── 1.png │ ├── 2.png │ ├── 3.png │ └── 4.png ├── .gitignore ├── FAQ.md ├── LICENSE ├── README.md ├── log.nu ├── rwppi-helper.nu └── source ├── main.cpp ├── main.hpp └── modules ├── ArgumentsParser ├── ArgumentsParser.cpp └── ArgumentsParser.hpp ├── AssistantFun ├── AssistantFun.cpp └── AssistantFun.hpp ├── JSONParser └── JSONParser.hpp ├── Methods ├── Google.hpp ├── Local.hpp ├── Methods.cpp ├── Methods.hpp └── tinyexpr │ ├── LICENCE │ ├── main.c │ └── main.h └── URLDownloader ├── URLDownloader.cpp └── URLDownloader.hpp /.ccls: -------------------------------------------------------------------------------- 1 | clang 2 | %cpp -std=c++20 3 | %hpp -std=c++20 4 | -Werror=return-type 5 | -Werror=implicit-fallthrough 6 | -Werror=implicit-int-conversion 7 | -Werror=narrowing 8 | -Werror=uninitialized 9 | -Werror=pointer-to-int-cast" 10 | -------------------------------------------------------------------------------- /.github/ISSUE_SLOWNESS.md: -------------------------------------------------------------------------------- 1 | 2 | $ time iris --log [your query] 3 | -------------------------------------------------------------------------------- /.github/development/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeyondMagic/iris/b8e5beccc2098cd6362ee0e2a94437bfce88f1f0/.github/development/1.png -------------------------------------------------------------------------------- /.github/development/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeyondMagic/iris/b8e5beccc2098cd6362ee0e2a94437bfce88f1f0/.github/development/2.png -------------------------------------------------------------------------------- /.github/development/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeyondMagic/iris/b8e5beccc2098cd6362ee0e2a94437bfce88f1f0/.github/development/3.png -------------------------------------------------------------------------------- /.github/development/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeyondMagic/iris/b8e5beccc2098cd6362ee0e2a94437bfce88f1f0/.github/development/4.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Library folder for all libraries of the project. 2 | /library/ 3 | 4 | # CCLS cache for intellisense and language server support. 5 | /**/.ccls-cache 6 | 7 | # Binaries folder. 8 | /binary/ 9 | 10 | # For HTML test files. 11 | HTML 12 | *.html 13 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | **Q: When will 1.0.0 come out?** 2 | 3 | A: I don't know. It may take 6~ months, it may take a year. Iris aims are too big, but I am willing to do it, I believe there is much we can do with much less than we often do now, it doesn't mean necessarily that we have to sacrifice performance and convenience for it, is just means that it will take time and work, that I hope I may have in the future. I hope you can see the reasons why it is important to create this skeleton before anything else, those bare bones will be what will sustain Iris in the long-term. 4 | 5 | **Q: What about proprietary assistant A, B and C?** 6 | 7 | A: Iris does not aim to compete with proprietary assistants, it aims to be just a better assistant overall; offering answers you don't have to open a browser for in less than a second. 8 | 9 | **Q: Why Iris was created?** 10 | 11 | A: I've contributed to [**tuxi**](https://github.com/Bugswriter/tuxi/) until it wasn't maintainable any more, we strived to go too far with shell-scripting, which it shouldn't have happened. So we had to change our focus. David Sherriff made [oi](https://github.com/PureArtistry/oi/) in Rust way before [**tuxi**](https://github.com/Bugswriter/tuxi/) got to that point, which made [oi](https://github.com/PureArtistry/oi/) a better project to switch for at the time; but I wanted to do something that could be minimal, lightweight and could endure a long-term vision of performance and certain features, I saw this possible on modern C++, but not on Rust. 12 | 13 | **Q: Google information not displaying at all or incorrectly?** 14 | 15 | Google uses a complex system to limit requests, depending of language, country, and even the user-agent and of what words are being used it can block the information that is scraped. 16 | 17 | However, it often fails, for more of a better word, it is a failure. Since Iris is open-source, Google can easily fix it behind the curtains and make Iris not able to scrape Google with the potentiality. 18 | 19 | Nevertheless Google changes how the data is displayed, well, to be honest, it changes IDs, TAGs and CLASSEs. Which makes it impossible to create a stable version of Google scraping, so the workaround is to simply update it when it changes. 20 | 21 | **Q: How I can get a prettier data with icons or lines, weather, etc...? Or show Images?** 22 | 23 | A: You can't only with Iris. Iris gives you the information, what you do with it is on you. Some people may be interested to make external projects to prettify Iris output, but this will not be implemented on Iris itself. 24 | 25 | **Q: Will there ever be a voice for Iris?** 26 | 27 | A: No. That's it. Some people may be interested to use or make an external program to read this output like a human, but this will not be implemented on Iris itself. 28 | 29 | **Q: Will there ever be a voice word-recognizer for Iris?** 30 | 31 | A: No. That's it. Some people may be interested to use or make an external program to understand what you're saying and send to Iris, but this will not be implemented on Iris itself. 32 | 33 | **Q: Too slow. What is it?** 34 | 35 | A: Can be almost anything. Make an [issue](). 36 | 37 | **Q: Why Iris is not telling me an information that is easily gathered in A web-site?** 38 | 39 | A: This A website is either [not implemented](/#README.md?##Source) on Iris, or the information that A website displays now is displaying in a different manner. Make an [issue](sources). 40 | 41 | **Q: Is Iris an AI?** 42 | 43 | A: No, it aims to be an AI prototype, however we may go further down the line if feasible. 44 | 45 | **Q: Why should I use Iris?** 46 | 47 | A: I don't know what you want, that's the point of Iris, it doesn't force you to only use for certain things. If you are developer and just want to use Iris in an external program that display lyrics every time your music server changes a song, you can easily. If you are user and want the same, you can. Freedom is what you have with Iris and always will. 48 | 49 | **Q: Why "Iris"?** 50 | 51 | Iris comes from ["arco-íris"]() in Portuguese it is a word for rainbow, which Iris by itself is not colourful at all, but essentially it is what I want to represent with what it does. Iris is pronounced as "íris" in Portuguese. 52 | 53 | **Q: Why C++?** 54 | 55 | A: It is still has ecosystem that we can see foundations for this project. Iris uses the New C++ (C++20 and later versions), the modernized version where we can make less mistakes and offer much more performance and less time spending many things. 56 | 57 | **Q: What are the limits of Iris?** 58 | 59 | A: Many, primarily how information is gathered. Iris can never be really stabilized, since web-pages update how it display its data and Iris has to update as well to get this data, which by itself can take a long time doing, that's why every release will be considered a stable release. 60 | 61 | **Q: What about feature A, B, or C?** 62 | 63 | A: Make an [issue]() telling what it is and why it should be added. 64 | 65 | **Q: What about collaborator A? Member B?** 66 | 67 | A: We are all humans behind screens. Working for a project without any aim-for-profit. At the of the day, we strive to make a better project overall, not matter our differences ideologically or ethically. The world out there is just like here, we have to accept there are people that don't believe the same things we do and that's okay. 68 | 69 | **Q: How do I contribute to the project?**: 70 | 71 | A: See [CONTRIBUTING.md](/CONTRIBUTING.md). 72 | 73 | **Q: How to donate and should I?** 74 | 75 | A: If you can, please, don't hesitate. I see a bright future for this project, which means lots of work, there are many things that can be added and optimized as always, and to work on it takes a long time. I don't receive any money for this project, it is free (as in freedom) and use, so any help will be appreciated, thank you very much. 76 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021-2022 João F. BeyondMagic 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Iris is an online eater and feeder.

2 | 3 | **Note:** 4 | 5 | Iris will take far longer than what I anticipated, the first working version will be released under 1.0.0 tag. 6 | 7 | --- 8 | 9 | # Progress 10 | 11 | The speed is mostly limited to your internet connection and how fast cURL can give back the information. On my machine and with my Internet speed, this can take from **608ms** to up **1203ms**. **16ms-25ms** is the time used to scrape the given information. 12 | 13 | Iris take a different approach to [tuxi] and [oi] as it will display any information that it can scrape. It leaves up to the user to do something with the information that is given to them, that's why each "method" will display its name before the information, as you can see below: 14 | 15 | ``` 16 | $ iris "Luna's Future lyrics" 17 | GoogleLyrics I see a cold wind blowing through 18 | GoogleLyrics I see days neither fun nor free 19 | GoogleLyrics 20 | GoogleLyrics I see a future caused by you 21 | GoogleLyrics I see a path not meant to be 22 | GoogleLyrics 23 | GoogleLyrics The future should be filled with magic 24 | GoogleLyrics Dreams and wishes brought to life 25 | GoogleLyrics 26 | GoogleLyrics But the days ahead are dark and tragic 27 | GoogleLyrics No time for hope when all is strife 28 | GoogleLyrics 29 | GoogleLyrics Whatever might have been 30 | GoogleLyrics All the dreams that ponies share 31 | GoogleLyrics 32 | GoogleLyrics Because of you, Snowfall Frost 33 | GoogleLyrics Now the future is a cold nightmare 34 | GoogleLyricsInformation Title Luna's Future 35 | GoogleLyricsInformation Singer Princess Luna 36 | GoogleLyricsInformation Source 提供元: Musixmatch 37 | GoogleLyricsInformation Songwriters ソングライター: Daniel Luke Ingram 38 | GoogleLyricsInformation Studios Luna's Future 歌詞 © Britteridge Publishing Llc 39 | ``` 40 | 41 | The user then can simply choose to have some kind of information, for example, if he/she only wants lyrics, then just use: 42 | 43 | ``` 44 | $ iris "Luna's Future lyrics" | awk '$1 == "GoogleLyrics" { $1=""; print substr($0,2) }' 45 | I see a cold wind blowing through 46 | I see days neither fun nor free 47 | 48 | I see a future caused by you 49 | I see a path not meant to be 50 | 51 | The future should be filled with magic 52 | Dreams and wishes brought to life 53 | 54 | But the days ahead are dark and tragic 55 | No time for hope when all is strife 56 | 57 | Whatever might have been 58 | All the dreams that ponies share 59 | 60 | Because of you, Snowfall Frost 61 | Now the future is a cold nightmare 62 | ``` 63 | 64 | --- 65 | 66 | # Installation 67 | 68 | The releases binaries can be get up [here](https://github.com/iris-cli/iris/releases). 69 | 70 | If you want, however, build it to test yourself, you can. See [Makefile](/Makefile). 71 | 72 | **Note:** I do not recommend you installing a single binary on your system or building just once the source code, because how information is displayed can often change, and for that often updates are necessary. 73 | 74 | --- 75 | 76 | # Acknowledgements: 77 | 78 | + [tinyexpr](https://github.com/codeplea/tinyexpr/) -- Minimal expression evaluation engine as library. 79 | + [lexbor](https://github.com/lexbor/lexbor) -- Fast and powerful way to parse HTML. 80 | + [cURL](https://curl.se/) -- Minimal way to download a source page as library. 81 | + [tuxi](https://github.com/Bugswriter/tuxi/) -- Inspired by. 82 | + [oi](https://github.com/PureArtistry/oi/) -- As motivation to create a better assistant! 83 | 84 | --- 85 | 86 |

APACHE 2.0 LICENSE

87 | -------------------------------------------------------------------------------- /log.nu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env nu 2 | # 3 | # Module for logging things with a kinda cute way. 4 | # Reference for terminfo (colours, reverse, etc): 5 | # https://www.mankier.com/5/terminfo#Description-Predefined_Capabilities 6 | # https://linux.101hacks.com/ps1-examples/prompt-color-using-tput/ 7 | # 8 | # João Farias © 2022-2024 BeyonadMagic 9 | 10 | const NAME_DEFAULT = "log.nu" 11 | const FORMAT_DEFAULT = "%s" 12 | 13 | # Printf parser. 14 | def fprint [ 15 | ...args: any # Strings/number to print. 16 | --name: string # Name of the program. 17 | --format: string # Format of the program. 18 | --colour: string # Colour of the whole message. 19 | --left: int = 0 # Extra padding. 20 | ] -> null { 21 | 22 | # Reverse video (foreground and background) characters. 23 | let t_reverse = (tput rev) 24 | # Turn off all attributes. 25 | let t_reset = (tput sgr0) 26 | # Bold character. 27 | let t_bold = (tput bold) 28 | 29 | let final_name = '[' + $name + ']' 30 | 31 | # Count how many "padders" we need to add based on newlines. 32 | let pad_chars = ($final_name | str length) + 1 + $left 33 | 34 | # Create the string full of spaces 35 | mut spaces = '' 36 | mut i = 1 37 | while $i <= $pad_chars { 38 | $i = $i + 1 39 | $spaces = $spaces + ' ' 40 | } 41 | 42 | let formats_replacement = (char newline) + $spaces + '$1' 43 | let final_format = "%s " + ($format | str replace --regex --all (char newline) $formats_replacement) + "%s" 44 | 45 | let command = [ 46 | 'printf' 47 | '--' 48 | # Parse separately adding double-quote to all arguments so that printf parse it correctly. 49 | ] | append ([ 50 | $final_format 51 | ($colour + $t_bold + $t_reverse + $final_name + $t_reset + $colour) 52 | ...$args 53 | ($t_reset) 54 | ] | each {|arg| 55 | '"' + $arg + '"' 56 | }) 57 | 58 | nu -c ($command | str join (char space)) 59 | printf '\n' 60 | } 61 | 62 | # Print a crossed item in red (represents task failed). 63 | export def fail [ 64 | ...message: string, # Message to print 65 | --name: string = $NAME_DEFAULT, # Name of the program. 66 | --format: string = $FORMAT_DEFAULT, # Format (printf specification) of the message. 67 | ] -> null { 68 | let t_fg_red = (tput setaf 1) 69 | fprint --left 4 --name $name --format ('[X] ' + $format) --colour $t_fg_red ...$message 70 | } 71 | 72 | # Print a checked item in green (represents task was done successfuly). 73 | export def success [ 74 | ...message: string, # Message to print 75 | --name: string = $NAME_DEFAULT, # Name of the program. 76 | --format: string = $FORMAT_DEFAULT, # Format (printf specification) of the message. 77 | ] -> null { 78 | let t_fg_green = (tput setaf 2) 79 | fprint --left 4 --name $name --format ('[✓] ' + $format) --colour $t_fg_green ...$message 80 | } 81 | 82 | # Print in red (represents information that needs to be paid). 83 | export def error [ 84 | ...message: string, # Message to print 85 | --name: string = $NAME_DEFAULT, # Name of the program. 86 | --format: string = $FORMAT_DEFAULT, # Format (printf specification) of the message. 87 | ] -> null { 88 | let t_fg_red = (tput setaf 1) 89 | fprint --name $name --format $format --colour $t_fg_red ...$message 90 | } 91 | 92 | # Print in yellow (represents information to be paid attention). 93 | export def warning [ 94 | ...message: string, # Message to print 95 | --name: string = $NAME_DEFAULT, # Name of the program. 96 | --format: string = $FORMAT_DEFAULT, # Format (printf specification) of the message. 97 | ] -> null { 98 | let t_fg_yellow = (tput setaf 3) 99 | fprint --name $name --format $format --colour $t_fg_yellow ...$message 100 | } 101 | 102 | # Print in gray (represents extra information). 103 | export def debug [ 104 | ...message: string, # Message to print 105 | --name: string = $NAME_DEFAULT, # Name of the program. 106 | --format: string = $FORMAT_DEFAULT, # Format (printf specification) of the message. 107 | ] -> null { 108 | let t_fg_white = (tput setaf 7) 109 | 110 | fprint --name $name --format $format --colour $t_fg_white ...$message 111 | } 112 | -------------------------------------------------------------------------------- /rwppi-helper.nu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env nu 2 | # 3 | # Helper for building, testing, and checking rwppi. 4 | # 5 | # On Nushell, start with: 6 | # $ use ./build.nu 7 | # 8 | # João Farias © 2022-2024 BeyonadMagic 9 | 10 | const library = './library/' 11 | const name = 'rwppi-helper' 12 | use ./log.nu 13 | 14 | # Build/update all libraries. 15 | export def libraries [ 16 | --lexbor = true # Compile/update lexbor. 17 | ] -> null { 18 | if (test -d $library | complete).exit_code != 0 { 19 | log debug --name $name $'No folder found at "($library)", creating one.' 20 | mkdir $library 21 | } 22 | 23 | let data = [ 24 | { 25 | name: 'lexbor', 26 | object: 'liblexbor_static.a' 27 | git: 'https://github.com/lexbor/lexbor', 28 | submodules: false, 29 | build: { 30 | cmake . -DLEXBOR_BUILD_TESTS=ON -DLEXBOR_BUILD_EXAMPLES=ON 31 | make 32 | make test 33 | }, 34 | } 35 | ] 36 | 37 | $data | each {|item| 38 | if $item.name == 'lexbor' and (not $lexbor) { 39 | log warning --name $name $'Skipping "($item.name)".' 40 | continue 41 | } 42 | log debug --name $name $'Getting the library "($item.name)".' 43 | 44 | let lib_folder = ($library | path join $item.name) 45 | 46 | mut result = test -d $lib_folder | complete 47 | 48 | mut changes = false 49 | 50 | if $result.exit_code != 0 { 51 | 52 | log debug --name $name $'Library folder for "($item.name)" does not exist, creating one.' 53 | mkdir $lib_folder 54 | 55 | # Get the repository. 56 | $result = (git clone --recursive $item.git $lib_folder | complete) 57 | if $result.exit_code != 0 { 58 | log fail --name $name --format "%s:\n%s" $'Failed to clone the repository for ($item.name):' $item.git 59 | log warning --name $name $'Skipping the library ($item.name).' 60 | continue 61 | } else { 62 | log success --name $name --format "%s\n%s" $'Successfully cloned the repository for ($item.name):' $item.git 63 | } 64 | 65 | $changes = true 66 | 67 | cd $lib_folder 68 | } else { 69 | cd $lib_folder 70 | 71 | log debug --name $name $"Updating the remote\(s\) of '($item.name)'." 72 | 73 | # Get the remote repository. 74 | $result = (git remote update | complete) 75 | if $result.exit_code != 0 { 76 | log fail --name $name $"Could not update the remote\(s\) of the '($item.name)'." 77 | log warning --name $name $'Skipping the library "($item.name)".' 78 | continue 79 | } else { 80 | log success --name $name $"Updated the remote\(s\) of the repository of '($item.name)'." 81 | } 82 | 83 | # Return the amount of different commits compared to the remote tree. 84 | $result = (git rev-list HEAD...origin/master --count | complete) 85 | if ($result.stdout | str substring 0..1) != '0' { 86 | log debug --name $name --format "%s\n%s" $'Pulling new ($result.stdout) changes from repository for the library "($item.name)".' $item.git 87 | 88 | $changes = true 89 | 90 | # Get latest update. 91 | $result = (git pull | complete) 92 | if $result.exit_code != 0 { 93 | log fail --name $name --format "%s\n%s" $'Failed to pull from the remote for the library "($item.name)":' $item.git 94 | log warning --name $name $'Skipping the library ($item.name).' 95 | continue 96 | } else { 97 | log success --name $name $'Updated the repository of the library "($item.name)".' 98 | } 99 | } 100 | 101 | if $item.submodules { 102 | git submodule foreach git rev-list HEAD...origin/master --count | complete 103 | 104 | log debug --name $name $'Updating the submodules from the repository for the library "($item.name)".' 105 | 106 | # Update the remote of each submodule. 107 | $result = (git submodule foreach git remote update | complete) 108 | if $result.exit_code != 0 { 109 | print $result.sdout 110 | log fail --name $name $"Could not update the remote\(s\) of the submodule\(s\) for the library '($item.name)', one submodule failed:" 111 | log warning --name $name $'Skipping the library ($item.name).' 112 | continue 113 | } else { 114 | log success --name $name $"Updated the remote\(s\) of the submodule\(s\) of the library '($item.name)'." 115 | } 116 | 117 | # TODO: Try to find if there is one change for one submodule the remote of each submodule. 118 | # $result = (git submodule foreach git remote update | complete) 119 | # if $result.exit_code != 0 { 120 | # print $result.sdout 121 | # log fail --name $name $"Could not update the remote\(s\) of the submodule\(s\) for the library '($item.name)', one submodule failed:" 122 | # log warning --name $name $'Skipping the library ($item.name).' 123 | # continue 124 | # } else { 125 | # log success --name $name $"Updated the remote\(s\) of the submodule\(s\) of the library '($item.name)'." 126 | # } 127 | 128 | # Update recursively all submodules of the repository. 129 | $result = (git submodule update --init --recursive | complete) 130 | if $result.exit_code != 0 { 131 | log fail --name $name $'Could not update the submodules for the library "($item.name)":' 132 | log warning --name $name $'Skipping the library ($item.name).' 133 | continue 134 | } else { 135 | log success --name $name $'Updated the submodules of the library "($item.name)".' 136 | if $result.stdout { 137 | $changes = true 138 | } 139 | } 140 | } 141 | } 142 | 143 | # Building the library. 144 | if not $changes { 145 | log debug --name $name $'No changes for the library "($item.name)".' 146 | continue 147 | } 148 | 149 | log debug --name $name $'Building the library "($item.name)".' 150 | do $item.build 151 | if $env.LAST_EXIT_CODE != 0 { 152 | log fail --name $name $'Could not build the library "($item.name)".' 153 | log warning --name $name $'Skipping the library ($item.name).' 154 | continue 155 | } else { 156 | log success --name $name $'Built the library "($item.name)".' 157 | } 158 | 159 | # Copying the files for the source code to build. 160 | let object_out = ($item.object | prepend '../' | str join) 161 | 162 | log debug --name $name --format "%s\n%s\n%s\n%s" ...[ 163 | $'Copying the necessary files of the library "($item.name)" from:' 164 | ($item.object | path expand) 165 | 'to' 166 | ($object_out | path expand) 167 | ] 168 | 169 | $result = (cp -f $item.object $object_out) 170 | if $result.exit_code != 0 { 171 | log fail --name $name $"Could not place the file\(s\)/folder\(s\) of the library '($item.name)' in the expected place." 172 | log warning --name $name $'Skipping the library ($item.name).' 173 | continue 174 | } else { 175 | log success --name $name $"Put file\(s\)/folder\(s\) the library '($item.name)'." 176 | } 177 | 178 | # Return to the $library folder. 179 | cd - 180 | } 181 | null 182 | } 183 | 184 | # Build source code. 185 | export def build [ 186 | --output : string = './binary/rwppi', # Binary output. 187 | --input : string = './source/main.c++', # Source main input. 188 | --eyes = false, # Print the command of build. 189 | --optimise = false, # Optimise (O3). 190 | --debug = true, # Add debug flags. 191 | ] -> any { 192 | mut args = [ 193 | # Latest standard of C++, however we'll set at C++23 upon module support in GCC. 194 | '-std=c++2b' 195 | ] 196 | 197 | let libs = [ 198 | # Curl: request POST to HTTP address. 199 | '-lcurl' 200 | # PThread: parse multiple things at the same time in different threads. 201 | '-lpthread' 202 | # fmt: for formating the output. 203 | '-lfmt' 204 | # Lexbor: parse HTML we'll fetch. 205 | ($library | path join 'liblexbor_static.a') 206 | ] 207 | 208 | if $optimise { 209 | $args = ($args | append [ 210 | # Optimise in accordance to O3 specification. 211 | '-O3' 212 | ]) 213 | } else { 214 | $args = ($args | append [ 215 | # No unused variables. 216 | '-Wno-unused' 217 | # All warnings to be treated as errors. 218 | '-Wall' 219 | # Extra flgs. 220 | '-Werror' 221 | # Further extra flags. 222 | '-Wextra' 223 | # Stick to the standardad. 224 | '-pedantic' 225 | # Disallow implicit conversion. 226 | '-Wconversion' 227 | # Disallow implicit sign conversion. 228 | '-Wsign-conversion' 229 | # Variable shadowing/repeating names of variables in the same context. 230 | '-Wshadow' 231 | ]) 232 | } 233 | 234 | if $debug { 235 | $args = ($args | append [ 236 | # For debugging with gdb. 237 | '-ggdb' 238 | # To catch UB as must as it can. 239 | '-fsanitize-undefined-trap-on-error' 240 | # To catch memory-leak-specific UB as must as it can. 241 | '-fsanitize-address-use-after-scope' 242 | # Sanitize all addresses and UB by default. 243 | '-fsanitize=address,undefined' 244 | ]) 245 | } 246 | 247 | let command = [ 248 | 'c++' 249 | ...$args 250 | ...$libs 251 | $input 252 | '-o' 253 | $output 254 | ] 255 | 256 | mkdir ($output | path dirname) 257 | 258 | let result = (nu -c ($command | str join (char space)) | complete) 259 | 260 | if ($eyes) or ($result.exit_code != 0) { 261 | print $command 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /source/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021-2022 João F. BeyondMagic 3 | * 4 | *Licensed under the Apache License, Version 2.0 (the "License"); 5 | *you may not use this file except in compliance with the License. 6 | *You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | *Unless required by applicable law or agreed to in writing, software 11 | *distributed under the License is distributed on an "AS IS" BASIS, 12 | *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | *See the License for the specific language governing permissions and 14 | *limitations under the License. 15 | */ 16 | 17 | /* 18 | * Libraries are included in the following files and so is expectable for the language server 19 | * to recognize support for certain standards libraries classes and functions. 20 | */ 21 | 22 | #include "main.hpp" 23 | 24 | /* 25 | * https://github.com/iris-cli/iris 26 | * 27 | * v0.0.1 28 | */ 29 | int main( const int argc, char** argv ) 30 | { 31 | 32 | AITricks tricks; 33 | Util util; 34 | 35 | const std::string language = getenv("LANG") 36 | ? std::string( getenv("LANG") ).substr(0, 5) 37 | : "en_US"; 38 | std::string query; 39 | std::string sources = "0"; 40 | std::string methods = "0"; 41 | bool log = false; 42 | bool load_file = false; 43 | bool save_file = false; 44 | bool unit_two = true; // In case we want to skip local methods. 45 | unsigned int unit = 3; // Default is all without log. 46 | 47 | 48 | /* 49 | * UNIT 0: Parse options and parameters. 50 | * 51 | * Modules: ArgumentParser, Util, AITricks. 52 | */ 53 | { 54 | try { 55 | 56 | InputParser arguments(argc, argv); 57 | 58 | { 59 | if ( arguments.exists( "-h", "--help" ) ) return util.print_help(); 60 | if ( arguments.exists( "-S", "--sources" ) ) return util.print_sources(); 61 | if ( arguments.exists( "-M", "--methods" ) ) return util.print_methods(); 62 | if ( arguments.exists( "-l", "--log" ) ) log = true; 63 | if ( arguments.exists( "-t", "--to" ) ) save_file = true; 64 | if ( arguments.exists( "-L", "--load" ) ) load_file = true; 65 | 66 | if (isatty(0) and isatty(1) and isatty(2) 67 | ) 68 | { 69 | is_atty = true; 70 | } 71 | 72 | if (save_file and load_file) throw "[1] EE: '--to' and '--load' options are exclusive."; 73 | } 74 | 75 | { 76 | 77 | std::string handler_settings; 78 | 79 | handler_settings = arguments.get( "-s", "--source" ); 80 | if ( !handler_settings.empty() ) 81 | { 82 | sources = handler_settings; 83 | handler_settings.clear(); 84 | unit_two = false; 85 | } 86 | handler_settings = arguments.get( "-m", "--method" ); 87 | if ( !handler_settings.empty() ) 88 | { 89 | methods = handler_settings; 90 | handler_settings.clear(); 91 | unit = 4; 92 | } 93 | 94 | handler_settings = arguments.get( "-u", "--unit" ); 95 | if ( !handler_settings.empty() ) 96 | { 97 | unit = stoi( handler_settings ); 98 | handler_settings.clear(); 99 | 100 | if (unit == 1) log = true; 101 | else if ( unit < 1 || unit > 3 ) throw "[1] EE: The unit option must be betweeen 1-3."; 102 | } 103 | 104 | } 105 | 106 | query = arguments.left(); 107 | 108 | if (log) 109 | { 110 | std::cout << "[1] Logging settled defaults... " << '\n'; 111 | std::cout << "[1] Unit set: " << unit << '\n'; 112 | std::cout << "[1] Language set: " << language << '\n'; 113 | std::cout << "[1] Log set: " << log << '\n'; 114 | std::cout << "[1] Sources set: " << sources << '\n'; 115 | std::cout << "[1] Method set: " << methods << '\n'; 116 | std::cout << "[1] Save file: " << save_file << '\n'; 117 | std::cout << "[1] Load file: " << load_file << '\n'; 118 | } 119 | 120 | if ( query.empty() ) throw 0; 121 | 122 | } 123 | 124 | // For out of range unit numbers: 125 | catch ( const char * & a ) 126 | { 127 | std::cerr << a << std::endl; 128 | return 1; 129 | } 130 | 131 | // For empty query. 132 | catch ( const int & a ) 133 | { 134 | std::cerr << tricks.nothing() << std::endl; 135 | return 1; 136 | } 137 | 138 | // For stoi throwing an invalid_argument error. 139 | catch ( const std::invalid_argument & ia ) 140 | { 141 | std::cerr << "[1] EE: You must use the unit option along a number." << std::endl; 142 | return 1; 143 | } 144 | 145 | } 146 | 147 | /* 148 | * In case the user does not define methods to use as listed by "--methods" and defined by "--method". 149 | */ 150 | if (unit != 4) 151 | { 152 | 153 | /* 154 | * UNIT 2: Run the local methods for simple computation tasks such as mathematical ones. 155 | */ 156 | if (unit >= 2 and unit_two) 157 | { 158 | 159 | MethodLocal local(query); 160 | 161 | local.All(); 162 | 163 | util.has_response(); 164 | 165 | } 166 | 167 | /* 168 | * UNIT 3: Run the remote methods, those are the scrapers. 169 | */ 170 | if (unit == 3) 171 | { 172 | 173 | URLDownloader downloader(language, query); 174 | 175 | std::vector sources_list = util.list_to_vector(sources); 176 | 177 | for (int name : sources_list) { 178 | 179 | std::string page; 180 | 181 | if (load_file) { 182 | 183 | std::ifstream t(query); 184 | std::stringstream buffer; 185 | buffer << t.rdbuf(); 186 | page = buffer.str(); 187 | 188 | } else { 189 | 190 | page = downloader.download(name); 191 | 192 | } 193 | 194 | if (save_file) { 195 | 196 | if (page.empty()) exit(1); 197 | 198 | std::cout << page << std::endl; 199 | 200 | exit(0); 201 | 202 | } 203 | 204 | MethodRemote remote(page); 205 | 206 | switch (name) { 207 | 208 | // Google. 209 | case 0: remote.Google_All(); break; 210 | 211 | } 212 | 213 | } 214 | 215 | util.has_response(); 216 | exit(1); 217 | 218 | } 219 | } 220 | 221 | /* 222 | * UNIT 4: 223 | * 224 | * The only way to call this unit is when define methods. 225 | * 226 | */ 227 | else { 228 | 229 | std::cerr << "[4] This feature is not built yet. The logic behind it must be simplified." << std::endl; 230 | return 1; 231 | 232 | //if (log) std::cout << "[4] Special unit." << std::endl; 233 | 234 | // for (;;) { 235 | 236 | // switch (method) 237 | // { 238 | // case 1: 239 | // Local local; 240 | // break; 241 | 242 | // case 2: 243 | // Remote remote; 244 | 245 | // break; 246 | 247 | // default: 248 | // std::cerr << "[4] EE: The method '" << method << "' is unknown." << std::endl; 249 | // return 1; 250 | // } 251 | // } 252 | 253 | } 254 | 255 | return 1; 256 | 257 | }; 258 | -------------------------------------------------------------------------------- /source/main.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021-2022 João F. BeyondMagic 3 | * 4 | *Licensed under the Apache License, Version 2.0 (the "License"); 5 | *you may not use this file except in compliance with the License. 6 | *You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | *Unless required by applicable law or agreed to in writing, software 11 | *distributed under the License is distributed on an "AS IS" BASIS, 12 | *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | *See the License for the specific language governing permissions and 14 | *limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "modules/ArgumentsParser/ArgumentsParser.hpp" 28 | #include "modules/AssistantFun/AssistantFun.hpp" 29 | #include "modules/URLDownloader/URLDownloader.hpp" 30 | #include "modules/Methods/Methods.hpp" 31 | 32 | struct Util { 33 | 34 | /* 35 | * Take a string listed delimited by "," and transforms into a vector string. 36 | * For example, "One,Two,Three", will become "One" "Two" "Three". 37 | */ 38 | std::vector 39 | list_to_vector ( std::string & list ) 40 | { 41 | 42 | std::vector guarder = {}; 43 | 44 | // I. The sources' string-option are split by ",". We may modify this latter for better syntax, but for now, it does what it is supposed to do. 45 | const char delimiter[2] = ","; 46 | 47 | // II. Loop through each source to download 48 | size_t pos = 0; while ( (pos = list.find(delimiter)) != std::string::npos ) { 49 | 50 | // A. Current source on the loop 51 | const int source = stoi(list.substr(0, pos)); 52 | list.erase(0, pos + 1); 53 | 54 | // B. Run download to get our little page :) 55 | guarder.push_back(source); 56 | 57 | }; guarder.push_back(stoi(list)); 58 | 59 | return guarder; 60 | 61 | } 62 | 63 | /* 64 | * Print the help list of Iris; should always be updated in case of major changes. 65 | * Will always return 0. 66 | */ 67 | const unsigned int 68 | print_help( void ) 69 | { 70 | 71 | std::cout << R"(iris - Version: 0.0.1 72 | 73 | DESCRIPTION: 74 | iris is a CLI-assistant that receives any type of input and tries to find an answer for it. 75 | It strives to have a simple codebase while being extremely performant and lightweight. 76 | 77 | USAGE: 78 | iris [--help] [--unit 1-3] [--source sources] [--sources] [--methods] 79 | 80 | --help | -h 81 | Print out this and immediately exit the program. 82 | 83 | --unit 3 | -u 3 84 | Goes to only a certain unit of the program, those are available: 85 | 86 | 1 = Reading settings and query. Sets logging true. 87 | 2 = Evaluate local methods. 88 | 3 = Evaluate remote methods. 89 | 90 | --to | -t 91 | Send HTML to output if found. 92 | 93 | --load | -L 94 | Load file to scrape without downloading. 95 | 96 | --log | -l 97 | Activates logging for debugging and information for developers. 98 | 99 | --source source1,source2,... | -s source1,source2,... 100 | Read respectively each source and tries to find and answer on it. If it doesn't recognize by 101 | name, it will stop and warn that the user did not make a valid input. 102 | 103 | The default one is set to Google as it is the best offering answers with their AI. 104 | 105 | --method method1,method2,... | -m method1,method2,... 106 | Read respectively each method and tries to find an answer for it. If it doesn't recognize by 107 | name, it will stop and warn the tuser did not make a valid input. 108 | 109 | --sources | -S 110 | List the sources that Iris currently can read from. 111 | 112 | --methods | -M 113 | List the methods, both locally and remotely, that will be used for the sources. 114 | 115 | MAINTAINER: (s): 116 | 2021-2022 João F. BeyondMagic 117 | 118 | SOURCE-CODE: 119 | https://github.com/iris-cli/iris 120 | 121 | ISSUES: 122 | https://github.com/iris-cli/iris/issues 123 | 124 | CONTRIBUTE: 125 | https://github.com/iris-cli/iris/blob/master/CONTRIBUTING.md 126 | 127 | LICENSE: 128 | APACHE 2.0)" << std::endl; 129 | 130 | return 0; 131 | 132 | } 133 | 134 | /* 135 | * Print the list of sources that can be used by Iris currently. 136 | * Will always return 0. 137 | */ 138 | const unsigned int 139 | print_sources ( void ) 140 | { 141 | 142 | std::cout << R"(Sources can be any of the following: 143 | 0 Google -> https://www.google.com/ -> Default source. 144 | 1 LetrasMusBr -> https://www.letras.mus.br/ 145 | 2 Google Images -> https://images.google.com/)" << std::endl; 146 | 147 | return 0; 148 | } 149 | 150 | /* 151 | * Print the list of methods that can be used by Iris currently. 152 | * Will always return 0. 153 | */ 154 | const unsigned int 155 | print_methods ( void ) 156 | { 157 | 158 | std::cout << 159 | R"(Local will always run unless source is specified. 160 | 161 | Local: 162 | 1 -> Math -> 4+6/3+27+sqrt(25) 163 | 164 | All remote methods will run by source specification, emphasis on all 165 | since it will scrape in a asynchronous manner for performance. 166 | 167 | Google: https://www.google.com/ 168 | 2 -> Math -> log_2(26) + 7/3 169 | 3 -> Definition -> meaning of love 170 | 4 -> Lyrics -> K.Flay - Maybe There's A Way lyrics 171 | 5 -> LyricsInformation -> K.Flay - Maybe There's A Way lyrics 172 | 6 -> Translation -> palavra fazer em português para japonês 173 | 7 -> Weather -> current weather 174 | 8 -> Tracklist -> noisia outer edges tracklist 175 | 9 -> InformationList -> social network cast 176 | 10 -> HeaderList -> Need for Speed Heat cars list 177 | 11 -> Chemistry -> density of hydrogen 178 | 12 -> Pronunciation -> pronounce linux 179 | 13 -> UnitConversion -> 1m into 1cm 180 | 14 -> CurrencyConversion -> 1 USD in rupee 181 | 15 -> InformationHeader -> christmas day 182 | 16 -> InformationWindow -> who is garfield 183 | 17 -> QuotesList -> mahatma gandhi quotes 184 | 18 -> TableSport -> Chelsea next game 185 | 19 -> InformationTable -> the office 186 | 20 -> GoogleCorrection -> lirics in the end 187 | 188 | LetrasMusBr: https://www.letras.mus.br/ 189 | 21 -> Lyrics -> Grupo Revelação Tá Escrito)" 190 | << std::endl; 191 | 192 | return 0; 193 | } 194 | 195 | /* 196 | * Verify if there's an answer and exit. 197 | */ 198 | void 199 | has_response ( void ) 200 | { 201 | 202 | if (response_found) { 203 | exit(0); 204 | } 205 | 206 | } 207 | 208 | }; 209 | -------------------------------------------------------------------------------- /source/modules/ArgumentsParser/ArgumentsParser.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021-2022 João F. BeyondMagic 3 | * 4 | *Licensed under the Apache License, Version 2.0 (the "License"); 5 | *you may not use this file except in compliance with the License. 6 | *You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | *Unless required by applicable law or agreed to in writing, software 11 | *distributed under the License is distributed on an "AS IS" BASIS, 12 | *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | *See the License for the specific language governing permissions and 14 | *limitations under the License. 15 | */ 16 | 17 | #include "ArgumentsParser.hpp" 18 | 19 | InputParser::InputParser (const int argc, char ** argv ) 20 | { 21 | // Loop through each argument and add it to the tokens private variable. 22 | for ( int i = 1; i < argc; ++i ) 23 | { 24 | this->tokens.push_back( std::string(argv[i]) ); 25 | } 26 | } 27 | 28 | const std::string 29 | InputParser::get( const std::string & short_name, const std::string & long_name ) 30 | { 31 | std::vector ::const_iterator itr; 32 | 33 | GET_STRING(short_name) 34 | { 35 | const std::string parameter = *--itr; 36 | REMOVE_STRING(parameter); 37 | return parameter; 38 | } 39 | else 40 | { 41 | GET_STRING(long_name) 42 | { 43 | const std::string parameter = *--itr; 44 | REMOVE_STRING(parameter); 45 | return parameter; 46 | } 47 | } 48 | 49 | // Return nothing if it doesn't find. 50 | static const std::string empty_string(""); 51 | return empty_string; 52 | } 53 | 54 | const std::string 55 | InputParser::left ( void ) const { 56 | 57 | std::string s; 58 | 59 | // Add all the arguments from the vector into a string. 60 | for (std::string word : tokens) 61 | { 62 | s += " " + word; 63 | } 64 | 65 | // Find trailing characters from the left side and remove them. 66 | size_t start = s.find_first_not_of(" \n\r\t\f\v"); 67 | s = (start == std::string::npos) ? "" : s.substr(start); 68 | 69 | // Find trailing characters from the right side and remove them. 70 | size_t end = s.find_last_not_of(" \n\r\t\f\v"); 71 | s = (end == std::string::npos) ? "" : s.substr(0, end + 1); 72 | 73 | return s; 74 | } 75 | 76 | bool 77 | InputParser::exists( const std::string & short_name, const std::string & long_name ) 78 | { 79 | 80 | // Loop through the options and see if exists either short_name or long_name option. 81 | if ( EXIST_STRING(short_name) ) 82 | { 83 | REMOVE_STRING(short_name); 84 | return true; 85 | } 86 | else if ( EXIST_STRING(long_name) ) 87 | { 88 | REMOVE_STRING(long_name); 89 | return true; 90 | } 91 | 92 | return false; 93 | 94 | } 95 | 96 | #undef REMOVE_STRING 97 | #undef GET_STRING 98 | #undef EXIST_STRING 99 | -------------------------------------------------------------------------------- /source/modules/ArgumentsParser/ArgumentsParser.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021-2022 João F. BeyondMagic 3 | * 4 | *Licensed under the Apache License, Version 2.0 (the "License"); 5 | *you may not use this file except in compliance with the License. 6 | *You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | *Unless required by applicable law or agreed to in writing, software 11 | *distributed under the License is distributed on an "AS IS" BASIS, 12 | *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | *See the License for the specific language governing permissions and 14 | *limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #define EXIST_STRING(A) ( find( this->tokens.begin(), this->tokens.end(), A ) != this->tokens.end() ) 24 | 25 | #define GET_STRING(A) \ 26 | itr = find( this -> tokens.begin(), this -> tokens.end(), A ); \ 27 | remove(this->tokens.begin(), this->tokens.end(), A); \ 28 | if ( itr != this->tokens.end() and ++itr != this->tokens.end() ) 29 | 30 | #define REMOVE_STRING(A) (this->tokens.erase(std::remove(this->tokens.begin(), this->tokens.end(), A), this->tokens.end())) 31 | 32 | /* 33 | * This is basically our library-free code to parse our arguments, it is obviously not feature full for the same purporse 34 | * we created this. Basically, we use it so that we don't have to compile a 1MiB header file to just parse our options. 35 | * Please, future self or future maintainers, do only replace this if we have a minimal library that can actually do this better. 36 | * 37 | * TODO: Find a smart way to parse multiple bool inline commands such as '-abcdef'. 38 | * TODO: Only use and remove the first match. 39 | */ 40 | class InputParser 41 | { 42 | private: 43 | std::vector tokens; 44 | 45 | public: 46 | 47 | InputParser (const int argc, char ** argv ); 48 | 49 | /* This will return the value after the option, so it verifies two strings. 50 | * It doesn't matter how you call it, but for both strings it will verify and take the next argument. 51 | * It will actually remove from the leftovers both the strings even if it just took the first one. 52 | * Original author is @iain, though it is modified for our needs. 53 | */ 54 | const std::string 55 | get( const std::string & short_name, const std::string & long_name ); 56 | 57 | /* 58 | * This will return the rest of our arguments after all the calls are made, such as this->get or this->exist. 59 | * Use this will great knowledge about what you're doing. 60 | */ 61 | const std::string 62 | left ( void ) const; 63 | 64 | /* To return true if an option was passed to the arguments. It will remove from this->tokens the strings it finds. 65 | * short_name : -a 66 | * long_name : --animal 67 | */ 68 | bool 69 | exists( const std::string & short_name, const std::string & long_name ); 70 | 71 | }; 72 | -------------------------------------------------------------------------------- /source/modules/AssistantFun/AssistantFun.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021-2022 João F. BeyondMagic 3 | * 4 | *Licensed under the Apache License, Version 2.0 (the "License"); 5 | *you may not use this file except in compliance with the License. 6 | *You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | *Unless required by applicable law or agreed to in writing, software 11 | *distributed under the License is distributed on an "AS IS" BASIS, 12 | *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | *See the License for the specific language governing permissions and 14 | *limitations under the License. 15 | */ 16 | 17 | #include 18 | 19 | #include "AssistantFun.hpp" 20 | 21 | const std::string 22 | AITricks::math () 23 | { 24 | return "No, that's not how you put a mathematical expression onto Internet!"; 25 | } 26 | 27 | const std::string 28 | AITricks::nothing () 29 | { 30 | return "Haha, your query is so small! I can't even see it!"; 31 | // What do you want me to do? Create information from out of thin electrons for you?" 32 | } 33 | 34 | // No wheater -> I don't know if you need a jacket, look out of the window you prick. 35 | -------------------------------------------------------------------------------- /source/modules/AssistantFun/AssistantFun.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021-2022 João F. BeyondMagic 3 | * 4 | *Licensed under the Apache License, Version 2.0 (the "License"); 5 | *you may not use this file except in compliance with the License. 6 | *You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | *Unless required by applicable law or agreed to in writing, software 11 | *distributed under the License is distributed on an "AS IS" BASIS, 12 | *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | *See the License for the specific language governing permissions and 14 | *limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | /* 20 | * This is a simple trivial non-performant affecter made for fun as Iris developmend can become quite tedious, you can always stop and just think of fun things you would like to see in certain exceptions or operands Iris cannot operate. 21 | */ 22 | struct AITricks { 23 | 24 | // public: 25 | // TODO: Random item from vector. 26 | // const unsigned int random ( & char */**/ ) 27 | 28 | // In case Iris tried to perform a mathematical expression locally and remotely and did not found any results for it. 29 | const std::string 30 | math (); 31 | 32 | // In case Iris received nothing. 33 | const std::string 34 | nothing (); 35 | 36 | }; 37 | -------------------------------------------------------------------------------- /source/modules/JSONParser/JSONParser.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeyondMagic/iris/b8e5beccc2098cd6362ee0e2a94437bfce88f1f0/source/modules/JSONParser/JSONParser.hpp -------------------------------------------------------------------------------- /source/modules/Methods/Google.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021-2022 João F. BeyondMagic 3 | * 4 | *Licensed under the Apache License, Version 2.0 (the "License"); 5 | *you may not use this file except in compliance with the License. 6 | *You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | *Unless required by applicable law or agreed to in writing, software 11 | *distributed under the License is distributed on an "AS IS" BASIS, 12 | *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | *See the License for the specific language governing permissions and 14 | *limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "Methods.hpp" 20 | 21 | /* 22 | * Walkers. 23 | */ 24 | 25 | WALKER_FUNCTION(GoogleLyricsWalker) 26 | { 27 | lexbor_str_t * str; 28 | context_t *my = (context_t *) ctx; 29 | 30 | switch (lxb_dom_node_tag_id(node)) { 31 | case LXB_TAG__TEXT: 32 | str = &lxb_dom_interface_text(node)->char_data.data; 33 | my->response = my->response + my->method + ' ' + (char *) str->data; 34 | break; 35 | 36 | case LXB_TAG_BR: 37 | my->response = my->response + '\n'; 38 | break; 39 | 40 | case LXB_TAG__EM_COMMENT: 41 | case LXB_TAG_SCRIPT: 42 | case LXB_TAG_STYLE: 43 | /* Skip node and his children's. */ 44 | return LEXBOR_ACTION_NEXT; 45 | 46 | default: 47 | break; 48 | } 49 | 50 | return LEXBOR_ACTION_OK; 51 | } 52 | 53 | /* 54 | * Printers. 55 | */ 56 | 57 | PRINT_FUNCTION(GoogleLyrics) 58 | { 59 | context_t *my = (context_t *) ctx; 60 | 61 | // A. Don't add a newline before any text. 62 | if (my->i != 0) my->response = my->response + my->method + '\n'; 63 | 64 | lxb_dom_node_simple_walk(node, __GoogleLyricsWalker, my); 65 | 66 | // B. To separate the paragraphs of the lyrics. 67 | my->response = my->response + '\n'; 68 | 69 | my->i++; 70 | 71 | return LXB_STATUS_OK; 72 | } 73 | 74 | PRINT_FUNCTION(GoogleLyricsInformation) 75 | { 76 | context_t *my = (context_t *) ctx; 77 | 78 | // 1. The order of information given by Google. 79 | { 80 | 81 | switch (my->i) { 82 | case 0: my->type = "Title"; break; 83 | case 1: my->type = "Singer"; break; 84 | case 2: my->type = "Source"; break; 85 | case 3: my->type = "Songwriters"; break; 86 | case 4: my->type = "Studios"; break; 87 | } 88 | 89 | my->type = colour_type(my->type); 90 | 91 | } 92 | 93 | RETURN_DEFAULT_WALKER(); 94 | } 95 | 96 | PRINT_FUNCTION(GoogleWeather) 97 | { 98 | context_t *my = (context_t *) ctx; 99 | 100 | // 1. The order of information given by Google. 101 | { 102 | 103 | const lxb_char_t name[] = "src"; 104 | const lxb_char_t * value; 105 | std::string value_handler; 106 | size_t value_len; 107 | 108 | switch (my->i) { 109 | case 0: my->type = "Celsius"; break; 110 | case 1: my->type = "Fahrenheit"; break; 111 | case 2: my->type = "Location"; break; 112 | case 3: my->type = "DayTime"; break; 113 | case 4: my->type = "Climate"; break; 114 | case 5: my->type = "Rain"; break; 115 | case 6: my->type = "Humidity"; break; 116 | case 7: my->type = "WindKM"; break; 117 | case 8: my->type = "WindMPH"; break; 118 | case 9: my->type = "Image"; 119 | value = lxb_dom_element_get_attribute(lxb_dom_interface_element(node), name, 3, &value_len); 120 | value_handler = std::string( (char * ) value ); 121 | value_handler.erase(0,2); 122 | break; 123 | } 124 | 125 | my->type = colour_type(my->type); 126 | 127 | if (!value_handler.empty()) 128 | my->response = my->response + my->method + ' ' + my->type + ' ' + value_handler + '\n'; 129 | } 130 | 131 | RETURN_DEFAULT_WALKER(); 132 | } 133 | 134 | PRINT_FUNCTION(GoogleTranslation) 135 | { 136 | context_t *my = (context_t *) ctx; 137 | 138 | // 1. The order of information given by Google. 139 | { 140 | switch (my->i) { 141 | case 0: my->type = "FromLanguage"; break; 142 | case 1: my->type = "ToLanguage"; break; 143 | case 2: my->type = "FromWord"; break; 144 | case 3: my->type = "ToWord"; break; 145 | case 4: my->type = "FromPronunciation"; break; 146 | case 5: my->type = "ToPronunciation"; break; 147 | } 148 | 149 | my->type = colour_type(my->type); 150 | } 151 | 152 | RETURN_DEFAULT_WALKER(); 153 | } 154 | 155 | PRINT_FUNCTION(GoogleUnitConversion) 156 | { 157 | context_t *my = (context_t *) ctx; 158 | 159 | // 2. The order of information given by Google. 160 | { 161 | const lxb_char_t name[] = "value"; 162 | const lxb_char_t * value; 163 | std::string value_handler; 164 | size_t value_len; 165 | 166 | switch (my->i) { 167 | case 0: 168 | my->type = "Header"; 169 | break; 170 | case 1: 171 | my->type = "FromType"; 172 | break; 173 | case 2: 174 | my->type = "ToType"; 175 | break; 176 | case 3: 177 | value = lxb_dom_element_get_attribute(lxb_dom_interface_element(node), name, 5, &value_len); 178 | my->type = "FromValue"; 179 | value_handler = std::string( (char * ) value ); 180 | break; 181 | case 4: 182 | value = lxb_dom_element_get_attribute(lxb_dom_interface_element(node), name, 5, &value_len); 183 | my->type = "ToValue"; 184 | value_handler = std::string( (char * ) value ); 185 | break; 186 | case 5: 187 | my->type = "Formula"; 188 | break; 189 | } 190 | 191 | my->type = colour_type(my->type); 192 | 193 | if (!value_handler.empty()) 194 | my->response = my->response + my->method + ' ' + my->type + ' ' + value_handler + '\n'; 195 | 196 | } 197 | 198 | RETURN_DEFAULT_WALKER(); 199 | } 200 | 201 | /* 202 | * Selectors and finders. 203 | */ 204 | 205 | void MethodRemote::Google_LyricsInfo() 206 | { 207 | 208 | // A. Title old version & new. 209 | // B. Singer old version & new. 210 | // C. Source old version & new. 211 | // D. Songwriters & Studios old version & new. 212 | // E. Album image showing. 213 | extract_info( 214 | ".kp-hc h2[data-attrid=\"title\"]," 215 | "div[data-attrid=\"title\"] > span[role=\"heading\"]," 216 | ".kp-hc a, div.wx62f[data-attrid=\"subtitle\"]," 217 | ".Oh5wg .j04ED," 218 | ".Z1hOCe > div[data-lyricid] > div:not(:first-child)," 219 | ".xpdxpnd > .auw0zb," 220 | "#tsuid21", 221 | "GoogleLyricsInformation", 222 | *__GoogleLyricsInformation); 223 | 224 | } 225 | 226 | void MethodRemote::Google_Math() 227 | { 228 | 229 | // A. Result of expression shown. 230 | extract_info( 231 | ".vUGUtc[jsname=\"ubtiRe\"], #cwos", 232 | "GoogleMath", 233 | *__one_line); 234 | 235 | } 236 | 237 | void MethodRemote::Google_Lyrics() 238 | { 239 | 240 | // I. New version of lyrics on many regions. 241 | extract_info( 242 | "div[data-lyricid] div[jsname=\"WbKHeb\"] > *", 243 | "GoogleLyrics", 244 | *__GoogleLyrics); 245 | 246 | } 247 | 248 | void MethodRemote::Google_Translation() 249 | { 250 | 251 | // 1. From language. 252 | // 2. To language. 253 | // 3. From word. 254 | // 4. To word. 255 | // 5. From pronunciation. 256 | // 6. To pronunciation. 257 | extract_info( 258 | ".source-language," 259 | ".target-language," 260 | "#tw-source-text-ta," 261 | "#tw-target-text," 262 | "#tw-source-rmn," 263 | "#tw-target-rmn", 264 | "GoogleTranslation", 265 | *__GoogleTranslation); 266 | 267 | } 268 | 269 | void MethodRemote::Google_UnitConversion() 270 | { 271 | 272 | // 1. Header. 273 | // 2. From type. 274 | // 3. To type. 275 | // 4. From value. 276 | // 5. To value. 277 | // 6. Formula. 278 | extract_info( 279 | ".rYVYn.LNn04b option[selected]," 280 | "#ssSucf option[selected]," 281 | "#NotFQb option[selected]," 282 | "#HG5Seb input," 283 | "#NotFQb input," 284 | ".bjhkR", 285 | "GoogleUnitConversion", 286 | *__GoogleUnitConversion); 287 | 288 | } 289 | 290 | void MethodRemote::Google_InformationHeader() 291 | { 292 | 293 | extract_info( 294 | ".g div[role=\"heading\"] > div > div", 295 | "GoogleInformationHeader", 296 | *__one_line); 297 | 298 | } 299 | 300 | void MethodRemote::Google_Correction() 301 | { 302 | 303 | extract_info( 304 | "#fprs > *:nth-child(-n+2)", 305 | "GoogleCorrection", 306 | *__one_line); 307 | 308 | } 309 | 310 | void MethodRemote::Google_InformationTable() 311 | { 312 | 313 | // A. One-liners of the table. 314 | extract_info( 315 | ".wp-ms.mnr-c div[data-attrid][style=\"clear:none\"]", 316 | "GoogleInformationTable", 317 | *__one_line); 318 | 319 | } 320 | 321 | void MethodRemote::Google_Weather() 322 | { 323 | 324 | // 1. Celsius. 325 | // 2. Fahrenheit. 326 | // 3. Location. 327 | // 4. Date. 328 | // 5. Climate. 329 | // 6. Rain, Humidyt. 330 | // 7. Wind (KM). 331 | // 8. Wind (MPH). 332 | // 9. Image. 333 | 334 | // A. One-liners of the table. 335 | extract_info( 336 | "#wob_wc #wob_tm.wob_t," 337 | "#wob_wc #wob_ttm.wob_t," 338 | "#wob_wc #wob_loc," 339 | "#wob_wc #wob_dts," 340 | "#wob_wc #wob_dc," 341 | "#wob_wc .wtsRwe > div:not(:last-child)," 342 | "#wob_wc .wtsRwe #wob_ws," 343 | "#wob_wc .wtsRwe #wob_tws," 344 | "#wob_wc #wob_tci", 345 | "GoogleWeather", 346 | *__GoogleWeather); 347 | 348 | } 349 | 350 | void MethodRemote::Google_All() 351 | { 352 | 353 | // A. Local methods call-in. 354 | auto math_handler = std::async(&MethodRemote::Google_Math, this); 355 | auto lyrics_handler = std::async(&MethodRemote::Google_Lyrics, this); 356 | auto lyricsinfo_handler = std::async(&MethodRemote::Google_LyricsInfo, this); 357 | auto correction_handler = std::async(&MethodRemote::Google_Correction, this); 358 | auto translation_handler = std::async(&MethodRemote::Google_Translation, this); 359 | auto unitconversion_handler = std::async(&MethodRemote::Google_UnitConversion, this); 360 | auto informationheader_handler = std::async(&MethodRemote::Google_InformationHeader, this); 361 | auto informationtable_handler = std::async(&MethodRemote::Google_InformationTable, this); 362 | auto weather_handler = std::async(&MethodRemote::Google_Weather, this); 363 | 364 | // B. Local methods call-out. 365 | math_handler.get(); 366 | lyrics_handler.get(); 367 | lyricsinfo_handler.get(); 368 | correction_handler.get(); 369 | translation_handler.get(); 370 | unitconversion_handler.get(); 371 | informationheader_handler.get(); 372 | informationtable_handler.get(); 373 | weather_handler.get(); 374 | 375 | } 376 | -------------------------------------------------------------------------------- /source/modules/Methods/Local.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021-2022 João F. BeyondMagic 3 | * 4 | *Licensed under the Apache License, Version 2.0 (the "License"); 5 | *you may not use this file except in compliance with the License. 6 | *You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | *Unless required by applicable law or agreed to in writing, software 11 | *distributed under the License is distributed on an "AS IS" BASIS, 12 | *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | *See the License for the specific language governing permissions and 14 | *limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "Methods.hpp" 20 | 21 | MethodLocal::MethodLocal( const std::string & data ) 22 | { 23 | 24 | this->expression = data; 25 | 26 | } 27 | 28 | void 29 | MethodLocal::Math() 30 | { 31 | 32 | // 1. Interpret the expression with the minimal library "tinyexpr". 33 | const double answer = te_interp(this->expression.c_str(), 0); 34 | 35 | // 2. If it's not a number (there's no valid answer for the expression). 36 | if ( !std::isnan(answer) ) { 37 | 38 | std::string name = "LocalMath"; 39 | 40 | std::cout << colour_method(name) << ' ' << answer << '\n'; 41 | 42 | if (!response_found) response_found = true; 43 | 44 | } 45 | 46 | } 47 | 48 | void 49 | MethodLocal::All() 50 | { 51 | 52 | // 1. Local methods call-in to start the thread. 53 | auto math_handler = std::async(&MethodLocal::Math, this); 54 | 55 | // 2. Local methods call-out to finish the thread. 56 | math_handler.get(); 57 | 58 | } 59 | -------------------------------------------------------------------------------- /source/modules/Methods/Methods.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021-2022 João F. BeyondMagic 3 | * 4 | *Licensed under the Apache License, Version 2.0 (the "License"); 5 | *you may not use this file except in compliance with the License. 6 | *You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | *Unless required by applicable law or agreed to in writing, software 11 | *distributed under the License is distributed on an "AS IS" BASIS, 12 | *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | *See the License for the specific language governing permissions and 14 | *limitations under the License. 15 | * 16 | */ 17 | 18 | #include "Methods.hpp" 19 | #include "Local.hpp" 20 | #include "Google.hpp" 21 | 22 | bool response_found = false; 23 | bool is_atty = false; 24 | 25 | // Format one single line of an element. 26 | PRINT_FUNCTION(one_line) 27 | { 28 | context_t *my = (context_t *) ctx; 29 | 30 | // 1. Save the text content of the element. 31 | const lxb_char_t * data = lxb_dom_node_text_content(node, nullptr); 32 | 33 | // 2. Print the text data of the element 34 | my->response = my->response + my->method + ' ' + (char *) data + '\n'; 35 | 36 | return LXB_STATUS_OK; 37 | }; 38 | 39 | // Format multiple lines of a single element. 40 | PRINT_FUNCTION(multi_lines) 41 | { 42 | context_t *my = (context_t *) ctx; 43 | 44 | // 1. Save the text content of the element. 45 | const lxb_char_t * data = lxb_dom_node_text_content(node, nullptr); 46 | 47 | std::string page = (const char *) data; 48 | 49 | if (!page.empty()) { 50 | 51 | page = std::regex_replace(page, std::regex("\n"), '\n' + std::string(my->method) + ' '); 52 | my->response = my->response + my->method + ' ' + page + '\n'; 53 | 54 | } 55 | 56 | return LXB_STATUS_OK; 57 | } 58 | 59 | /* 60 | * Remote initializers of the method to gather the HTML content. 61 | * 62 | * It is using the blazing fast library called lexbor (which is in current development). 63 | */ 64 | 65 | MethodRemote::MethodRemote( const std::string & page ) 66 | { 67 | 68 | lxb_status_t status; 69 | 70 | lexbor_document = lxb_html_document_create(); 71 | if (lexbor_document == NULL ) FAILURE("[Methods] Failed to create HTML Document"); 72 | 73 | status = lxb_html_document_parse(lexbor_document, (lxb_char_t *) page.c_str(), page.length() - 1); 74 | if (status != LXB_STATUS_OK ) FAILURE("[Methods] Failed to parse HTML"); 75 | 76 | // 1. Create CSS parser. 77 | lexbor_parser = lxb_css_parser_create(); 78 | status = lxb_css_parser_init(lexbor_parser, NULL, NULL); 79 | if (status != LXB_STATUS_OK ) FAILURE("[Methods] Failed to create CSS parser."); 80 | 81 | // 2. Selectors. 82 | lexbor_selectors = lxb_selectors_create(); 83 | status = lxb_selectors_init(lexbor_selectors); 84 | if (status != LXB_STATUS_OK ) FAILURE("[Methods] "); 85 | 86 | // 3. Find DOM/HTML nodes by selectors. 87 | lexbor_body = lxb_dom_interface_node(lxb_html_document_body_element(lexbor_document)); 88 | if (lexbor_body == NULL ) FAILURE("[Methods] Failed to create BODY interface."); 89 | 90 | } 91 | 92 | MethodRemote::~MethodRemote() 93 | { 94 | 95 | // 1. Destroy Selectors object. 96 | (void) lxb_selectors_destroy(lexbor_selectors, true); 97 | 98 | // 2. Destroy resources for CSS Parser. 99 | (void) lxb_css_parser_destroy(lexbor_parser, true); 100 | 101 | // 3. Destroy HTML Document. 102 | lxb_html_document_destroy(lexbor_document); 103 | 104 | } 105 | 106 | std::string & 107 | colour_method( std::string & method ) { 108 | 109 | if (is_atty) 110 | method = "\033[98;3m" + method + "\033[0m"; 111 | 112 | return method; 113 | 114 | } 115 | 116 | std::string & 117 | colour_type( std::string & type ) { 118 | 119 | if (is_atty) 120 | type = "\033[90;3m" + type + "\033[0m"; 121 | 122 | return type; 123 | 124 | } 125 | 126 | void MethodRemote::extract_info ( std::string target, 127 | std::string id, 128 | lxb_status_t (*in)(lxb_dom_node_t *, lxb_css_selector_specificity_t *, void *)) 129 | { 130 | 131 | // 1. To define the main (most-left forward) ID of the scraper. 132 | context_t data; 133 | data.method = id; 134 | data.i = 0; 135 | data.method = colour_method(data.method); 136 | 137 | // 2. To create a copy of the current parser already defined by the main selector. 138 | lxb_css_parser_t *copy_parser = lxb_css_parser_create(); 139 | lxb_css_parser_init(copy_parser, NULL, NULL); 140 | 141 | // 3. 142 | lxb_selectors_t *copy_selectors = lxb_selectors_create(); 143 | lxb_selectors_init(copy_selectors); 144 | 145 | // 4. Most memory rampage will occur here since all HTML information of the document will be guarded here. 146 | // For Google, this is a major problem since they pass-in JSON decrypted information for the client side to handle. 147 | lxb_dom_node_t *copy_body = lxb_dom_interface_node(lxb_html_document_body_element(lexbor_document)); 148 | 149 | // 5. 150 | lxb_css_selector_list_t *list = lxb_css_selectors_parse(copy_parser, (const lxb_char_t *) target.c_str(), target.length()); 151 | lxb_status_t status = lxb_selectors_find(copy_selectors, copy_body, list, in, &data); 152 | 153 | // 6. Clear off all of the selectors usage. 154 | lxb_css_selector_list_destroy_memory(list); 155 | 156 | // 7. 157 | if (!data.response.empty()) { 158 | 159 | // A. Just print out the result once. 160 | std::cout << data.response; 161 | 162 | // B. To avoid double definition of the status of responses. 163 | if (!response_found) response_found = true; 164 | } 165 | 166 | } 167 | -------------------------------------------------------------------------------- /source/modules/Methods/Methods.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021-2022 João F. BeyondMagic 3 | * 4 | *Licensed under the Apache License, Version 2.0 (the "License"); 5 | *you may not use this file except in compliance with the License. 6 | *You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | *Unless required by applicable law or agreed to in writing, software 11 | *distributed under the License is distributed on an "AS IS" BASIS, 12 | *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | *See the License for the specific language governing permissions and 14 | *limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "tinyexpr/main.h" 28 | #include "lexbor/css/css.h" 29 | #include "lexbor/selectors/selectors.h" 30 | #include "lexbor/html/interface.h" 31 | #include "lexbor/html/html.h" 32 | #include "lexbor/html/parser.h" 33 | 34 | std::string & 35 | colour_method( std::string & method); 36 | 37 | std::string & 38 | colour_type( std::string & type ); 39 | 40 | typedef struct { 41 | 42 | unsigned int i; 43 | std::string response; 44 | std::string method; 45 | std::string type; 46 | 47 | } context_t; 48 | 49 | class MethodRemote 50 | { 51 | 52 | public: 53 | 54 | MethodRemote( const std::string & ); 55 | 56 | ~MethodRemote(); 57 | 58 | void 59 | extract_info(std::string, std::string, 60 | lxb_status_t (*)(lxb_dom_node_t *, lxb_css_selector_specificity_t *, void *)); 61 | 62 | void 63 | Google_All(); 64 | 65 | void 66 | Google_Math(); 67 | 68 | void 69 | Google_Lyrics(); 70 | 71 | void 72 | Google_Correction(); 73 | 74 | void 75 | Google_LyricsInfo(); 76 | 77 | void 78 | Google_Translation(); 79 | 80 | void 81 | Google_Definition(); 82 | 83 | void 84 | Google_Weather(); 85 | 86 | void 87 | Google_TrackList(); 88 | 89 | void 90 | Google_InformationList(); 91 | 92 | void 93 | Google_HeaderList(); 94 | 95 | void 96 | Google_Chemistry(); 97 | 98 | void 99 | Google_Pronunciation(); 100 | 101 | void 102 | Google_UnitConversion(); 103 | 104 | void 105 | Google_CurrencyConversion(); 106 | 107 | void 108 | Google_InformationHeader(); 109 | 110 | void 111 | Google_InformationWindow(); 112 | 113 | void 114 | QuotesList(); 115 | 116 | void 117 | TableSport(); 118 | 119 | void 120 | Google_InformationTable(); 121 | 122 | private: 123 | lxb_dom_node_t * lexbor_body; 124 | lxb_html_document_t * lexbor_document; 125 | lxb_css_parser_t * lexbor_parser; 126 | lxb_selectors_t * lexbor_selectors; 127 | 128 | }; 129 | 130 | class MethodLocal 131 | { 132 | 133 | public: 134 | 135 | MethodLocal( const std::string & ); 136 | 137 | void 138 | Math(); 139 | 140 | void 141 | All(); 142 | 143 | private: 144 | std::string expression; 145 | 146 | }; 147 | 148 | extern bool response_found; 149 | extern bool is_atty; 150 | extern lxb_status_t __one_line (lxb_dom_node_t *node, lxb_css_selector_specificity_t *spec, void *ctx); 151 | extern lxb_status_t __multi_lines (lxb_dom_node_t *node, lxb_css_selector_specificity_t *spec, void *ctx); 152 | 153 | #define WALKER_FUNCTION(name) \ 154 | lexbor_action_t \ 155 | __##name(lxb_dom_node_t *node, void *ctx) 156 | 157 | #define PRINT_FUNCTION(name) \ 158 | lxb_status_t \ 159 | __##name (lxb_dom_node_t *node, lxb_css_selector_specificity_t *spec, void *ctx) 160 | 161 | #define FAILURE(message) \ 162 | { \ 163 | std::cerr << message << std::endl; \ 164 | exit(1); \ 165 | } 166 | 167 | #define RETURN_DEFAULT_WALKER() \ 168 | const lxb_char_t * lxb_data = lxb_dom_node_text_content(node, nullptr); \ 169 | const std::string data = std::string( (char *) lxb_data ); \ 170 | if (!data.empty()) { \ 171 | my->response = my->response + my->method + ' ' + my->type + ' ' + data + '\n'; \ 172 | } \ 173 | my->i++; \ 174 | my->type.clear(); \ 175 | return LXB_STATUS_OK; 176 | -------------------------------------------------------------------------------- /source/modules/Methods/tinyexpr/LICENCE: -------------------------------------------------------------------------------- 1 | zlib License 2 | 3 | Copyright (C) 2015, 2016 Lewis Van Winkle 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any damages 7 | arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any purpose, 10 | including commercial applications, and to alter it and redistribute it 11 | freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must not 14 | claim that you wrote the original software. If you use this software 15 | in a product, an acknowledgement in the product documentation would be 16 | appreciated but is not required. 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 3. This notice may not be removed or altered from any source distribution. 20 | -------------------------------------------------------------------------------- /source/modules/Methods/tinyexpr/main.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | /* 3 | * TINYEXPR - Tiny recursive descent parser and evaluation engine in C 4 | * 5 | * Copyright (c) 2015-2020 Lewis Van Winkle 6 | * 7 | * http://CodePlea.com 8 | * 9 | * This software is provided 'as-is', without any express or implied 10 | * warranty. In no event will the authors be held liable for any damages 11 | * arising from the use of this software. 12 | * 13 | * Permission is granted to anyone to use this software for any purpose, 14 | * including commercial applications, and to alter it and redistribute it 15 | * freely, subject to the following restrictions: 16 | * 17 | * 1. The origin of this software must not be misrepresented; you must not 18 | * claim that you wrote the original software. If you use this software 19 | * in a product, an acknowledgement in the product documentation would be 20 | * appreciated but is not required. 21 | * 2. Altered source versions must be plainly marked as such, and must not be 22 | * misrepresented as being the original software. 23 | * 3. This notice may not be removed or altered from any source distribution. 24 | */ 25 | 26 | /* COMPILE TIME OPTIONS */ 27 | 28 | /* Exponentiation associativity: 29 | For a^b^c = (a^b)^c and -a^b = (-a)^b do nothing. 30 | For a^b^c = a^(b^c) and -a^b = -(a^b) uncomment the next line.*/ 31 | /* #define TE_POW_FROM_RIGHT */ 32 | 33 | /* Logarithms 34 | For log = base 10 log do nothing 35 | For log = natural log uncomment the next line. */ 36 | /* #define TE_NAT_LOG */ 37 | 38 | #include "main.h" 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #ifndef NAN 47 | #define NAN (0.0/0.0) 48 | #endif 49 | 50 | #ifndef INFINITY 51 | #define INFINITY (1.0/0.0) 52 | #endif 53 | 54 | 55 | typedef double (*te_fun2)(double, double); 56 | 57 | enum { 58 | TOK_NULL = TE_CLOSURE7+1, TOK_ERROR, TOK_END, TOK_SEP, 59 | TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_VARIABLE, TOK_INFIX 60 | }; 61 | 62 | 63 | enum {TE_CONSTANT = 1}; 64 | 65 | 66 | typedef struct state { 67 | const char *start; 68 | const char *next; 69 | int type; 70 | union {double value; const double *bound; const void *function;}; 71 | void *context; 72 | 73 | const te_variable *lookup; 74 | int lookup_len; 75 | } state; 76 | 77 | 78 | #define TYPE_MASK(TYPE) ((TYPE)&0x0000001F) 79 | 80 | #define IS_PURE(TYPE) (((TYPE) & TE_FLAG_PURE) != 0) 81 | #define IS_FUNCTION(TYPE) (((TYPE) & TE_FUNCTION0) != 0) 82 | #define IS_CLOSURE(TYPE) (((TYPE) & TE_CLOSURE0) != 0) 83 | #define ARITY(TYPE) ( ((TYPE) & (TE_FUNCTION0 | TE_CLOSURE0)) ? ((TYPE) & 0x00000007) : 0 ) 84 | #define NEW_EXPR(type, ...) new_expr((type), (const te_expr*[]){__VA_ARGS__}) 85 | 86 | static te_expr *new_expr(const int type, const te_expr *parameters[]) { 87 | const int arity = ARITY(type); 88 | const int psize = sizeof(void*) * arity; 89 | const int size = (sizeof(te_expr) - sizeof(void*)) + psize + (IS_CLOSURE(type) ? sizeof(void*) : 0); 90 | te_expr *ret = malloc(size); 91 | memset(ret, 0, size); 92 | if (arity && parameters) { 93 | memcpy(ret->parameters, parameters, psize); 94 | } 95 | ret->type = type; 96 | ret->bound = 0; 97 | return ret; 98 | } 99 | 100 | 101 | void te_free_parameters(te_expr *n) { 102 | if (!n) return; 103 | switch (TYPE_MASK(n->type)) { 104 | case TE_FUNCTION7: case TE_CLOSURE7: te_free(n->parameters[6]); /* Falls through. */ 105 | case TE_FUNCTION6: case TE_CLOSURE6: te_free(n->parameters[5]); /* Falls through. */ 106 | case TE_FUNCTION5: case TE_CLOSURE5: te_free(n->parameters[4]); /* Falls through. */ 107 | case TE_FUNCTION4: case TE_CLOSURE4: te_free(n->parameters[3]); /* Falls through. */ 108 | case TE_FUNCTION3: case TE_CLOSURE3: te_free(n->parameters[2]); /* Falls through. */ 109 | case TE_FUNCTION2: case TE_CLOSURE2: te_free(n->parameters[1]); /* Falls through. */ 110 | case TE_FUNCTION1: case TE_CLOSURE1: te_free(n->parameters[0]); 111 | } 112 | } 113 | 114 | 115 | void te_free(te_expr *n) { 116 | if (!n) return; 117 | te_free_parameters(n); 118 | free(n); 119 | } 120 | 121 | 122 | static double pi(void) {return 3.14159265358979323846;} 123 | static double e(void) {return 2.71828182845904523536;} 124 | static double fac(double a) {/* simplest version of fac */ 125 | if (a < 0.0) 126 | return NAN; 127 | if (a > UINT_MAX) 128 | return INFINITY; 129 | unsigned int ua = (unsigned int)(a); 130 | unsigned long int result = 1, i; 131 | for (i = 1; i <= ua; i++) { 132 | if (i > ULONG_MAX / result) 133 | return INFINITY; 134 | result *= i; 135 | } 136 | return (double)result; 137 | } 138 | static double ncr(double n, double r) { 139 | if (n < 0.0 || r < 0.0 || n < r) return NAN; 140 | if (n > UINT_MAX || r > UINT_MAX) return INFINITY; 141 | unsigned long int un = (unsigned int)(n), ur = (unsigned int)(r), i; 142 | unsigned long int result = 1; 143 | if (ur > un / 2) ur = un - ur; 144 | for (i = 1; i <= ur; i++) { 145 | if (result > ULONG_MAX / (un - ur + i)) 146 | return INFINITY; 147 | result *= un - ur + i; 148 | result /= i; 149 | } 150 | return result; 151 | } 152 | static double npr(double n, double r) {return ncr(n, r) * fac(r);} 153 | 154 | #ifdef _MSC_VER 155 | #pragma function (ceil) 156 | #pragma function (floor) 157 | #endif 158 | 159 | static const te_variable functions[] = { 160 | /* must be in alphabetical order */ 161 | {"abs", fabs, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 162 | {"acos", acos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 163 | {"asin", asin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 164 | {"atan", atan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 165 | {"atan2", atan2, TE_FUNCTION2 | TE_FLAG_PURE, 0}, 166 | {"ceil", ceil, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 167 | {"cos", cos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 168 | {"cosh", cosh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 169 | {"e", e, TE_FUNCTION0 | TE_FLAG_PURE, 0}, 170 | {"exp", exp, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 171 | {"fac", fac, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 172 | {"floor", floor, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 173 | {"ln", log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 174 | #ifdef TE_NAT_LOG 175 | {"log", log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 176 | #else 177 | {"log", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 178 | #endif 179 | {"log10", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 180 | {"ncr", ncr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, 181 | {"npr", npr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, 182 | {"pi", pi, TE_FUNCTION0 | TE_FLAG_PURE, 0}, 183 | {"pow", pow, TE_FUNCTION2 | TE_FLAG_PURE, 0}, 184 | {"sin", sin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 185 | {"sinh", sinh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 186 | {"sqrt", sqrt, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 187 | {"tan", tan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 188 | {"tanh", tanh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, 189 | {0, 0, 0, 0} 190 | }; 191 | 192 | static const te_variable *find_builtin(const char *name, int len) { 193 | int imin = 0; 194 | int imax = sizeof(functions) / sizeof(te_variable) - 2; 195 | 196 | /*Binary search.*/ 197 | while (imax >= imin) { 198 | const int i = (imin + ((imax-imin)/2)); 199 | int c = strncmp(name, functions[i].name, len); 200 | if (!c) c = '\0' - functions[i].name[len]; 201 | if (c == 0) { 202 | return functions + i; 203 | } else if (c > 0) { 204 | imin = i + 1; 205 | } else { 206 | imax = i - 1; 207 | } 208 | } 209 | 210 | return 0; 211 | } 212 | 213 | static const te_variable *find_lookup(const state *s, const char *name, int len) { 214 | int iters; 215 | const te_variable *var; 216 | if (!s->lookup) return 0; 217 | 218 | for (var = s->lookup, iters = s->lookup_len; iters; ++var, --iters) { 219 | if (strncmp(name, var->name, len) == 0 && var->name[len] == '\0') { 220 | return var; 221 | } 222 | } 223 | return 0; 224 | } 225 | 226 | 227 | 228 | static double add(double a, double b) {return a + b;} 229 | static double sub(double a, double b) {return a - b;} 230 | static double mul(double a, double b) {return a * b;} 231 | static double divide(double a, double b) {return a / b;} 232 | static double negate(double a) {return -a;} 233 | static double comma(double a, double b) {(void)a; return b;} 234 | 235 | 236 | void next_token(state *s) { 237 | s->type = TOK_NULL; 238 | 239 | do { 240 | 241 | if (!*s->next){ 242 | s->type = TOK_END; 243 | return; 244 | } 245 | 246 | /* Try reading a number. */ 247 | if ((s->next[0] >= '0' && s->next[0] <= '9') || s->next[0] == '.') { 248 | s->value = strtod(s->next, (char**)&s->next); 249 | s->type = TOK_NUMBER; 250 | } else { 251 | /* Look for a variable or builtin function call. */ 252 | if (isalpha(s->next[0])) { 253 | const char *start; 254 | start = s->next; 255 | while (isalpha(s->next[0]) || isdigit(s->next[0]) || (s->next[0] == '_')) s->next++; 256 | 257 | const te_variable *var = find_lookup(s, start, s->next - start); 258 | if (!var) var = find_builtin(start, s->next - start); 259 | 260 | if (!var) { 261 | s->type = TOK_ERROR; 262 | } else { 263 | switch(TYPE_MASK(var->type)) 264 | { 265 | case TE_VARIABLE: 266 | s->type = TOK_VARIABLE; 267 | s->bound = var->address; 268 | break; 269 | 270 | case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3: /* Falls through. */ 271 | case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: /* Falls through. */ 272 | s->context = var->context; /* Falls through. */ 273 | 274 | case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: /* Falls through. */ 275 | case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: /* Falls through. */ 276 | s->type = var->type; 277 | s->function = var->address; 278 | break; 279 | } 280 | } 281 | 282 | } else { 283 | /* Look for an operator or special character. */ 284 | switch (s->next++[0]) { 285 | case '+': s->type = TOK_INFIX; s->function = add; break; 286 | case '-': s->type = TOK_INFIX; s->function = sub; break; 287 | case '*': s->type = TOK_INFIX; s->function = mul; break; 288 | case '/': s->type = TOK_INFIX; s->function = divide; break; 289 | case '^': s->type = TOK_INFIX; s->function = pow; break; 290 | case '%': s->type = TOK_INFIX; s->function = fmod; break; 291 | case '(': s->type = TOK_OPEN; break; 292 | case ')': s->type = TOK_CLOSE; break; 293 | case ',': s->type = TOK_SEP; break; 294 | case ' ': case '\t': case '\n': case '\r': break; 295 | default: s->type = TOK_ERROR; break; 296 | } 297 | } 298 | } 299 | } while (s->type == TOK_NULL); 300 | } 301 | 302 | 303 | static te_expr *list(state *s); 304 | static te_expr *expr(state *s); 305 | static te_expr *power(state *s); 306 | 307 | static te_expr *base(state *s) { 308 | /* = | | {"(" ")"} | | "(" {"," } ")" | "(" ")" */ 309 | te_expr *ret; 310 | int arity; 311 | 312 | switch (TYPE_MASK(s->type)) { 313 | case TOK_NUMBER: 314 | ret = new_expr(TE_CONSTANT, 0); 315 | ret->value = s->value; 316 | next_token(s); 317 | break; 318 | 319 | case TOK_VARIABLE: 320 | ret = new_expr(TE_VARIABLE, 0); 321 | ret->bound = s->bound; 322 | next_token(s); 323 | break; 324 | 325 | case TE_FUNCTION0: 326 | case TE_CLOSURE0: 327 | ret = new_expr(s->type, 0); 328 | ret->function = s->function; 329 | if (IS_CLOSURE(s->type)) ret->parameters[0] = s->context; 330 | next_token(s); 331 | if (s->type == TOK_OPEN) { 332 | next_token(s); 333 | if (s->type != TOK_CLOSE) { 334 | s->type = TOK_ERROR; 335 | } else { 336 | next_token(s); 337 | } 338 | } 339 | break; 340 | 341 | case TE_FUNCTION1: 342 | case TE_CLOSURE1: 343 | ret = new_expr(s->type, 0); 344 | ret->function = s->function; 345 | if (IS_CLOSURE(s->type)) ret->parameters[1] = s->context; 346 | next_token(s); 347 | ret->parameters[0] = power(s); 348 | break; 349 | 350 | case TE_FUNCTION2: case TE_FUNCTION3: case TE_FUNCTION4: 351 | case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: 352 | case TE_CLOSURE2: case TE_CLOSURE3: case TE_CLOSURE4: 353 | case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: 354 | arity = ARITY(s->type); 355 | 356 | ret = new_expr(s->type, 0); 357 | ret->function = s->function; 358 | if (IS_CLOSURE(s->type)) ret->parameters[arity] = s->context; 359 | next_token(s); 360 | 361 | if (s->type != TOK_OPEN) { 362 | s->type = TOK_ERROR; 363 | } else { 364 | int i; 365 | for(i = 0; i < arity; i++) { 366 | next_token(s); 367 | ret->parameters[i] = expr(s); 368 | if(s->type != TOK_SEP) { 369 | break; 370 | } 371 | } 372 | if(s->type != TOK_CLOSE || i != arity - 1) { 373 | s->type = TOK_ERROR; 374 | } else { 375 | next_token(s); 376 | } 377 | } 378 | 379 | break; 380 | 381 | case TOK_OPEN: 382 | next_token(s); 383 | ret = list(s); 384 | if (s->type != TOK_CLOSE) { 385 | s->type = TOK_ERROR; 386 | } else { 387 | next_token(s); 388 | } 389 | break; 390 | 391 | default: 392 | ret = new_expr(0, 0); 393 | s->type = TOK_ERROR; 394 | ret->value = NAN; 395 | break; 396 | } 397 | 398 | return ret; 399 | } 400 | 401 | 402 | static te_expr *power(state *s) { 403 | /* = {("-" | "+")} */ 404 | int sign = 1; 405 | while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { 406 | if (s->function == sub) sign = -sign; 407 | next_token(s); 408 | } 409 | 410 | te_expr *ret; 411 | 412 | if (sign == 1) { 413 | ret = base(s); 414 | } else { 415 | ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); 416 | ret->function = negate; 417 | } 418 | 419 | return ret; 420 | } 421 | 422 | #ifdef TE_POW_FROM_RIGHT 423 | static te_expr *factor(state *s) { 424 | /* = {"^" } */ 425 | te_expr *ret = power(s); 426 | 427 | int neg = 0; 428 | 429 | if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && ret->function == negate) { 430 | te_expr *se = ret->parameters[0]; 431 | free(ret); 432 | ret = se; 433 | neg = 1; 434 | } 435 | 436 | te_expr *insertion = 0; 437 | 438 | while (s->type == TOK_INFIX && (s->function == pow)) { 439 | te_fun2 t = s->function; 440 | next_token(s); 441 | 442 | if (insertion) { 443 | /* Make exponentiation go right-to-left. */ 444 | te_expr *insert = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, insertion->parameters[1], power(s)); 445 | insert->function = t; 446 | insertion->parameters[1] = insert; 447 | insertion = insert; 448 | } else { 449 | ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s)); 450 | ret->function = t; 451 | insertion = ret; 452 | } 453 | } 454 | 455 | if (neg) { 456 | ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, ret); 457 | ret->function = negate; 458 | } 459 | 460 | return ret; 461 | } 462 | #else 463 | static te_expr *factor(state *s) { 464 | /* = {"^" } */ 465 | te_expr *ret = power(s); 466 | 467 | while (s->type == TOK_INFIX && (s->function == pow)) { 468 | te_fun2 t = s->function; 469 | next_token(s); 470 | ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s)); 471 | ret->function = t; 472 | } 473 | 474 | return ret; 475 | } 476 | #endif 477 | 478 | 479 | 480 | static te_expr *term(state *s) { 481 | /* = {("*" | "/" | "%") } */ 482 | te_expr *ret = factor(s); 483 | 484 | while (s->type == TOK_INFIX && (s->function == mul || s->function == divide || s->function == fmod)) { 485 | te_fun2 t = s->function; 486 | next_token(s); 487 | ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, factor(s)); 488 | ret->function = t; 489 | } 490 | 491 | return ret; 492 | } 493 | 494 | 495 | static te_expr *expr(state *s) { 496 | /* = {("+" | "-") } */ 497 | te_expr *ret = term(s); 498 | 499 | while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { 500 | te_fun2 t = s->function; 501 | next_token(s); 502 | ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, term(s)); 503 | ret->function = t; 504 | } 505 | 506 | return ret; 507 | } 508 | 509 | 510 | static te_expr *list(state *s) { 511 | /* = {"," } */ 512 | te_expr *ret = expr(s); 513 | 514 | while (s->type == TOK_SEP) { 515 | next_token(s); 516 | ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, expr(s)); 517 | ret->function = comma; 518 | } 519 | 520 | return ret; 521 | } 522 | 523 | 524 | #define TE_FUN(...) ((double(*)(__VA_ARGS__))n->function) 525 | #define M(e) te_eval(n->parameters[e]) 526 | 527 | 528 | double te_eval(const te_expr *n) { 529 | if (!n) return NAN; 530 | 531 | switch(TYPE_MASK(n->type)) { 532 | case TE_CONSTANT: return n->value; 533 | case TE_VARIABLE: return *n->bound; 534 | 535 | case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: 536 | case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: 537 | switch(ARITY(n->type)) { 538 | case 0: return TE_FUN(void)(); 539 | case 1: return TE_FUN(double)(M(0)); 540 | case 2: return TE_FUN(double, double)(M(0), M(1)); 541 | case 3: return TE_FUN(double, double, double)(M(0), M(1), M(2)); 542 | case 4: return TE_FUN(double, double, double, double)(M(0), M(1), M(2), M(3)); 543 | case 5: return TE_FUN(double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4)); 544 | case 6: return TE_FUN(double, double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4), M(5)); 545 | case 7: return TE_FUN(double, double, double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4), M(5), M(6)); 546 | default: return NAN; 547 | } 548 | 549 | case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3: 550 | case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: 551 | switch(ARITY(n->type)) { 552 | case 0: return TE_FUN(void*)(n->parameters[0]); 553 | case 1: return TE_FUN(void*, double)(n->parameters[1], M(0)); 554 | case 2: return TE_FUN(void*, double, double)(n->parameters[2], M(0), M(1)); 555 | case 3: return TE_FUN(void*, double, double, double)(n->parameters[3], M(0), M(1), M(2)); 556 | case 4: return TE_FUN(void*, double, double, double, double)(n->parameters[4], M(0), M(1), M(2), M(3)); 557 | case 5: return TE_FUN(void*, double, double, double, double, double)(n->parameters[5], M(0), M(1), M(2), M(3), M(4)); 558 | case 6: return TE_FUN(void*, double, double, double, double, double, double)(n->parameters[6], M(0), M(1), M(2), M(3), M(4), M(5)); 559 | case 7: return TE_FUN(void*, double, double, double, double, double, double, double)(n->parameters[7], M(0), M(1), M(2), M(3), M(4), M(5), M(6)); 560 | default: return NAN; 561 | } 562 | 563 | default: return NAN; 564 | } 565 | 566 | } 567 | 568 | #undef TE_FUN 569 | #undef M 570 | 571 | static void optimize(te_expr *n) { 572 | /* Evaluates as much as possible. */ 573 | if (n->type == TE_CONSTANT) return; 574 | if (n->type == TE_VARIABLE) return; 575 | 576 | /* Only optimize out functions flagged as pure. */ 577 | if (IS_PURE(n->type)) { 578 | const int arity = ARITY(n->type); 579 | int known = 1; 580 | int i; 581 | for (i = 0; i < arity; ++i) { 582 | optimize(n->parameters[i]); 583 | if (((te_expr*)(n->parameters[i]))->type != TE_CONSTANT) { 584 | known = 0; 585 | } 586 | } 587 | if (known) { 588 | const double value = te_eval(n); 589 | te_free_parameters(n); 590 | n->type = TE_CONSTANT; 591 | n->value = value; 592 | } 593 | } 594 | } 595 | 596 | 597 | te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *error) { 598 | state s; 599 | s.start = s.next = expression; 600 | s.lookup = variables; 601 | s.lookup_len = var_count; 602 | 603 | next_token(&s); 604 | te_expr *root = list(&s); 605 | 606 | if (s.type != TOK_END) { 607 | te_free(root); 608 | if (error) { 609 | *error = (s.next - s.start); 610 | if (*error == 0) *error = 1; 611 | } 612 | return 0; 613 | } else { 614 | optimize(root); 615 | if (error) *error = 0; 616 | return root; 617 | } 618 | } 619 | 620 | 621 | double te_interp(const char *expression, int *error) { 622 | te_expr *n = te_compile(expression, 0, 0, error); 623 | double ret; 624 | if (n) { 625 | ret = te_eval(n); 626 | te_free(n); 627 | } else { 628 | ret = NAN; 629 | } 630 | return ret; 631 | } 632 | 633 | static void pn (const te_expr *n, int depth) { 634 | int i, arity; 635 | printf("%*s", depth, ""); 636 | 637 | switch(TYPE_MASK(n->type)) { 638 | case TE_CONSTANT: printf("%f\n", n->value); break; 639 | case TE_VARIABLE: printf("bound %p\n", n->bound); break; 640 | 641 | case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: 642 | case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: 643 | case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3: 644 | case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: 645 | arity = ARITY(n->type); 646 | printf("f%d", arity); 647 | for(i = 0; i < arity; i++) { 648 | printf(" %p", n->parameters[i]); 649 | } 650 | printf("\n"); 651 | for(i = 0; i < arity; i++) { 652 | pn(n->parameters[i], depth + 1); 653 | } 654 | break; 655 | } 656 | } 657 | 658 | 659 | void te_print(const te_expr *n) { 660 | pn(n, 0); 661 | } 662 | -------------------------------------------------------------------------------- /source/modules/Methods/tinyexpr/main.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | /* 3 | * TINYEXPR - Tiny recursive descent parser and evaluation engine in C 4 | * 5 | * Copyright (c) 2015-2020 Lewis Van Winkle 6 | * 7 | * http://CodePlea.com 8 | * 9 | * This software is provided 'as-is', without any express or implied 10 | * warranty. In no event will the authors be held liable for any damages 11 | * arising from the use of this software. 12 | * 13 | * Permission is granted to anyone to use this software for any purpose, 14 | * including commercial applications, and to alter it and redistribute it 15 | * freely, subject to the following restrictions: 16 | * 17 | * 1. The origin of this software must not be misrepresented; you must not 18 | * claim that you wrote the original software. If you use this software 19 | * in a product, an acknowledgement in the product documentation would be 20 | * appreciated but is not required. 21 | * 2. Altered source versions must be plainly marked as such, and must not be 22 | * misrepresented as being the original software. 23 | * 3. This notice may not be removed or altered from any source distribution. 24 | */ 25 | 26 | #ifndef TINYEXPR_H 27 | #define TINYEXPR_H 28 | 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | 35 | 36 | typedef struct te_expr { 37 | int type; 38 | union {double value; const double *bound; const void *function;}; 39 | void *parameters[1]; 40 | } te_expr; 41 | 42 | 43 | enum { 44 | TE_VARIABLE = 0, 45 | 46 | TE_FUNCTION0 = 8, TE_FUNCTION1, TE_FUNCTION2, TE_FUNCTION3, 47 | TE_FUNCTION4, TE_FUNCTION5, TE_FUNCTION6, TE_FUNCTION7, 48 | 49 | TE_CLOSURE0 = 16, TE_CLOSURE1, TE_CLOSURE2, TE_CLOSURE3, 50 | TE_CLOSURE4, TE_CLOSURE5, TE_CLOSURE6, TE_CLOSURE7, 51 | 52 | TE_FLAG_PURE = 32 53 | }; 54 | 55 | typedef struct te_variable { 56 | const char *name; 57 | const void *address; 58 | int type; 59 | void *context; 60 | } te_variable; 61 | 62 | 63 | 64 | /* Parses the input expression, evaluates it, and frees it. */ 65 | /* Returns NaN on error. */ 66 | double te_interp(const char *expression, int *error); 67 | 68 | /* Parses the input expression and binds variables. */ 69 | /* Returns NULL on error. */ 70 | te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *error); 71 | 72 | /* Evaluates the expression. */ 73 | double te_eval(const te_expr *n); 74 | 75 | /* Prints debugging information on the syntax tree. */ 76 | void te_print(const te_expr *n); 77 | 78 | /* Frees the expression. */ 79 | /* This is safe to call on NULL pointers. */ 80 | void te_free(te_expr *n); 81 | 82 | 83 | #ifdef __cplusplus 84 | } 85 | #endif 86 | 87 | #endif /*TINYEXPR_H*/ 88 | -------------------------------------------------------------------------------- /source/modules/URLDownloader/URLDownloader.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021-2022 João F. BeyondMagic 3 | * 4 | *Licensed under the Apache License, Version 2.0 (the "License"); 5 | *you may not use this file except in compliance with the License. 6 | *You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | *Unless required by applicable law or agreed to in writing, software 11 | *distributed under the License is distributed on an "AS IS" BASIS, 12 | *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | *See the License for the specific language governing permissions and 14 | *limitations under the License. 15 | */ 16 | 17 | #include "URLDownloader.hpp" 18 | 19 | URLDownloader::URLDownloader ( const std::string & lang, std::string & text ) 20 | { 21 | 22 | language = lang; 23 | curl = curl_easy_init(); 24 | 25 | if (curl) 26 | { 27 | // Encode the string into URL type, like " " to "%20". 28 | query = curl_easy_escape(curl, text.c_str(), 0); 29 | 30 | // Set up the options for cURL 31 | curl_easy_setopt( curl, CURLOPT_USERAGENT, 32 | "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) QtWebEngine/5.15.2 Chrome/87.0.4280.144 Safari/537.36" ); 33 | curl_easy_setopt( curl, CURLOPT_NOPROGRESS, 1L ); 34 | curl_easy_setopt( curl, CURLOPT_MAXREDIRS, 50L ); 35 | curl_easy_setopt( curl, CURLOPT_TCP_KEEPALIVE, 1L ); 36 | curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, this->write ); 37 | curl_easy_setopt( curl, CURLOPT_WRITEDATA, & response ); 38 | //curl_easy_setopt( curl, CURLOPT_HEADERDATA, & header ); 39 | 40 | } 41 | else 42 | { 43 | 44 | std::cout << "[URLDownloader] cURL was not found." << std::endl; 45 | exit(1); 46 | 47 | } 48 | 49 | } 50 | 51 | URLDownloader::~URLDownloader () 52 | { 53 | // Clean cURL method 54 | curl_easy_cleanup( curl ); 55 | curl_global_cleanup(); 56 | } 57 | 58 | const std::string 59 | URLDownloader::download( int & id ) 60 | { 61 | 62 | std::string source; 63 | 64 | // Set URL based on the ID. 65 | switch (id) { 66 | 67 | // Google. 68 | case 0: 69 | source = "https://www.google.com/search?hl=" + language + "&q=" + query; 70 | break; 71 | 72 | default: 73 | std::cout << "[URLDownloader] EE: There is no source URL for that ID: " << id << std::endl; 74 | exit(1); 75 | break; 76 | 77 | } 78 | 79 | // Set up the URL 80 | curl_easy_setopt( curl, CURLOPT_URL, source.c_str() ); 81 | 82 | // Run our cURL method 83 | curl_easy_perform( curl ); 84 | 85 | return response; 86 | 87 | } 88 | -------------------------------------------------------------------------------- /source/modules/URLDownloader/URLDownloader.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021-2022 João F. BeyondMagic 3 | * 4 | *Licensed under the Apache License, Version 2.0 (the "License"); 5 | *you may not use this file except in compliance with the License. 6 | *You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | *Unless required by applicable law or agreed to in writing, software 11 | *distributed under the License is distributed on an "AS IS" BASIS, 12 | *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | *See the License for the specific language governing permissions and 14 | *limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | 22 | /* 23 | * Finnally, this class will be the way to download our page and return its source code so that we can send it to the parser. 24 | * This is using cURL currently, though I don't see a better future ahead if it is not this minimal library that can be compiled 25 | * in almost every system out there. Be happy with the ugliness it provides because of its benefits, it is fast! 26 | */ 27 | class URLDownloader 28 | { 29 | 30 | public: 31 | 32 | // Initialise cURL. 33 | URLDownloader ( const std::string &, std::string & ); 34 | 35 | ~URLDownloader (); 36 | 37 | // Use cURL to get the source code based on the type of source (std::string), such as "Google" or "Wikipedia". 38 | const std::string download( int & ); 39 | 40 | private: 41 | std::string response; 42 | std::string language; 43 | char * query; 44 | CURL * curl; 45 | 46 | // The function to write the source code form cURL. It will just be used in one place. 47 | static size_t 48 | write ( void* ptr, size_t size, size_t nmemb, std::string* data) 49 | { 50 | 51 | data->append( (char*)ptr, size * nmemb ); 52 | return size * nmemb; 53 | 54 | } 55 | 56 | }; 57 | --------------------------------------------------------------------------------