All Projects â†’ jamiebuilds â†’ Ghost Lang

jamiebuilds / Ghost Lang

👻 A friendly little language for you and me.

Labels

Projects that are alternatives of or similar to Ghost Lang

speck
A concise and composable syntax for your function specs
Stars: ✭ 53 (-82.09%)
Mutual labels:  spec
wdio-spec-reporter
A WebdriverIO v4 plugin. Reporter that creates 'spec'-style reports
Stars: ✭ 20 (-93.24%)
Mutual labels:  spec
automock
A library for testing classes with auto mocking capabilities using jest-mock-extended
Stars: ✭ 26 (-91.22%)
Mutual labels:  spec
oci-spec-rs
OCI Runtime, Image and Distribution Spec in Rust
Stars: ✭ 117 (-60.47%)
Mutual labels:  spec
es-abstract
ECMAScript spec abstract operations.
Stars: ✭ 86 (-70.95%)
Mutual labels:  spec
es-to-primitive
ECMAScript "ToPrimitive" algorithm. Provides ES5 and ES6/ES2015 versions.
Stars: ✭ 21 (-92.91%)
Mutual labels:  spec
spec-pattern
Specification design pattern for JavaScript and TypeScript with bonus classes
Stars: ✭ 43 (-85.47%)
Mutual labels:  spec
ngff
Next-generation file format (NGFF) specifications for storing bioimaging data in the cloud.
Stars: ✭ 52 (-82.43%)
Mutual labels:  spec
falcon-apispec
apispec plugin that generates OpenAPI specification (aka Swagger Docs) for Falcon web applications.
Stars: ✭ 44 (-85.14%)
Mutual labels:  spec
eggplant
A behaviour driven development (BDD) library for Clojure. Simplicity is key.
Stars: ✭ 16 (-94.59%)
Mutual labels:  spec
spec-kemal
Easy testing for Kemal
Stars: ✭ 51 (-82.77%)
Mutual labels:  spec
dataset
qri dataset definition
Stars: ✭ 16 (-94.59%)
Mutual labels:  spec
form-validator-cljs
ClojureScript library to validate forms
Stars: ✭ 52 (-82.43%)
Mutual labels:  spec
geojson.specs
a Geojson utility for validating data using Clojure(script)'s spec (RFC 7946)
Stars: ✭ 18 (-93.92%)
Mutual labels:  spec
ntast
Notion Abstract Syntax Tree specification.
Stars: ✭ 101 (-65.88%)
Mutual labels:  spec
dropbox-api-spec
The Official API Spec for Dropbox API V2 SDKs.
Stars: ✭ 36 (-87.84%)
Mutual labels:  spec
meta-schema
Little DSL to make data processing sane with clojure.spec and spec-tools
Stars: ✭ 25 (-91.55%)
Mutual labels:  spec
Phrase
Clojure(Script) library for phrasing spec problems.
Stars: ✭ 275 (-7.09%)
Mutual labels:  spec
table-spec
Specs from SQL database schema for data generation and validation
Stars: ✭ 32 (-89.19%)
Mutual labels:  spec
microsub
For tracking issues on the Microsub specification
Stars: ✭ 23 (-92.23%)
Mutual labels:  spec

Ghost Lang 👻

A friendly little language for you and me.

Motivation

Ghost is the language I wish we had.

There are so many newer programming languages that have brilliant ideas inside them, but I think they have failed to make them feel familiar to programmers.

Ghost steals as much of its design as possible from other programming languages. If someone else did it really well, why do it any differently? As such most of Ghost won't feel new. If you've used some of these languages before you will probably recognize features of Ghost.

There is no compiler or any tools for Ghost right now, I hope to someday work on some. But for now, this is just an exercise in programming language design.

Syntax

Ghost's syntax should feel light. There are no semicons, comments are a single character (#), syntax avoid too many characters.

But it also avoids having "optional" syntax like optional parenthesis or curly braces. A lot of this encourages a more "vertical" coding style.

Places where syntax could go multiple different ways, I've just picked what seems to be most popular. That way it should feel familiar to lots of developers.

Comments

# inline comments only
# combine to make multiline

Declarations

let name = expression

Null

let missing = null

Booleans

let positive = true
let negative = false

Strings

let plain = "hello world"
let interpolated = "hello {target}"
let escapes = "hello \"world\""

let multiline =
  """
  The quick {color} fox           # comment
  {action} over the lazy dog     \# not comment
  """
let multilineWithNewlines =
  """
  This is the first line
  This is the second line\
  This is still the second line
  """
let multilineWithIndentation =
  """
  This is indented by 0 spaces.
    This is indented by 2 spaces.
      This is indented by 4 spaces.
  """

Numbers

let float = 4.14
let float32 = 4.14f
let decimal = 0.15d
let bigint = 9999i

let largeNumber = 98_521_391_124i

Regex

let regex = /^one|^two|^three|^orfour/i # equivalent to below
let regexMultiline = ///
  ^ one   |  # all whitespace
  ^ two   |  # and comments
  ^ three |  # will be ignored
  ^ or four  # to make it readable
