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}