Skip to content

aiya000/hs-time-script

Repository files navigation

Now this project is the implementing phase!

Please see the test and the test status to check this progress.

💠 Time script 💠

Build Status

Typed VIM Engergisch SCRIPT = Vim script + strong static typing

Overview

"*
 * Comment
 *"
function ExecuteFizzBuzz(): Void  " abort by default
  const xs: List<Int> = range(0, 100)
  const fizzbuzz: List<String> = []

  for x in xs
    call add(    " Add comments anywhere.
      fizzbuzz,  " Multi line without the line continuation '\'.
      s:fizzbuzz(x),
    )
  endfor

  echo string(fizzbuzz)
endfunction

function s:fizzbuzz(x: Int): String
  return
    x % 15 is 0 ? 'FizzBuzz' :
    x %  5 is 0 ? 'Fizz' :
    x %  3 is 0 : 'Buzz' : string(x)
endfunction

"*
 * Function variables with naming of snake_case (not PascalCase).
 * snake_case: [a-zA-Z0-9_]+
 * PascalCase: [A-Z][A-Za-z0-9_]+
 *"
const f: () => Void = ::FizzBuzz  " Function references by ::
call f()

Purpose

To Vim script

  • Introduce strong static types: here
  • Introduce advanced features: here
  • The innovative, the consistency, and the easy to writeing is emphasizer than the backword compatibility
    • Because Vim script definiately saves the backword compatibility

Types

Basic

Simular to :help type().

let x: Int =  42
let y: Int = +10  " signs
let z: Int = -20
let x: String = 'you'
let y: String = "me"
let x:  Float = 1.0
let y:  Bool  = v:true
let z1: Null  = v:null
let z2: Null  = v:none  " This is a Null to save `type(v:null) is type(v:none)`
" Compile error!
" Because v:null is not an Int
let num: Int = v:null

Also types can be embraced by parens ().

This is useful when use generic types.

let you: (Int)  = 1000
let me: ((Int)) = 000

Natural numbers

Meaning non negative numbers

let n: Nat = 10
let m: Nat = 0
let l: Nat = n - m  " 0

" Compile error!
" Because integral signs means Int literals
let i: Nat = +1
let i: Nat = -1

Characters

let c: Char = 'x'
" Compile error!
" Because two or more strings cannot be assigned
let d: Char = 'xx'

Any

let foo: Any = 10
let foo = 'string'  " re-assignment
let foo = v:null    " 

In the latest spec, if the variable type omitted, be typed by Any.

(In the future, to be inferenced concrete types.)

let bar = 'string'  " bar is an Any

" The returned value is an Any
function F()
  return 10
endfunction

" x is an Any
function G(x)
endfunction

Typing to functions

Please also see abort by default.

function F(x: Int): String
  return string(a:x)
endfunction

Supporting defining generic functions is planned on the future spec.

Void

Void is same as Nat.

function G(): Void
endfunction

" This spec is derived by below Vim script spec
echo type(G()) is v:t_number  " true

Builtin generic types

let xs: List String = ['sugar', 'sweet', 'moon']
let x:  Dict Int    = {'foo': 10, 'bar': 20}
let xss: List (List Int) = [range(0, 1), range(2, 3)]
let x:   Dict (Dict Int) = {
  'foo': { 'x': 10 },
  'bar': { 'y': 20 },
}

Lists

let xs: List Int = range(0, 10)  " Initialize with 10 elements
let j: Nat = 0
let i: Int = -1

echo xs[i]  " 0
let xs[i] = 999
echo xs[i]  " 999

echo xs[j]  " 10
let xs[j] = 42
echo xs[j]  " 42
let xs: List Int = range(0, 10)
let i: String = 'x'

" Both compile error!
" Because lists can be accessed only by Int or Nat in Time script
let xs[i] = 999
echo xs[i]

" NOTE: Vim script allows using String as an index, it is used as a number 0.

Dicts

Please also see unnecessary-quotes-in-dicts.

let x = Dict Int = {}
let i: String = 'i'

let x[i] = 10

Please also see unnecessary-trivial-back-slashes.

let x: Dict Int = {
  10: 100,  " This index same as a String '10'
}

" Compile error!
" Because 10 is not a String
echo x[10]

" You should use '10' instead on getting the value
echo x['10']
" Compile error!
" Because 'foo' is not an Int
let x: Dict Int = {
    foo: 'foo',
}

Function types

let F:  Int -> String     = function('string')
let G:  (Int, Int) -> Int = function('range')

" Same as `Int -> (String -> Bool)`
let H: Int -> String -> Bool = { _ ->
  { _ -> v:true }
}

Variadic argument

TODO

Unions

let foo: Int | Null = v:null

Tuples

" 2 dimensions
let t1: Tuple Char Nat = ['a', 97]
let t2: Tuple (Tuple Char Nat) String = [t1, 'can be nested']

" 3 dimensions
let u: Tuple Int String Bool = [-10, 'me', v:true]
" Compile error!
" Because 3 Dimensional Tuple cannot assign into 2 Dimensional
let i: Tuple Null Null = [v:null, v:null, v:null]

" Compile error!
" Because 2 dimensional tuple doesn't have the 3rd element
echo i[2]

Objects

let x: {x: Nat, y: String} = {
  y: '000',
  x: 1000,
}

Others

Variadic parameters

function F(...) abort
  " a:000 is typed by List<any>
endfunction

Advanced features

Optional parameter

function F(x?: Nat)
  echo a:x
endfunction

call F(10)  " 10
call F()    " v:null

Default values for parameters

function F(x: Nat = 20)
  echo a:x
endfunction

call F(10)  " 10
call F()    " 20

Multi-line comments

"*
 This
 is
 a
 comment
 *"

Allowing comments anywhere

command! -bar ContLine  " This is a command
    \ call gift#for#you()

Enable function options by default

Below options are enabled by default.

  • abort
  • closure
  • range
  • dict (Only if dict qualified names specified. Like x.f, x.y.f)
function s:f()
  throw 'error!'
  echo 'finish.'
endfunction

echo s:f()  " E605: Exception not caught: error!

But you can disable abort by [[no-abort]].

function s:g() [[no-abort]]
  throw 'error!'
  echo 'finish.'
endfunction

echo s:g()
" E605: Exception not caught: error!
" finish.

Also below same as it.

  • [[no-closure]]
  • [[no-range]]
  • [[no-dict]]

String interpolations

let n: Nat = 10

" expressions to be stringified via string()
echo $'$n ${n + 1}'
" 10 11

Allowing to names of non upper cases [a-z_]+ for function references

let to_string = function('string')

Don't require unnecessary back-slashes on trivial cases

let xs = [
  10, 20, 30,
]

echo map(xs, { _, x ->
  f(x) + g(x)
})
Don't require unnecessary quotes without #{} (on {} notation) dicts

Also allowing mixin names both quoted and not quoted.

echo {foo: 10} == {'foo': 10}
" 1

let x = {
  aaa: 'caramel',     " Allowed because all keys are named by [a-z_]+
  " keba-b: 'sweet',  " Not allowed because a name uses a char '-'
  'keba-b': 'sweet',  " Not allowed because a name uses a char '-'
}
Named function arguments
function Log(name, message) abort
    echo a:name .. ': ' .. a:message
endfunction

call Log(
    message: 'howdy!',
    name: 'flowey',
    )
Assigning new value without declrarations
let  x = 10
let* x = 20  " Assign new value
" Compile error!
" Because y is never declared.
let* y = 30

let  z = {}
let* z.a = 40  " error too!

Future specs

Calling functions without :call

F()

" In this future, other than 'call', 'return', and 'echo' must be called with the prefix `:`.
:vsplit
:split

" Conversely, 'call', 'return', and 'echo' must not be called with the prefix `:`.
call F()
echo 10
function G() abort
  return 10
endfunction

" This is able to be parsed, but without any optimizations of parsing.
:call F()
:echo 10

Type synonyms

type Map = List (Tuple Char Nat)

let char_code: Map = [
  ['a', 97],
  ['b', 98],
  ['c', 99],
]

Type casts

" This often raises some problems
let x: Int = 10
let y: Any = x as Any

Generics

Naming [a-z][a-zA-Z0-9_] to types that is meaning generic type parameter

" In this case, `a` is a generic type parameter
function Identity(x: a) a
  return a:x
endfunction

let x: Nat = Identity[Nat](10)  " A type specifying
let y: Nat = Identity(20)       " Or the specifying can be omited

enum and pattern matching

enum Mode
  Normal
  Command
  Visual(kind: Virtual)  -- Can put one or more fields
endenum

enum Virtual
  Charwise
  Linewise
  Blockwise
endenum
const mode: Mode = Visual(Charwise)

const symbol =
  " All enum values must be exhaustive.
  match(mode)
    " Simple
    Normal -> 'n'
    Command -> 'c'
    " Nested fields
    Visual(Charwise) -> 'v'
    Visual(Linewise) -> 'V'
    Visual(Blockwise) -> ''
  endmatch

Decorators

@DeepConst
const x = {xx: 10}

@Assert({ x, y -> y > 0 })
function Div(x, y) abort
  return x / y
endfunction

function DeepConst(ast_of_binding: AST): Either CompileError AST
  " Modifying of ast_of_binding
endfunction

function Assert(ast_of_div: AST): Either CompileError AST
  " Return a CompileError value if you want to abort compiling
endfunction

Structural subtyping

const a: {x: Nat, y: Char} = {
  x: 10,
  y: 'a',
}

function F(x: {x: Nat}): Void
endfunction

call F(a)

???

TODO

type IntContainer = {
  " An associated type
  type Element = Int

  " A property
  values: List Int

  " A function (
  map(f: Int -> Int): Void
}

Type inferences

let x: Int = some
let y = x  " Now y is an Int

Type references

let map: type(char_code) = char_code