compile.go

  1// Copyright (C) 2016 Kohei YOSHIDA. All rights reserved.
  2//
  3// This program is free software; you can redistribute it and/or
  4// modify it under the terms of The BSD 3-Clause License
  5// that can be found in the LICENSE file.
  6
  7package uritemplate
  8
  9import (
 10	"fmt"
 11	"unicode/utf8"
 12)
 13
 14type compiler struct {
 15	prog *prog
 16}
 17
 18func (c *compiler) init() {
 19	c.prog = &prog{}
 20}
 21
 22func (c *compiler) op(opcode progOpcode) uint32 {
 23	i := len(c.prog.op)
 24	c.prog.op = append(c.prog.op, progOp{code: opcode})
 25	return uint32(i)
 26}
 27
 28func (c *compiler) opWithRune(opcode progOpcode, r rune) uint32 {
 29	addr := c.op(opcode)
 30	(&c.prog.op[addr]).r = r
 31	return addr
 32}
 33
 34func (c *compiler) opWithRuneClass(opcode progOpcode, rc runeClass) uint32 {
 35	addr := c.op(opcode)
 36	(&c.prog.op[addr]).rc = rc
 37	return addr
 38}
 39
 40func (c *compiler) opWithAddr(opcode progOpcode, absaddr uint32) uint32 {
 41	addr := c.op(opcode)
 42	(&c.prog.op[addr]).i = absaddr
 43	return addr
 44}
 45
 46func (c *compiler) opWithAddrDelta(opcode progOpcode, delta uint32) uint32 {
 47	return c.opWithAddr(opcode, uint32(len(c.prog.op))+delta)
 48}
 49
 50func (c *compiler) opWithName(opcode progOpcode, name string) uint32 {
 51	addr := c.op(opcode)
 52	(&c.prog.op[addr]).name = name
 53	return addr
 54}
 55
 56func (c *compiler) compileString(str string) {
 57	for i := 0; i < len(str); {
 58		// NOTE(yosida95): It is confirmed at parse time that literals
 59		// consist of only valid-UTF8 runes.
 60		r, size := utf8.DecodeRuneInString(str[i:])
 61		c.opWithRune(opRune, r)
 62		i += size
 63	}
 64}
 65
 66func (c *compiler) compileRuneClass(rc runeClass, maxlen int) {
 67	for i := 0; i < maxlen; i++ {
 68		if i > 0 {
 69			c.opWithAddrDelta(opSplit, 7)
 70		}
 71		c.opWithAddrDelta(opSplit, 3)                 // raw rune or pct-encoded
 72		c.opWithRuneClass(opRuneClass, rc)            // raw rune
 73		c.opWithAddrDelta(opJmp, 4)                   //
 74		c.opWithRune(opRune, '%')                     // pct-encoded
 75		c.opWithRuneClass(opRuneClass, runeClassPctE) //
 76		c.opWithRuneClass(opRuneClass, runeClassPctE) //
 77	}
 78}
 79
 80func (c *compiler) compileRuneClassInfinite(rc runeClass) {
 81	start := c.opWithAddrDelta(opSplit, 3)        // raw rune or pct-encoded
 82	c.opWithRuneClass(opRuneClass, rc)            // raw rune
 83	c.opWithAddrDelta(opJmp, 4)                   //
 84	c.opWithRune(opRune, '%')                     // pct-encoded
 85	c.opWithRuneClass(opRuneClass, runeClassPctE) //
 86	c.opWithRuneClass(opRuneClass, runeClassPctE) //
 87	c.opWithAddrDelta(opSplit, 2)                 // loop
 88	c.opWithAddr(opJmp, start)                    //
 89}
 90
 91func (c *compiler) compileVarspecValue(spec varspec, expr *expression) {
 92	var specname string
 93	if spec.maxlen > 0 {
 94		specname = fmt.Sprintf("%s:%d", spec.name, spec.maxlen)
 95	} else {
 96		specname = spec.name
 97	}
 98
 99	c.prog.numCap++
100
101	c.opWithName(opCapStart, specname)
102
103	split := c.op(opSplit)
104	if spec.maxlen > 0 {
105		c.compileRuneClass(expr.allow, spec.maxlen)
106	} else {
107		c.compileRuneClassInfinite(expr.allow)
108	}
109
110	capEnd := c.opWithName(opCapEnd, specname)
111	c.prog.op[split].i = capEnd
112}
113
114func (c *compiler) compileVarspec(spec varspec, expr *expression) {
115	switch {
116	case expr.named && spec.explode:
117		split1 := c.op(opSplit)
118		noop := c.op(opNoop)
119		c.compileString(spec.name)
120
121		split2 := c.op(opSplit)
122		c.opWithRune(opRune, '=')
123		c.compileVarspecValue(spec, expr)
124
125		split3 := c.op(opSplit)
126		c.compileString(expr.sep)
127		c.opWithAddr(opJmp, noop)
128
129		c.prog.op[split2].i = uint32(len(c.prog.op))
130		c.compileString(expr.ifemp)
131		c.opWithAddr(opJmp, split3)
132
133		c.prog.op[split1].i = uint32(len(c.prog.op))
134		c.prog.op[split3].i = uint32(len(c.prog.op))
135
136	case expr.named && !spec.explode:
137		c.compileString(spec.name)
138
139		split2 := c.op(opSplit)
140		c.opWithRune(opRune, '=')
141
142		split3 := c.op(opSplit)
143
144		split4 := c.op(opSplit)
145		c.compileVarspecValue(spec, expr)
146
147		split5 := c.op(opSplit)
148		c.prog.op[split4].i = split5
149		c.compileString(",")
150		c.opWithAddr(opJmp, split4)
151
152		c.prog.op[split3].i = uint32(len(c.prog.op))
153		c.compileString(",")
154		jmp1 := c.op(opJmp)
155
156		c.prog.op[split2].i = uint32(len(c.prog.op))
157		c.compileString(expr.ifemp)
158
159		c.prog.op[split5].i = uint32(len(c.prog.op))
160		c.prog.op[jmp1].i = uint32(len(c.prog.op))
161
162	case !expr.named:
163		start := uint32(len(c.prog.op))
164		c.compileVarspecValue(spec, expr)
165
166		split1 := c.op(opSplit)
167		jmp := c.op(opJmp)
168
169		c.prog.op[split1].i = uint32(len(c.prog.op))
170		if spec.explode {
171			c.compileString(expr.sep)
172		} else {
173			c.opWithRune(opRune, ',')
174		}
175		c.opWithAddr(opJmp, start)
176
177		c.prog.op[jmp].i = uint32(len(c.prog.op))
178	}
179}
180
181func (c *compiler) compileExpression(expr *expression) {
182	if len(expr.vars) < 1 {
183		return
184	}
185
186	split1 := c.op(opSplit)
187	c.compileString(expr.first)
188
189	for i, size := 0, len(expr.vars); i < size; i++ {
190		spec := expr.vars[i]
191
192		split2 := c.op(opSplit)
193		if i > 0 {
194			split3 := c.op(opSplit)
195			c.compileString(expr.sep)
196			c.prog.op[split3].i = uint32(len(c.prog.op))
197		}
198		c.compileVarspec(spec, expr)
199		c.prog.op[split2].i = uint32(len(c.prog.op))
200	}
201
202	c.prog.op[split1].i = uint32(len(c.prog.op))
203}
204
205func (c *compiler) compileLiterals(lt literals) {
206	c.compileString(string(lt))
207}
208
209func (c *compiler) compile(tmpl *Template) {
210	c.op(opLineBegin)
211	for i := range tmpl.exprs {
212		expr := tmpl.exprs[i]
213		switch expr := expr.(type) {
214		default:
215			panic("unhandled expression")
216		case *expression:
217			c.compileExpression(expr)
218		case literals:
219			c.compileLiterals(expr)
220		}
221	}
222	c.op(opLineEnd)
223	c.op(opEnd)
224}