///i

Ranges

let range = 0..3                   # 0,1,2
let rangeInclusive = 0...3         # 0,1,2,3
let rangeExpr = 0..num             # 0 to whatever `num` is
let reverseWithNegatives = 2..-3   # 2,1,0,-1,-2

Properties

let property1 = .name # properties are global
let property2 = .name # both of these are equal

Lists

let list = [1, 2, 3]
let trailingCommas = [
  1,
  2,
  3,
]

let combineList = [1, ..list, 3] # fast
let combineIter = [1, ...iter, 3] # slow

let getterIndex = list[3]
let getterRange = list[0..3]

Arrays

let array = Array [1, 2, 3]
let trailingCommas = Array [
  1,
  2,
  3,
]

let getterIndex = array[3]
let getterIndexNegative = array[-3]
let getterRange = array[0..3]

Sets

let set = Set [
  "one",
  "two",
  "three",
]

Records

let three = .three

let record = [
  one = 1,
  two = 2,
  [three] = 3,
}

let one = record.one
let two = record[.two]
let three = record[three]

let newRecord = {
  one = "default",
  ...record,
  three = "new value",
}

Maps

let map = Map {
  "key1" = "value1",
  "key2" = "value2",
  3.1415 = "Ï€"
}

Operators

# Arithmetic
let addition = a + b
let subtraction = a - b
let division = a / b
let multiplication = a * b
let remainder = a % b
let exponentiation = a ** b
let negation = -a

# Bitwise
let and = a & b
let or = a | b
let xor = a ^ b
let not = ~a
let leftShift = a << b
let signPropRightShift = a >> b
let zeroFillRightShift = a >>> b

# Comparison
let equality = a == b
let referentialEquality = a === b

let lessThan = a < b
let lessThan2 = a < b < c # equivalent to `a < b && b < c`, except `b` is only evaluated once
let lte = a <= b
let lte2 = a <= b <= c
let lessThan3 = a <= b < c

let greaterThan = a > b
let greaterThan2 = a > b > c
let gte = a >= b
let gte2 = a >= b >= c
let greaterThan3 = a >= b > c

# Logical
let and = expr && expr
let or = expr || expr
let not = !expr

# Grouping
let either = (a && b) || (c && d)

If-Else

if (condition) { doSomethingIfTruthy() }

if (condition) {
  doSomethingIfTruthy()
} else {
  doSomethingElse()
}

if (condition) {
  doSomethingIfTruthy()
} else if (condition2) {
  doSomethingElseIf2Truthy()
} else {
  doSomethingElse()
}

let result = if (n == 0) {
  "none"
} else if (n == 1) {
  "one"
} else {
  "many"
}

Destructuring

let [one, two, ...rest] = [1, 2, 3, 4]
# one = 1
# two = 2
# rest = [3, 4]

let [one, two, ...rest] = Array [1, 2, 3, 4]
# one = 1
# two = 2
# rest = [|3, 4|]

let {one, two, ...rest} = {one = 1, two = 2, three = 3, four = 4}
# one = 1
# two = 2
# rest = {three = 3, four = 4}

let {one as uno, two as dos, ...rest as resto} = {one = 1, two = 2, three = 3, four = 4}
# uno = 1
# dos = 2
# resto = {three = 3, four = 4}

Is

value is any
value is true # assert any static type
value is false
value is Number
value is Number.Float # return true or false if it matches
value is { prop: any }
value is { prop: Number }

Match

let result = match (value) {
  is true { "true" }
  is false { "false" }
  else { "other" }
}

Throw

throw Error("message")
throw Error("message", reason)

Try-Catch-Finally

let result = try {
  doSomething()
} catch (err is SpecialError) {
  doSomethingSpecial()
} catch (err) {
  doSomethingElse()
} finally {
  doSomethingAlways()
}

Effects

let doSomething = fn () {
  let myResult = effect "myEffect"
}

let result = try {
  doSomething()
} catch (myEffect is "myEffect") {
  resume "myResult"
}
let inner = fn () {
  log("inner start")
  let result = effect "myEffect"
  log("inner end", result)
  result
}

let outer = fn () {
  log("outer start")
  let result = inner()
  log("outer end", result)
  result
}

let finalResult = try {
  log("try start")
  let result = outer()
  log("try end", result)
  result
} catch (effect is "myEffect") {
  log("catch start", effect)
  let result = resume "myEffectHandlerResult"
  log("catch end", result)
}

# log: try start
# log: outer start
# log: inner start
# log: catch start "myEffect"
# log: inner end "myEffectHandlerResult"
# log: outer end "myEffectHandlerResult"
# log: try end "myEffectHandlerResult"
# log: catch end "myEffectHandlerResult"

finalResult == "myEffectHandlerResult"

For-As

for (iterable as item) {
  if (item.shouldBeSkipped) { continue }
  if (item.shouldEndTheLoop) { break }
  doSomething()
}

While

