Skip to content

Instantly share code, notes, and snippets.

@PhilipWitte
Last active August 29, 2015 13:56
Show Gist options
  • Save PhilipWitte/9324201 to your computer and use it in GitHub Desktop.
Save PhilipWitte/9324201 to your computer and use it in GitHub Desktop.
Unified Nimrod Syntax

Nimrod - Unified Command Syntax

RULES

<command> <ident> <pragma> <type> : <body>

<command> <ident>
  <pragma> <type> :
    <body>

<command>
  <ident> <pragma> <type> :
    <body>
  <ident>
    <pragma>
    <type> :
      <body>
  ...
  • : - Required (possible use = instead for var let const).
  • <command> - Keywords (type, proc, etc) or custom macro/templates who consume StmtLists. Gets invoke multiple times when no <ident> is on it's line (ie, last example).
  • <ident> - Always first symbol after <command>.
  • <pragma> - Optional. Any builtin like ref/pure/etc, but also any macro/template (just like now).
  • <type> - Optional. Always last before :, eg after <pragma>.
  • <body> - Always after :

How It's Consistent

CONSISTENT FOR TYPE & PROC

# current syntax..
type Foo*[T] {.pure.} = ref object ...
proc bar*[T](i:int): T {.inline.} = ...
# proposed syntax..
type Foo*[T] pure ref object: ...
proc bar*[T](i int) inline T: ...

CONSISTENT FOR MACROS

# current syntax..
macro init*(head:expr, body:stmt): stmt {.immediate.} =
  ## builds initializer

proc new*(this:T): T {.inline, noInit.} = ... # '=' enforced
init new*() T {.inline noInit.}: ... # ':' enforced (NOTE: currently {. .} causes ICE)
# proposed syntax..
macro init*(head expr, body stmt) immediate stmt:
  ## builds initilizer

proc new*(this T) inline noInit T: ... # identical to..
init new*() inline noInit T: ...       # ..this macro

CONSISTENT MULTI-USE FOR ALL

# keywords, like 'type' here, can be used in two ways..
type Foo: ... # invokes keyword 'type(Foo, ...)'

type
  Foo: ... # invokes keyword 'type(Foo, ...)'
  Bar: ... # invokes keyword 'type(Bar, ...)'
# ..macros, like 'class' here, can be used the same ways..
class Foo: ... # invokes macro 'class(Foo, ...)'

class
  Foo: ... # invokes macro 'class(Foo, ...)'
  Bar: ... # invokes macro 'class(Bar, ...)'

Why It's Better

  • less noise in syntax (no {. .} or = needed) means it's easier to read, type, and advertise
  • consistent across almost all idioms means it's easy to learn (better for adoption)
  • consistent between builtins and user-defined macros means OOP macros feel like true first-class citizens (good for adoption especially among OOP fans)

More Examples

# ..same for compiler built-ins, like types..
type Foo ref object:
  a, b int

type
  Foo ref object:
    a, b int
  
  Bar pure ref object:
    x, y float
# .. and for procs too..
proc foo(a, b int) int:
  a + b

proc
  foo(a, b int) int:
    a + b
  
  bar(x, y float) float:
    x + y
# ..and for all the rest..
var foo int

var
  foo int
  bar float

const foo float: 10
let foo float: 10
var foo float: 10

# QUESTION: should use '=' for these?
const foo float = 10
let foo float = 10
var foo float = 10
# .. even these!
import foo
import foo: bar
import
  foo
  bar: baz

if isFoo: foo()
else isBar: bar()

ADITIONAL THOUGHTS

  • No "odd man out" means it's really easy to learn, and removes the need {. .} and '=' altogether.
  • Could even use 'pub' instead of '*' for symbol exposure (though i have no problem with the later).
  • Syntax is basically available today minus the universal-multi-call and ability to put on multiple lines.
  • Could optionally use {} brackets for indent-insensitive block, and : for indent-sensitive block

Larger Sample (plus {} : mix-match)

macro init*(head expr, body stmt) immediate stmt:
  ## makes initializer procs with 'this' var

# ---

const
  accDamp = 0.98
var
  gavity = Vector.down(0.5)
  deltaTime = 1.0

type Ship* inheritable ref object {
  acc Vector
  vel Vector
}

type 
  XWing* final Ship:
    wingsOpen* bool
  
  TieFighter* final Ship:
    target* Ship

proc
  acc*(this Ship) inline noInit Vector: this.acc
  vel*(this Ship) inline noInit Vector: this.vel

proc update*(this Ship):
  this.acc += gravity * deltaTime
  this.acc *= accDamp
  this.vel += this.acc

init XWing.new*(wingsOpen bool) inline:
  this.wingsOpen = wingsOpen

init TieFighter.new*(target Ship) inline {
  this.target = target
}

when isMainModule:
  var
    rebel = XWing.new(false)
    enime = TieFighter.new(rebel)
  
  update rebel
  update enime

REF AS KEYWORD IDEA

  • To help remove T/P prefix. Make 'ref' a keyword like var/let/const and always define objects as non-ref.
type Person:
  name* string
  age* int

when isMainModule:
  var
    andrew = Person(name:"Andrew", age:28)
    philip = Person(name:"Philip", age:26)
  ref
    someone = andrew
  
  andrew = philip # copy
  echo someone.age # prints '26'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment