[POC] gigo: go generate on steroids!

193 views
Skip to first unread message

mhh...@gmail.com

unread,
Mar 29, 2017, 4:29:56 PM3/29/17
to golang-nuts
Hi !

I m very happy to share with you gigo

Its a go generate on steroids, Its more close to something like babel than go generate, see,

in input it takes


type
Todo struct {
 
Name string
 
Done bool
}

type
Todos implements<Mutexed (Slice .Todo)> {
 
// it reads as a mutexed list of todo.
}

func
(t *Todos) Hello(){fmt.Println("Hello")}


template Mutexed<.Name> struct {
 
lock *sync.Mutex
  embed
<.Name>
}

<range $m := .Methods> func (m Mutexed<$.Name>) <$m.Name>(<$m.Params>) <$m.Out> {
 
lock.Lock()
  defer
lock.Unlock()
  m
.embed.<$m.GetName>(<$m.Args>)
}

template <.Name>Slice struct {
  items
[]<.Name>
}

func
(s <.Name>Slice) Push(item <.Name>) int {
  s
.items = append(s.items, item)
 
return len(s.items)
}

func
(s <.Name>Slice) Index(item <.Name>) int {
 
for i, items := range s.items {
   
if item == search {
     
return i
   
}
 
}
 
return -1
}

func
(s <.Name>Slice) RemoveAt(i index) int {
    s
.items = append(s.items[:i], s.items[i+1:]...)
}

func
(s <.Name>Slice) Remove(item <.Name>) int {
 
if i:= s.Index(item); i > -1 {
    s
.RemoveAt(i)
   
return i
 
}
 
return -1
}



It produces




type
Todo struct {
 
Name string
 
Done bool
}


type
TodoSlice struct {
  items
[]Todo
}


func
(s TodoSlice) Push(item Todo) int {
  s
.items = append(s.items, item)
 
return len(s.items)
}


func
(s TodoSlice) Index(item Todo) int {
 
for i, items := range s.items {
   
if item == search {
     
return i
   
}
 
}
 
return -1
}


func
(s TodoSlice) RemoveAt(i index) int {
        s
.items = append(s.items[:i], s.items[i+1:]...)
}


func
(s TodoSlice) Remove(item Todo) int {
 
if i:= s.Index(item); i > -1 {
    s
.RemoveAt(i)
   
return i
 
}
 
return -1
}
// while this is compatible with its local contracts,
// it will work and still takes advantages of concrete types exported by consumed package.

type
MutexedTodoSlice struct {
 
lock *sync.Mutex
  embed
TodoSlice
}


 func
(m MutexedTodoSlice)  Push((item Todo))  int {
 
lock.Lock()
  defer
lock.Unlock()
  m
.embed.<$m.GetName>(<$m.Args>)
}
 func
(m MutexedTodoSlice)  Index((item Todo))  int {
 
lock.Lock()
  defer
lock.Unlock()
  m
.embed.<$m.GetName>(<$m.Args>)
}
 func
(m MutexedTodoSlice)  RemoveAt((i index))  int {
 
lock.Lock()
  defer
lock.Unlock()
  m
.embed.<$m.GetName>(<$m.Args>)
}
 func
(m MutexedTodoSlice)  Remove((item Todo))  int {
 
lock.Lock()
  defer
lock.Unlock()
  m
.embed.<$m.GetName>(<$m.Args>)
}


type
Todos struct {
       
MutexedTodoSlice
 
// it reads as a mutexed list of todo.
}

func
(t *Todos) Hello(){fmt.Println("Hello")}

Still some work to be done, but you got the idea!



It implements a whole tokenizer/interpreter  of go code (almost),

a bit like go/ast, but its way more lighter(so far),

the idea being to be able add more customization based on that.


The package currently tokenize a source code,

interprets it into declarations,

manipulates nodes to use a regular go template.Template

to execute the generation,

finally it builds a go file and output its.


It adds new syntaxes such as


implements, which produces a struct

type Todos implements<Mutexed (Slice .Todo)>


template, to define virutal structs type

template Mutexed<.Name> struct


Then the systems understands instructions blocks

such as "regular" method declaration to template
func (s <.Name>Slice) Push(item <.Name>) int {
  s.items = append(s.items, item)
  return len(s.items)
}


And pure template expressions,

(although, at that moment, its a limited to methods only)
<range $m := .Methods> func (m Mutexed<$.Name>) <$m.Name>(<$m.Params>) <$m.Out> {
  lock.Lock()
  defer lock.Unlock()
  m.embed.<$m.GetName>(<$m.Args>)
}


I plan to be able to declare func to inject into template instructions,

so you ll be able to do pretty much anything i think.


I m not sure the new syntaxes are correct,

but i d that this project is the starting point of an effort to

improve go coding experience.

In that goal, any comments are welcome!


Last thing, at that very moment the package is extremly new,

don t expect too much,

it did work with the demo file https://github.com/mh-cbon/gigo/blob/master/demo.gigo.go ;)


~~ Happy coding !

mhh...@gmail.com

unread,
Apr 1, 2017, 5:52:44 AM4/1/17
to golang-nuts
Hi!

Just an update to tell i cleaned up the code and added a cli to ease usage and demo it!

Since last post i added parameters to type mutators, for example you write

type Todos implements<:Mutexed (Slice .Todo "Name")>



The Slice Type will handle the extra params to generate a FindByName method.

// range over args to produce new FindBy methods
<:range $a := .Args> func (m <:$.Name>Slice) FindBy<:$a>(<:$a> <:$.ArgType $a>) (<:$.Name>,bool) {

 
for i, items := range s.items {

   
if item.<:$a> == <:$a> {
     
return item, true
   
}
 
}
 
return <:$.Name>{}, false
}

I also updated error reporting,

For a decl like this,

package tomate

type tomate struct qsdqd{} // bad

It outputs

unexpected token
In file=<noname> At=3:19
Found=wordToken wanted=[bracketOpenToken]

...
5  package tomate
6  type tomate struct qsdqd{}
   
---------------------↑
...

BTW, if template.Template could report position of the error within the line it would be better ;)
See an error reported from a template execution
0  
1  type Todos implements<:Mutexed (Slice, .Todo "Name"):> {
✘- ↑↑↑ ???
2    // it reads as a mutexed list of todo.
3  }
...

panic
: in gigo template: gigo:3: unexpected "," in operand at line 3:-1 [recovered]
    panic
: in gigo template: gigo:3: unexpected "," in operand at line 3:-1


And finally the cli was added so you can gen/dump/str an input file,
check the readme https://github.com/mh-cbon/gigo#-go-run-maingo--symbol-push-gen-demogigogo

~~Happy coding!
Reply all
Reply to author
Forward
0 new messages