Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/qiushiyan/qlang
A toy programming language with a mix of R and Python's goodies
https://github.com/qiushiyan/qlang
Last synced: about 1 month ago
JSON representation
A toy programming language with a mix of R and Python's goodies
- Host: GitHub
- URL: https://github.com/qiushiyan/qlang
- Owner: qiushiyan
- License: mit
- Created: 2022-09-17T22:23:50.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2024-01-09T18:52:54.000Z (12 months ago)
- Last Synced: 2024-06-21T06:45:29.563Z (6 months ago)
- Language: Go
- Homepage: https://qlang.qiushiyan.dev/
- Size: 4.26 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
Q Progrmaming Language
================Q is a toy programming language with a mix of R and Python’s syntax. It
was written in Go and inspired by .## Usage
Go to [releases](https://github.com/qiushiyan/qlang/releases/tag/v0.0.1-lw) and download the corresponding binary.
Alternatively if you have Go installed, you can clone and cd into this repo and run
```
go run cmd/q/main.go
```## Assignments
Both `=` and `<-` can be used for assignment. Variable names can contain
letters, numbers, and underscores, but must start with a letter.``` q
x = 1
y <- x
x + y
```#> 2
There is also a `let` keyword for variable declaration.
``` q
let x <- 1
```Q makes a difference between assignment and declartion in the context of
nested scopes. This is when we work inside functions, `if` and `for`
statements. See more details in the section on control structures below.## Data structures
### Primitives
Primitive data structures include: numbers (integers and floats),
strings and booleans``` q
1 + 1 + (10 * 2) / 4
```#> 7
``` q
"hello" + " " + "world"
```#> "hello world"
``` q
!false
```#> true
### Vectors
Both R and Python has 1-dimensional containers for storing a series of
values. Q offers the vector data structure as created by `[]```` q
[1, 2, 3]
```#> [1, 2, 3]
The `print()` helper shows the vector’s type as well as the number fo
elements.``` q
print([1, 2, "hello"])
```#> Vector with 3 elements
#> [1, 2, "hello"]As in R, a vector is typed by its inner elements. Vectors containing
only numbers are numeric vectors, vectors with only string elements are
character vectors, and so on. A vector with mixed types is simply a base
`Vector` type, similar to a Python list. No type conversion is done
automatically, if the elements are heterogeneous the base type will be
used.Vectors in Q have 1-based indexing: the first element starts at index 1,
not 0. Built-in functions for vectors include `len()`, `append()`,
`head()`, `tail()```` q
x = as_vector(1:10)print(x[1:3])
print(append(x, [11, 12, 13], 14, "15"))
print(head(x, 10))
```#> NumericVector with 3 elements
#> [1, 2, 3]
#> Vector with 15 elements
#> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, "15"]
#> NumericVector with 10 elements
#> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]Inspired by R, operators are vectorized element-wise. Or in numpy’s
terms, they are “broadcasted”.``` q
[1, 2, 3] + [4, 5, 6]
```#> [5, 7, 9]
``` q
[1, 2, 3] * [4, 5, 6]
```#> [4, 10, 18]
``` q
["hello ", "good "] + ["world", "morning"]
```#> ["hello world", "good morning"]
Elements are recycled only if it has lenght 1 or is a scalar.
``` q
[1, 2, 3] * 2
```#> [2, 4, 6]
``` q
[1, 2, 3] + [4]
```#> [5, 6, 7]
``` q
[1, 2, 3] + [4, 5]
```#> ERROR: Incompatible vector lengths, left=3 and right=2
Boolean indexing works as well
``` q
s = random(10, 1, 3)
s[s > 1.5]
```#> [2.2093205759592394, 2.881018176090025, 2.3291201064369806, 1.8754283743739604, 1.8492749941425313, 2.373646145734219, 1.6018237211705741]
### Dictionaries
You can create a hash table structure in Q called a dictionary with a
pair of `{`, similar to Python, except that you don’t have to quote the
keys.``` q
property = "functional"
q = {name: "Q", age: 0, property: true}
print(q)q["age"] = q["age"] + 1
print(keys(q))
print(values(q))
```#> {"age": 0, "functional": true, "name": "Q"}
#> CharacterVector with 3 elements
#> ["age", "functional", "name"]
#> Vector with 3 elements
#> [1, true, "Q"]### Control flows
Q supports `for ... in` loops and `if eles` conditions. Both the
iteration and condition need to be put in parentheses.``` q
for (name in ["Q", "R", "Python"]) {
if (name != "Python") {
print(name)
} else {
print("I don't like Python")
}
}
```#> "Q"
#> "R"
#> "I don't like Python"`for in` and `if` blocks have their own scopes. Inside their inner
scope, we can (recursively) access and rebind a variable name defined in
the outer scope using assignment without the `let` keyword like so.``` q
result = []
for (i in 1:3) {
result = append(result, i)
}
result
```#> [1, 2, 3]
Or you can create vector with specified length with `vector()` and then
start filling in the elements with indexing.``` q
result = vector(3)
for (i in 1:3) {
result[i] = i
}
result
```#> [1, 2, 3]
However, if we declare a variable in the inner scope with the `let`
keyowrd, that variable will shadow the outer scope variable and get lost
when the block ends. For example, the following code will not work as
expected.``` q
flag = true
if (flag) {
let flag = false
}
flag
```#> true
### Functions
Function definition uses the R style, simply create a function object
with the `fn` keyword and then bind it to a name. Default arguments and
named arguments are supported.``` q
add = fn(x, y = 1, z = 1) {
x + y + z * 2
}add(1, z = 2)
```#> 6
Functions in Q are first-class citizens. They can be passed around as
arguments and returned from other functions. There is a `return` keyword
but functions can also use implicit returns. Here we define a `map`
function that takes a function and a vector and applies the function to
each element of the vector.``` q
map = fn(arr, f) {
result = vector(len(arr))
for (i in arr) {
result[i] = f(arr[i])
}
result
}[1, 2, 3] |> map(fn(x) x * 2)
```#> [2, 4, 6]
Of course the preferred the way to to double a vector is to simply use
the vectorized operator `*`.Another example of implementing a `filter()` function that take a vector
and a predicate function and returns a vector with only the elements
that satisfy the predicate.``` q
filter = fn(x, f) {
result = []
for (i in 1:len(x)) {
if (f(x[i])) {
result = append(result, x[i])
}
}
result
}[
{name: "Ross", job: "Paleontology"},
{name: "Monoca", job: "Chef"}
] |> filter(fn(x) x["job"] == "Chef")
```#> [{"name": "Monoca", "job": "Chef"}]
## Next steps
- `...` for variadic arguments and spread operator
- index tests for vector and dict
- dataframe interface
- improve error message with token col and line
- more standard library functions