// Copyright (C) 2016 Kohei YOSHIDA. All rights reserved.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of The BSD 3-Clause License
// that can be found in the LICENSE file.

package uritemplate

import (
	
	
)

type compiler struct {
	prog *prog
}

func ( *compiler) () {
	.prog = &prog{}
}

func ( *compiler) ( progOpcode) uint32 {
	 := len(.prog.op)
	.prog.op = append(.prog.op, progOp{code: })
	return uint32()
}

func ( *compiler) ( progOpcode,  rune) uint32 {
	 := .op()
	(&.prog.op[]).r = 
	return 
}

func ( *compiler) ( progOpcode,  runeClass) uint32 {
	 := .op()
	(&.prog.op[]).rc = 
	return 
}

func ( *compiler) ( progOpcode,  uint32) uint32 {
	 := .op()
	(&.prog.op[]).i = 
	return 
}

func ( *compiler) ( progOpcode,  uint32) uint32 {
	return .opWithAddr(, uint32(len(.prog.op))+)
}

func ( *compiler) ( progOpcode,  string) uint32 {
	 := .op()
	(&.prog.op[]).name = 
	return 
}

func ( *compiler) ( string) {
	for  := 0;  < len(); {
		// NOTE(yosida95): It is confirmed at parse time that literals
		// consist of only valid-UTF8 runes.
		,  := utf8.DecodeRuneInString([:])
		.opWithRune(opRune, )
		 += 
	}
}

func ( *compiler) ( runeClass,  int) {
	for  := 0;  < ; ++ {
		if  > 0 {
			.opWithAddrDelta(opSplit, 7)
		}
		.opWithAddrDelta(opSplit, 3)                 // raw rune or pct-encoded
		.opWithRuneClass(opRuneClass, )            // raw rune
		.opWithAddrDelta(opJmp, 4)                   //
		.opWithRune(opRune, '%')                     // pct-encoded
		.opWithRuneClass(opRuneClass, runeClassPctE) //
		.opWithRuneClass(opRuneClass, runeClassPctE) //
	}
}

func ( *compiler) ( runeClass) {
	 := .opWithAddrDelta(opSplit, 3)        // raw rune or pct-encoded
	.opWithRuneClass(opRuneClass, )            // raw rune
	.opWithAddrDelta(opJmp, 4)                   //
	.opWithRune(opRune, '%')                     // pct-encoded
	.opWithRuneClass(opRuneClass, runeClassPctE) //
	.opWithRuneClass(opRuneClass, runeClassPctE) //
	.opWithAddrDelta(opSplit, 2)                 // loop
	.opWithAddr(opJmp, )                    //
}

func ( *compiler) ( varspec,  *expression) {
	var  string
	if .maxlen > 0 {
		 = fmt.Sprintf("%s:%d", .name, .maxlen)
	} else {
		 = .name
	}

	.prog.numCap++

	.opWithName(opCapStart, )

	 := .op(opSplit)
	if .maxlen > 0 {
		.compileRuneClass(.allow, .maxlen)
	} else {
		.compileRuneClassInfinite(.allow)
	}

	 := .opWithName(opCapEnd, )
	.prog.op[].i = 
}

func ( *compiler) ( varspec,  *expression) {
	switch {
	case .named && .explode:
		 := .op(opSplit)
		 := .op(opNoop)
		.compileString(.name)

		 := .op(opSplit)
		.opWithRune(opRune, '=')
		.compileVarspecValue(, )

		 := .op(opSplit)
		.compileString(.sep)
		.opWithAddr(opJmp, )

		.prog.op[].i = uint32(len(.prog.op))
		.compileString(.ifemp)
		.opWithAddr(opJmp, )

		.prog.op[].i = uint32(len(.prog.op))
		.prog.op[].i = uint32(len(.prog.op))

	case .named && !.explode:
		.compileString(.name)

		 := .op(opSplit)
		.opWithRune(opRune, '=')

		 := .op(opSplit)

		 := .op(opSplit)
		.compileVarspecValue(, )

		 := .op(opSplit)
		.prog.op[].i = 
		.compileString(",")
		.opWithAddr(opJmp, )

		.prog.op[].i = uint32(len(.prog.op))
		.compileString(",")
		 := .op(opJmp)

		.prog.op[].i = uint32(len(.prog.op))
		.compileString(.ifemp)

		.prog.op[].i = uint32(len(.prog.op))
		.prog.op[].i = uint32(len(.prog.op))

	case !.named:
		 := uint32(len(.prog.op))
		.compileVarspecValue(, )

		 := .op(opSplit)
		 := .op(opJmp)

		.prog.op[].i = uint32(len(.prog.op))
		if .explode {
			.compileString(.sep)
		} else {
			.opWithRune(opRune, ',')
		}
		.opWithAddr(opJmp, )

		.prog.op[].i = uint32(len(.prog.op))
	}
}

func ( *compiler) ( *expression) {
	if len(.vars) < 1 {
		return
	}

	 := .op(opSplit)
	.compileString(.first)

	for ,  := 0, len(.vars);  < ; ++ {
		 := .vars[]

		 := .op(opSplit)
		if  > 0 {
			 := .op(opSplit)
			.compileString(.sep)
			.prog.op[].i = uint32(len(.prog.op))
		}
		.compileVarspec(, )
		.prog.op[].i = uint32(len(.prog.op))
	}

	.prog.op[].i = uint32(len(.prog.op))
}

func ( *compiler) ( literals) {
	.compileString(string())
}

func ( *compiler) ( *Template) {
	.op(opLineBegin)
	for  := range .exprs {
		 := .exprs[]
		switch expr := .(type) {
		default:
			panic("unhandled expression")
		case *expression:
			.compileExpression()
		case literals:
			.compileLiterals()
		}
	}
	.op(opLineEnd)
	.op(opEnd)
}