Compare commits

..

2 Commits

Author SHA1 Message Date
49a04fa5ac Small changes 2024-02-20 05:53:38 -05:00
46f162620b Use val and var 2024-02-20 05:17:21 -05:00
21 changed files with 127 additions and 92 deletions

View File

@ -11,7 +11,7 @@ enum Suit
Spades, Spades,
} }
let suit = Suit::Hearts val suit = Suit::Hearts
``` ```
@ -24,7 +24,7 @@ enum IpAddress
V6(String), V6(String),
} }
let addr_1 = IpAddress::V4("192.168.0.1") val addr_1 = IpAddress::V4("192.168.0.1")
match addr_1 match addr_1
| IpAddress::V4(ip) | IpAddress::V4(ip)

View File

@ -4,23 +4,23 @@ thp distinguishes between mutable and immutable variables.
## Immutable variables ## Immutable variables
Defined with `let`, followed by a variable name and a value. Defined with `val`, followed by a variable name and a value.
```thp ```thp
let surname = "Doe" val surname = "Doe"
let year_of_birth = 1984 val year_of_birth = 1984
``` ```
### Datatype annotation ### Datatype annotation
Written after the `let` keyword but before the variable name. Written after the `val` keyword but before the variable name.
```thp ```thp
let String surname = "Doe" val String surname = "Doe"
let Int year_of_birth = 1984 val Int year_of_birth = 1984
``` ```
When annotating an immutable variable the `let` keyword is optional When annotating an immutable variable the `val` keyword is optional
```thp ```thp
// Equivalent to the previous code // Equivalent to the previous code
@ -34,28 +34,28 @@ This means that if a variable has only a datatype, it is immutable.
## Mutable variables ## Mutable variables
Defined with `let mut`, followed by a variable name and a value. Defined with `var`, followed by a variable name and a value.
```thp ```thp
let mut name = "John" var name = "John"
let mut age = 32 var age = 32
``` ```
### Datatype annotation ### Datatype annotation
Written after the `let mut` keywords but before the variable name. Written after the `var` keywords but before the variable name.
```thp ```thp
let mut String name = "John" var String name = "John"
let mut Int age = 32 var Int age = 32
``` ```
When annotating a mutable variable the keyword `let` is optional. `mut` is still **required**. When annotating a mutable variable the keyword `var` is still **required**.
```thp ```thp
// Equivalent to the previous code // Equivalent to the previous code
mut String name = "John" var String name = "John"
mut Int age = 32 var Int age = 32
``` ```

View File

