text:
# ![Aimbot V2](https://user-images.githubusercontent.com/76539058/211103495-6f208311-c282-43d7-982e-d3ead86bef26.png) [![Visitors](https://visitor-badge.glitch.me/badge?page_id=Exunys.Aimbot-V2)](https://github.com/Exunys/Aimbot-V2)
## ![Fast](https://img.shields.io/badge/Fast-blue) ![Secure](https://img.shields.io/badge/Secure-darkgreen) ![Customizable](https://img.shields.io/badge/Customizable-purple) ![Organized](https://img.shields.io/badge/Organized-red) ![Optimized](https://img.shields.io/badge/Optimized-darkblue) ![Data Storing](https://img.shields.io/badge/Data-Storing-green) ![Undetectable](https://img.shields.io/badge/Undetectable-violet) ![Simple](https://img.shields.io/badge/Simple-yellow) ![Support](https://img.shields.io/badge/Multiplatform-Support-darkred) ![Reliable](https://img.shields.io/badge/Reliable-lightblue) ![Trusted](https://img.shields.io/badge/Trusted-lightgreen)
##
| [Update Log](https://github.com/Exunys/Aimbot-V2#update-log-ddmmyyyy0) | [Notices / Dependencies](https://github.com/Exunys/Aimbot-V2#notices) | [Configuration](https://github.com/Exunys/Aimbot-V2#configuration) | [Fixes / Troubleshooting](https://github.com/Exunys/Aimbot-V2#fixes) | [Hotkey](https://github.com/Exunys/Aimbot-V2#hotkey) | [Functions](https://github.com/Exunys/Aimbot-V2#functions) | [Previews](https://github.com/Exunys/Aimbot-V2#previews) | [Script](https://github.com/Exunys/Aimbot-V2#script)
| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
##
![Aimbot V2](https://user-images.githubusercontent.com/76539058/197401886-b988d375-a258-4555-a014-476295b67752.png)
## Information

### ℹThis repository is discontinued. Use the new script [AirHub](https://github.com/Exunys/AirHub).

This script is a universal aim-locking script. Works on all games which use the default character. This specific version has lots of crucial improvements such as:
- More Functions / Features & Settings
- Security / Decreased Detection Risk
- Optimized & Organized Code
- Speed / Faster Execution & Functionality
- Configurable Settings
- Storing
- Execution / Precise & Efficient
- Third Person Support
### License
This project is completely free and open sourced. But, that does not mean you own rights to it. Read this [document](https://github.com/Exunys/Aimbot-V2/blob/main/LICENSE) for more information.
You can re-use / stitch this script or any system of this project into any of your repositories, as long as you credit the developer [Exunys](https://github.com/Exunys).

## Update Log *(DD/MM/YYYY)*
- [23/10/2022] - **Added Third Person Support** (Read [*Script Examples*](https://github.com/Exunys/Aimbot-V2#script-examples))
- [08/01/2023] - **Bug fixes, optimizations & new script - [Aimbot V2 GUI](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Aimbot%20V2%20GUI.lua)** (Read [*Aimbot V2 GUI*](https://github.com/Exunys/Aimbot-V2#aimbot-v2-gui))
## Notices
* The script will not function properly unless your exploit fully supports / includes these following functions & libraries:
  - `isfolder()`, `makefolder()` & `delfolder()`
  - `isfile()`, `writefile()` & `delfile()`
  - `getgenv()`
  - `syn.queue_on_teleport()` / `queue_on_teleport()`
  - `Drawing`
* This script will store your changed settings every 10 seconds passed. You can also disable this feature:
```lua
getgenv().Aimbot.Settings.SaveSettings = false
```
* This script is indeed universal, but it might not run on games with specific character constructions (custom characters).
* A recommended exploit to run this script on is [Synapse X](https://x.synapse.to).
## Environment
The script's environment is stored as:
```lua
getgenv().Aimbot
```
More on how to configure the aimbot below this part.
## Configuration
This script includes settings which can be easily configured to your preference.
### Preview Of The Settings
```lua
getgenv().Aimbot.Settings = {
    SendNotifications = true,
    SaveSettings = true, -- Re-execute upon changing
    ReloadOnTeleport = true,
    Enabled = true,
    TeamCheck = false,
    AliveCheck = true,
    WallCheck = false, -- Laggy
    Sensitivity = 0, -- Animation length (in seconds) before fully locking onto target
    ThirdPerson = false, -- Uses mousemoverel instead of CFrame to support locking in third person (could be choppy)
    ThirdPersonSensitivity = 3, -- Boundary: 0.1 - 5
    TriggerKey = "MouseButton2",
    Toggle = false,
    LockPart = "Head" -- Body part to lock on (Character part's name)
}

getgenv().Aimbot.FOVSettings = {
    Enabled = true,
    Visible = true,
    Amount = 90,
    Color = "255, 255, 255",
    LockedColor = "255, 70, 70",
    Transparency = 0.5,
    Sides = 60,
    Thickness = 1,
    Filled = false
}
```
**Graphic View (Horizontal Tree Diagram)**

![image](https://user-images.githubusercontent.com/76539058/167846294-9a9f023a-14a8-42f2-b693-8557bc6a8ba5.png)

⚠WARNING⚠ - ***The WallCheck function is very laggy, it is recommended you have it set as*** **false** ***at all times!***
* You can also find the JSON format (which is the way they get stored) [here](https://github.com/Exunys/Aimbot-V2/tree/main/Resources/Settings).
* The Lua format of the settings / the factory reset script can be found [here](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Manual%20Factory%20Reset%20Settings.lua).

By reading the visual representation of the configuration part of the environment table, it should be pretty easy to configure the script afterwards. Here are some examples:

### Script Examples

The following script will disable the aimbot temporarily:
```lua
getgenv().Aimbot.Settings.Enabled = false
```
You can also change the color of the FOV Circle:
```lua
getgenv().Aimbot.FOVSettings.Color = "50, 255, 70" -- The colors must be fed as strings in RGB format. [(R)ed (0 - 255); (G)reen (0 - 255); (B)lue (0 - 255)]
```
**The script only accepts RGB configurations in strings as colors, if you input anything else, the script will break and not execute. Read below on how to fix this.**

You can also disable FOV Checking incase you don't find it useful / don't need it:
```lua
getgenv().Aimbot.FOVSettings.Enabled = false
```
If you want the script to work in third person, you can enable the *ThirdPerson* setting:
```lua
getgenv().Aimbot.Settings.ThirdPerson = true
```
If you want to edit the sensitivity for the third person mode:
```lua
-- The locking animation becomes faster as you increase the value (meaning 5 = fastest). The script becomes choppy if you increase the value.
getgenv().Aimbot.Settings.ThirdPersonSensitivity = 3 -- Boundary: 0.1 - 5
```

The options are endless, you can configure the script in any way you desire.

Read about the input types and more information about the drawing library in use for the FOV Circle (to learn how to configure it) [here](https://x.synapse.to/docs/reference/drawing_lib.html).

### Disable Storage System
If you want to disable the script saving the settings & delete the saved settings, you can do that automatically by executing the code below:
```lua
getgenv().Aimbot.Settings.SaveSettings = false
loadstring(game:HttpGet("https://raw.githubusercontent.com/Exunys/Aimbot-V2/main/Resources/Scripts/Delete%20Settings%20-%20Fix%20Script.lua"))()
```

## Fixes
If the script is not running upon execution, try a few of the solutions below:
### Solutions
* There is possibly a configuration that is unacceptable at most cases. Execute [this script](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Delete%20Settings%20-%20Fix%20Script.lua) and restart your game for changes to take effect.
* Open your exploit's root folder, find a folder named `workspace` and look for a folder named `Exunys Developer`, inside there should be another folder named `Aimbot`. Once you find it, delete this folder and restart your game.

If none of these solutions work, check if your exploit is supported (read the **Notices** part). If the script still doesn't work, contact Exunys & report the problem you are experiencing in detail.

Check if your exploit is supported [here](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Support%20Checker.lua).
## Hotkey
If you want to change the key you want to press to trigger the Aimbot, configure the ***TriggerKey*** setting.
- *Examples* :
```lua
getgenv().Aimbot.Settings.TriggerKey = "E" -- E is the key's name (This is equivalent to Enum.KeyCode.E, except, the script only handles strings)
```
```lua
getgenv().Aimbot.Settings.TriggerKey = "MouseButton1" -- MouseButton1 is the key's name [LMB] (This is equivalent to Enum.UserInputType.MouseButton1, except, the script only handles strings)
```
This script will work if the input type selected is either a *KeyCode* or *UserInputType* so all you have to input is the last part / the key's name.
- Read: [KeyCode](https://developer.roblox.com/en-us/api-reference/enum/KeyCode), [UserInputType](https://developer.roblox.com/en-us/api-reference/enum/UserInputType)
## Functions
This script includes built-in functions to control the Aimbot.
The functions can be accessed by indexing **Functions** in the Environment. Example:
```lua
getgenv().Aimbot.Functions
```
### Their purposes
* `Functions:Exit()`
  - Exits (unexecutes) the script and leaves no traces back.
* `Functions:Restart()`
  - Restarts the script, good for incase the script starts lagging.
* `Functions:ResetSettings()`
  - Factory resets the settings and wipes the previous ones that were saved to the workspace.

- Exit
```lua
getgenv().Aimbot.Functions:Exit()
```
- Restart
```lua
getgenv().Aimbot.Functions:Restart()
```
- Reset Settings
```lua
getgenv().Aimbot.Functions:ResetSettings()
```
## Previews

https://user-images.githubusercontent.com/76539058/148576112-928455f3-b071-4650-b2a6-5fb45cbfcf04.mp4

Settings used in the video above:
```lua
getgenv().Aimbot.Settings.Sensitivity = 0.5
getgenv().Aimbot.FOVSettings.Amount = 300
getgenv().Aimbot.FOVSettings.Sides = 18
getgenv().Aimbot.FOVSettings.Transparency = 1
getgenv().Aimbot.FOVSettings.Thickness = 2
getgenv().Aimbot.FOVSettings.Color = "25, 70, 200"
```

https://user-images.githubusercontent.com/76539058/148576459-11e0d92b-6658-4dc5-9841-2c18f61adde9.mp4

The video above presents the `Environment.Functions:ResetSettings()` function. Read the **Functions** part for more.
```lua
getgenv().Aimbot.Functions:ResetSettings()
```

https://user-images.githubusercontent.com/76539058/148576715-c90af8c4-a530-45b3-8b76-a014797b3eb5.mp4

The video above presents the `Environment.Functions:Exit()` function. Read the **Functions** part for more.
```lua
getgenv().Aimbot.Functions:Exit()
```

https://user-images.githubusercontent.com/76539058/197402881-bb298b50-6e90-4b57-ab70-258f3f2b4521.mp4

The video above shows how the script functions when the `Environment.Settings.ThirdPerson` value is set to `true`.
```lua
getgenv().Aimbot.Settings.ThirdPerson = true
```

https://user-images.githubusercontent.com/76539058/211179224-df514393-1d7f-46fc-ad3d-eab1035b2ac3.mp4

The video above shows the [Aimbot V2 GUI](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Aimbot%20V2%20GUI.lua).

## Raw Source
- This source does not include any Exunys Developer systems like storing the settings, sending notifications, using colors as strings, reloading the script on teleport etc.
- This source is more optimized and relatively smaller to allow the user to modify the code and freely put it into their own script. But of course, do not forget that this script has a [license](https://github.com/Exunys/Aimbot-V2/blob/main/LICENSE).
- The raw source can be found [here](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Raw%20Main.lua).
## Aimbot V2 GUI
This script can be found [here](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Aimbot%20V2%20GUI.lua). The script is powered by [Pepsi's UI Library](https://v3rmillion.net/showthread.php?tid=1139856) and it uses the [Raw Aimbot V2 Source](https://raw.githubusercontent.com/Exunys/Aimbot-V2/main/Resources/Scripts/Raw%20Main.lua).

This script is the same as the raw source but implemented into a UI Library which can be used as an independent script rather then an embeddable script for other developers.
## Script
### GUI
Load the script by copying it from [here](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Aimbot%20V2%20GUI.lua) or by executing the code below.
```lua
loadstring(game:HttpGet("https://raw.githubusercontent.com/Exunys/Aimbot-V2/main/Resources/Scripts/Aimbot%20V2%20GUI.lua"))()
```
### Default (With Exunys Developer Systems)
Load the script by copying it from [here](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Main.lua) or by executing the code below.
```lua
loadstring(game:HttpGet("https://raw.githubusercontent.com/Exunys/Aimbot-V2/main/Resources/Scripts/Main.lua"))()
```
### Raw (Without Exunys Developer Systems)
Load the script by copying it from [here](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Raw%20Main.lua) or by executing the code below.

```lua
loadstring(game:HttpGet("https://raw.githubusercontent.com/Exunys/Aimbot-V2/main/Resources/Scripts/Raw%20Main.lua"))()
```
## Developer Contact Information
 ![Untitled](https://user-images.githubusercontent.com/76539058/211099673-3d1d2025-1739-483f-b752-627097b49f44.png)

![Untitled](https://user-images.githubusercontent.com/76539058/211104101-d850c778-6273-49ee-aa48-70846ddd2d45.png) ![Untitled](https://user-images.githubusercontent.com/76539058/211103792-1546762b-03ef-453c-9f7f-78839cf3bc7a.png) [![E-Mail](https://raw.githubusercontent.com/gauravghongde/social-icons/master/PNG/White/Gmail_white.png)](mailto:exunys@gang.email) ![Untitled](https://user-images.githubusercontent.com/76539058/211103824-21b70a3c-1d65-4920-bf69-0ddb61ca59c3.png) [![Discord](https://raw.githubusercontent.com/gauravghongde/social-icons/master/PNG/White/Discord_white.png)](https://discord.com/users/611111398818316309)

 ![Untitled](https://user-images.githubusercontent.com/76539058/211099673-3d1d2025-1739-483f-b752-627097b49f44.png)

repo_language: Lua

file_name: Delete Settings - Fix Script.lua

mime_type: text/plain


text:
--// Preventing Multiple Processes

pcall(function()
	getgenv().Aimbot.Functions:Exit()
end)

--// Environment

getgenv().Aimbot = {}
local Environment = getgenv().Aimbot

--// Services

local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local HttpService = game:GetService("HttpService")
local TweenService = game:GetService("TweenService")
local StarterGui = game:GetService("StarterGui")
local Players = game:GetService("Players")
local Camera = game:GetService("Workspace").CurrentCamera

--// Variables

local LocalPlayer = Players.LocalPlayer
local Title = "Exunys Developer"
local FileNames = {"Aimbot", "Configuration.json", "Drawing.json"}
local Typing, Running, Animation, RequiredDistance, ServiceConnections = false, false, nil, 2000, {}

--// Support Functions

local mousemoverel = mousemoverel or (Input and Input.MouseMove)
local queueonteleport = queue_on_teleport or syn.queue_on_teleport

--// Script Settings

Environment.Settings = {
	SendNotifications = true,
	SaveSettings = true, -- Re-execute upon changing
	ReloadOnTeleport = true,
	Enabled = true,
	TeamCheck = false,
	AliveCheck = true,
	WallCheck = false, -- Laggy
	Sensitivity = 0, -- Animation length (in seconds) before fully locking onto target
	ThirdPerson = false, -- Uses mousemoverel instead of CFrame to support locking in third person (could be choppy)
	ThirdPersonSensitivity = 3, -- Boundary: 0.1 - 5
	TriggerKey = "MouseButton2",
	Toggle = false,
	LockPart = "Head" -- Body part to lock on
}

Environment.FOVSettings = {
	Enabled = true,
	Visible = true,
	Amount = 90,
	Color = "255, 255, 255",
	LockedColor = "255, 70, 70",
	Transparency = 0.5,
	Sides = 60,
	Thickness = 1,
	Filled = false
}

Environment.FOVCircle = Drawing.new("Circle")
Environment.Locked = nil

--// Core Functions

local function Encode(Table)
	if Table and type(Table) == "table" then
		local EncodedTable = HttpService:JSONEncode(Table)

		return EncodedTable
	end
end

local function Decode(String)
	if String and type(String) == "string" then
		local DecodedTable = HttpService:JSONDecode(String)

		return DecodedTable
	end
end

local function GetColor(Color)
	local R = tonumber(string.match(Color, "([%d]+)[%s]*,[%s]*[%d]+[%s]*,[%s]*[%d]+"))
	local G = tonumber(string.match(Color, "[%d]+[%s]*,[%s]*([%d]+)[%s]*,[%s]*[%d]+"))
	local B = tonumber(string.match(Color, "[%d]+[%s]*,[%s]*[%d]+[%s]*,[%s]*([%d]+)"))

	return Color3.fromRGB(R, G, B)
end

local function SendNotification(TitleArg, DescriptionArg, DurationArg)
	if Environment.Settings.SendNotifications then
		StarterGui:SetCore("SendNotification", {
			Title = TitleArg,
			Text = DescriptionArg,
			Duration = DurationArg
		})
	end
end

--// Functions

local function SaveSettings()
	if Environment.Settings.SaveSettings then
		if isfile(Title.."/"..FileNames[1].."/"..FileNames[2]) then
			writefile(Title.."/"..FileNames[1].."/"..FileNames[2], Encode(Environment.Settings))
		end

		if isfile(Title.."/"..FileNames[1].."/"..FileNames[3]) then
			writefile(Title.."/"..FileNames[1].."/"..FileNames[3], Encode(Environment.FOVSettings))
		end
	end
end

local function GetClosestPlayer()
	if not Environment.Locked then
		if Environment.FOVSettings.Enabled then
			RequiredDistance = Environment.FOVSettings.Amount
		else
			RequiredDistance = 2000
		end

		for _, v in next, Players:GetPlayers() do
			if v ~= LocalPlayer then
				if v.Character and v.Character:FindFirstChild(Environment.Settings.LockPart) and v.Character:FindFirstChildOfClass("Humanoid") then
					if Environment.Settings.TeamCheck and v.Team == LocalPlayer.Team then continue end
					if Environment.Settings.AliveCheck and v.Character:FindFirstChildOfClass("Humanoid").Health <= 0 then continue end
					if Environment.Settings.WallCheck and #(Camera:GetPartsObscuringTarget({v.Character[Environment.Settings.LockPart].Position}, v.Character:GetDescendants())) > 0 then continue end

					local Vector, OnScreen = Camera:WorldToViewportPoint(v.Character[Environment.Settings.LockPart].Position)
					local Distance = (Vector2.new(UserInputService:GetMouseLocation().X, UserInputService:GetMouseLocation().Y) - Vector2.new(Vector.X, Vector.Y)).Magnitude

					if Distance < RequiredDistance and OnScreen then
						RequiredDistance = Distance
						Environment.Locked = v
					end
				end
			end
		end
	elseif (Vector2.new(UserInputService:GetMouseLocation().X, UserInputService:GetMouseLocation().Y) - Vector2.new(Camera:WorldToViewportPoint(Environment.Locked.Character[Environment.Settings.LockPart].Position).X, Camera:WorldToViewportPoint(Environment.Locked.Character[Environment.Settings.LockPart].Position).Y)).Magnitude > RequiredDistance then
		Environment.Locked = nil
		Animation:Cancel()
		Environment.FOVCircle.Color = GetColor(Environment.FOVSettings.Color)
	end
end

--// Typing Check

ServiceConnections.TypingStartedConnection = UserInputService.TextBoxFocused:Connect(function()
	Typing = true
end)

ServiceConnections.TypingEndedConnection = UserInputService.TextBoxFocusReleased:Connect(function()
	Typing = false
end)

--// Create, Save & Load Settings

if Environment.Settings.SaveSettings then
	if not isfolder(Title) then
		makefolder(Title)
	end

	if not isfolder(Title.."/"..FileNames[1]) then
		makefolder(Title.."/"..FileNames[1])
	end

	if not isfile(Title.."/"..FileNames[1].."/"..FileNames[2]) then
		writefile(Title.."/"..FileNames[1].."/"..FileNames[2], Encode(Environment.Settings))
	else
		Environment.Settings = Decode(readfile(Title.."/"..FileNames[1].."/"..FileNames[2]))
	end

	if not isfile(Title.."/"..FileNames[1].."/"..FileNames[3]) then
		writefile(Title.."/"..FileNames[1].."/"..FileNames[3], Encode(Environment.FOVSettings))
	else
		Environment.Visuals = Decode(readfile(Title.."/"..FileNames[1].."/"..FileNames[3]))
	end

	coroutine.wrap(function()
		while wait(10) and Environment.Settings.SaveSettings do
			SaveSettings()
		end
	end)()
else
	if isfolder(Title) then
		delfolder(Title)
	end
end

local function Load()
	ServiceConnections.RenderSteppedConnection = RunService.RenderStepped:Connect(function()
		if Environment.FOVSettings.Enabled and Environment.Settings.Enabled then
			Environment.FOVCircle.Radius = Environment.FOVSettings.Amount
			Environment.FOVCircle.Thickness = Environment.FOVSettings.Thickness
			Environment.FOVCircle.Filled = Environment.FOVSettings.Filled
			Environment.FOVCircle.NumSides = Environment.FOVSettings.Sides
			Environment.FOVCircle.Color = GetColor(Environment.FOVSettings.Color)
			Environment.FOVCircle.Transparency = Environment.FOVSettings.Transparency
			Environment.FOVCircle.Visible = Environment.FOVSettings.Visible
			Environment.FOVCircle.Position = Vector2.new(UserInputService:GetMouseLocation().X, UserInputService:GetMouseLocation().Y)
		else
			Environment.FOVCircle.Visible = false
		end

		if Running and Environment.Settings.Enabled then
			GetClosestPlayer()

			if Environment.Settings.ThirdPerson then
				Environment.Settings.ThirdPersonSensitivity = math.clamp(Environment.Settings.ThirdPersonSensitivity, 0.1, 5)

				local Vector = Camera:WorldToViewportPoint(Environment.Locked.Character[Environment.Settings.LockPart].Position)
				mousemoverel((Vector.X - UserInputService:GetMouseLocation().X) * Environment.Settings.ThirdPersonSensitivity, (Vector.Y - UserInputService:GetMouseLocation().Y) * Environment.Settings.ThirdPersonSensitivity)
			else
				if Environment.Settings.Sensitivity > 0 then
					Animation = TweenService:Create(Camera, TweenInfo.new(Environment.Settings.Sensitivity, Enum.EasingStyle.Sine, Enum.EasingDirection.Out), {CFrame = CFrame.new(Camera.CFrame.Position, Environment.Locked.Character[Environment.Settings.LockPart].Position)})
					Animation:Play()
				else
					Camera.CFrame = CFrame.new(Camera.CFrame.Position, Environment.Locked.Character[Environment.Settings.LockPart].Position)
				end
			end

			Environment.FOVCircle.Color = GetColor(Environment.FOVSettings.LockedColor)
		end
	end)

	ServiceConnections.InputBeganConnection = UserInputService.InputBegan:Connect(function(Input)
		if not Typing then
			pcall(function()
				if Input.KeyCode == Enum.KeyCode[Environment.Settings.TriggerKey] then
					if Environment.Settings.Toggle then
						Running = not Running

						if not Running then
							Environment.Locked = nil
							Animation:Cancel()
							Environment.FOVCircle.Color = GetColor(Environment.FOVSettings.Color)
						end
					else
						Running = true
					end
				end
			end)

			pcall(function()
				if Input.UserInputType == Enum.UserInputType[Environment.Settings.TriggerKey] then
					if Environment.Settings.Toggle then
						Running = not Running

						if not Running then
							Environment.Locked = nil
							Animation:Cancel()
							Environment.FOVCircle.Color = GetColor(Environment.FOVSettings.Color)
						end
					else
						Running = true
					end
				end
			end)
		end
	end)

	ServiceConnections.InputEndedConnection = UserInputService.InputEnded:Connect(function(Input)
		if not Typing then
			pcall(function()
				if Input.KeyCode == Enum.KeyCode[Environment.Settings.TriggerKey] then
					if not Environment.Settings.Toggle then
						Running = false
						Environment.Locked = nil
						Animation:Cancel()
						Environment.FOVCircle.Color = GetColor(Environment.FOVSettings.Color)
					end
				end
			end)

			pcall(function()
				if Input.UserInputType == Enum.UserInputType[Environment.Settings.TriggerKey] then
					if not Environment.Settings.Toggle then
						Running = false
						Environment.Locked = nil
						Animation:Cancel()
						Environment.FOVCircle.Color = GetColor(Environment.FOVSettings.Color)
					end
				end
			end)
		end
	end)
end

--// Functions

Environment.Functions = {}

function Environment.Functions:Exit()
	SaveSettings()

	for _, v in next, ServiceConnections do
		v:Disconnect()
	end

	if Environment.FOVCircle.Remove then Environment.FOVCircle:Remove() end

	getgenv().Aimbot.Functions = nil
	getgenv().Aimbot = nil
end

function Environment.Functions:Restart()
	SaveSettings()

	for _, v in next, ServiceConnections do
		v:Disconnect()
	end

	Load()
end

function Environment.Functions:ResetSettings()
	Environment.Settings = {
		SendNotifications = true,
		SaveSettings = true, -- Re-execute upon changing
		ReloadOnTeleport = true,
		Enabled = true,
		TeamCheck = false,
		AliveCheck = true,
		WallCheck = false,
		Sensitivity = 0, -- Animation length (in seconds) before fully locking onto target
		ThirdPerson = false,
		ThirdPersonSensitivity = 3,
		TriggerKey = "MouseButton2",
		Toggle = false,
		LockPart = "Head" -- Body part to lock on
	}

	Environment.FOVSettings = {
		Enabled = true,
		Visible = true,
		Amount = 90,
		Color = "255, 255, 255",
		LockedColor = "255, 70, 70",
		Transparency = 0.5,
		Sides = 60,
		Thickness = 1,
		Filled = false
	}
end

--// Support Check

if not Drawing or not getgenv then
	SendNotification(Title, "Your exploit does not support this script", 3); return
end

--// Reload On Teleport

if Environment.Settings.ReloadOnTeleport then
	if queueonteleport then
		queueonteleport(game:HttpGet("https://raw.githubusercontent.com/Exunys/Aimbot-V2/main/Resources/Scripts/Main.lua"))
	else
		SendNotification(Title, "Your exploit does not support \"syn.queue_on_teleport()\"")
	end
end

--// Load

Load(); SendNotification(Title, "Aimbot script successfully loaded! Check the GitHub page on how to configure the script.", 5)


text:
--// Cache

local select = select
local pcall, getgenv, next, Vector2, mathclamp, type, mousemoverel = select(1, pcall, getgenv, next, Vector2.new, math.clamp, type, mousemoverel or (Input and Input.MouseMove))

--// Preventing Multiple Processes

pcall(function()
	getgenv().Aimbot.Functions:Exit()
end)

--// Environment

getgenv().Aimbot = {}
local Environment = getgenv().Aimbot

--// Services

local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local Camera = workspace.CurrentCamera
local LocalPlayer = Players.LocalPlayer

--// Variables

local RequiredDistance, Typing, Running, Animation, ServiceConnections = 2000, false, false, nil, {}

--// Script Settings

Environment.Settings = {
	Enabled = true,
	TeamCheck = false,
	AliveCheck = true,
	WallCheck = false, -- Laggy
	Sensitivity = 0, -- Animation length (in seconds) before fully locking onto target
	ThirdPerson = false, -- Uses mousemoverel instead of CFrame to support locking in third person (could be choppy)
	ThirdPersonSensitivity = 3, -- Boundary: 0.1 - 5
	TriggerKey = "MouseButton2",
	Toggle = false,
	LockPart = "Head" -- Body part to lock on
}

Environment.FOVSettings = {
	Enabled = true,
	Visible = true,
	Amount = 90,
	Color = Color3.fromRGB(255, 255, 255),
	LockedColor = Color3.fromRGB(255, 70, 70),
	Transparency = 0.5,
	Sides = 60,
	Thickness = 1,
	Filled = false
}

Environment.FOVCircle = Drawing.new("Circle")

--// Functions

local function CancelLock()
	Environment.Locked = nil
	if Animation then Animation:Cancel() end
	Environment.FOVCircle.Color = Environment.FOVSettings.Color
end

local function GetClosestPlayer()
	if not Environment.Locked then
		RequiredDistance = (Environment.FOVSettings.Enabled and Environment.FOVSettings.Amount or 2000)

		for _, v in next, Players:GetPlayers() do
			if v ~= LocalPlayer then
				if v.Character and v.Character:FindFirstChild(Environment.Settings.LockPart) and v.Character:FindFirstChildOfClass("Humanoid") then
					if Environment.Settings.TeamCheck and v.Team == LocalPlayer.Team then continue end
					if Environment.Settings.AliveCheck and v.Character:FindFirstChildOfClass("Humanoid").Health <= 0 then continue end
					if Environment.Settings.WallCheck and #(Camera:GetPartsObscuringTarget({v.Character[Environment.Settings.LockPart].Position}, v.Character:GetDescendants())) > 0 then continue end

					local Vector, OnScreen = Camera:WorldToViewportPoint(v.Character[Environment.Settings.LockPart].Position)
					local Distance = (Vector2(UserInputService:GetMouseLocation().X, UserInputService:GetMouseLocation().Y) - Vector2(Vector.X, Vector.Y)).Magnitude

					if Distance < RequiredDistance and OnScreen then
						RequiredDistance = Distance
						Environment.Locked = v
					end
				end
			end
		end
	elseif (Vector2(UserInputService:GetMouseLocation().X, UserInputService:GetMouseLocation().Y) - Vector2(Camera:WorldToViewportPoint(Environment.Locked.Character[Environment.Settings.LockPart].Position).X, Camera:WorldToViewportPoint(Environment.Locked.Character[Environment.Settings.LockPart].Position).Y)).Magnitude > RequiredDistance then
		CancelLock()
	end
end

--// Typing Check

ServiceConnections.TypingStartedConnection = UserInputService.TextBoxFocused:Connect(function()
	Typing = true
end)

ServiceConnections.TypingEndedConnection = UserInputService.TextBoxFocusReleased:Connect(function()
	Typing = false
end)

--// Main

local function Load()
	ServiceConnections.RenderSteppedConnection = RunService.RenderStepped:Connect(function()
		if Environment.FOVSettings.Enabled and Environment.Settings.Enabled then
			Environment.FOVCircle.Radius = Environment.FOVSettings.Amount
			Environment.FOVCircle.Thickness = Environment.FOVSettings.Thickness
			Environment.FOVCircle.Filled = Environment.FOVSettings.Filled
			Environment.FOVCircle.NumSides = Environment.FOVSettings.Sides
			Environment.FOVCircle.Color = Environment.FOVSettings.Color
			Environment.FOVCircle.Transparency = Environment.FOVSettings.Transparency
			Environment.FOVCircle.Visible = Environment.FOVSettings.Visible
			Environment.FOVCircle.Position = Vector2(UserInputService:GetMouseLocation().X, UserInputService:GetMouseLocation().Y)
		else
			Environment.FOVCircle.Visible = false
		end

		if Running and Environment.Settings.Enabled then
			GetClosestPlayer()

			if Environment.Locked then
				if Environment.Settings.ThirdPerson then
					Environment.Settings.ThirdPersonSensitivity = mathclamp(Environment.Settings.ThirdPersonSensitivity, 0.1, 5)

					local Vector = Camera:WorldToViewportPoint(Environment.Locked.Character[Environment.Settings.LockPart].Position)
					mousemoverel((Vector.X - UserInputService:GetMouseLocation().X) * Environment.Settings.ThirdPersonSensitivity, (Vector.Y - UserInputService:GetMouseLocation().Y) * Environment.Settings.ThirdPersonSensitivity)
				else
					if Environment.Settings.Sensitivity > 0 then
						Animation = TweenService:Create(Camera, TweenInfo.new(Environment.Settings.Sensitivity, Enum.EasingStyle.Sine, Enum.EasingDirection.Out), {CFrame = CFrame.new(Camera.CFrame.Position, Environment.Locked.Character[Environment.Settings.LockPart].Position)})
						Animation:Play()
					else
						Camera.CFrame = CFrame.new(Camera.CFrame.Position, Environment.Locked.Character[Environment.Settings.LockPart].Position)
					end
				end

			Environment.FOVCircle.Color = Environment.FOVSettings.LockedColor

			end
		end
	end)

	ServiceConnections.InputBeganConnection = UserInputService.InputBegan:Connect(function(Input)
		if not Typing then
			pcall(function()
				if Input.KeyCode == Enum.KeyCode[Environment.Settings.TriggerKey] then
					if Environment.Settings.Toggle then
						Running = not Running

						if not Running then
							CancelLock()
						end
					else
						Running = true
					end
				end
			end)

			pcall(function()
				if Input.UserInputType == Enum.UserInputType[Environment.Settings.TriggerKey] then
					if Environment.Settings.Toggle then
						Running = not Running

						if not Running then
							CancelLock()
						end
					else
						Running = true
					end
				end
			end)
		end
	end)

	ServiceConnections.InputEndedConnection = UserInputService.InputEnded:Connect(function(Input)
		if not Typing then
			if not Environment.Settings.Toggle then
				pcall(function()
					if Input.KeyCode == Enum.KeyCode[Environment.Settings.TriggerKey] then
						Running = false; CancelLock()
					end
				end)

				pcall(function()
					if Input.UserInputType == Enum.UserInputType[Environment.Settings.TriggerKey] then
						Running = false; CancelLock()
					end
				end)
			end
		end
	end)
end

--// Functions

Environment.Functions = {}

function Environment.Functions:Exit()
	for _, v in next, ServiceConnections do
		v:Disconnect()
	end

	if Environment.FOVCircle.Remove then Environment.FOVCircle:Remove() end

	getgenv().Aimbot.Functions = nil
	getgenv().Aimbot = nil

	Load = nil; GetClosestPlayer = nil; CancelLock = nil
end

function Environment.Functions:Restart()
	for _, v in next, ServiceConnections do
		v:Disconnect()
	end

	Load()
end

function Environment.Functions:ResetSettings()
	Environment.Settings = {
		Enabled = true,
		TeamCheck = false,
		AliveCheck = true,
		WallCheck = false,
		Sensitivity = 0, -- Animation length (in seconds) before fully locking onto target
		ThirdPerson = false, -- Uses mousemoverel instead of CFrame to support locking in third person (could be choppy)
		ThirdPersonSensitivity = 3, -- Boundary: 0.1 - 5
		TriggerKey = "MouseButton2",
		Toggle = false,
		LockPart = "Head" -- Body part to lock on
	}

	Environment.FOVSettings = {
		Enabled = true,
		Visible = true,
		Amount = 90,
		Color = Color3.fromRGB(255, 255, 255),
		LockedColor = Color3.fromRGB(255, 70, 70),
		Transparency = 0.5,
		Sides = 60,
		Thickness = 1,
		Filled = false
	}
end

--// Load

Load()


text:
--// Cache

local loadstring, game, getgenv, setclipboard = loadstring, game, getgenv, setclipboard

--// Loaded check

if getgenv().Aimbot then return end

--// Load Aimbot V2 (Raw)

loadstring(game:HttpGet("https://raw.githubusercontent.com/Exunys/Aimbot-V2/main/Resources/Scripts/Raw%20Main.lua"))()

--// Variables

local Aimbot = getgenv().Aimbot
local Settings, FOVSettings, Functions = Aimbot.Settings, Aimbot.FOVSettings, Aimbot.Functions

local Library = loadstring(game:GetObjects("rbxassetid://7657867786")[1].Source)() -- Pepsi's UI Library

local Parts = {"Head", "HumanoidRootPart", "Torso", "Left Arm", "Right Arm", "Left Leg", "Right Leg", "LeftHand", "RightHand", "LeftLowerArm", "RightLowerArm", "LeftUpperArm", "RightUpperArm", "LeftFoot", "LeftLowerLeg", "UpperTorso", "LeftUpperLeg", "RightFoot", "RightLowerLeg", "LowerTorso", "RightUpperLeg"}

--// Frame

Library.UnloadCallback = Functions.Exit

local MainFrame = Library:CreateWindow({
	Name = "Aimbot V2",
	Themeable = {
		Image = "7059346386",
		Info = "Made by Exunys\nPowered by Pepsi's UI Library",
		Credit = false
	},
	Background = "",
	Theme = [[{"__Designer.Colors.section":"ADC7FF","__Designer.Colors.topGradient":"1B242F","__Designer.Settings.ShowHideKey":"Enum.KeyCode.RightShift","__Designer.Colors.otherElementText":"54637D","__Designer.Colors.hoveredOptionBottom":"38667D","__Designer.Background.ImageAssetID":"","__Designer.Colors.unhoveredOptionTop":"407495","__Designer.Colors.innerBorder":"2C4168","__Designer.Colors.unselectedOption":"4E6EA0","__Designer.Background.UseBackgroundImage":true,"__Designer.Files.WorkspaceFile":"Aimbot V2","__Designer.Colors.main":"23A0FF","__Designer.Colors.outerBorder":"162943","__Designer.Background.ImageColor":"FFFFFF","__Designer.Colors.tabText":"C9DFF1","__Designer.Colors.elementBorder":"111D26","__Designer.Colors.sectionBackground":"0E141C","__Designer.Colors.selectedOption":"558AC2","__Designer.Colors.background":"11182A","__Designer.Colors.bottomGradient":"202B42","__Designer.Background.ImageTransparency":95,"__Designer.Colors.hoveredOptionTop":"4885A0","__Designer.Colors.elementText":"7692B8","__Designer.Colors.unhoveredOptionBottom":"5471C4"}]]
})

--// Tabs

local SettingsTab = MainFrame:CreateTab({
	Name = "Settings"
})

local FOVSettingsTab = MainFrame:CreateTab({
	Name = "FOV Settings"
})

local FunctionsTab = MainFrame:CreateTab({
	Name = "Functions"
})

--// Settings - Sections

local Values = SettingsTab:CreateSection({
	Name = "Values"
})

local Checks = SettingsTab:CreateSection({
	Name = "Checks"
})

local ThirdPerson = SettingsTab:CreateSection({
	Name = "Third Person"
})

--// FOV Settings - Sections

local FOV_Values = FOVSettingsTab:CreateSection({
	Name = "Values"
})

local FOV_Appearance = FOVSettingsTab:CreateSection({
	Name = "Appearance"
})

--// Functions - Sections

local FunctionsSection = FunctionsTab:CreateSection({
	Name = "Functions"
})

--// Settings / Values

Values:AddToggle({
	Name = "Enabled",
	Value = Settings.Enabled,
	Callback = function(New, Old)
		Settings.Enabled = New
	end
}).Default = Settings.Enabled

Values:AddToggle({
	Name = "Toggle",
	Value = Settings.Toggle,
	Callback = function(New, Old)
		Settings.Toggle = New
	end
}).Default = Settings.Toggle

Settings.LockPart = Parts[1]; Values:AddDropdown({
	Name = "Lock Part",
	Value = Parts[1],
	Callback = function(New, Old)
		Settings.LockPart = New
	end,
	List = Parts,
	Nothing = "Head"
}).Default = Parts[1]

Values:AddTextbox({ -- Using a Textbox instead of a Keybind because the UI Library doesn't support Mouse inputs like Left Click / Right Click...
	Name = "Hotkey",
	Value = Settings.TriggerKey,
	Callback = function(New, Old)
		Settings.TriggerKey = New
	end
}).Default = Settings.TriggerKey

--[[
Values:AddKeybind({
	Name = "Hotkey",
	Value = Settings.TriggerKey,
	Callback = function(New, Old)
		Settings.TriggerKey = stringmatch(tostring(New), "Enum%.[UserInputType]*[KeyCode]*%.(.+)")
	end,
}).Default = Settings.TriggerKey
]]

Values:AddSlider({
	Name = "Sensitivity",
	Value = Settings.Sensitivity,
	Callback = function(New, Old)
		Settings.Sensitivity = New
	end,
	Min = 0,
	Max = 1,
	Decimals = 2
}).Default = Settings.Sensitivity

--// Settings / Checks

Checks:AddToggle({
	Name = "Team Check",
	Value = Settings.TeamCheck,
	Callback = function(New, Old)
		Settings.TeamCheck = New
	end
}).Default = Settings.TeamCheck

Checks:AddToggle({
	Name = "Wall Check",
	Value = Settings.WallCheck,
	Callback = function(New, Old)
		Settings.WallCheck = New
	end
}).Default = Settings.WallCheck

Checks:AddToggle({
	Name = "Alive Check",
	Value = Settings.AliveCheck,
	Callback = function(New, Old)
		Settings.AliveCheck = New
	end
}).Default = Settings.AliveCheck

--// Settings / ThirdPerson

ThirdPerson:AddToggle({
	Name = "Enable Third Person",
	Value = Settings.ThirdPerson,
	Callback = function(New, Old)
		Settings.ThirdPerson = New
	end
}).Default = Settings.ThirdPerson

ThirdPerson:AddSlider({
	Name = "Sensitivity",
	Value = Settings.ThirdPersonSensitivity,
	Callback = function(New, Old)
		Settings.ThirdPersonSensitivity = New
	end,
	Min = 0.1,
	Max = 5,
	Decimals = 1
}).Default = Settings.ThirdPersonSensitivity

--// FOV Settings / Values

FOV_Values:AddToggle({
	Name = "Enabled",
	Value = FOVSettings.Enabled,
	Callback = function(New, Old)
		FOVSettings.Enabled = New
	end
}).Default = FOVSettings.Enabled

FOV_Values:AddToggle({
	Name = "Visible",
	Value = FOVSettings.Visible,
	Callback = function(New, Old)
		FOVSettings.Visible = New
	end
}).Default = FOVSettings.Visible

FOV_Values:AddSlider({
	Name = "Amount",
	Value = FOVSettings.Amount,
	Callback = function(New, Old)
		FOVSettings.Amount = New
	end,
	Min = 10,
	Max = 300
}).Default = FOVSettings.Amount

--// FOV Settings / Appearance

FOV_Appearance:AddToggle({
	Name = "Filled",
	Value = FOVSettings.Filled,
	Callback = function(New, Old)
		FOVSettings.Filled = New
	end
}).Default = FOVSettings.Filled

FOV_Appearance:AddSlider({
	Name = "Transparency",
	Value = FOVSettings.Transparency,
	Callback = function(New, Old)
		FOVSettings.Transparency = New
	end,
	Min = 0,
	Max = 1,
	Decimal = 1
}).Default = FOVSettings.Transparency

FOV_Appearance:AddSlider({
	Name = "Sides",
	Value = FOVSettings.Sides,
	Callback = function(New, Old)
		FOVSettings.Sides = New
	end,
	Min = 3,
	Max = 60
}).Default = FOVSettings.Sides

FOV_Appearance:AddSlider({
	Name = "Thickness",
	Value = FOVSettings.Thickness,
	Callback = function(New, Old)
		FOVSettings.Thickness = New
	end,
	Min = 1,
	Max = 50
}).Default = FOVSettings.Thickness

FOV_Appearance:AddColorpicker({
	Name = "Color",
	Value = FOVSettings.Color,
	Callback = function(New, Old)
		FOVSettings.Color = New
	end
}).Default = FOVSettings.Color

FOV_Appearance:AddColorpicker({
	Name = "Locked Color",
	Value = FOVSettings.LockedColor,
	Callback = function(New, Old)
		FOVSettings.LockedColor = New
	end
}).Default = FOVSettings.LockedColor

--// Functions / Functions

FunctionsSection:AddButton({
	Name = "Reset Settings",
	Callback = function()
		Functions.ResetSettings()
		Library.ResetAll()
	end
})

FunctionsSection:AddButton({
	Name = "Restart",
	Callback = Functions.Restart
})

FunctionsSection:AddButton({
	Name = "Exit",
	Callback = function()
		Functions:Exit()
		Library.Unload()
	end
})

FunctionsSection:AddButton({
	Name = "Copy Script Page",
	Callback = function()
		setclipboard("https://github.com/Exunys/Aimbot-V2")
	end
})


text:
getgenv().Aimbot.Settings = {
	SendNotifications = true,
	SaveSettings = true, -- Re-execute upon changing
	ReloadOnTeleport = true,
	Enabled = true,
	TeamCheck = false,
	AliveCheck = true,
	WallCheck = false, -- Laggy
	Sensitivity = 0, -- Animation length (in seconds) before fully locking onto target
	ThirdPerson = false, -- Uses mousemoverel instead of CFrame to support locking in third person (could be choppy)
	ThirdPersonSensitivity = 3, -- Boundary: 0.1 - 5
	TriggerKey = "MouseButton2",
	Toggle = false,
	LockPart = "Head" -- Body part to lock on
}

getgenv().Aimbot.FOVSettings = {
	Enabled = true,
	Visible = true,
	Amount = 90,
	Color = "255, 255, 255",
	LockedColor = "255, 70, 70",
	Transparency = 0.5,
	Sides = 60,
	Thickness = 1,
	Filled = false
}


text:
--// Variables

local Title = "Exunys Developer"
local StarterGui = game:GetService("StarterGui")

--// Functions

local function SendNotification(TitleArg, DescriptionArg, DurationArg)
    StarterGui:SetCore("SendNotification", {
        Title = TitleArg,
        Text = DescriptionArg,
        Duration = DurationArg
    })
end

--// Main

if not writefile then SendNotification(Title, "Your exploit does not support writefile()", 3) return end
if not isfile then SendNotification(Title, "Your exploit does not support isfile()", 3) return end
if not delfile then SendNotification(Title, "Your exploit does not support delfile()", 3) return end
if not makefolder then SendNotification(Title, "Your exploit does not support makefolder()", 3) return end
if not isfolder then SendNotification(Title, "Your exploit does not support isfolder()", 3) return end
if not delfolder then SendNotification(Title, "Your exploit does not support delfolder()", 3) return end
if not syn then SendNotification(Title, "Your exploit does not support the library syn", 3) return end
if not syn.queue_on_teleport then SendNotification(Title, "Your exploit does not support syn.queue_on_teleport()", 3) return end
if not Drawing then SendNotification(Title, "Your exploit does not support the library Drawing", 3) return end
if not getgenv then SendNotification(Title, "Your exploit does not support getgenv()", 3) return end

SendNotification(Title, "Your exploit supports this script.", 3)


text:
pcall(function()
	if isfolder("Exunys Developer/Aimbot") then
		delfolder("Exunys Developer/Aimbot")
	end
end)


text:
#### 0.7.2 - June 9, 2018
* multi-target net45, netstandard2.0
* support SourceLink
* FSharp.Core 4.5

#### 0.7.1 - December 29, 2017
* multi-target release: net45, net47, netstandard2.0, netcoreapp2.0

#### 0.7.0 - May 16, 2017
* initial release as FsRegEx
* breaking change, derived from FSharpVerbalExpressons, which is now deprecated

repo_name: jackfoxy/FsRegEx

repo_language: F#


text:
source https://nuget.org/api/v2

nuget FSharp.Core

nuget Expecto
nuget Expecto.BenchmarkDotNet
nuget Expecto.FsCheck
nuget Expecto.VisualStudio.TestAdapter version_in_path: true

clitool dotnet-fake

group Formatting
source https://nuget.org/api/v2
source https://ci.appveyor.com/nuget/fsharp-formatting

nuget FSharp.Formatting prerelease
nuget FSharp.Formatting.CommandTool prerelease

group FakeBuild
source https://api.nuget.org/v3/index.json

storage: none

nuget Fake.Core.Target
nuget Fake.IO.FileSystem
nuget Fake.DotNet.Cli
nuget Fake.Tools.Git
nuget Fake.DotNet.MSBuild
nuget Fake.Core.ReleaseNotes
nuget Fake.DotNet.AssemblyInfoFile
nuget Fake.DotNet.Paket
nuget Fake.DotNet.Testing.Expecto
nuget Fake.DotNet.FSFormatting


text:
init:
  - git config --global core.autocrlf input
build_script:
  - cmd: build.cmd All
test: off
version: 0.0.1.{build}
artifacts:
  - path: bin
    name: bin


text:
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27428.2037
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".paket", ".paket", "{63297B98-5CED-492C-A5B7-A5B4F73CF142}"
	ProjectSection(SolutionItems) = preProject
		paket.dependencies = paket.dependencies
		paket.lock = paket.lock
	EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{A6A6AF7D-D6E3-442D-9B1E-58CC91879BE1}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FsRegEx", "src\FsRegEx\FsRegEx.fsproj", "{A44BCE88-C621-4102-BD37-95C4843D50CE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "project", "project", "{BF60BC93-E09B-4E5F-9D85-95A519479D54}"
	ProjectSection(SolutionItems) = preProject
		.github\ISSUE_TEMPLATE.md = .github\ISSUE_TEMPLATE.md
		LICENSE.txt = LICENSE.txt
		src\FsRegEx\paket.template = src\FsRegEx\paket.template
		README.md = README.md
		RELEASE_NOTES.md = RELEASE_NOTES.md
	EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{83F16175-43B1-4C90-A1EE-8E351C33435D}"
	ProjectSection(SolutionItems) = preProject
		docsrc\tools\templates\template.cshtml = docsrc\tools\templates\template.cshtml
	EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "content", "content", "{8E6D5255-776D-4B61-85F9-73C37AA1FB9A}"
	ProjectSection(SolutionItems) = preProject
		docsrc\content\capture.fsx = docsrc\content\capture.fsx
		docsrc\content\emailregex.fsx = docsrc\content\emailregex.fsx
		docsrc\content\group.fsx = docsrc\content\group.fsx
		docsrc\content\index.fsx = docsrc\content\index.fsx
		docsrc\content\matching.fsx = docsrc\content\matching.fsx
		docsrc\content\replacesplit.fsx = docsrc\content\replacesplit.fsx
		docsrc\content\tutorial.fsx = docsrc\content\tutorial.fsx
	EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{ED8079DD-2B06-4030-9F0F-DC548F98E1C4}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FsRegEx.Tests", "tests\FsRegEx.Tests\FsRegEx.Tests.fsproj", "{9D0671EB-3ADB-4866-997D-BA677D4347AA}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "git", "git", "{078A9C52-DDC1-46F4-9235-9E6C89C87AFD}"
	ProjectSection(SolutionItems) = preProject
		.gitattributes = .gitattributes
		.gitignore = .gitignore
	EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".build", ".build", "{7C6D08E7-3EAC-4335-8F4B-252C193C27C9}"
	ProjectSection(SolutionItems) = preProject
		.travis.yml = .travis.yml
		appveyor.yml = appveyor.yml
		build.cmd = build.cmd
		build.fsx = build.fsx
		build.proj = build.proj
		build.sh = build.sh
	EndProjectSection
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Email.Tests", "tests\Email.Tests\Email.Tests.fsproj", "{0D1BE493-6880-4EE4-96F7-F9D8665310F0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{72A309C4-6DDB-478E-A7BA-78613372772E}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Release|Any CPU = Release|Any CPU
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{A44BCE88-C621-4102-BD37-95C4843D50CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{A44BCE88-C621-4102-BD37-95C4843D50CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{A44BCE88-C621-4102-BD37-95C4843D50CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{A44BCE88-C621-4102-BD37-95C4843D50CE}.Release|Any CPU.Build.0 = Release|Any CPU
		{9D0671EB-3ADB-4866-997D-BA677D4347AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{9D0671EB-3ADB-4866-997D-BA677D4347AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{9D0671EB-3ADB-4866-997D-BA677D4347AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{9D0671EB-3ADB-4866-997D-BA677D4347AA}.Release|Any CPU.Build.0 = Release|Any CPU
		{0D1BE493-6880-4EE4-96F7-F9D8665310F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{0D1BE493-6880-4EE4-96F7-F9D8665310F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{0D1BE493-6880-4EE4-96F7-F9D8665310F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{0D1BE493-6880-4EE4-96F7-F9D8665310F0}.Release|Any CPU.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(NestedProjects) = preSolution
		{83F16175-43B1-4C90-A1EE-8E351C33435D} = {A6A6AF7D-D6E3-442D-9B1E-58CC91879BE1}
		{8E6D5255-776D-4B61-85F9-73C37AA1FB9A} = {A6A6AF7D-D6E3-442D-9B1E-58CC91879BE1}
		{9D0671EB-3ADB-4866-997D-BA677D4347AA} = {ED8079DD-2B06-4030-9F0F-DC548F98E1C4}
		{0D1BE493-6880-4EE4-96F7-F9D8665310F0} = {ED8079DD-2B06-4030-9F0F-DC548F98E1C4}
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {66CFF8F9-0961-4245-9C27-746AEF03CC95}
	EndGlobalSection
EndGlobal


text:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <!-- Enable the restore command to run before builds -->
    <RestorePackages Condition=" '$(RestorePackages)' == '' ">true</RestorePackages>
    <!-- Download Paket.exe if it does not already exist -->
    <DownloadPaket Condition=" '$(DownloadPaket)' == '' ">true</DownloadPaket>
    <PaketToolsPath>$(MSBuildThisFileDirectory)</PaketToolsPath>
    <PaketRootPath>$(MSBuildThisFileDirectory)</PaketRootPath>
  </PropertyGroup>
  <PropertyGroup>
    <!-- Paket command -->
    <PaketExePath Condition=" '$(PaketExePath)' == '' ">$(PaketToolsPath)paket.exe</PaketExePath>
    <PaketBootStrapperExePath Condition=" '$(PaketBootStrapperExePath)' == '' ">$(PaketToolsPath)paket.bootstrapper.exe</PaketBootStrapperExePath>
    <PaketCommand Condition=" '$(OS)' == 'Windows_NT'">"$(PaketExePath)"</PaketCommand>
    <PaketCommand Condition=" '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 "$(PaketExePath)"</PaketCommand>
    <PaketBootStrapperCommand Condition=" '$(OS)' == 'Windows_NT'">"$(PaketBootStrapperExePath)"</PaketBootStrapperCommand>
    <PaketBootStrapperCommand Condition=" '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 $(PaketBootStrapperExePath)</PaketBootStrapperCommand>
    <!-- Commands -->
    <RestoreCommand>$(PaketCommand) restore</RestoreCommand>
    <DownloadPaketCommand>$(PaketBootStrapperCommand)</DownloadPaketCommand>
    <!-- We need to ensure packages are restored prior to assembly resolve -->
    <BuildDependsOn Condition="$(RestorePackages) == 'true'">RestorePackages; $(BuildDependsOn);</BuildDependsOn>
  </PropertyGroup>
  <Target Name="CheckPrerequisites">
    <!-- Raise an error if we're unable to locate paket.exe -->
    <Error Condition="'$(DownloadPaket)' != 'true' AND !Exists('$(PaketExePath)')" Text="Unable to locate '$(PaketExePath)'" />
    <MsBuild Targets="DownloadPaket" Projects="$(MSBuildThisFileFullPath)" Properties="Configuration=NOT_IMPORTANT;DownloadPaket=$(DownloadPaket)" />
  </Target>
  <Target Name="DownloadPaket">
    <Exec Command="$(DownloadPaketCommand)" Condition=" '$(DownloadPaket)' == 'true' AND !Exists('$(PaketExePath)')" />
  </Target>
  <Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
    <Exec Command="$(RestoreCommand)" WorkingDirectory="$(PaketRootPath)" />
  </Target>
</Project>


text:
<?xml version="1.0" encoding="utf-8"?>
<Project>
  <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
  <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
  <PropertyGroup>
    <RepoRootDir>$([System.IO.Path]::GetFullPath("$(MSBuildThisFileDirectory)"))</RepoRootDir>
    <BuildDependsOn>
    </BuildDependsOn>
    <CoreBuildDependsOn>
    </CoreBuildDependsOn>
  </PropertyGroup>
  <Target Name="Build">
    <Exec IgnoreStandardErrorWarningFormat="true" Command="dotnet fake build" WorkingDirectory="$(RepoRootDir)" />
  </Target>
  <Target Name="Pack">
  </Target>
  <Target Name="Test">
  </Target>
  <Target Name="VSTest" DependsOnTargets="Test" />
  <Import Project=".paket\Paket.Restore.targets" />
</Project>


text:
#!/usr/bin/env bash

set -eu

cd "$(dirname "$0")"

PAKET_BOOTSTRAPPER_EXE=/paket.bootstrapper.exe
PAKET_EXE=/paket.exe
FAKE_EXE=packages/build/FAKE/tools/FAKE.exe

FSIARGS=""
FSIARGS2=""
OS=${OS:-"unknown"}
if [ "$OS" != "Windows_NT" ]
then
  # Can't use FSIARGS="--fsiargs -d:MONO" in zsh, so split it up
  # (Can't use arrays since dash can't handle them)
  FSIARGS="--fsiargs"
  FSIARGS2="-d:MONO"
fi

run() {
  if [ "$OS" != "Windows_NT" ]
  then
    mono "$@"
  else
    "$@"
  fi
}

yesno() {
  # NOTE: Defaults to NO
  read -p "$1 [y/N] " ynresult
  case "$ynresult" in
    [yY]*) true ;;
    *) false ;;
  esac
}

set +e
run $PAKET_BOOTSTRAPPER_EXE
bootstrapper_exitcode=$?
set -e

if [ "$OS" != "Windows_NT" ] &&
       [ $bootstrapper_exitcode -ne 0 ] &&
       [ $(certmgr -list -c Trust | grep X.509 | wc -l) -le 1 ] &&
       [ $(certmgr -list -c -m Trust | grep X.509 | wc -l) -le 1 ]
then
  echo "Your Mono installation has no trusted SSL root certificates set up."
  echo "This may result in the Paket bootstrapper failing to download Paket"
  echo "because Github's SSL certificate can't be verified. One way to fix"
  echo "this issue would be to download the list of SSL root certificates"
  echo "from the Mozilla project by running the following command:"
  echo ""
  echo "    mozroots --import --sync"
  echo ""
  echo "This will import over 100 SSL root certificates into your Mono"
  echo "certificate repository."
  echo ""
  if yesno "Run 'mozroots --import --sync' now?"
  then
    mozroots --import --sync
  else
    echo "Attempting to continue without running mozroots. This might fail."
  fi
  # Re-run bootstrapper whether or not the user ran mozroots, because maybe
  # they fixed the problem in a separate terminal window.
  run $PAKET_BOOTSTRAPPER_EXE
fi

run $PAKET_EXE restore

[ ! -e build.fsx ] && run $PAKET_EXE update
[ ! -e build.fsx ] && run $FAKE_EXE init.fsx
run $FAKE_EXE "$@" $FSIARGS $FSIARGS2 build.fsx


text:
[![Issue Stats](http://issuestats.com/github/jackfoxy/FsRegEx/badge/issue)](http://issuestats.com/github/jackfoxy/FsRegEx)
[![Issue Stats](http://issuestats.com/github/jackfoxy/FsRegEx/badge/pr)](http://issuestats.com/github/jackfoxy/FsRegEx)

# FsRegEx

The `FsRegEx` library contains composable functions representing all available `Regex` functionality (except timeouts) with the target input string uniformly the last parameter to better support pipe forward `|>` composition and partial application.

- Documentation: [FsRegEx](http://jackfoxy.github.io/FsRegEx/)
- Nuget: [FsRegEx](https://www.nuget.org/packages/FsRegEx "FsRegEx")

## Pull requests welcome so long as

- they include excellent unit test coverage
- they include correct intellisense documentation
- they adhere to the concepts of composability

FsRegEx adheres to [Semantic Versioning](http://semver.org/ "Semantic Versioning"). So long as the project is pre-1.0.0 minor versions may be breaking. Once the project reaches 1.0.0 minor enhancements will be backwards-compatible.

## Maintainer(s)

- [@jackfoxy](https://github.com/jackfoxy)


text:
@echo off
cls

dotnet restore build.proj
dotnet fake build %*


text:
// --------------------------------------------------------------------------------------
// FAKE build script
// --------------------------------------------------------------------------------------

#r "paket: groupref FakeBuild //"

#load "./.fake/build.fsx/intellisense.fsx"

open System.IO
open Fake.Core
open Fake.Core.TargetOperators
open Fake.DotNet
open Fake.IO
open Fake.IO.FileSystemOperators
open Fake.IO.Globbing.Operators
open Fake.DotNet.Testing

// --------------------------------------------------------------------------------------
// START TODO: Provide project-specific details below
// --------------------------------------------------------------------------------------

// Information about the project are used
//  - for version and project name in generated AssemblyInfo file
//  - by the generated NuGet package
//  - to run tests and to publish documentation on GitHub gh-pages
//  - for documentation, you also need to edit info in "docsrc/tools/generate.fsx"

// The name of the project
// (used by attributes in AssemblyInfo, name of a NuGet package and directory in 'src')
let project = "FsRegEx"

// Short summary of the project
// (used as description in AssemblyInfo and as a short summary for NuGet package)
let summary = "Composable F# regular expressions."

// Longer description of the project
// (used as a description for NuGet package; line breaks are automatically cleaned up)
let description = "FsRegEx provides composable F# functionality for all the capabilities of the .NET Regex class including supporting pipe forward |> composability."

// List of author names (for NuGet package)
let author = "Jack Fox"

// Tags for your project (for NuGet package)
let tags = "F# fsharp regularexpression"

// File system information
let solutionFile  = "FsRegEx.sln"

// Default target configuration
let configuration = "Release"

// Pattern specifying assemblies to be tested using Expecto
let testAssemblies = "tests/**/bin" </> configuration </> "net47" </> "FsRegEx.Tests.exe"

// Git configuration (used for publishing documentation in gh-pages branch)
// The profile where the project is posted
let gitOwner = "jackfoxy"
let gitHome = sprintf "%s/%s" "https://github.com" gitOwner

// The name of the project on GitHub
let gitName = "FsRegEx"

// The url for the raw files hosted
let gitRaw = Environment.environVarOrDefault "gitRaw" "https://raw.githubusercontent.com/jackfoxy"

let website = "/FsRegEx"

// --------------------------------------------------------------------------------------
// END TODO: The rest of the file includes standard build steps
// --------------------------------------------------------------------------------------

// Read additional information from the release notes document
let release = ReleaseNotes.load "RELEASE_NOTES.md"

// Helper active pattern for project types
let (|Fsproj|Csproj|Vbproj|Shproj|) (projFileName:string) =
    match projFileName with
    | f when f.EndsWith("fsproj") -> Fsproj
    | f when f.EndsWith("csproj") -> Csproj
    | f when f.EndsWith("vbproj") -> Vbproj
    | f when f.EndsWith("shproj") -> Shproj
    | _                           -> failwith (sprintf "Project file %s not supported. Unknown project type." projFileName)

// Generate assembly info files with the right version & up-to-date information
Target.create "AssemblyInfo" (fun _ ->
    let getAssemblyInfoAttributes projectName =
        [ AssemblyInfo.Title (projectName)
          AssemblyInfo.Product project
          AssemblyInfo.Description summary
          AssemblyInfo.Version release.AssemblyVersion
          AssemblyInfo.FileVersion release.AssemblyVersion
          AssemblyInfo.Configuration configuration ]

    let getProjectDetails projectPath =
        let projectName = Path.GetFileNameWithoutExtension(projectPath)
        ( projectPath,
          projectName,
          Path.GetDirectoryName(projectPath),
          (getAssemblyInfoAttributes projectName)
        )

    !! "src/**/*.??proj"
    |> Seq.map getProjectDetails
    |> Seq.iter (fun (projFileName, _, folderName, attributes) ->
        match projFileName with
        | Fsproj -> AssemblyInfoFile.createFSharp (folderName </> "AssemblyInfo.fs") attributes
        | Csproj -> AssemblyInfoFile.createCSharp ((folderName </> "Properties") </> "AssemblyInfo.cs") attributes
        | Vbproj -> AssemblyInfoFile.createVisualBasic ((folderName </> "My Project") </> "AssemblyInfo.vb") attributes
        | Shproj -> ()
        )
)

// Copies binaries from default VS location to expected bin folder
// But keeps a subdirectory structure for each project in the
// src folder to support multiple project outputs
Target.create "CopyBinaries" (fun _ ->
    !! "src/**/*.??proj"
    -- "src/**/*.shproj"
    |>  Seq.map (fun f -> ((Path.getDirectory f) </> "bin" </> configuration, "bin" </> (Path.GetFileNameWithoutExtension f)))
    |>  Seq.iter (fun (fromDir, toDir) -> Shell.copyDir toDir fromDir (fun _ -> true))
)

// --------------------------------------------------------------------------------------
// Clean build results

let buildConfiguration = DotNet.Custom <| Environment.environVarOrDefault "configuration" configuration

Target.create "Clean" (fun _ ->
    Shell.cleanDirs ["bin"; "temp"]
)

Target.create "CleanDocs" (fun _ ->
    Shell.cleanDirs ["docs"]
)

// --------------------------------------------------------------------------------------
// Build library & test project

Target.create "Build" (fun _ ->
    solutionFile
    |> DotNet.build (fun p ->
        { p with
            Configuration = buildConfiguration })
)

// --------------------------------------------------------------------------------------
// Run the unit tests using test runner

Target.create "RunTests" (fun _ ->
    !! testAssemblies
    |> Expecto.run id
)

// --------------------------------------------------------------------------------------
// Build a NuGet package

Target.create "NuGet" (fun _ ->
    Paket.pack(fun p ->
        { p with
            OutputPath = "bin"
            Version = release.NugetVersion
            ReleaseNotes = String.toLines release.Notes})
)

// --------------------------------------------------------------------------------------
// Generate the documentation

// Paths with template/source/output locations
let bin        = __SOURCE_DIRECTORY__ @@ "bin"
let content    = __SOURCE_DIRECTORY__ @@ "docsrc\\content"
let output     = __SOURCE_DIRECTORY__ @@ "docs"
let files      = __SOURCE_DIRECTORY__ @@ "docsrc\\files"
let templates  = __SOURCE_DIRECTORY__ @@ "docsrc\\tools\\templates"
let formatting = __SOURCE_DIRECTORY__ @@ "packages\\formatting\\FSharp.Formatting"
let docTemplate = "docpage.cshtml"

let github_release_user = Environment.environVarOrDefault "github_release_user" gitOwner
let githubLink = sprintf "https://github.com/%s/%s" github_release_user gitName

let info =
  [ "project-name", project
    "project-author", author
    "project-summary", summary
    "project-github", githubLink
    "project-nuget", "http://nuget.org/packages/FsRegEx" ]

let root = website

let referenceBinaries = []

let layoutRootsAll = new System.Collections.Generic.Dictionary<string, string list>()
layoutRootsAll.Add("en",[   templates;
                            formatting @@ "templates"
                            formatting @@ "templates/reference" ])

Target.create "ReferenceDocs" (fun _ ->
    Directory.ensure (output @@ "reference")

    let binaries () =
        let manuallyAdded =
            referenceBinaries
            |> List.map (fun b -> bin @@ b)

        let conventionBased =
            DirectoryInfo.getSubDirectories <| DirectoryInfo bin
            |> Array.map (fun d ->
                let net45Bin =
                    DirectoryInfo.getSubDirectories d |> Array.filter(fun x -> x.FullName.ToLower().Contains("net45"))
                let net47Bin =
                    DirectoryInfo.getSubDirectories d |> Array.filter(fun x -> x.FullName.ToLower().Contains("net47"))
                if net45Bin.Length > 0 then
                    d.Name, net45Bin.[0]
                else
                    d.Name, net47Bin.[0]  )
            |> Array.map (fun (name, d) ->
                d.GetFiles()
                |> Array.filter (fun x ->
                    x.Name.ToLower() = (sprintf "%s.dll" name).ToLower())
                |> Array.map (fun x -> x.FullName)
                )
            |> Array.concat
            |> List.ofArray

        conventionBased @ manuallyAdded

    binaries()
    |> FSFormatting.createDocsForDlls (fun args ->
        { args with
            OutputDirectory = output @@ "reference"
            LayoutRoots =  layoutRootsAll.["en"]
            ProjectParameters =  ("root", root)::info
            SourceRepository = githubLink @@ "tree/master" }
           )
)

let copyFiles () =
    Shell.copyRecursive files output true
    |> Trace.logItems "Copying file: "
    Directory.ensure (output @@ "content")
    Shell.copyRecursive (formatting @@ "styles") (output @@ "content") true
    |> Trace.logItems "Copying styles and scripts: "

Target.create "Docs" (fun _ ->
    File.delete "docsrc/content/release-notes.md"
    Shell.copyFile "docsrc/content/" "RELEASE_NOTES.md"
    Shell.rename "docsrc/content/release-notes.md" "docsrc/content/RELEASE_NOTES.md"

    File.delete "docsrc/content/license.md"
    Shell.copyFile "docsrc/content/" "LICENSE.txt"
    Shell.rename "docsrc/content/license.md" "docsrc/content/LICENSE.txt"

    DirectoryInfo.getSubDirectories (DirectoryInfo.ofPath templates)
    |> Seq.iter (fun d ->
                    let name = d.Name
                    if name.Length = 2 || name.Length = 3 then
                        layoutRootsAll.Add(
                                name, [templates @@ name
                                       formatting @@ "templates"
                                       formatting @@ "templates/reference" ]))
    copyFiles ()

    for dir in  [ content; ] do
        let langSpecificPath(lang, path:string) =
            path.Split([|'/'; '\\'|], System.StringSplitOptions.RemoveEmptyEntries)
            |> Array.exists(fun i -> i = lang)
        let layoutRoots =
            let key = layoutRootsAll.Keys |> Seq.tryFind (fun i -> langSpecificPath(i, dir))
            match key with
            | Some lang -> layoutRootsAll.[lang]
            | None -> layoutRootsAll.["en"] // "en" is the default language

        FSFormatting.createDocs (fun args ->
            { args with
                Source = content
                OutputDirectory = output
                LayoutRoots = layoutRoots
                ProjectParameters  = ("root", root)::info
                Template = docTemplate } )
)

Target.create "GenerateDocs" ignore

// --------------------------------------------------------------------------------------
// Run all targets by default. Invoke 'build <Target>' to override

Target.create "All" ignore

"Clean"
  ==>"AssemblyInfo"
  ==> "Build"
  ==> "CopyBinaries"
  ==> "RunTests"
  ==> "GenerateDocs"
  ==> "NuGet"
  ==> "All"

"RunTests" ?=> "CleanDocs"

"CleanDocs"
  ==> "Docs"
  ==> "ReferenceDocs"
  ==> "GenerateDocs"

Target.runOrDefault "All"


text:
<!DOCTYPE html>

    <meta charset="utf-8"/>
    <title>release-notes</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <meta name="description" content="Composable F# regular expressions."/>
    <meta name="author" content="Jack Fox"/>

    <link href="https://netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/css/bootstrap-combined.min.css" rel="stylesheet"/>

    <link type="text/css" rel="stylesheet" href="/FsRegEx/content/style.css" />

fsharp.org

github page

FsRegEx

0.7.2 - June 9, 2018

multi-target net45, netstandard2.0

support SourceLink

FSharp.Core 4.5

0.7.1 - December 29, 2017

multi-target release: net45, net47, netstandard2.0, netcoreapp2.0

0.7.0 - May 16, 2017

initial release as FsRegEx

breaking change, derived from FSharpVerbalExpressons, which is now deprecated

FsRegEx

Home page

Get Library via NuGet

Source Code on GitHub

License

Release Notes

Examples

Matching

Match Groups

Replace and Split

Capture

Documentation

API Reference

More...

On the Futility of Email Regex Validation

F# Verbal Expressions Tutorial


text:
<!DOCTYPE html>

    <meta charset="utf-8"/>
    <title>FsRegEx
</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <meta name="description" content="Composable F# regular expressions."/>
    <meta name="author" content="Jack Fox"/>

    <link href="https://netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/css/bootstrap-combined.min.css" rel="stylesheet"/>

    <link type="text/css" rel="stylesheet" href="/FsRegEx/content/style.css" />

fsharp.org

github page

FsRegEx

FsRegEx

The FsRegEx library provides composable F# functionality for nearly all the capabilites of the .NET Regex class, supporting uniform pipe forward |> composability and all Regex features except timeouts. Optionally you can compose F# verbal expressions in natural language. Lazy evaluation ensures natural language composition imposes no performance penalty.

      The FsRegEx library can be installed from NuGet:

PM> Install-Package FsRegEx

Introduction

The FsRegEx library brings a composable regular expression experience to F#.

Composable functions representing all available Regex functionality (except timeouts) with the target input string uniformly the last parameter to better support pipe forward |> composition and partial application.

Collections returned as F# arrays rather than Regex special collections for better composability (but sacrificing lazy match evaluation and timeout support).

Short-cut functions like capture on group name make common multi-step processes a single function.

FsRegEx - A composable F# wrapper type over System.Text.RegularExpressions.Regex for natural language regular expression composition.

FsMatch - A composable F# wrapper type over System.Text.RegularExpressions.Match.

FsGroup - A composable F# wrapper type over System.Text.RegularExpressions.Group.

Basic functional operations on FsRegEx (exists, fold, foldBack, iter, and map), among other composable functions.

FsRegEx speciall collections are returned as arrays. This does lose the advantange of lazy match evaluation the special collections provide.
The underlying object can always be returned from the wrapper.

Better F# Composition

 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:

open FsRegEx

let carRegExp = @"(\w+)\s+(car)"

sprintf "%s %s %s" "One car" "red car" "blue car"
|> matches carRegExp
|> Array.iter(fun m -> printfn "%s" m.Value)

// One car
// red car
// blue car

Short-cut common procedures to a single function

1:
2:
3:
4:
5:
6:
7:
8:

let groupNumberRegExp = @"COD(?<GroupNumber>[0-9]{3})END"
let groupNumberName = "GroupNumber"

sprintf "%s" "COD123END"
|> capture groupNumberRegExp groupNumberName
|> printfn "%s"

// 123

Natural Language Regular Expressions

The library also provides the composable Verbal Expressions language for constructing simple regular expressions in a readable fashion.

 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
14:
15:
16:
17:

open FsRegEx
open System.Text.RegularExpressions
open VerbalExpressions

let groupName =  "GroupNumber"

FsRegEx()
|> add "COD"
|> beginCaptureNamed groupName
|> any "0-9"
|> repeatPrevious 3
|> endCapture
|> then' "END"
|> capture "COD123END" groupName
|> printfn "%s"

// 123

A comparison of using Regex natively, and Verbal Expressions to retrieve the database parameter from a connection string:

 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
14:
15:
16:
17:

let TestConnString =
    """<add name="MyConnString" connectionString="Server=MyServer;Database=MyDatabase;User ID=me;Password=secret;Encrypt=True;TrustServerCertificate=True;Enlist=False;" />"""

let databaseFromRegex connectionString =

    Regex.Match(connectionString, "[dD]atabase=(?<database>.*?);").Groups.["database"].Value

let databaseFromFsRegEx connectionString =

    FsRegEx()
    |> add "[dD]atabase=(?<database>.*?);"
    |> capture connectionString "database"

(databaseFromRegex TestConnString) = (databaseFromFsRegEx TestConnString)
|> printfn "%b"

// true

This example shows the more verbose and descriptive Verbal Expressions:

 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
14:
15:

let databaseFromVerboseFsRegEx connectionString =

    FsRegEx()
    |> anyOf "dD"
    |> then' "atabase="
    |> beginCaptureNamed "database"
    |> add ".*?"
    |> endCapture
    |> then' ";"
    |> capture connectionString "database"

(databaseFromRegex TestConnString) = (databaseFromVerboseFsRegEx TestConnString)
|> printfn "%b"

// true

Documentation

FsRegEx comes with comprehensive API documentation and a tutorial.

API Reference contains documentation for all types, modules, and functions in the library.

F# Verbal Expressions Tutorial contains further explanation of the natural language DSL syntax and many more usage examples.

Versioning

FsRegEx adheres to Semantic Versioning. So long as the project is pre-1.0.0 minor versions may be breaking. Once the project reaches 1.0.0 minor enhancements will be backwards-compatible.

Contributing and copyright

FsRegEx is hosted on GitHub where you can report issues, fork
the project, and submit pull requests, so long as pull requests:

include excellent unit test coverage

include correct intellisense documentation

adhere to the concepts of composability

FsRegEx is available under Public Domain license, which allows modification and
redistribution for both commercial and non-commercial purposes. For more information see the
License file in the GitHub repository.

val carRegExp : string

val sprintf : format:Printf.StringFormat<'T> -> 'T

module Array

from Microsoft.FSharp.Collections

val iter : action:('T -> unit) -> array:'T [] -> unit

val m : obj

val printfn : format:Printf.TextWriterFormat<'T> -> 'T

val groupNumberRegExp : string

val groupNumberName : string

namespace System

namespace System.Text

namespace System.Text.RegularExpressions

val groupName : string

val TestConnString : string

val databaseFromRegex : connectionString:string -> string

val connectionString : string

Multiple items
type Regex =
  new : pattern:string -> Regex + 2 overloads
  member GetGroupNames : unit -> string[]
  member GetGroupNumbers : unit -> int[]
  member GroupNameFromNumber : i:int -> string
  member GroupNumberFromName : name:string -> int
  member IsMatch : input:string -> bool + 1 overload
  member Match : input:string -> Match + 2 overloads
  member MatchTimeout : TimeSpan
  member Matches : input:string -> MatchCollection + 1 overload
  member Options : RegexOptions
  ...

--------------------
Regex(pattern: string) : Regex
Regex(pattern: string, options: RegexOptions) : Regex
Regex(pattern: string, options: RegexOptions, matchTimeout: System.TimeSpan) : Regex

Regex.Match(input: string, pattern: string) : Match
Regex.Match(input: string, pattern: string, options: RegexOptions) : Match
Regex.Match(input: string, pattern: string, options: RegexOptions, matchTimeout: System.TimeSpan) : Match

val databaseFromFsRegEx : connectionString:'a -> 'b

val connectionString : 'a

val databaseFromVerboseFsRegEx : connectionString:'a -> 'b

FsRegEx

Home page

Get Library via NuGet

Source Code on GitHub

License

Release Notes

Examples

Matching

Match Groups

Replace and Split

Capture

Documentation

API Reference

More...

On the Futility of Email Regex Validation

F# Verbal Expressions Tutorial


text:
<!DOCTYPE html>

    <meta charset="utf-8"/>
    <title>On the Futility of Email Regex Validation
</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <meta name="description" content="Composable F# regular expressions."/>
    <meta name="author" content="Jack Fox"/>

    <link href="https://netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/css/bootstrap-combined.min.css" rel="stylesheet"/>

    <link type="text/css" rel="stylesheet" href="/FsRegEx/content/style.css" />

fsharp.org

github page

FsRegEx

On the Futility of Email Regex Validation

Using regular expressions to validate email addresses is notoriously error prone.
The complexity of the address format specification makes constructing a machine readable
grammar correctly discerning the difference between valid and invalid addresses hard.

Evolution of the Internet Message Format

RFC 561 Standardizing Network Mail Headers, September 5, 1973

RFC 680 Message Transmission Protocol, April 30, 1975

RFC 724 Proposed Official Standard for the Format of ARPA Network Messages, May 12, 1977

RFC 733 STANDARD FOR THE FORMAT OF ARPA NETWORK TEXT MESSAGES, November 21, 1977

Obsoletes:  RFC 561, RFC 680, RFC 724

RFC 822 STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES, August 13, 1982

Obsoletes:  RFC 733

RFC 2822 Internet Message Format, April 2001

Obsoletes: RFC 822

RFC 5322 Internet Message Format, October 2008

Obsoletes: RFC 2822

Testing Regular Expressions that Parse Email Addresses

The tests are derived from a suite of tests available at RFC 822 Email Address Parser in PHP,
which we have converted to run under FsRegEx. The PHP parser author claims a much better success rate than we were able to achieve with
any regular expression, although still not perfect. The site also provides more background on the "muddy" internet specification.

The suite consists of 280 validataion tests, which we converted to Expecto tests
here. Run with the
Email.Tests console app.

We chose some email parsing regular expressions available on the internet for testing:

Simple, from emailregex.com.

Moderate, from emailregex.com.

Complex, from emailregex.com.

Ultimate, from
Mail::RFC822::Address: regexp-based address validation. This regular expression was machine generated.

Note that emailregex.com headlines "Email Address Regular Expression That 99.99% Works". The site offers many different
email address regular expressions. Apparently we never found the one that works 99.99% on our test suite.

We also tested a much simpler (non-regular expression) checker, that simply tests string for a single at sign that is not wrapped in quotes and is not either
the first or last character in the string here.

Results

Simple:

132 passed, 148 failed, 98 false negatives

Moderate:

190 passed, 90 failed, 72 false negatives

Complex:

172 passed, 108 failed, 60 false negatives

Ultimate:

170 passed, 110 failed, 11 false negatives

One At Sign:

162 passed, 118 failed, 1 false negative (test166)

Conclusion

The evolution of the Internet Email Address specification and way it is documented make it impossible to settle on a completely deterministic grammar covering all cases.
With that in mind it is already impossible to make any complete parser, let alone one based on a regular expression. That being said, the test results do not reflect
the likelihood of meeting any of the 280 test cases in the wild.

Simply determining if there is a correctly placed at sign is perhaps the best filter for most practical purposes, given the importance of this criterion.
(Note that even our attempt at rejecting multiple at signs resulted in a false negative.)

FsRegEx

Home page

Get Library via NuGet

Source Code on GitHub

License

Release Notes

Examples

Matching

Match Groups

Replace and Split

Capture

Documentation

API Reference

More...

On the Futility of Email Regex Validation

F# Verbal Expressions Tutorial


text:
<!DOCTYPE html>

    <meta charset="utf-8"/>
    <title>Matching
</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <meta name="description" content="Composable F# regular expressions."/>
    <meta name="author" content="Jack Fox"/>

    <link href="https://netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/css/bootstrap-combined.min.css" rel="stylesheet"/>

    <link type="text/css" rel="stylesheet" href="/FsRegEx/content/style.css" />

fsharp.org

github page

FsRegEx

Matching

IsMatch

The System.Text.RegularExpressions.Regex Class provides several overloads of isMatch. The same overloads are available in the FsRegEx class and as individually
named functions, including overloads and functions using RegexOptions. Timeout is not supported at this time.

is match on first occurence

 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:

open FsRegEx
module F = FsRegEx

let partNumbers = [| "1298-673-4192"; "A08Z-931-468A";
                    "_A90-123-129X"; "12345-KKA-1230";
                    "0919-2893-1256" |]

for partNumber in partNumbers do
    printfn "%s %s a valid part number."
        partNumber
        (if (F.isMatch @"^[a-zA-Z0-9]\d{2}[a-zA-Z0-9](-\d{3}){2}[A-Za-z0-9]$" partNumber ) then "is"
            else "is not")

let fsRegEx = new FsRegEx(@"^[a-zA-Z0-9]\d{2}[a-zA-Z0-9](-\d{3}){2}[A-Za-z0-9]$")

for partNumber in partNumbers do
    printfn "%s %s a valid part number."
        partNumber
        (if (fsRegEx.IsMatch(partNumber)) then "is"
            else "is not")

// 1298-673-4192 is a valid part number.
// A08Z-931-468A is a valid part number.
// _A90-123-129X is not a valid part number.
// 12345-KKA-1230 is not a valid part number.
// 0919-2893-1256 is not a valid part number.

is match starting at position

 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:

let labeledPartNumbers = [| "Part Number: 1298-673-4192"; "Part No: A08Z-931-468A";
                        "_A90-123-129X"; "123K-000-1230";
                        "SKU: 0919-2893-1256" |]

for partNumber in labeledPartNumbers do
    let start = partNumber.IndexOf(':')
    if (start >= 0) then
        printfn "%s %s a valid part number."
            partNumber
            (if (F.isMatchAt @"^[a-zA-Z0-9]\d{2}[a-zA-Z0-9](-\d{3}){2}[A-Za-z0-9]$" start partNumber) then "is" else "is not")
    else
        printfn "Cannot find starting position in %s." partNumber

for partNumber in labeledPartNumbers do
    let start = partNumber.IndexOf(':')
    if (start >= 0) then
        printfn "%s %s a valid part number."
            partNumber
            (if (fsRegEx.IsMatch(partNumber, start)) then "is" else "is not")
    else
        printfn "Cannot find starting position in %s." partNumber

// Part Number: 1298-673-4192 is a valid part number.
// Part No: A08Z-931-468A is a valid part number.
// Cannot find starting position in _A90-123-129X.
// Cannot find starting position in 123K-000-1230.
// SKU: 0919-2893-1256 is not a valid part number.

FsMatch

FsMatch provides the functionality in the System.Text.RegularExpressions.Match Class, but returning arrays of objects instead of special collections.

iterate over matches

1:
2:
3:
4:
5:
6:

"Who writes these notes?"
|> FsRegEx.matches @"\b\w+es\b"
|> Array.iter (fun m -> printfn "Found '%s' at position %i" m.Value m.Index)

// Found 'writes' at position 4
// Found 'notes' at position 17

first match and iteration

 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
14:

let regExpr = @"\b\w+es\b"
let sentence = "Who writes these notes and uses our paper?"

let m = FsRegEx.firstMatch regExpr sentence

if m.Success then
    printfn "Found first 'es' in '%s' at position %i" m.Value m.Index
    sentence
    |> FsRegEx.matchesAt regExpr (m.Index + m.Length)
    |> Array.iter (fun m -> printfn "Also found '%s' at position %i" m.Value m.Index)

// Found first 'es' in 'writes' at position 4
// Also found 'notes' at position 17
// Also found 'uses' at position 27

val partNumbers : string []

val partNumber : string

val printfn : format:Printf.TextWriterFormat<'T> -> 'T

val fsRegEx : obj

val labeledPartNumbers : string []

val start : int

System.String.IndexOf(value: string) : int
System.String.IndexOf(value: char) : int
System.String.IndexOf(value: string, comparisonType: System.StringComparison) : int
System.String.IndexOf(value: string, startIndex: int) : int
System.String.IndexOf(value: char, startIndex: int) : int
System.String.IndexOf(value: string, startIndex: int, comparisonType: System.StringComparison) : int
System.String.IndexOf(value: string, startIndex: int, count: int) : int
System.String.IndexOf(value: char, startIndex: int, count: int) : int
System.String.IndexOf(value: string, startIndex: int, count: int, comparisonType: System.StringComparison) : int

module Array

from Microsoft.FSharp.Collections

val iter : action:('T -> unit) -> array:'T [] -> unit

val m : obj

val regExpr : string

val sentence : string

FsRegEx

Home page

Get Library via NuGet

Source Code on GitHub

License

Release Notes

Examples

Matching

Match Groups

Replace and Split

Capture

Documentation

API Reference

More...

On the Futility of Email Regex Validation

F# Verbal Expressions Tutorial


text:
<!DOCTYPE html>

    <meta charset="utf-8"/>
    <title>license</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <meta name="description" content="Composable F# regular expressions."/>
    <meta name="author" content="Jack Fox"/>

    <link href="https://netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/css/bootstrap-combined.min.css" rel="stylesheet"/>

    <link type="text/css" rel="stylesheet" href="/FsRegEx/content/style.css" />

fsharp.org

github page

FsRegEx

This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to http://unlicense.org/

FsRegEx

Home page

Get Library via NuGet

Source Code on GitHub

License

Release Notes

Examples

Matching

Match Groups

Replace and Split

Capture

Documentation

API Reference

More...

On the Futility of Email Regex Validation

F# Verbal Expressions Tutorial


text:
<!DOCTYPE html>

    <meta charset="utf-8"/>
    <title>Tutorial: F# Verbal Expressions
</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <meta name="description" content="Composable F# regular expressions."/>
    <meta name="author" content="Jack Fox"/>

    <link href="https://netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/css/bootstrap-combined.min.css" rel="stylesheet"/>

    <link type="text/css" rel="stylesheet" href="/FsRegEx/content/style.css" />

fsharp.org

github page

FsRegEx

Tutorial: F# Verbal Expressions

The VerbalExpressions module includes the FsRegEx type which wraps the familiar .NET RegEx in a type with useful functional members.
Multiple constructors start with a regular expression in the constructor.

1:

let fsRegEx = new FsRegEx(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}")

This the module is an experimental DSL that allows you to compose regular expressions in natural language
using the immutable FsRegEx type. The remainder of this tutorial is concerned with the experimental DSL which is, frankly, not that practical.

For practical examples of using the core FsRegEx module for composability, see the following examples:

Matching

Matching Groups

Replace and Split

Capture

Verbal Expressions DSL

You can compose values of the FsRegEx type with the |> operator, including creating a new regular expression by logical or on 2 existing FsRegExs.

 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:

#r "FsRegEx.dll"
open FsRegEx
open System.Text.RegularExpressions
open VerbalExpressions

let v =
    CommonFsRegEx.Email
    |> fsRegExOrFsRegEx RegexOptions.None CommonFsRegEx.Url

let foundEmail =
    v
    |> isMatch "test@github.com"

let foundUrl =
    v
    |> isMatch "http://www.google.com"

printfn "%b" foundEmail
printfn "%b" foundUrl

// true
// true

Natural language composition consists of building up a new FsRegEx from an old by functions which append special characters, groups, modifiers, and other attributes of the regular expression language.

function : 'T -> FsRegEx -> FsRegEx

See the API documentation for all the regular expression functions available.

 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:

open VerbalExpressions

let foundFromGithub =
    FsRegEx()
    |> startOfLine
    |> something
    |> then' "github.com"
    |> endOfLine
    |> isMatch "test@github.com"

printfn "%b" foundFromGithub

// true

You do not have to worry about escaping special characters in your regular expression.

 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:

let foundSomethingSpecial =
    FsRegEx()
    |> startOfLine
    |> something
    |> then' "*+?"
    |> anything
    |> isMatch "blah blah blah*+?yackety yack"

printfn "%b" foundSomethingSpecial

// true

Sometimes you may need more power than the natural language provides, or you just need to include a snippet of native regular expression. The add function lets you do that.

1:
2:
3:
4:
5:
6:
7:
8:
9:

let foundSpecialInMultiline =
    FsRegEx()
    |> add @"phrase1\*\+\?"
    |> anything
    |> isMatch @"phrase1*+?RestOfLine\n"

printfn "%b" foundSpecialInMultiline

// true

FsRegExs posses all the power of the .Net RegEx class in a composable form.

 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:

let n =
    FsRegEx()
    |> word
    |> matches "three words here"

printfn "%i" n.Length

// 3

let betterFormat =
    FsRegEx()
    |> add "\s+"
    |> or' "whitespace"
    |> replace "This     is   text with   far  too   much   whitespace" " "

printfn "%s" betterFormat

// This is text with far too much

let groupName =  "GroupNumber"

FsRegEx()
|> add "COD"
|> beginCaptureNamed groupName
|> any "0-9"
|> repeatPrevious 3
|> endCapture
|> then' "END"
|> capture "COD123END" groupName
|> printfn "%s"

// 123

FsRegEx comes with first class support for unicode, including unicode general categories and .Net extension blocks.

1:
2:
3:
4:
5:
6:
7:
8:
9:

FsRegEx()
|> beginCaptureNamed "upper"
|> unicodeCategory Unicode.UnicodeGeneralCategory.LetterUppercase
|> add "+"
|> endCapture
|> capture "some mixed case WORDS" "upper"
|> printfn "%s"

// WORDS

val fsRegEx : obj

namespace System

namespace System.Text

namespace System.Text.RegularExpressions

val v : obj

type RegexOptions =
  | None = 0
  | IgnoreCase = 1
  | Multiline = 2
  | ExplicitCapture = 4
  | Compiled = 8
  | Singleline = 16
  | IgnorePatternWhitespace = 32
  | RightToLeft = 64
  | ECMAScript = 256
  | CultureInvariant = 512

field RegexOptions.None: RegexOptions = 0

val foundEmail : bool

val foundUrl : bool

val printfn : format:Printf.TextWriterFormat<'T> -> 'T

val foundFromGithub : bool

val foundSomethingSpecial : bool

val foundSpecialInMultiline : bool

val n : obj

val betterFormat : string

val groupName : string

FsRegEx

Home page

Get Library via NuGet

Source Code on GitHub

License

Release Notes

Examples

Matching

Match Groups

Replace and Split

Capture

Documentation

API Reference

More...

On the Futility of Email Regex Validation

F# Verbal Expressions Tutorial


text:
<!DOCTYPE html>

    <meta charset="utf-8"/>
    <title>Replacing and Splitting
</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <meta name="description" content="Composable F# regular expressions."/>
    <meta name="author" content="Jack Fox"/>

    <link href="https://netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/css/bootstrap-combined.min.css" rel="stylesheet"/>

    <link type="text/css" rel="stylesheet" href="/FsRegEx/content/style.css" />

fsharp.org

github page

FsRegEx

Replacing and Splitting

Replace

The System.Text.RegularExpressions.Regex Class provides several overloads of replace. The same overloads are available in the FsRegEx class and as individually
named functions, including overloads and functions using RegexOptions. Timeout is not supported at this time.

simple replacement of all occurence

1:
2:
3:
4:
5:
6:
7:
8:
9:

open System
open System.Text.RegularExpressions

"This is   text with   far  too   much   " +
                "whitespace."
|> FsRegEx.replace "\\s+" " "
|>printfn "Replacement String: %s"

// Replacement String: This is text with far too much whitespace.

replace max time starting at

Replaces a specified maximum number of strings starting at location that match a regular expression pattern with a specified replacement string.

 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
14:

"Instantiating a New Type\n" +
    "Generally, there are two ways that an\n" +
    "instance of a class or structure can\n" +
    "be instantiated. "
|> FsRegEx.replaceMaxTimesStartAtOpt "^.*$" RegexOptions.Multiline "\n$&"  -1 1
|> printfn "%s"

// Instantiating a New Type
//
// Generally, there are two ways that an
//
// instance of a class or structure can
//
// be instntiated.

replace with MatchEvaluator

 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
14:
15:

let capText (m : Match) =
    // Get the matched string.
    let x = m.Value
    // If the first char is lower case...
    if (Char.IsLower(x.[0])) then
        // Capitalize it.
        (Char.ToUpper(x.[0]).ToString()) + (x.Substring(1, x.Length - 1))
    else
        x

"four score and seven years ago"
|> FsRegEx.replaceByMatch @"\w+" (new MatchEvaluator(capText))
|> printfn "result=[%s]"

// result=[Four Score And Seven Years Ago]

Splitting

The System.Text.RegularExpressions.Regex Class provides several overloads of split to split strings into arrays. The same overloads are available in the FsRegEx class and as individually
named functions, including overloads and functions using RegexOptions. Timeout is not supported at this time.

split with Regex option

1:
2:
3:
4:
5:
6:
7:
8:
9:

"Abc1234Def5678Ghi9012Jklm"
|> FsRegEx.splitOpt "[a-z]+" RegexOptions.IgnoreCase
|> Array.iter (fun x -> printfn "'%s'" x)

// ''
// '1234'
// '5678'
// '9012'
// ''

namespace System

namespace System.Text

namespace System.Text.RegularExpressions

val printfn : format:Printf.TextWriterFormat<'T> -> 'T

type RegexOptions =
  | None = 0
  | IgnoreCase = 1
  | Multiline = 2
  | ExplicitCapture = 4
  | Compiled = 8
  | Singleline = 16
  | IgnorePatternWhitespace = 32
  | RightToLeft = 64
  | ECMAScript = 256
  | CultureInvariant = 512

field RegexOptions.Multiline: RegexOptions = 2

val capText : m:Match -> string

val m : Match

type Match =
  inherit Group
  member Groups : GroupCollection
  member NextMatch : unit -> Match
  member Result : replacement:string -> string
  static member Empty : Match
  static member Synchronized : inner:Match -> Match

val x : string

property Capture.Value: string

type Char =
  struct
    member CompareTo : value:obj -> int + 1 overload
    member Equals : obj:obj -> bool + 1 overload
    member GetHashCode : unit -> int
    member GetTypeCode : unit -> TypeCode
    member ToString : unit -> string + 1 overload
    static val MaxValue : char
    static val MinValue : char
    static member ConvertFromUtf32 : utf32:int -> string
    static member ConvertToUtf32 : highSurrogate:char * lowSurrogate:char -> int + 1 overload
    static member GetNumericValue : c:char -> float + 1 overload
    ...
  end

Char.IsLower(c: char) : bool
Char.IsLower(s: string, index: int) : bool

Char.ToUpper(c: char) : char
Char.ToUpper(c: char, culture: Globalization.CultureInfo) : char

String.Substring(startIndex: int) : string
String.Substring(startIndex: int, length: int) : string

property String.Length: int

type MatchEvaluator =
  delegate of Match -> string

field RegexOptions.IgnoreCase: RegexOptions = 1

type Array =
  member Clone : unit -> obj
  member CopyTo : array:Array * index:int -> unit + 1 overload
  member GetEnumerator : unit -> IEnumerator
  member GetLength : dimension:int -> int
  member GetLongLength : dimension:int -> int64
  member GetLowerBound : dimension:int -> int
  member GetUpperBound : dimension:int -> int
  member GetValue : [<ParamArray>] indices:int[] -> obj + 7 overloads
  member Initialize : unit -> unit
  member IsFixedSize : bool
  ...

val iter : action:('T -> unit) -> array:'T [] -> unit

FsRegEx

Home page

Get Library via NuGet

Source Code on GitHub

License

Release Notes

Examples

Matching

Match Groups

Replace and Split

Capture

Documentation

API Reference

More...

On the Futility of Email Regex Validation

F# Verbal Expressions Tutorial