Carp 1.0.5
dotnet tool install --global Carp --version 1.0.5
dotnet new tool-manifest
dotnet tool install --local Carp --version 1.0.5
#tool dotnet:?package=Carp&version=1.0.5
nuke :add-package Carp --version 1.0.5
Learning Carp
Reference manual and instruction guide for Carp
Version
1.0.6for Carp1.0.5
This is a reference manual and instruction guide filled with samples and explanations learning Carp.
Outline
- Learning Carp
Getting started
Installation
Ensure dotnet is installed
Run the following to install it from NuGet
$ dotnet tool install --global CarpCheck carp is installed
$ carp --versionIt is recommended to use Visual Studio Code with the Carp Language extension to aide with development
Hello world
Create a new file, give it the .carp extension, and write the following code:
import std.io
IO.println('Hello, world!')
Now run the file with the Carp interpreter:
$ carp hello.carp
You should see the output Hello, world! printed to the console.
Alternatively you can go file-less and use the REPL:
$ carp
: import std.io
:
: IO.println('Hello, world!')
Basics
Types
Primitive types
# Strings are only denoted with single quotes
str my_string = 'Hello, world!'
# Booleans are either true or false
bool my_bool = true
# Integers are whole numbers, decimals, and negative numbers
int my_int = 10
int my_decimal_int = -10.5
# Obj is the base type for all objects
obj my_obj = 5
# Null is the absence of a value
obj my_null = null
# Objects are automatically assigned null if not given a value
obj my_auto_null
# value is null
# If the object is a struct though, they will be auto-instatiated
int my_auto_int
# value is 0
# Let implicitly determines the type of the variable based on the value
let my_auto_typed_int = 5
Compound types
# Collections are a list of objects, they are denoted with *
int* my_list = [1, 2, 3, 4, 5]
# Maps are a collection of key-value pairs
str:int my_map = [
'key1': 1,
'key2': 2,
'key3': 3
]
# Ranges are a collection between two values
range<int> my_range = 1..10
Type behaviours
Object obj
This is the base object of everything.
String str
Strings are immutable, meaning they cannot be changed after they are created. They are represented by surrounding the text value with single quotes 'hello world'. Unlike other languages, double quotes are not permitted. They can use backslashes to escape a quote character or another backslash.
Casting:
- From any type to
strwill perform override.stringon it intConverts the string into an integerchrConverts the string into a character, using the first character onlyboolConverts the string into a boolean, comparing it to 'true' and 'false'
Properties:
.lengthintCalculates the length of the string.lowerstrConverts the string to lowercase.upperstrConverts the string to uppercase.cleanstrRemoves leading and trailing whitespace.split(str delimiter)str*Splits the string into an array of strings based on a delimiter.replace(str source, str replacement)strReplaces all occurrences of a substring with another substring.contains(str search)boolChecks if the string contains a substring.startswith(str prefix)boolChecks if the string starts with a substring.endswith(str suffix)boolChecks if the string ends with a substring
Overrides:
iterateString overrides the iterate method to allow enumeration of each character in the stringaddString overrides the add method to concatenate two stringsmultiplyString overrides the multiply method to repeat the string a specified number of timesdivideString overrides the divide method to split the string into a collection of strings based on a delimiter, which can be astror achrindexString overrides the index method to get the character at a specified index, if a range is passed, it will return a substring based on that range
Character chr
Characters are represented by using a backtick and then a single character \a` .
Casting:
intConverts the character into an integer
Properties:
.lowerchrConverts the character to lowercase.upperchrConverts the character to uppercase.is_upperboolChecks if the character is uppercase.is_lowerboolChecks if the character is lowercase.is_digitboolChecks if the character is a digit.is_alphaboolChecks if the character is a letter.is_symbolboolChecks if the character is a symbol.is_whitespaceboolChecks if the character is whitespace.is_upperboolChecks if the character is uppercase.is_lowerboolChecks if the character is lowercase
Integer int
Integers are whole numbers, decimals, and negative numbers. They are represented by a number, and can be negative or decimal. They can be used in mathematical operations, and are represented by 5, -5, 5.5. They are a struct type which means they default to 0 instead of null.
Casting:
chrConverts the integer into a character
Overrides:
addInteger overrides the add method to add two integerssubtractInteger overrides the subtract method to subtract two integersmultiplyInteger overrides the multiply method to multiply two integersdivideInteger overrides the divide method to divide two integersmoduloInteger overrides the modulo method to get the remainder of two integerspowInteger overrides the power method to raise the integer to the power of another integergreaterInteger overrides the greater method to check if the integer is greater than another integerlessInteger overrides the less method to check if the integer is less than another integernegateInteger overrides the negate method to get the inverse of the integerstepInteger overrides the step method to get the current integer plus 1iterateIterator from 0 to the integer
Boolean bool
Booleans are either true or false. They are represented by true or false. They can be used in logical operations, and are a struct type which means they default to false instead of null.
Overrides:
notBoolean overrides the not method to get the inverse of the boolean
Function func
Functions are a type of object that can be called. They are represented by func<treturn> where the generic type is the return type of the function. They can be called by using the () operator.
Overrides:
callFunction overrides the call method to execute the body of the function
Collection obj*
Collections are a list of objects, they are represented by [obj0, obj1, obj2]. They can be accessed by index, and can be iterated over. When a collection is not supplied with a type, it will default to the highest common type, if one exists, else obj*
Casting:
tvalue*Converts the collection into a new collection, if it the item's type extends the sub_type
Properties:
.lengthintCalculates the length of the collection.firsttvalueGets the first item in the collection.lasttvalueGets the last item in the collection.append(tvalue item)voidAppends an item to the end of the collection.remove(tvalue item)voidRemoves an item from the collection.removeat(int index)voidRemoves an item from the collection at a specified index.insert(int index, tvalue item)voidInserts an item into the collection at a specified index.contains(tvalue item)boolChecks if the collection contains an item.within(int index)boolChecks if the index is within the bounds of the collection.clear()voidClears the collection.get(int index, tvalue default)tvalueGets the item at a specified index, if a range is passed, it will return a sub-collection based on that range
Overrides:
addCollection overrides the add method allow merging two collections of the same typeiterateCollection overrides the iterate method to allow enumeration of each item in the collectionindexCollection overrides the index method to get the item at a specified index, if a range is passed, it will return a sub-collection based on that range
Map obj:obj
Maps are a collection of key-value pairs, they are represented by ['key0': value0, 'key1': value1]. They can be accessed by key, and can be iterated over. It uses the same high common type rule as collections, if no type is supplied.
Properties:
.lengthintCalculates the length of the map.keystkey*Gets all the keys in the map.valuestvalue*Gets all the values in the map.remove(tkey key)voidRemoves an item from the map.contains(tkey key)boolChecks if the map contains a key.clear()voidClears the map.get(tkey key, tvalue default)tvalueGets the value at a specified key, if the key is not found, it will return the default value
Range range
Ranges are represented as start..end, they must be the same type and the object must override Less, Subtract and Step. As a generic type, their type is referenced as range<tvalue>
Casting:
tvalue*Converts range into a collection of type
Properties:
.lengthintCalculates length of range based onend-start.starttvalueStart of range.endtvalueEnd of range
Overrides:
iterateRange overrides the iterate method to allow enumeration between the start and the end points
Type type
Types are used to store the type of an object, they are represented by t(obj). They can be used to check the type of an object, and can be used in casting.
Casting:
- From any type to
typewill return the type of the object
Properties:
.namestrGets the name of the type.basetypeGets the base type of the type.is_structboolChecks if the type is a struct
Byte Sequence byte_sequence
Byte sequences are a collection of bytes. They have no representation in the language, but can be used to store binary data. Any resources of an unknown type will default to a byte sequence.
Properties:
.lengthintCalculates the length of the byte sequence
Overrides:
indexByte Sequence overrides the index method to get the byte at a specified index, if a range is passed, it will return a sub-sequence based on that range. Note that the byte returned will be in the form of an integer, not a character.
Comments
Comments are used to annotate code, they are ignored by the interpreter.
# This is a comment
#(
This is a multi-line comment
)
Make sure a space is present after the # symbol, otherwise the preprocessor will get confused with a directive.
Blocks
Blocks are used to group statements together, there are two types of blocks in Carp: the shorthand block and the full block.
# Shorthand block
int add(int a, int b) -> a + b
# Full block
int add(int a, int b) {
return a + b
}
Casting and handling types
Casting is used to convert one type to another, and type checking is used to determine the type of an object.
# Object type coercion, this is implicit type conversion
int my_num = '5'
# value is 5
# Explicit type conversion
int my_num = '5' ~ int
# value is 5
# Type checking
bool is_int = my_num ~~ int
# value is true
# Type storage
type my_type = 'hi' ~ type
# value is str
Operators
Operators are used to perform operations on values, such as addition, subtraction, and comparison.
# Arithmetic operators
int add = 5 + 5
int subtract = 5 - 5
int multiply = 5 * 5
int divide = 5 / 5
int modulo = 5 % 5
int power = 5 ^ 5
# All of these operators can be combined with the assignment operator to modify the value of a variable
int num = 5
num += 5
# Comparison operators
bool equal = 5 == 5
bool not_equal = 5 != 5
# or alternatively
not_equal = 5 <> 5
bool greater_than = 5 > 5
bool less_than = 5 < 5
bool greater_than_or_equal = 5 >= 5
bool less_than_or_equal = 5 <= 5
# Logical operators
bool and = true & false
bool or = true | false
# Note that logical operators work on not only booleans, but also other objects,
# Where it will compare them with null (null being false and anything else being true)
# For example, null | 5 will return 5, as null is falsey
str value = null | 'hi'
# value is 'hi'
bool not = !true
bool inverse = -num
Control flow
Control flow is used to determine the flow of the program, such as loops and conditionals.
# If statements
if 5 > 4 -> IO.println('5 is greater than 4')
# If-else statements
if 5 > 4 -> IO.println('5 is greater than 4')
else -> IO.println('5 is not greater than 4')
# While loops
int i = 0
while i < 5 {
IO.println(i)
i += 1
}
# For loops
for i : 0..5 {
IO.println(i)
}
# The 0..5 can actually be simplified to ..5
# But since for requires a collection, it will even take just 5
# For loops without storing the index
for 0..5 -> IO.println('hi')
Functions and methods
Functions are used to encapsulate code into reusable blocks, they can take arguments and return values.
# The return type is specified first, or if none, void
int add(int a, int b) -> a + b
void print(str message) -> IO.println(message)
# The arguments and their types are specified in the parentheses
# The block is specified directly after the arguments
Classes and structs
Classes are used to define objects with properties and methods, they can be instantiated and used in the program.
class MyClass {
# Properties are defined the same way as variables
int my_prop
# Methods are defined in the same way as functions
void print_prop() -> IO.println(this.my_prop)
# Constructors are used to initialize the object
# Also! You can have multiple constructors with different arguments
void init(int prop) -> this.my_prop = prop
}
# Instantiating a class
MyClass my_obj = MyClass.new(5)
my_obj.print_prop()
Structs
Structs are similar to classes, but they cannot be null, meaning they must have an empty constructor for implicit instantiation
struct Vec2 {
int x
int y
void init() {
this.x = 0
this.y = 0
}
void init(int x, int y) {
this.x = x
this.y = y
}
str string() -> 'Vec2(' + this.x + ', ' + this.y + ')'
}
Overriding
Classes and structs can override special methods to change how operators and other operations work on them. The following methods can be overridden:
initvoid- Constructor, this is called when the object is created, you can have multiple constructors with different argumentsstringstr- String representation of the object, casting to a string or usingIO.printlnwill call this methodproperty(str prop)obj- Gets a property of the object, this is called when using the.operatorsetproperty(str prop, obj value)obj- Sets a property of the object, this is called when assigning to a.propertycall(obj* args)obj- Calls the object as a function, this is called when using the()operatorindex(obj index)obj- Gets an index of the object, this is called when using the[]operatorsetindex(obj index, obj value)obj- Sets an index of the object, this is called when assigning to a[]indexadd(obj value)obj- Adds a value to the object, this is called when using the+operatorsubtract(obj value)obj- Subtracts a value from the object, this is called when using the-operatormultiply(obj value)obj- Multiplies the object by a value, this is called when using the*operatordivide(obj value)obj- Divides the object by a value, this is called when using the/operatormodulus(obj value)obj- Gets the remainder of the object divided by a value, this is called when using the%operatorless(obj value)bool- Checks if the object is less than a value, this is called when using the<operatorgreater(obj value)bool- Checks if the object is greater than a value, this is called when using the>operatorpow(obj value)obj- Raises the object to the power of a value, this is called when using the^operatormatch(obj value)bool- Checks if the object matches a value, a default implementation exists for this, this is called when using the==operatorstep()obj- Gets the next value of the object, this is used inrangeobjects and can be used for iterationnegate()obj- Gets the inverse of the object, this is called when using the-operatoriterate()obj*- Gets an iterator for the object, this is called when using theforloop or a winded expression
Shortcuts
Winding and filtering allow applying an operation to a sequence of values. As of the current version, filtering is not supported, but uses the ;; operator. Winding can be done using the :: operator.
int nums = [1, 2, 3, 4, 5]
# this can actually be done using int* nums = 1..6
# or 1..6 ~ int* if type coercion is disabled
int new_nums = nums :: * 10
# value will be [10, 20, 30, 40, 50]
The winding operator :: converts a collection into a "winded" object where all operations applied to it, will be applied to all items. This includes operators and properties, note that only one operation can be applied per wind.
str* names = ['John', 'Jane', 'Francis', 'Joe']
str* lower_names = names :: .lower
# value is ['john', 'jane', 'francis', 'joe']
As of the latest version, bool operators cannot be used with winded expressions and produce a collection, instead they will produce a single value, based on whether they all matched true.
The standard library and imports
Imports are used to include external code into the program, such as the standard library or other scripts.
They are controlled by the active package resolver. The std prefix is available to access the standard library. The git prefix is available to access the git package resolver. Not specifying one of these will search the current active directory or the current package's source.
# Standard library import
import std.io
# Github import
import git.username.repo
# Local import
import utils
IO
The IO package is responsible for input and output operations in Carp. It contains functions for controlling the connected console.
<details> <summary>Reference</summary>
void println(obj str)
Writes a line to the output.
void printw(obj str)
Writes a string to the output, without a newline.
str readln(obj str)
Reads a line from the input.
chr readw(bool hideKeyStrokes)
Reads a character from the input.
void clear()
Clears the output.
void move(int x, int y)
Move the cursor to the specified position.
</details>
Math
Math is for extended mathematical operations, not included in the base language's operators. It also contains random number generation functions.
<details>
<summary>Reference</summary>
int random(int min, int max)
Generates a random integer within the specified range.
bool chance(int chance)
Determines if a random event occurs based on the specified chance.
void wait(int ms)
Pauses the execution of the current thread for the specified amount of time.
int abs(int num)
Returns the absolute value of the specified number.
int ceil(int num)
Rounds the specified number up to the nearest integer.
int floor(int num)
Rounds the specified number down to the nearest integer.
int round(int num)
Rounds the specified number to the nearest integer.
int max(int a, int b)
Returns the larger of two specified numbers.
int min(int a, int b)
Returns the smaller of two specified numbers.
int clamp(int num, int min, int max)
Clamps a number to a specified range.
int sqrt(int num)
Calculates the square root of a specified number.
int pow(int num, int pow)
Raises a specified number to the power of another specified number.
int sin(int num)
The sine of a specified number.
int cos(int num)
The cosine of a specified number.
int tan(int num)
The tangent of a specified number.
int asin(int num)
The arcsine of a specified number.
int acos(int num)
The arccosine of a specified number.
int atan(int num)
The arctangent of a specified number.
int linearinterpolation(int a, int b, int t)
Linearly interpolates between two values based on a specified index (t).
</details>
FS
The FS package is for file system operations, such as reading and writing files.
<details>
<summary>Reference</summary>
str readfile(str path)
Reads the content of a file at the given path.
str* readfilelines(str path)
Reads all lines from a file at the given path.
void writefile(str path, str cont)
Writes the specified content to a file at the given path.
void writefilelines(str path, str* cont)
Writes the specified lines to a file at the given path.
bool exists(str path)
Checks if a file or directory exists at the given path.
void delete(str path)
Deletes a file at the given path.
void createdir(str path)
Creates a directory at the given path.
void deletedir(str path)
Deletes a directory at the given path.
str* listdir(str path)
Lists all files in a directory at the given path.
str* listdirs(str path)
Lists all directories in a directory at the given path.
</details>
Parse
Parse is a universal package for parsing JSON, XML, HTML and for Regex operations.
<details>
<summary>Reference</summary>
obj Json.loadjson(str json)
Loads a JSON string and converts it into a Carp object.
MatchResult Regex.match(str pattern, str text)
Converts a dynamic object into a Carp object.
MatchResult* Regex.matches(str pattern, str text)
Matches a regular expression pattern against a text and returns all matches.
str Regex.replace(str pattern, str text, str replacement)
Replaces all occurrences of a regular expression pattern in a text with a replacement string.
str* Regex.split(str pattern, str text)
Splits a text into an array of strings based on a regular expression pattern.
bool Regex.test(str pattern, str text)
Tests if a regular expression pattern matches a text.
</details>
Net
Net is for network operations, such as HTTP requests.
<details>
<summary>Reference</summary>
str Http.get(str url)
Makes a GET request to the specified URL.
str Http.post(str url, str content)
Makes a POST request to the specified URL with the specified content.
</details>
Resource
Resource is for loading and managing external resources (the ones stored in /resources), such as text files.
<details>
<summary>Reference</summary>
obj load(str name)
Loads a resource with the specified name.
obj list()
Lists all the resources in the package.
</details>
System
System is for system operations, such as executing shell commands.
<details>
<summary>Reference</summary>
str runcommand(str command)
Executes a command in the command prompt and returns the output.
str username
Gets the username of the current user.
str machine_name
Gets the name of the machine.
str os_version
Gets the version of the operating system.
int processor_count
Gets the number of processors on the current machine.
</details>
Projects
Projects are designed for larger codebases, they may contain multiple files, and have resources stored within them. They can later be packaged into .caaarp files for distribution and execution.
Creating a project
To create a new project, run the following command:
$ carp new -n my_project
Or you can convert an existing script into a project using:
$ carp new -s my_script.carp
Optionally the name of the project can be specified with the -n flag.
Running a project
To run a project, navigate to the project directory and run:
$ carp .
This will build and run the project. To just build it, use:
$ carp build .
These will generate a .caaarp file in the /export directory, named the projects name suffixed with the current version in the .carpproj file.
Project structure
A project will have all its scripts in the initial directory, and resources in the /resources directory. The /export directory will contain the generated .caaarp files.
Project configuration
The project configuration is stored in a .carpproj file in the project directory. It contains the project name, version, and other metadata.
Advanced
Interpreter flags
-i, --interactive Start the Carp REPL after executing the script.
-c, --line Execute the Carp code provided in the command line.
-v, --verbose (Default: true) Print the result of the script execution to the console.
-d, --debug Start the Carp debugger.
-f, --force-throw Force the internal errors to trigger the native stacktrace.
--strict-warnings Treat warnings as errors
--default-non-structs (Default: true) Allow non-struct objects to be auto-initialized
--implicit-casts (Default: true) Enable implicit casting/type coercion
--help Display this help screen.
--version Display version information.
Path (pos. 0) The path to the script, project or package to run.
The preprocessor
The preprocessor is responsible for formatting the input code for the lexer and parser. It adds support for directives, strips comments, re-writes imports and can be expanded in the future to do even more.
Directives
Include
The include directive, directly includes the specified file path in the current file. Note that this is not package-safe and will only use the current directory as a source, which makes it helpful for the REPL. It will also be preprocessed before being injected.
#include header.carp
Define
Define allows you to create macros, which are similar to methods except they directly replace tokens and their arguments with the body. Brackets can be used to allow it to span multiple lines
#define add(a, b) = (
a + b
)
int sum = add(1, 2)
# This will be replaced with 1 + 2
It can also be used for things such as constants, as it does not need to take parameters
#define my_const = 25.5
my_const
# Replaced with 25.5
Style guide
Block bodies
The shorthand block body should be used whenever possible (whenever the body is a single expression).
# Single-line block body
int add(int a, int b) -> a + b
# Multi-line block body
int slowmultiply(int a, int b) {
int result = 0
for b -> result = add(result, a)
return result
}
Naming conventions
Variables and properties are named in snake_case. Classes are named in upper CamelCase. Methods and functions are lower mergecase.
For example:
int my_var = 10
void print(str message) -> IO.println(message)
class MyObj {
void dosomething() -> print('Hello, world!')
}
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.