@ -8,7 +8,7 @@ Basically kotlin syntax.
`new` not required, in fact, forbidden. `new` not required, in fact, forbidden.
```thp ```thp
let dog = Dog() val dog = Dog()
``` ```
## Simple class ## Simple class
@ -18,7 +18,7 @@ Why'd you do this tho?
```thp ```thp
class SimpleClass class SimpleClass
let instance = SimpleClass() val instance = SimpleClass()
``` ```
## Properties & methods ## Properties & methods
@ -27,10 +27,10 @@ let instance = SimpleClass()
class SimpleClass class SimpleClass
{ {
// Properties are private by default // Properties are private by default
mut String? name = ... var String? name = ...
// Made public with `pub` // Made public with `pub`
pub mut String? surname = ... pub var String? surname = ...
// Methods are private by default // Methods are private by default
fun display_name() fun display_name()
@ -58,10 +58,9 @@ Kotlin style
```thp ```thp
class Cat( class Cat(
// If a parameter has pub, protected or private they are promoted to properties var String name,
private String name, var Int lives = 9,
pub mut Int lives = 9, val String surname = "Doe",
protected String surname = "Doe",
) )
{ {
pub fun get_name() -> String pub fun get_name() -> String
@ -90,14 +89,13 @@ print(michifu.get_name())
With kotlin's `init` block. With kotlin's `init` block.
```thp ```thp
class Dog(pub String name) class Dog(val String name)
{ {
Int name_length Int name_length = name.length()
init init
{ {
print("Dog has been instantiated") print("Dog has been instantiated")
$name_length = name.length()
} }
} }
``` ```
@ -107,7 +105,7 @@ class Dog(pub String name)
Kotlin style Kotlin style
```thp ```thp
class Animal(pub String name) class Animal(var String name)
{ {
pub fun say_name() pub fun say_name()
{ {
@ -120,6 +118,35 @@ class Cat(String name, Int lives) -> Animal(name)
Cat("Michi", 9).say_name() Cat("Michi", 9).say_name()
``` ```
## Mutable methods
By default methods cannot mutate the state of the object.
```thp
class Animal(var String name)
{
pub fun set_name(String new_name)
{
$name = new_name // Error: Cannot mutate $
}
}
```
To do so the method must be annotated. The caller must also
declare a mutable variable.
```thp
class Animal(var String name)
{
pub mut fun set_name(String new_name)
{
$name = new_name // Ok
}
}
var michi = Animal("Michifu")
michi.set_name("Garfield")
```

View File

@ -16,10 +16,9 @@ class Cat
```thp ```thp
let option = Some("GAAA") val option = Some("GAAA")
let Some(value) = option val Some(value) = option
let colors = Array("red", "green", "blue")
let Array()
val colors = Array("red", "green", "blue")
val Array()
``` ```

View File

@ -5,13 +5,13 @@ Use square brackets as usual.
## Usage ## Usage
```thp ```thp
let fruits = ["apple", "banana", "cherry"] val fruits = ["apple", "banana", "cherry"]
let apple = fruits[0] val apple = fruits[0]
print(apple) print(apple)
let mut numbers = [0, 1, 2, 3] val mut numbers = [0, 1, 2, 3]
numbers[3] = 5 numbers[3] = 5

View File

@ -6,7 +6,7 @@ Also known as Associative Arrays
## Usage without a declaration ## Usage without a declaration
```thp ```thp
let mut person = Obj { val mut person = Obj {
name: "John", name: "John",
surname: "Doe", surname: "Doe",
age: 33, age: 33,
@ -31,7 +31,7 @@ obj Person = {
} }
let john_doe = Person { val john_doe = Person {
name: "John", name: "John",
surname: "Doe", surname: "Doe",
age: 33, age: 33,

View File

@ -3,7 +3,7 @@
```thp ```thp
// Set[Int] // Set[Int]
let ages = Set(30, 31, 33, 35) val ages = Set(30, 31, 33, 35)
for age in ages { for age in ages {
print("{age}") print("{age}")

View File

@ -6,9 +6,9 @@ calls (`()`).
## Definition ## Definition
```thp ```thp
let person = #("John", "Doe", 32) val person = #("John", "Doe", 32)
let #(name, surname, age) = person val #(name, surname, age) = person
``` ```

View File

@ -22,7 +22,7 @@ else
} }
let result = if condition { value1 } else { value2 } val result = if condition { value1 } else { value2 }
``` ```
@ -40,7 +40,7 @@ if variable is Datatype
## If variable is of enum ## If variable is of enum
```thp ```thp
let user_id = POST::get("user_id") val user_id = POST::get("user_id")
if Some(user_id) = user_id if Some(user_id) = user_id
{ {

View File

@ -5,7 +5,7 @@
Braces are required. Braces are required.
```thp ```thp
let numbers = [0, 1, 2, 3] val numbers = [0, 1, 2, 3]
for number in numbers for number in numbers
{ {
@ -19,7 +19,7 @@ for #(index, number) in numbers.entries()
``` ```
```thp ```thp
let dict = Obj { val dict = Obj {
apple: 10, apple: 10,
banana: 7, banana: 7,
cherries: 3, cherries: 3,
@ -45,8 +45,8 @@ for value in collection
## While loop ## While loop
```thp ```thp
let colors = ["red", "green", "blue"] val colors = ["red", "green", "blue"]
let mut index = 0 val mut index = 0
while index < colors.size() while index < colors.size()
{ {

View File

@ -5,7 +5,7 @@
Braces are **required**. Braces are **required**.
```thp ```thp
let user_id = POST::get("user_id") val user_id = POST::get("user_id")
match user_id match user_id

View File

@ -21,7 +21,7 @@ fun get_random_number() -> Int
Random::get(0, 35_222) Random::get(0, 35_222)
} }
let number = get_random_number() val number = get_random_number()
``` ```
## With parameters and return type ## With parameters and return type
@ -32,7 +32,7 @@ fun get_secure_random_number(Int min, Int max) -> Int
Random::get_secure(min, max) Random::get_secure(min, max)
} }
let number = get_secure_random_number(0, 65535) val number = get_secure_random_number(0, 65535)
``` ```
@ -44,10 +44,10 @@ fun get_first_item[T](Array[T] array) -> T
array[0] array[0]
} }
let first = get_first_item[Int](numbers) val first = get_first_item[Int](numbers)
// The type annotation is optional if the compiler can infer the type // The type annotation is optional if the compiler can infer the type
let first = get_first_item(numbers) val first = get_first_item(numbers)
``` ```
@ -91,7 +91,7 @@ fun greet(
print("Hello {name} from {city}!") print("Hello {name} from {city}!")
} }
greet(name: "John", from: "LA") greet("John", from: "LA")
``` ```

View File

@ -23,8 +23,8 @@ fun generate_generator() -> () -> Int
} }
let generator = generate_generator() // A function val generator = generate_generator() // A function
let value = generate_generator()() // An Int val value = generate_generator()() // An Int
``` ```

View File

@ -22,9 +22,9 @@ By default closures **always** capture variables as **references**.
```thp ```thp
let mut x = 20 var x = 20
let f = fun() { val f = fun() {
print(x) print(x)
} }
@ -44,9 +44,9 @@ fun(parameters) clone(variables) {
``` ```
```thp ```thp
let mut x = 20 var x = 20
let f = fun() clone(x) { val f = fun() clone(x) {
print(x) print(x)
} }

View File

@ -10,16 +10,18 @@ fun add_25(Array[Int] numbers) {
``` ```
When using a regular type as a parameter, only it's immutable When using a regular type as a parameter, only it's immutable
properties can be used inside the function properties can be used
```thp ```thp
fun count(Array[Int] numbers) -> Int { fun count(Array[Int] numbers) -> Int {
let items_count = numbers.size() // Ok, `size` is pure val items_count = numbers.size() // Ok, `size` is pure
items_count items_count
} }
``` ```
To use immutable properties you must use a mutable reference.
## Mutable reference ## Mutable reference
@ -36,7 +38,7 @@ data **can** be mutated.
The caller *must* also use `mut`. The caller *must* also use `mut`.
```thp ```thp
let numbers = Array(0, 1, 2, 3) val numbers = Array(0, 1, 2, 3)
push_25(mut numbers) // Pass `numbers` as reference. push_25(mut numbers) // Pass `numbers` as reference.
@ -58,7 +60,7 @@ of the parameter (CoW). The original data will **not** be mutated.
```thp ```thp
let numbers = Array(1, 2, 3, 4) val numbers = Array(1, 2, 3, 4)
add_25(clone numbers) // Pass `numbers` as clone. add_25(clone numbers) // Pass `numbers` as clone.

View File

@ -16,20 +16,21 @@ If you want to learn the language, go to the learn section.
## Goals ## Goals
- Bring static typing to PHP: Not just type hints, not use `mixed` for everything - Bring static typing to PHP: Not just type hints, not just `mixed` for everything
that isn't a primitive type. that isn't a primitive type.
- Avoid automatic type conversion. - Generics & ADTs
- Avoid implicit type conversion.
- Remove the inconsistencies in the language. - Remove the inconsistencies in the language.
- Organize the stdlib. - Organize the stdlib into modules.
- Differentiate between Arrays, Tuples, Maps and Sets. - Differentiate between Arrays, Tuples, Maps and Sets.
- Create a **consistent** language. - Create a **consistent** language.
- Create typings for popular libraries (like TS's `.d.ts`). - Have typings for popular libraries (like TS's `.d.ts`).
- Have a simple instalation and configuration (requiring just Composer). - Have a simple instalation and configuration (requiring just Composer).
- Ship a fast, native binary (written in Rust) (why use PHP when we can go native?). - Ship a fast, native binary (written in Rust) (why use PHP when we can go **_blazingly fast_**?).
- Sub 10ms watch mode.
- Support in-place compilation. - Support in-place compilation.
- Emit readable PHP code. - (Try to) emit readable PHP.
- Implement a LSP server. - Implement an LSP server.
- Implement a formatter.
## Not goals ## Not goals
@ -55,7 +56,7 @@ These are **not** aspects that THP looks to solve or implement.
$has_key = str_contains($haystack, 'needle'); $has_key = str_contains($haystack, 'needle');
// THP // THP
let has_key = haystack.contains("needle") val has_key = haystack.contains("needle")
``` ```
- Explicit variable declaration - Explicit variable declaration
@ -83,7 +84,7 @@ Obj {
``` ```
- Tuples, Arrays, Sets, Maps are clearly different - Tuples, Arrays, Sets, Maps are clearly different
- JS-like object syntax - JSON-like object syntax
--- ---
@ -93,7 +94,7 @@ $cat = new Cat("Michifu", 7);
$cat->meow(); $cat->meow();
// THP // THP
let cat = Cat("Michifu", 7) val cat = Cat("Michifu", 7)
cat.meow(); cat.meow();
``` ```
@ -133,7 +134,7 @@ For example:
```thp ```thp
// This expression // This expression
let greeting = val greeting =
match get_person() match get_person()
| Some(person) if person.age > 18 | Some(person) if person.age > 18
{ {
@ -176,7 +177,7 @@ enum IpAddress {
V6(String), V6(String),
} }
let ip_1 = IpAddress::V4("255.255.0.0") val ip_1 = IpAddress::V4("255.255.0.0")
// Would possibly compile to: // Would possibly compile to:

View File

@ -13,7 +13,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link <link
href="https://fonts.googleapis.com/css2?family=Fira+Sans:wght@400;500;600;700;800;900&family=Fugaz+One&family=Inconsolata&family=Inter&display=swap" href="https://fonts.googleapis.com/css2?family=Fira+Sans:wght@400;500;600;700;800;900&display=swap"
rel="stylesheet"> rel="stylesheet">
<link <link
href="https://fonts.googleapis.com/css2?family=Fira+Code&family=Josefin+Sans:ital,wght@0,400;1,700&display=swap" href="https://fonts.googleapis.com/css2?family=Fira+Code&family=Josefin+Sans:ital,wght@0,400;1,700&display=swap"
@ -40,6 +40,8 @@
</h1> </h1>
<p class="font-display text-c-text opacity-60 text-xl pt-4"> <p class="font-display text-c-text opacity-60 text-xl pt-4">
Syntax, stdlib and types for PHP Syntax, stdlib and types for PHP
<br>
Written in Rust
</p> </p>
</div> </div>
@ -60,17 +62,17 @@
<pre style="padding: 0 !important;"> <pre style="padding: 0 !important;">
<code class="language-thp"> <code class="language-thp">
fun find_person(Str person_id) -> Result[Str] { fun find_person(Str person_id) -> Result[Str] {
let person_id = try person_id.parse[Int]() val person_id = try person_id.parse[Int]()
try Person::find_by_id(person_id) try Person::find_by_id(person_id)
} }
let id = POST::get("person_id") ?: die val id = POST::get("person_id") ?: die
let person = find_person(person_id: id) ?: die val person = find_person(person_id: id) ?: die
print( print(
&lt;a href="/person/reports/{person.id}"&gt; &lt;a href="/person/reports/{person.id}"&gt;
Hello {person.name} Hello {person.name}
&lt;/div&gt; &lt;/a&gt;
) )
</code> </code>
</pre> </pre>
@ -86,7 +88,11 @@
Learn Learn
</a> </a>
<a class="inline-block font-display text-lg border-2 border-[#5BCEFA] py-3 px-8 mx-6 rounded" href="/install/"> <a
class="inline-block font-display text-lg border-2 border-sky-400 py-3 px-8 mx-6 rounded
transition-colors hover:text-black hover:bg-sky-400"
href="/install/"
>
Install Install
</a> </a>
</div> </div>

View File

@ -15,7 +15,7 @@ Prism.languages.thp = {
pattern: /(["])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, pattern: /(["])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,
greedy: true, greedy: true,
}, },
"keyword": /\b(?:static|const|enum|loop|use|break|catch|continue|do|else|finally|for|fun|if|in|fn|nil|return|throw|try|while|type|match|with|of|abstract|class|interface|private|pub|obj|override|open|init|let|mut|clone)\b/, "keyword": /\b(?:static|const|enum|loop|use|break|catch|continue|do|else|finally|for|fun|if|in|fn|nil|return|throw|try|while|type|match|with|of|abstract|class|interface|private|pub|obj|override|open|init|val|var|mut|clone)\b/,
"number": /\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i, "number": /\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,
"operator": /[<>]=?|[!=]=?=?|--?|\$|\+\+?|&&?|\|\|?|[?*/~^%]/, "operator": /[<>]=?|[!=]=?=?|--?|\$|\+\+?|&&?|\|\|?|[?*/~^%]/,
"punctuation": /[{}[\];(),.]/, "punctuation": /[{}[\];(),.]/,

View File

@ -14,14 +14,11 @@
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link <link
href="https://fonts.googleapis.com/css2?family=Fira+Sans:wght@400;500;600;700;800;900&family=Fugaz+One&family=Inconsolata&family=Inter&display=swap" href="https://fonts.googleapis.com/css2?family=Fira+Sans:wght@400;500;600;700;800;900&display=swap"
rel="stylesheet"> rel="stylesheet">
<link <link
href="https://fonts.googleapis.com/css2?family=Fira+Code&family=Josefin+Sans:ital,wght@0,400;1,700&display=swap" href="https://fonts.googleapis.com/css2?family=Fira+Code&family=Josefin+Sans:ital,wght@0,400;1,700&display=swap"
rel="stylesheet"> rel="stylesheet">
<style>
</style>
</head> </head>
<body class="bg-c-bg text-c-text"> <body class="bg-c-bg text-c-text">
@ -44,7 +41,7 @@
<div class="pt-12 max-h-screen border-r border-r-[rgba(91,205,250,0.25)] overflow-x-scroll sticky top-0"> <div class="pt-12 max-h-screen border-r border-r-[rgba(91,205,250,0.25)] overflow-x-scroll sticky top-0">
<nav class="rounded-md mt-2 p-4"> <nav class="rounded-md mt-2 p-4">
<h2 class="text-xl font-display pb-4">On this page</h2> <h2 class="text-xl font-display pb-4 opacity-75">On this page</h2>
{{sidebar}} {{sidebar}}
</nav> </nav>

View File

@ -22,11 +22,14 @@ module.exports = {
"body": ["'Fira Sans'", "Inter", "sans-serif"], "body": ["'Fira Sans'", "Inter", "sans-serif"],
}, },
}, },
corePlugins: {
container: false
},
plugins: [ plugins: [
function ({ addComponents }) { function ({ addComponents }) {
addComponents({ addComponents({
'.container': { '.container': {
maxWidth: '95%', width: '98%',
'@screen sm': { '@screen sm': {
maxWidth: '640px', maxWidth: '640px',
}, },

View File

@ -17,7 +17,7 @@
@media (prefers-color-scheme: light) { @media (prefers-color-scheme: light) {
:root { :root {
--c-bg: rgb(255, 247, 255); --c-bg: rgb(255, 253, 255);
--c-text: #121212; --c-text: #121212;
--c-purple: #374259; --c-purple: #374259;
--c-purple-light: #BA94D1; --c-purple-light: #BA94D1;