while (condition) {
  onlyRunsIfConditionIsTrue()
  repeatsAsLongAsItRemainsTrue()
}

Do-While

do {
  runsOnceImmediatelyRegardlessIfConditionIsTrue()
  repeatsAsLongAsItRemainsTrue()
} while (condition)

Loop

loop {
  if (condition1) { break }
  if (condition2) { continue }
  doSomethingInfinitelyUntilBreak()
}

Functions

let add = fn (a, b) { a + b }
let add = fn (a, b) {
  let addend = a
  let augend = b
  a + b
}

let result = add(400, 20)

Named Params

let divide = fn (dividend, divisor) {
  dividend / divisor
}

# All of these are equivalent:
divide(400, 20)
divide(dividend: 400, divisor: 20)
divide(divisor: 20, dividend: 400)
divide(dividend: 400, 20)
divide(400, divisor: 20)
divide(divisor: 20, 400)

# Compiler Errors: Cannot pass multiple values to the same parameter
divide(dividend: 20, dividend: 400)
divide(20, dividend: 400)

Iterable Functions

let doubles = fn (range) iter {
  for (range as index) {
    yield index * 2
  }
}

Do

let value = do {
  let a = 42
  let b = 10
  a * b
}

Pipeline

let results =
  |> books
  |> Iter.filter(^^, fn (book) { book.popularity > 0.8 })
  |> Iter.map(^^, fn (book) { Http.request(.get, book.url) })

# Equivalent code without pipelines:
let filtered = Iter.filter(books, fn (book) { book.popularity > 0.8 })
let results = Iter.map(filtered, fn (book) { Http.request(.get, book.url) })

Use

let append = fn (fileName, buffer) {
  let file = use File.open(fileName)
  File.append(file, buffer)
}

# Effectively:
let append = fn (fileName, buffer) {
  let file = File.open(fileName)
  try {
    let result = File.append(file, buffer)
  } finally {
    if (file) {
      file.dispose()
    }
  }
  result
}
let append = fn (fileName, buffer) {
  let file = use File.open(fileName)
  File.append(file, buffer)
  file.dispose() # manually call
}

Junk

let _ = "ignore me"
let [_, _, three] = [1, 2, 3]
let {a as _, b as _, ...rest as _} = { a = 1, b = 2, c = 3, d = 4 }

doSomething(fn (_, _, value) {
  value
})

log(_)
# SyntaxError: "Junk" bindings (_) are not valid
# identifiers and cannot be referenced. Give your
# binding a name instead.

Elements (GSX)

let MyComponent = fn (props) {
  let {prop, items} = props

  <OtherComponent { prop, bar: true }>
    let target = "world"
    <.h1>"hello {target}"</.h1>
    <.ul>
      for (items as item) {
        <.li { key: item.id }>item.name</.li>
      }
    </.ul>
  </OtherComponent>
}

Blocks

let fn = fn () {
  # block
}

for (iter as item) {
  # block
}

if (cond) {
  # block
} else if (cond) {
  # block
} else {
  # block
}

try {
  # block
} catch (err) {
  # block
} finally {
  # block
}

Block Scoping

let a = 1
if (cond) {
  let a = 2
  let b = 3
  log(a) # > 2
}
log(a) # > 1
log(b) # Error! There's no variable named "b" in this scope!

Imports

import Time
Time.Instant()

import Time as MyTime
MyTime.Instant()

import Math as { cos, PI }
cos(PI)

import ./utils/currency
currency.convert(42, .usd, .aud)

import ../lib/utils/i18n as { t }
t("Hello $1", "World")

Exports

export let add = fn (a, b) { a + b }
export let PI = 3.14

Type Syntax

Type Alias

type MyType = type

Basic Types

type MyType = any
type MyType = null
type MyType = Boolean
type MyType = true
type MyType = false
type MyType = String
type MyType = "string"
type MyType = Number
type MyType = Number.Float
type MyType = Regex
type MyType = Range
type MyType = Property
type MyType = .property
type MyType = List<String>
type MyType = Array<Boolean>
type MyType = Map<Number, Regex>
type MyType = Set<Range>
type MyType = Iter<String>

Record Types

type MyType = { prop: String }
type MyType = { prop?: String }
type MyType = { ...OtherRecordType }

Optional Types

type MyType = Boolean?
type MyType = Boolean | null # same

Function Types

type MyType = fn (param: String): Number
type MyType = fn (param: String): Iter<Number>       # fn () iter {}

Generics

type MyType<T> = Array<T>
type MyType = fn <T> (param: T): T

Unions

type MyType = String | Boolean | Range
type MyType =
  | String
  | Boolean
  | Range

Types in Syntax

Variables

let value: Boolean = true
let value: Boolean | String = "cool"

Functions

let fn = fn (param1: String, param2: Number) {
  # ...
}

let fn = fn (...rest: Array<String>) {
  # ...
}

let fn = fn (): String {
  "nice"
}

let fn = fn (): Iter<String> iter {
  yield "good"
}
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].