<commit_before>// Copyright (C) 2015 Thomas de Zeeuw.
//
// Licensed onder the MIT license that can be found in the LICENSE file.

package ini

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"unicode"
)

const (
	commentStart byte = ';'
	seperator    byte = '='
	sectionStart byte = '['
	sectionEnd   byte = ']'
	escape       byte = '\\'
	lineEnd      byte = '\n'
	doubleQuote  byte = '"'
	singleQuote  byte = '\''
	nilQuote     byte = 0
)

type lineType int

const (
	commentLine lineType = iota
	sectionLine
	keyValuePairLine
)

type parser struct {
	Config            Config
	scanner           *bufio.Scanner
	currentSection    string
	currentLine       []byte
	currentlineNumber int
}

func (p *parser) parse() error {
	for p.scanner.Scan() {
		line := p.scanner.Bytes()

		if err := p.handleLine(line); err != nil {
			return newSynthaxError(p.currentlineNumber, err.Error())
		}
	}

	if err := p.scanner.Err(); err != nil {
		return fmt.Errorf("ini: error reading: %s", err.Error())
	}
	return nil
}

func (p *parser) handleLine(line []byte) error {
	line = bytes.TrimSpace(line)
	p.currentLine = line
	p.currentlineNumber++

	if len(line) == 0 {
		return nil
	}

	switch detectLineType(line[0]) {
	case sectionLine:
		sectionName, err := parseSection(line)
		if err != nil {
			return err
		}

		p.updateSection(sectionName)
	case keyValuePairLine:
		key, value, err := parseKeyValue(line)
		if err != nil {
			return err
		}

		if err := p.addKeyValue(key, value); err != nil {
			return err
		}
	}

	return nil
}

func (p *parser) updateSection(sectionName string) {
	p.currentSection = sectionName
	p.Config[sectionName] = map[string]string{}
}

func (p *parser) addKeyValue(key, value string) error {
	sectionName := p.currentSection
	if _, ok := p.Config[sectionName][key]; ok {
		if sectionName == Global {
			sectionName = globalName
		}
		return fmt.Errorf("key %q already used in section %q", key, sectionName)
	}

	p.Config[sectionName][key] = value
	return nil
}

// Parse parses ini formatted input.
//
// *Note the reader already gets buffered, so there is no need to buffer it
// youself.
func Parse(r io.Reader) (Config, error) {
	p := newParser(r)
	if err := p.parse(); err != nil {
		return nil, err
	}

	return p.Config, nil
}

func newParser(r io.Reader) *parser {
	return &parser{
		Config:         Config{Global: {}},
		currentSection: Global,
		scanner:        bufio.NewScanner(r),
	}
}

func detectLineType(b byte) lineType {
	if b == commentStart {
		return commentLine
	} else if b == sectionStart {
		return sectionLine
	}

	return keyValuePairLine
}

func parseSection(line []byte) (string, error) {
	if line[0] != sectionStart {
		return "", fmt.Errorf("section should start with %q", string(sectionStart))
	}

	var sectionName = make([]byte, 0, len(line)-2)
	var sectionEnded bool

	// Skipping the opening bracket.
	for i := 1; i < len(line); i++ {
		b := line[i]

		if b == sectionEnd {
			sectionEnded = true
			continue
		} else if b == commentStart && sectionEnded {
			break
		} else if sectionEnded && !unicode.IsSpace(rune(b)) {
			return "", fmt.Errorf("unexpected %q after section closed", string(b))
		}

		sectionName = append(sectionName, b)
	}

	if !sectionEnded {
		return "", errors.New("unclosed section")
	}

	section := string(bytes.TrimSpace(sectionName))
	return section, nil
}

func parseKeyValue(line []byte) (key, value string, err error) {
	var values [2][]byte
	var hasSeperator bool

	// Keep track of the byte number for both the key and value.
	for i, valueNumber := 0, 0; valueNumber <= 1; valueNumber++ {
		var isQouted, isEscaped, nextShouldBeSeperator bool
		var usedQouted byte

		for ; i < len(line); i++ {
			b := line[i]

			if nextShouldBeSeperator && !unicode.IsSpace(rune(b)) && b != seperator {
				return "", "", fmt.Errorf("unexpected %q, expected the seperator %q",
					string(b), string(seperator))
			} else if (b == doubleQuote || b == singleQuote) && !isEscaped {
				if !isQouted {
					isQouted = true
					usedQouted = b
					continue
				} else if b == usedQouted {
					isQouted = false
					usedQouted = nilQuote
					if valueNumber == 0 {
						nextShouldBeSeperator = true
					}
					continue
				}
			} else if b == commentStart && !isQouted && hasSeperator {
				break
			} else if b == escape && !isEscaped {
				isEscaped = true
				continue
			} else if b == seperator && valueNumber == 0 && !isQouted {
				hasSeperator = true
				i++ // skip the current byte (the separator) in the next loop.
				break
			}

			isEscaped = false
			values[valueNumber] = append(values[valueNumber], b)
		}

		if isQouted {
			return "", "", errors.New("qoute not closed")
		}
	}

	if !hasSeperator {
		return "", "", errors.New("no separator found")
	}

	key = string(bytes.TrimSpace(values[0]))
	value = string(bytes.TrimSpace(values[1]))
	if len(key) == 0 {
		return "", "", errors.New("key can't be empty")
	}
	return key, value, nil
}
<commit_msg>Drop currentLine from the parser<commit_after>// Copyright (C) 2015 Thomas de Zeeuw.
//
// Licensed onder the MIT license that can be found in the LICENSE file.

package ini

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"unicode"
)

const (
	commentStart byte = ';'
	seperator    byte = '='
	sectionStart byte = '['
	sectionEnd   byte = ']'
	escape       byte = '\\'
	lineEnd      byte = '\n'
	doubleQuote  byte = '"'
	singleQuote  byte = '\''
	nilQuote     byte = 0
)

type lineType int

const (
	commentLine lineType = iota
	sectionLine
	keyValuePairLine
)

type parser struct {
	Config            Config
	scanner           *bufio.Scanner
	currentSection    string
	currentlineNumber int
}

func (p *parser) parse() error {
	for p.scanner.Scan() {
		line := p.scanner.Bytes()

		if err := p.handleLine(line); err != nil {
			return newSynthaxError(p.currentlineNumber, err.Error())
		}
	}

	if err := p.scanner.Err(); err != nil {
		return fmt.Errorf("ini: error reading: %s", err.Error())
	}
	return nil
}

func (p *parser) handleLine(line []byte) error {
	line = bytes.TrimSpace(line)
	p.currentlineNumber++

	if len(line) == 0 {
		return nil
	}

	switch detectLineType(line[0]) {
	case sectionLine:
		sectionName, err := parseSection(line)
		if err != nil {
			return err
		}

		p.updateSection(sectionName)
	case keyValuePairLine:
		key, value, err := parseKeyValue(line)
		if err != nil {
			return err
		}

		if err := p.addKeyValue(key, value); err != nil {
			return err
		}
	}

	return nil
}

func (p *parser) updateSection(sectionName string) {
	p.currentSection = sectionName
	p.Config[sectionName] = map[string]string{}
}

func (p *parser) addKeyValue(key, value string) error {
	sectionName := p.currentSection
	if _, ok := p.Config[sectionName][key]; ok {
		if sectionName == Global {
			sectionName = globalName
		}
		return fmt.Errorf("key %q already used in section %q", key, sectionName)
	}

	p.Config[sectionName][key] = value
	return nil
}

// Parse parses ini formatted input.
//
// *Note the reader already gets buffered, so there is no need to buffer it
// youself.
func Parse(r io.Reader) (Config, error) {
	p := newParser(r)
	if err := p.parse(); err != nil {
		return nil, err
	}

	return p.Config, nil
}

func newParser(r io.Reader) *parser {
	return &parser{
		Config:         Config{Global: {}},
		currentSection: Global,
		scanner:        bufio.NewScanner(r),
	}
}

func detectLineType(b byte) lineType {
	if b == commentStart {
		return commentLine
	} else if b == sectionStart {
		return sectionLine
	}

	return keyValuePairLine
}

func parseSection(line []byte) (string, error) {
	if line[0] != sectionStart {
		return "", fmt.Errorf("section should start with %q", string(sectionStart))
	}

	var sectionName = make([]byte, 0, len(line)-2)
	var sectionEnded bool

	// Skipping the opening bracket.
	for i := 1; i < len(line); i++ {
		b := line[i]

		if b == sectionEnd {
			sectionEnded = true
			continue
		} else if b == commentStart && sectionEnded {
			break
		} else if sectionEnded && !unicode.IsSpace(rune(b)) {
			return "", fmt.Errorf("unexpected %q after section closed", string(b))
		}

		sectionName = append(sectionName, b)
	}

	if !sectionEnded {
		return "", errors.New("unclosed section")
	}

	section := string(bytes.TrimSpace(sectionName))
	return section, nil
}

func parseKeyValue(line []byte) (key, value string, err error) {
	var values [2][]byte
	var hasSeperator bool

	// Keep track of the byte number for both the key and value.
	for i, valueNumber := 0, 0; valueNumber <= 1; valueNumber++ {
		var isQouted, isEscaped, nextShouldBeSeperator bool
		var usedQouted byte

		for ; i < len(line); i++ {
			b := line[i]

			if nextShouldBeSeperator && !unicode.IsSpace(rune(b)) && b != seperator {
				return "", "", fmt.Errorf("unexpected %q, expected the seperator %q",
					string(b), string(seperator))
			} else if (b == doubleQuote || b == singleQuote) && !isEscaped {
				if !isQouted {
					isQouted = true
					usedQouted = b
					continue
				} else if b == usedQouted {
					isQouted = false
					usedQouted = nilQuote
					if valueNumber == 0 {
						nextShouldBeSeperator = true
					}
					continue
				}
			} else if b == commentStart && !isQouted && hasSeperator {
				break
			} else if b == escape && !isEscaped {
				isEscaped = true
				continue
			} else if b == seperator && valueNumber == 0 && !isQouted {
				hasSeperator = true
				i++ // skip the current byte (the separator) in the next loop.
				break
			}

			isEscaped = false
			values[valueNumber] = append(values[valueNumber], b)
		}

		if isQouted {
			return "", "", errors.New("qoute not closed")
		}
	}

	if !hasSeperator {
		return "", "", errors.New("no separator found")
	}

	key = string(bytes.TrimSpace(values[0]))
	value = string(bytes.TrimSpace(values[1]))
	if len(key) == 0 {
		return "", "", errors.New("key can't be empty")
	}
	return key, value, nil
}
<|endoftext|>


<commit_before>// Copyright 2012-2015 Vladimir Gorbunov. All rights reserved.  Use of
// this source code is governed by a MIT license that can be found in
// the LICENSE file.

package gmtrn

import (
	"errors"
	"fmt"
	"html"
	"regexp"
	"strings"
)

// Single part of definition, contains word, link to word page and
// additional information.  Link may not be opened without
// "http://www.multitran.ru" referer for access.
type MeaningWord struct {
	Word,
	Link,
	Add string
}

func (w MeaningWord) String() string {
	return fmt.Sprintf(`<MeaningWord "%s" link: "%s" add: "%s">`,
		w.Word, w.Link, w.Add)
}

// Meaning of some word by topic.
type Meaning struct {
	Words []MeaningWord
	Topic,
	Link,
	Abbrev string
}

func (d Meaning) String() string {
	return fmt.Sprintf(`<Meaning, Topic: "%s", "%s", "%s" "%s">`,
		d.Topic, d.Link, d.Abbrev, d.Words)
}

// Word with list of meanings
type Word struct {
	Meanings []Meaning
	Word,
	Link,
	Part string
}

func (w Word) String() string {
	return fmt.Sprintf(`<Word "%s", "%s", "%s", "%s">`,
		w.Word, w.Link, w.Part, w.Meanings)
}

// List of words for requested page
type WordList struct {
	Words []Word
	Query,
	Link string
}

func (w WordList) String() string {
	return fmt.Sprintf(`<WordList "%s", "%s": "%s">`,
		w.Query, w.Link, w.Words)
}

// Link to page
type link struct {
	word,
	link string
}

// Multitran base domain with search path
var domain = "http://www.multitran.ru/c/"

/* Regexes */
// Links to other words
var otherRe = regexp.MustCompile(
	`(?s)height="20"> (.*)  \r\n<br> найдены`)
var linkRe = regexp.MustCompile(`(?s)href="([^"]+)">([^<]+)`)

// Main content
var blockRe = regexp.MustCompile("(?s)<table(.*)
")

// Word
var wordRe = regexp.MustCompile(`^<a href="([^"]+)">(.+?)</a>.*( <em>| )?`)

// Part of speech (if exists)
var partRe = regexp.MustCompile(` <em>([^<]+)</em>`)

// Meaning
var itemsRe = regexp.MustCompile(
	`(?s)<a title="(.*?)" href="([^"]+)"><i>(.*?)</i> ` +
		`</a>\r\n</td><td>(.*?)(<span STYLE="color:black"></td>` +
		`|<td >  |</a></td></tr>|<tr>)`)

// MeaningWord
var mwordRe = regexp.MustCompile(`<a href="([^"]+)">([^<]+)`)

// Additional word info
var addRe = regexp.MustCompile(`<span STYLE="color:gray">(.*)`)

// Strip tags
var stripRe = regexp.MustCompile("<(.*?)>")

// Remove tags from string
func stripTags(str string) string {
	return stripRe.ReplaceAllString(str, "")
}

// Remove   from string
func removeNbsp(str string) string {
	return strings.Replace(str, " ", "", -1)
}

// Get list of word meanings by topic
func parseWord(chunk string) []Meaning {
	items := itemsRe.FindAllStringSubmatch(chunk, -1)
	result := make([]Meaning, 0, len(items))
	// Process multiple words in meaning
	for _, item := range items {
		if len(item) != 6 {
			continue
		}
		cleanStr := html.UnescapeString(removeNbsp(item[4]))
		words := strings.Split(cleanStr, ";")
		mwords := make([]MeaningWord, 0, len(words))
		for _, v := range words {
			word := mwordRe.FindStringSubmatch(v)
			if len(word) != 3 {
				continue
			}
			mw := MeaningWord{word[2], domain + word[1], ""}
			// Get additional information if exist
			if add := addRe.FindStringSubmatch(v); len(add) == 2 {
				mw.Add = stripRe.ReplaceAllString(add[1], "")
			}
			mwords = append(mwords, mw)
		}
		m := Meaning{mwords,
			html.UnescapeString(item[1]),
			domain + item[2],
			removeNbsp(item[3])}
		result = append(result, m)
	}
	return result
}

// Parse page content and return word meanings
func parsePage(content string) (result []Word, err error) {
	// Get main block with content
	block := blockRe.FindStringSubmatch(content)
	if len(block) != 2 {
		err = errors.New("Translation can not be found")
		return
	}
	// Split content to chunks with word
	chunks := strings.Split(block[1],
		`<td bgcolor="#DBDBDB" colspan="2" width="700"> `)
	result = make([]Word, 0, len(chunks))
	for _, chunk := range chunks {
		matches := wordRe.FindStringSubmatch(chunk)
		if len(matches) == 4 {
			meanings := parseWord(chunk)
			part := ""
			partMatches := partRe.FindStringSubmatch(chunk)
			if len(partMatches) == 2 {
				part = partMatches[1]
			}
			word := Word{meanings,
				stripTags(html.UnescapeString(matches[2])),
				domain + matches[1],
				part}
			result = append(result, word)
		}
	}
	return
}
<commit_msg>Fix word parsing when phrase contains multiple parts<commit_after>// Copyright 2012-2015 Vladimir Gorbunov. All rights reserved.  Use of
// this source code is governed by a MIT license that can be found in
// the LICENSE file.

package gmtrn

import (
	"errors"
	"fmt"
	"html"
	"regexp"
	"strings"
)

// Single part of definition, contains word, link to word page and
// additional information.  Link may not be opened without
// "http://www.multitran.ru" referer for access.
type MeaningWord struct {
	Word,
	Link,
	Add string
}

func (w MeaningWord) String() string {
	return fmt.Sprintf(`<MeaningWord "%s" link: "%s" add: "%s">`,
		w.Word, w.Link, w.Add)
}

// Meaning of some word by topic.
type Meaning struct {
	Words []MeaningWord
	Topic,
	Link,
	Abbrev string
}

func (d Meaning) String() string {
	return fmt.Sprintf(`<Meaning, Topic: "%s", "%s", "%s" "%s">`,
		d.Topic, d.Link, d.Abbrev, d.Words)
}

// Word with list of meanings
type Word struct {
	Meanings []Meaning
	Word,
	Link,
	Part string
}

func (w Word) String() string {
	return fmt.Sprintf(`<Word "%s", "%s", "%s", "%s">`,
		w.Word, w.Link, w.Part, w.Meanings)
}

// List of words for requested page
type WordList struct {
	Words []Word
	Query,
	Link string
}

func (w WordList) String() string {
	return fmt.Sprintf(`<WordList "%s", "%s": "%s">`,
		w.Query, w.Link, w.Words)
}

// Link to page
type link struct {
	word,
	link string
}

// Multitran base domain with search path
var domain = "http://www.multitran.ru/c/"

/* Regexes */
// Links to other words
var otherRe = regexp.MustCompile(
	`(?s)height="20"> (.*)  \r\n<br> найдены`)
var linkRe = regexp.MustCompile(`(?s)href="([^"]+)">([^<]+)`)

// Main content
var blockRe = regexp.MustCompile("(?s)<table(.*)
")

// Word
var wordRe = regexp.MustCompile(`^<a href="([^"]+)">(.+?)</a>.*( <em>| )?`)

// Part of speech (if exists)
var partRe = regexp.MustCompile(` <em>([^<]+)</em>`)

// Regex for "go to start" and "phrases" links that need to be removed
var removeLinkRe = regexp.MustCompile(`<a href="#(start|phrases)">(.*)</a>`)

// Meaning
var itemsRe = regexp.MustCompile(
	`(?s)<a title="(.*?)" href="([^"]+)"><i>(.*?)</i> ` +
		`</a>\r\n</td><td>(.*?)(<span STYLE="color:black"></td>` +
		`|<td >  |</a></td></tr>|<tr>)`)

// MeaningWord
var mwordRe = regexp.MustCompile(`<a href="([^"]+)">([^<]+)`)

// Additional word info
var addRe = regexp.MustCompile(`<span STYLE="color:gray">(.*)`)

// Strip tags
var stripRe = regexp.MustCompile("<(.*?)>")

// Remove tags from string
func stripTags(str string) string {
	return stripRe.ReplaceAllString(str, "")
}

// Remove   from string
func removeNbsp(str string) string {
	return strings.Replace(str, " ", "", -1)
}

// Get list of word meanings by topic
func parseWord(chunk string) []Meaning {
	items := itemsRe.FindAllStringSubmatch(chunk, -1)
	result := make([]Meaning, 0, len(items))
	// Process multiple words in meaning
	for _, item := range items {
		if len(item) != 6 {
			continue
		}
		cleanStr := html.UnescapeString(removeNbsp(item[4]))
		words := strings.Split(cleanStr, ";")
		mwords := make([]MeaningWord, 0, len(words))
		for _, v := range words {
			word := mwordRe.FindStringSubmatch(v)
			if len(word) != 3 {
				continue
			}
			mw := MeaningWord{word[2], domain + word[1], ""}
			// Get additional information if exist
			if add := addRe.FindStringSubmatch(v); len(add) == 2 {
				mw.Add = stripRe.ReplaceAllString(add[1], "")
			}
			mwords = append(mwords, mw)
		}
		m := Meaning{mwords,
			html.UnescapeString(item[1]),
			domain + item[2],
			removeNbsp(item[3])}
		result = append(result, m)
	}
	return result
}

// Extract full word without additional links and part
func cleanWord(line string) string {
	withoutPart := partRe.ReplaceAllString(line, "")
	withoutLinks := removeLinkRe.ReplaceAllString(withoutPart, "")
	splitted := strings.Split(stripTags(html.UnescapeString(withoutLinks)), "|")
	return strings.TrimSpace(splitted[0])
}

// Parse page content and return word meanings
func parsePage(content string) (result []Word, err error) {
	// Get main block with content
	block := blockRe.FindStringSubmatch(content)
	if len(block) != 2 {
		err = errors.New("Translation can not be found")
		return
	}
	// Split content to chunks with word
	chunks := strings.Split(block[1],
		`<td bgcolor="#DBDBDB" colspan="2" width="700"> `)
	result = make([]Word, 0, len(chunks))
	for _, chunk := range chunks {
		matches := wordRe.FindStringSubmatch(chunk)
		if len(matches) == 4 {
			meanings := parseWord(chunk)
			part := ""
			partMatches := partRe.FindStringSubmatch(chunk)
			if len(partMatches) == 2 {
				part = partMatches[1]
			}
			word := Word{meanings,
				cleanWord(matches[0]),
				domain + matches[1],
				part}
			result = append(result, word)
		}
	}
	return
}
<|endoftext|>


<commit_before>/*
Package inceis "Every package should have a package comment, a block comment preceding
the package clause.
For multi-file packages, the package comment only needs to be present in one file, and any
one will do. The package comment should introduce the package and provide information
relevant to the package as a whole. It will appear first on the godoc page and should set
up the detailed documentation that follows."
*/
package inceis

import (
	"github.com/MerinEREN/iiPackages/api/account"
	"github.com/MerinEREN/iiPackages/api/contents"
	"github.com/MerinEREN/iiPackages/api/demand"
	"github.com/MerinEREN/iiPackages/api/demands"
	"github.com/MerinEREN/iiPackages/api/languages"
	"github.com/MerinEREN/iiPackages/api/offer"
	"github.com/MerinEREN/iiPackages/api/offers"
	"github.com/MerinEREN/iiPackages/api/page"
	"github.com/MerinEREN/iiPackages/api/pages"
	"github.com/MerinEREN/iiPackages/api/role"
	"github.com/MerinEREN/iiPackages/api/roleType"
	"github.com/MerinEREN/iiPackages/api/roleTypes"
	"github.com/MerinEREN/iiPackages/api/roles"
	"github.com/MerinEREN/iiPackages/api/servicePacks"
	"github.com/MerinEREN/iiPackages/api/settingsAccount"
	"github.com/MerinEREN/iiPackages/api/signin"
	"github.com/MerinEREN/iiPackages/api/signout"
	"github.com/MerinEREN/iiPackages/api/tag"
	"github.com/MerinEREN/iiPackages/api/tags"
	"github.com/MerinEREN/iiPackages/api/timeline"
	"github.com/MerinEREN/iiPackages/api/user"
	"github.com/MerinEREN/iiPackages/api/userRoles"
	"github.com/MerinEREN/iiPackages/api/userTags"
	"github.com/MerinEREN/iiPackages/api/users"
	"github.com/MerinEREN/iiPackages/session"
	"strings"
	// "github.com/MerinEREN/iiPackages/cookie"
	"github.com/MerinEREN/iiPackages/page/template"
	"google.golang.org/appengine/memcache"
	"log"
	"net/http"
	// "regexp"
	"time"
)

var _ memcache.Item // For debugging, delete when done.

var (
// CHANGE THE REGEXP BELOW !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// validPath = regexp.MustCompile("^/|[/A-Za-z0-9]$")
)

func init() {
	// http.Handle("/favicon.ico", http.NotFoundHandler())
	http.Handle("/",
		http.TimeoutHandler(http.HandlerFunc(makeHandlerFunc(signin.Handler)),
			1000*time.Millisecond,
			"This is http.TimeoutHandler(handler, time.Duration, message) "+
				"message bitch =)"))
	// http.HandleFunc("/", makeHandlerFunc(signin.Handler))
	http.HandleFunc("/contents", makeHandlerFunc(contents.Handler))
	http.HandleFunc("/users/", makeHandlerFunc(user.Handler))
	http.HandleFunc("/userTags/", makeHandlerFunc(userTags.Handler))
	http.HandleFunc("/userRoles/", makeHandlerFunc(userRoles.Handler))
	http.HandleFunc("/languages", makeHandlerFunc(languages.Handler))
	http.HandleFunc("/signout", makeHandlerFunc(signout.Handler))
	http.HandleFunc("/accounts/", makeHandlerFunc(account.Handler))
	http.HandleFunc("/demands", makeHandlerFunc(demands.Handler))
	http.HandleFunc("/demands/", makeHandlerFunc(demand.Handler))
	http.HandleFunc("/offers", makeHandlerFunc(offers.Handler))
	http.HandleFunc("/offers/", makeHandlerFunc(offer.Handler))
	http.HandleFunc("/servicePacks", makeHandlerFunc(servicePacks.Handler))
	http.HandleFunc("/timeline", makeHandlerFunc(timeline.Handler))
	http.HandleFunc("/pages", makeHandlerFunc(pages.Handler))
	http.HandleFunc("/pages/", makeHandlerFunc(page.Handler))
	http.HandleFunc("/tags", makeHandlerFunc(tags.Handler))
	http.HandleFunc("/tags/", makeHandlerFunc(tag.Handler))
	http.HandleFunc("/roles", makeHandlerFunc(roles.Handler))
	http.HandleFunc("/roles/", makeHandlerFunc(role.Handler))
	http.HandleFunc("/roleTypes", makeHandlerFunc(roleTypes.Handler))
	http.HandleFunc("/roleTypes/", makeHandlerFunc(roleType.Handler))
	http.HandleFunc("/settingsAccount", makeHandlerFunc(settingsAccount.Handler))
	http.HandleFunc("/users", makeHandlerFunc(users.Handler))
	// http.HandleFunc("/accounts", makeHandlerFunc(accounts.Handler))
	// http.HandleFunc("/signUp", makeHandlerFunc(signUpHandler))
	// http.HandleFunc("/logIn", makeHandlerFunc(logInHandler))
	// http.HandleFunc("/accounts", makeHandlerFunc(accountsHandler))
	/* if http.PostForm("/logIn", data); err != nil {
		http.Err(w, "Internal server error while login",
			http.StatusBadRequest)
	} */
	fs := http.FileServer(http.Dir("../iiClient/public"))
	// http.Handle("/css/", fs)
	http.Handle("/img/", fs)
	http.Handle("/js/", fs)
	/* log.Printf("About to listen on 10443. " +
	"Go to https://192.168.1.100:10443/ " +
	"or https://localhost:10443/") */
	// Redirecting to a port or a domain etc.
	// go http.ListenAndServe(":8080",
	// http.RedirectHandler("https://192.168.1.100:10443", 301))
	// err := http.ListenAndServeTLS(":10443", "cert.pem", "key.pem", nil)
	// ListenAndServe and ListenAndServeTLS always returns a non-nil error !!!
	// log.Fatal(err)
}

/* func logInHandler(w http.ResponseWriter, r *http.Request, s string) {
	p, err := content.Get(r, s)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if r.Method == "POST" {
		key := "email"
		email := r.PostFormValue(key)
		key = "password"
		password := r.PostFormValue(key)
		acc, err := account.VerifyUser(c, email, password)
		switch err {
		case account.EmailNotExist:
			fmt.Fprintln(w, err)
		case account.ExistingEmail:
			for _, u := range acc.Users {
				if u.Email == email {
					// ALLWAYS CREATE COOKIE BEFORE EXECUTING TEMPLATE
					cookie.Set(w, r, "session", u.UUID)
				}
			}
			// NEWER EXECUTE TEMPLATE OR WRITE ANYTHING TO THE BODY BEFORE
			// REDIRECT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
			http.Redirect(w, r, "/accounts/"+acc.Name, http.StatusSeeOther)
		case account.InvalidPassword:
			fmt.Fprintln(w, err)
		default:
			// Status code could be wrong
			http.Error(w, err.Error(), http.StatusNotImplemented)
			log.Fatalln(err)
		}
	}
	template.RenderLogIn(w, p)
} */

type handlerFuncWithSessionParam func(s *session.Session)

// CHANGE THE SESSION THING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
func makeHandlerFunc(fn handlerFuncWithSessionParam) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		/* m := validPath.FindStringSubmatch(r.URL.Path)
		if m == nil {
			log.Printf("Invalid Path: %s\n", r.URL.Path)
			http.NotFound(w, r)
			return
		} */
		/* for _, val := range m {
			fmt.Println(val)
		}*/
		// CHANGE CONTENT AND TEMPLATE THINGS !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
		s := new(session.Session)
		s.Init(w, r)
		if strings.Contains(r.Header.Get("Accept"), "text/html") {
			// Authenticate the client
			// Check should be in here to be able to make content data requests
			// and redirect content page request.
			if s.U == nil && r.URL.Path != "/" {
				log.Println("REDIRECTTT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
				http.Redirect(w, r, "/", http.StatusSeeOther)
				return
			}
			log.Println("Getting template !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
			template.RenderIndex(w)
			// } else if strings.Contains(r.Header.Get("Accept"), "application/json") {
		} else {
			log.Println("Getting data !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
			// SECURITY CHECK
			// add other landing page request calls later.
			if s.U != nil || (r.URL.Path == "/contents" || r.URL.Path == "/") {
				fn(s)
			}
		}
	}
}

// HEADER ALWAYS SHOULD BE SET BEFORE ANYTHING WRITE A PAGE BODY !!!!!!!!!!!!!!!!!!!!!!!!!!
// w.Header().Set("Content-Type", "text/html"; charset=utf-8")
//fmt.Fprintln(w, things...) // Writes to the body
<commit_msg>Windows update backup<commit_after>/*
Package inceis "Every package should have a package comment, a block comment preceding
the package clause.
For multi-file packages, the package comment only needs to be present in one file, and any
one will do. The package comment should introduce the package and provide information
relevant to the package as a whole. It will appear first on the godoc page and should set
up the detailed documentation that follows."
*/
package inceis

import (
	"github.com/MerinEREN/iiPackages/api/account"
	"github.com/MerinEREN/iiPackages/api/contexts"
	"github.com/MerinEREN/iiPackages/api/demand"
	"github.com/MerinEREN/iiPackages/api/demands"
	"github.com/MerinEREN/iiPackages/api/languages"
	"github.com/MerinEREN/iiPackages/api/offer"
	"github.com/MerinEREN/iiPackages/api/offers"
	"github.com/MerinEREN/iiPackages/api/page"
	"github.com/MerinEREN/iiPackages/api/pages"
	"github.com/MerinEREN/iiPackages/api/photos"
	"github.com/MerinEREN/iiPackages/api/role"
	"github.com/MerinEREN/iiPackages/api/roleType"
	"github.com/MerinEREN/iiPackages/api/roleTypes"
	"github.com/MerinEREN/iiPackages/api/roles"
	"github.com/MerinEREN/iiPackages/api/rolesUser"
	"github.com/MerinEREN/iiPackages/api/servicePacks"
	"github.com/MerinEREN/iiPackages/api/settingsAccount"
	"github.com/MerinEREN/iiPackages/api/signin"
	"github.com/MerinEREN/iiPackages/api/signout"
	"github.com/MerinEREN/iiPackages/api/tag"
	"github.com/MerinEREN/iiPackages/api/tags"
	"github.com/MerinEREN/iiPackages/api/tagsUser"
	"github.com/MerinEREN/iiPackages/api/timeline"
	"github.com/MerinEREN/iiPackages/api/user"
	"github.com/MerinEREN/iiPackages/api/users"
	"github.com/MerinEREN/iiPackages/session"
	"strings"
	// "github.com/MerinEREN/iiPackages/cookie"
	"github.com/MerinEREN/iiPackages/page/template"
	"google.golang.org/appengine/memcache"
	"log"
	"net/http"
	// "regexp"
	"time"
)

var _ memcache.Item // For debugging, delete when done.

var (
// CHANGE THE REGEXP BELOW !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// validPath = regexp.MustCompile("^/|[/A-Za-z0-9]$")
)

func init() {
	// http.Handle("/favicon.ico", http.NotFoundHandler())
	http.Handle("/",
		http.TimeoutHandler(http.HandlerFunc(makeHandlerFunc(signin.Handler)),
			1000*time.Millisecond,
			"This is http.TimeoutHandler(handler, time.Duration, message) "+
				"message bitch =)"))
	// http.HandleFunc("/", makeHandlerFunc(signin.Handler))
	http.HandleFunc("/contexts", makeHandlerFunc(contexts.Handler))
	http.HandleFunc("/users/", makeHandlerFunc(user.Handler))
	http.HandleFunc("/photos", makeHandlerFunc(photos.Handler))
	http.HandleFunc("/rolesUser/", makeHandlerFunc(rolesUser.Handler))
	http.HandleFunc("/tagsUser/", makeHandlerFunc(tagsUser.Handler))
	http.HandleFunc("/signout", makeHandlerFunc(signout.Handler))
	http.HandleFunc("/languages", makeHandlerFunc(languages.Handler))
	http.HandleFunc("/accounts/", makeHandlerFunc(account.Handler))
	http.HandleFunc("/demands", makeHandlerFunc(demands.Handler))
	http.HandleFunc("/demands/", makeHandlerFunc(demand.Handler))
	http.HandleFunc("/offers", makeHandlerFunc(offers.Handler))
	http.HandleFunc("/offers/", makeHandlerFunc(offer.Handler))
	http.HandleFunc("/servicePacks", makeHandlerFunc(servicePacks.Handler))
	http.HandleFunc("/timeline", makeHandlerFunc(timeline.Handler))
	http.HandleFunc("/pages", makeHandlerFunc(pages.Handler))
	http.HandleFunc("/pages/", makeHandlerFunc(page.Handler))
	http.HandleFunc("/tags", makeHandlerFunc(tags.Handler))
	http.HandleFunc("/tags/", makeHandlerFunc(tag.Handler))
	http.HandleFunc("/roles", makeHandlerFunc(roles.Handler))
	http.HandleFunc("/roles/", makeHandlerFunc(role.Handler))
	http.HandleFunc("/roleTypes", makeHandlerFunc(roleTypes.Handler))
	http.HandleFunc("/roleTypes/", makeHandlerFunc(roleType.Handler))
	http.HandleFunc("/settingsAccount", makeHandlerFunc(settingsAccount.Handler))
	http.HandleFunc("/users", makeHandlerFunc(users.Handler))
	// http.HandleFunc("/accounts", makeHandlerFunc(accounts.Handler))
	// http.HandleFunc("/signUp", makeHandlerFunc(signUpHandler))
	// http.HandleFunc("/logIn", makeHandlerFunc(logInHandler))
	// http.HandleFunc("/accounts", makeHandlerFunc(accountsHandler))
	/* if http.PostForm("/logIn", data); err != nil {
		http.Err(w, "Internal server error while login",
			http.StatusBadRequest)
	} */
	fs := http.FileServer(http.Dir("../iiClient/public"))
	// http.Handle("/css/", fs)
	http.Handle("/img/", fs)
	http.Handle("/js/", fs)
	/* log.Printf("About to listen on 10443. " +
	"Go to https://192.168.1.100:10443/ " +
	"or https://localhost:10443/") */
	// Redirecting to a port or a domain etc.
	// go http.ListenAndServe(":8080",
	// http.RedirectHandler("https://192.168.1.100:10443", 301))
	// err := http.ListenAndServeTLS(":10443", "cert.pem", "key.pem", nil)
	// ListenAndServe and ListenAndServeTLS always returns a non-nil error !!!
	// log.Fatal(err)
}

/* func logInHandler(w http.ResponseWriter, r *http.Request, s string) {
	p, err := context.Get(r, s)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if r.Method == "POST" {
		key := "email"
		email := r.PostFormValue(key)
		key = "password"
		password := r.PostFormValue(key)
		acc, err := account.VerifyUser(c, email, password)
		switch err {
		case account.EmailNotExist:
			fmt.Fprintln(w, err)
		case account.ExistingEmail:
			for _, u := range acc.Users {
				if u.Email == email {
					// ALLWAYS CREATE COOKIE BEFORE EXECUTING TEMPLATE
					cookie.Set(w, r, "session", u.UUID)
				}
			}
			// NEWER EXECUTE TEMPLATE OR WRITE ANYTHING TO THE BODY BEFORE
			// REDIRECT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
			http.Redirect(w, r, "/accounts/"+acc.Name, http.StatusSeeOther)
		case account.InvalidPassword:
			fmt.Fprintln(w, err)
		default:
			// Status code could be wrong
			http.Error(w, err.Error(), http.StatusNotImplemented)
			log.Fatalln(err)
		}
	}
	template.RenderLogIn(w, p)
} */

type handlerFuncWithSessionParam func(s *session.Session)

// CHANGE THE SESSION THING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
func makeHandlerFunc(fn handlerFuncWithSessionParam) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		/* m := validPath.FindStringSubmatch(r.URL.Path)
		if m == nil {
			log.Printf("Invalid Path: %s\n", r.URL.Path)
			http.NotFound(w, r)
			return
		} */
		/* for _, val := range m {
			fmt.Println(val)
		}*/
		// CHANGE CONTEXT AND TEMPLATE THINGS !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
		s := new(session.Session)
		s.Init(w, r)
		if strings.Contains(r.Header.Get("Accept"), "text/html") {
			// Authenticate the client
			// Check should be in here to be able to make context data requests
			// and redirect context page request.
			if s.U == nil && r.URL.Path != "/" {
				log.Println("REDIRECTTT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
				http.Redirect(w, r, "/", http.StatusSeeOther)
				return
			}
			log.Println("Getting template !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
			template.RenderIndex(w)
			// } else if strings.Contains(r.Header.Get("Accept"), "application/json") {
		} else {
			log.Println("Getting data !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
			// SECURITY CHECK
			// add other landing page request calls later.
			if s.U != nil || (r.URL.Path == "/contexts" || r.URL.Path == "/") {
				fn(s)
			}
		}
	}
}

// HEADER ALWAYS SHOULD BE SET BEFORE ANYTHING WRITE A PAGE BODY !!!!!!!!!!!!!!!!!!!!!!!!!!
// w.Header().Set("Content-Type", "text/html"; charset=utf-8")
//fmt.Fprintln(w, things...) // Writes to the body
<|endoftext|>


<commit_before>package sprite_sass

import (
	"bytes"
	"fmt"
	"io"
	"log"
)

func init() {
	log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime)
}

type Replace struct {
	Start, End int
	Value      []byte
}

type Parser struct {
	Index, shift         int
	Chop                 []Replace
	Pwd, Input, ImageDir string
	Includes             []string
	Items                []Item
	Output               []byte
	Sprites              map[string]ImageList
	Vars                 map[string]string
}

func NewParser() *Parser {
	return &Parser{}
}

// Parser reads the tokens from the lexer and performs
// conversions and/or substitutions for sprite*() calls.
//
// Parser creates a map of all variables and sprites
// (created via sprite-map calls).
func (p *Parser) Start(in io.Reader, pkgdir string) []byte {
	p.Vars = make(map[string]string)
	p.Sprites = make(map[string]ImageList)

	if p.ImageDir == "" {
		p.ImageDir = pkgdir
	}
	buf := bytes.NewBuffer(make([]byte, 0, bytes.MinRead))
	buf.ReadFrom(in)

	// This pass resolves all the imports, but positions will
	// be off due to @import calls
	tokens, input, err := p.start(pkgdir, string(buf.Bytes()))
	// This call will have valid token positions
	tokens, input, err = p.start(pkgdir, input)

	p.Input = input
	p.Items = tokens
	if err != nil {
		panic(err)
	}
	p.loop()
	// I don't recall the point of this, but process
	// will result in whitespace errors in the output.
	// p.Output = process(p.Input, p.Items, 0)
	p.Output = []byte(p.Input)
	p.Replace()
	// DEBUG
	for _, item := range p.Items {
		_ = item
		// fmt.Printf("%s %s\n", item.Type, item)
	}
	return p.Output
}

func (p *Parser) loop() {
	var (
		def, cmd string
	)
	tokens := p.Items

	for ; p.Index < len(tokens); p.Index++ {
		token := tokens[p.Index]
		last := p.Index
		// Generate list of vars
		if token.Type == VAR {
			def = fmt.Sprintf("%s", token)
			val := ""
			nested := false
			for {
				p.Index++
				token = tokens[p.Index]
				// p.Index = i // Sync Index for now as we refactor away from i
				switch token.Type {
				case LPAREN:
					nested = true
				case RPAREN:
					nested = false
				case CMD:
					//p.Command()
					// Changing the behavior of CMD!
					cmd = fmt.Sprintf("%s", token)
					val += cmd
				case FILE:
					p.Index = p.File(cmd, last, p.Index)
					def = ""
					cmd = ""
				case SUB:
					fmt.Println("SUB:", tokens[p.Index-1],
						tokens[p.Index], tokens[p.Index+1])
					// fmt.Println(p.Input[tokens[i-20].Pos:tokens[i+20].Pos])
					// Cowardly give up and hope these variables do not matter
					// Cases:
					// - @for $i from 1 through $variable
					// - $variable: ($i - 1)
					// -
					fallthrough
				default:
					// fmt.Printf("Default: %s\n", token)
					val += fmt.Sprintf("%s", token)
				}

				if !nested && tokens[p.Index].Type != CMD {
					break
				}
			}
			if def != "" {
				p.Vars[def] = val
			}
			//Replace subsitution tokens
		} else if token.Type == SUB {
			repl := ""
			switch cmd {
			case "sprite":
				//Capture sprite
				sprite := p.Sprites[fmt.Sprintf("%s", token)]
				//Capture filename
				p.Index++
				name := fmt.Sprintf("%s", tokens[p.Index])
				repl = sprite.CSS(name)

				p.Mark(tokens[p.Index-3].Pos, tokens[p.Index+2].Pos, repl)
				tokens = append(tokens[:p.Index-3], tokens[p.Index:]...)
				p.Index = p.Index - 3
				def = ""
				cmd = ""
			case "sprite-height":
				sprite := p.Sprites[fmt.Sprintf("%s", token)]
				repl = fmt.Sprintf("height: %dpx;",
					sprite.ImageHeight(tokens[p.Index+1].String()))
				// Walk forward to file name
				p.Index++
				p.Mark(tokens[p.Index-4].Pos, tokens[p.Index+3].Pos, repl)
				tokens = append(tokens[:p.Index-4], tokens[p.Index:]...)
				p.Index = p.Index - 4
				def = ""
				cmd = ""
			case "sprite-width":
				sprite := p.Sprites[fmt.Sprintf("%s", token)]
				repl = fmt.Sprintf("width: %dpx;",
					sprite.ImageWidth(tokens[p.Index+1].String()))
				// Walk forward to file name
				p.Index++
				p.Mark(tokens[p.Index-4].Pos, tokens[p.Index+3].Pos, repl)
				tokens = append(tokens[:p.Index-4], tokens[p.Index:]...)
				p.Index = p.Index - 4
				def = ""
				cmd = ""
			case "sprite-dimensions":
				sprite := p.Sprites[fmt.Sprintf("%s", token)]
				repl = sprite.Dimensions(tokens[p.Index+1].String())
				// Walk forward to file name
				p.Index++
				p.Mark(tokens[p.Index-4].Pos, tokens[p.Index+3].Pos, repl)
				tokens = append(tokens[:p.Index-4], tokens[p.Index:]...)
				p.Index = p.Index - 4
				def = ""
				cmd = ""
			default:
				tokens[p.Index].Value = p.Vars[token.Value]
			}
		} else if token.Type == CMD {
			// Sync the index during the refactor
			cmd = fmt.Sprintf("%s", token)
			switch token.Value {
			case "inline-image", "sprite-file":
				cmd = ""
				p.Mixin()
			default:
				//log.Fatal("Danger will robinson")
			}
		}
	}
}

func (p *Parser) Command() string {
	return ""
}

// Mixin processes tokens in the format @include mixin(args...)
// and perform requested actions.
func (p *Parser) Mixin() {

	// Commands always end in ); else panic
	start := p.Index
	cmd := p.Items[start]

	var file Item
	for {
		cur := p.Items[p.Index]
		if cur.Type == RPAREN {
			p.Index++
			if p.Items[p.Index].Type != SEMIC {
				f, l := p.Items[start].Pos, p.Items[p.Index].Pos
				log.Fatal("Commands must end with semicolon",
					fmt.Sprintf(p.Input[f:l]))
			} else {
				break
			}
		} else if cur.Type == FILE {
			file = cur
		}
		p.Index++
	}
	if file.Type != FILE {
		log.Fatal("File for command was not found")
	}
	if cmd.Value == "inline-image" {
		img := ImageList{}
		img.Decode(p.ImageDir + "/" + file.Value)
		repl := img.Inline()
		p.Index-- // Preserve the final semic
		p.Mark(cmd.Pos-1, p.Items[p.Index].Pos+1, repl)
	}
	fmt.Println("Mixin", cmd.Value)
}

// Replace iterates through the list of substrings to
// cut or replace, adjusting for shift of the output
// buffer as a result of these ops.
func (p *Parser) Replace() {

	for _, c := range p.Chop {

		begin := c.Start - p.shift
		end := c.End - p.shift
		// fmt.Println(string(p.Input[begin:end]), "~>`"+string(c.Value)+"`")
		// fmt.Println("*******")
		// Adjust shift for number of bytes deleted and inserted
		p.shift += end - begin
		p.shift -= len(c.Value)
		suf := append(c.Value, p.Output[end:]...)

		p.Output = append(p.Output[:begin], suf...)
	}
}

// Mark segments of the input string for future deletion.
func (p *Parser) Mark(start, end int, val string) {
	// fmt.Println("Mark:", string(p.Input[start:end]), "~")
	p.Chop = append(p.Chop, Replace{start, end, []byte(val)})
}

// Processes file which usually mean cutting some of the input
// text.
func (p *Parser) File(cmd string, start, end int) int {
	first := p.Items[start]
	item := p.Items[end]
	i := end
	if cmd == "sprite-map" {
		// Find the next semicolon and remove it
		for ; p.Items[i].Type != RPAREN; i++ {
		}
		if p.Items[i+1].Type != SEMIC {
			panic("Statements must end in semicolon")
		}
		i++
		// Verify that the statement ends with semicolon
		interest := p.Items[i]
		// Mark the entire line plus semicolon for deletion
		p.Mark(first.Pos, interest.Pos+1, "")
		imgs := ImageList{}
		glob := fmt.Sprintf("%s", item)
		name := fmt.Sprintf("%s", p.Items[start])
		imgs.Decode(p.ImageDir + "/" + glob)
		imgs.Vertical = true
		imgs.Combine()
		p.Sprites[name] = imgs
		//TODO: Generate filename
		//imgs.Export("generated.png")
	}
	return i + 1
}

// func process(in string, items []Item, pos int) []byte {

// 	var out []byte
// 	l := len(items)

// 	if pos >= len(in) {
// 		return []byte("")
// 	}

// 	// TODO: There's an error where items[1] has an invalid
// 	// position.
// 	if l > 1 && items[1].Pos > items[0].Pos {
// 		out = append(out, in[items[0].Pos:items[1].Pos]...)
// 		out = append(out, process(in, items[1:], pos)...)
// 	} else {
// 		out = append(out, in[items[0].Pos:]...)
// 	}

// 	return out
// }

// start recursively resolves all imports.  It lexes the input
// adding the tokens to the Parser object.
// TODO: Convert this to byte slice in/out
func (p *Parser) start(pwd, input string) ([]Item, string, error) {

	var (
		status    []Item
		importing bool
		output    []byte
		pos       int
		last      *Item
	)

	lex := New(func(lex *Lexer) StateFn {
		return lex.Action()
	}, input)

	for {
		item := lex.Next()
		err := item.Error()
		//fmt.Println(item.Type, item.Value)
		if err != nil {
			return nil, string(output),
				fmt.Errorf("Error: %v (pos %d)", err, item.Pos)
		}
		switch item.Type {
		case ItemEOF:
			output = append(output, input[pos:]...)
			return status, string(output), nil
		case IMPORT:
			output = append(output, input[pos:item.Pos]...)
			last = item
			importing = true
		case INCLUDE, CMT:
			output = append(output, input[pos:item.Pos]...)
			pos = item.Pos
			status = append(status, *item)
		default:
			if importing {

				pwd, contents, err := p.ImportPath(pwd, fmt.Sprintf("%s", *item))
				if err != nil {
					log.Fatal(err)
				}
				//Eat the semicolon
				item := lex.Next()
				pos = item.Pos + len(item.Value)
				if item.Type != SEMIC {
					panic("@import statement must be followed by ;")
				}

				moreTokens, moreOutput, err := p.start(
					pwd,
					contents)
				// If importing was successful, each token must be moved forward
				// by the position of the @import call that made it available.
				for i, _ := range moreTokens {
					moreTokens[i].Pos += last.Pos
				}

				if err != nil {
					panic(err)
				}
				output = append(output, moreOutput...)
				status = append(status, moreTokens...)
				importing = false
			} else {
				output = append(output, input[pos:item.Pos]...)
				pos = item.Pos
				status = append(status, *item)
			}
		}
	}
}
<commit_msg>Start of recursive parser<commit_after>package sprite_sass

import (
	"bytes"
	"fmt"
	"io"
	"log"
)

func init() {
	log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime)
}

type Replace struct {
	Start, End int
	Value      []byte
}

type Parser struct {
	Index, shift         int
	Chop                 []Replace
	Pwd, Input, ImageDir string
	Includes             []string
	Items                []Item
	Output               []byte
	Sprites              map[string]ImageList
	NewVars, Vars        map[string]string
}

func NewParser() *Parser {
	return &Parser{}
}

// Parser reads the tokens from the lexer and performs
// conversions and/or substitutions for sprite*() calls.
//
// Parser creates a map of all variables and sprites
// (created via sprite-map calls).
func (p *Parser) Start(in io.Reader, pkgdir string) []byte {
	p.Vars = make(map[string]string)
	p.Sprites = make(map[string]ImageList)

	if p.ImageDir == "" {
		p.ImageDir = pkgdir
	}
	buf := bytes.NewBuffer(make([]byte, 0, bytes.MinRead))
	buf.ReadFrom(in)

	// This pass resolves all the imports, but positions will
	// be off due to @import calls
	tokens, input, err := p.start(pkgdir, string(buf.Bytes()))
	// This call will have valid token positions
	tokens, input, err = p.start(pkgdir, input)

	p.Input = input
	p.Items = tokens
	if err != nil {
		panic(err)
	}
	p.loop()
	// I don't recall the point of this, but process
	// will result in whitespace errors in the output.
	// p.Output = process(p.Input, p.Items, 0)
	p.Output = []byte(p.Input)
	p.Replace()
	// DEBUG
	for _, item := range p.Items {
		_ = item
		// fmt.Printf("%s %s\n", item.Type, item)
	}
	return p.Output
}

func (p *Parser) Parse(items []Item) []byte {
	i := 0
	for {
		item := items[i]
		if item.Type == VAR {
			var j int
			for items[j].Type != SEMIC {
			}

			p.NewVars[item.String()] = string(p.Parse(items[i+1 : j]))
		} else if item.Type == CMD {
			var j int
			for items[j].Type != RPAREN {
			}

		}
		i++
	}
}

func (p *Parser) loop() {
	var (
		def, cmd string
	)
	tokens := p.Items

	for ; p.Index < len(tokens); p.Index++ {
		token := tokens[p.Index]
		last := p.Index
		// Generate list of vars
		if token.Type == VAR {
			def = fmt.Sprintf("%s", token)
			val := ""
			nested := false
			for {
				p.Index++
				token = tokens[p.Index]
				// p.Index = i // Sync Index for now as we refactor away from i
				switch token.Type {
				case LPAREN:
					nested = true
				case RPAREN:
					nested = false
				case CMD:
					//p.Command()
					// Changing the behavior of CMD!
					cmd = fmt.Sprintf("%s", token)
					val += cmd
				case FILE:
					p.Index = p.File(cmd, last, p.Index)
					def = ""
					cmd = ""
				case SUB:
					fmt.Println("SUB:", tokens[p.Index-1],
						tokens[p.Index], tokens[p.Index+1])
					// fmt.Println(p.Input[tokens[i-20].Pos:tokens[i+20].Pos])
					// Cowardly give up and hope these variables do not matter
					// Cases:
					// - @for $i from 1 through $variable
					// - $variable: ($i - 1)
					// -
					fallthrough
				default:
					// fmt.Printf("Default: %s\n", token)
					val += fmt.Sprintf("%s", token)
				}

				if !nested && tokens[p.Index].Type != CMD {
					break
				}
			}
			if def != "" {
				p.Vars[def] = val
			}
			//Replace subsitution tokens
		} else if token.Type == SUB {
			repl := ""
			switch cmd {
			case "sprite":
				//Capture sprite
				sprite := p.Sprites[fmt.Sprintf("%s", token)]
				//Capture filename
				p.Index++
				name := fmt.Sprintf("%s", tokens[p.Index])
				repl = sprite.CSS(name)

				p.Mark(tokens[p.Index-3].Pos, tokens[p.Index+2].Pos, repl)
				tokens = append(tokens[:p.Index-3], tokens[p.Index:]...)
				p.Index = p.Index - 3
				def = ""
				cmd = ""
			case "sprite-height":
				sprite := p.Sprites[fmt.Sprintf("%s", token)]
				repl = fmt.Sprintf("height: %dpx;",
					sprite.ImageHeight(tokens[p.Index+1].String()))
				// Walk forward to file name
				p.Index++
				p.Mark(tokens[p.Index-4].Pos, tokens[p.Index+3].Pos, repl)
				tokens = append(tokens[:p.Index-4], tokens[p.Index:]...)
				p.Index = p.Index - 4
				def = ""
				cmd = ""
			case "sprite-width":
				sprite := p.Sprites[fmt.Sprintf("%s", token)]
				repl = fmt.Sprintf("width: %dpx;",
					sprite.ImageWidth(tokens[p.Index+1].String()))
				// Walk forward to file name
				p.Index++
				p.Mark(tokens[p.Index-4].Pos, tokens[p.Index+3].Pos, repl)
				tokens = append(tokens[:p.Index-4], tokens[p.Index:]...)
				p.Index = p.Index - 4
				def = ""
				cmd = ""
			case "sprite-dimensions":
				sprite := p.Sprites[fmt.Sprintf("%s", token)]
				repl = sprite.Dimensions(tokens[p.Index+1].String())
				// Walk forward to file name
				p.Index++
				p.Mark(tokens[p.Index-4].Pos, tokens[p.Index+3].Pos, repl)
				tokens = append(tokens[:p.Index-4], tokens[p.Index:]...)
				p.Index = p.Index - 4
				def = ""
				cmd = ""
			default:
				tokens[p.Index].Value = p.Vars[token.Value]
			}
		} else if token.Type == CMD {
			// Sync the index during the refactor
			cmd = fmt.Sprintf("%s", token)
			switch token.Value {
			case "inline-image", "sprite-file":
				cmd = ""
				p.Mixin()
			default:
				//log.Fatal("Danger will robinson")
			}
		}
	}
}

func (p *Parser) Command() string {
	items := p.Items
	item := items[p.Index]
	def, cmd, repl := "", "", ""
	switch item.Value {
	case "sprite":
		//Capture sprite
		sprite := p.Sprites[fmt.Sprintf("%s", item)]
		//Capture filename
		p.Index++
		name := fmt.Sprintf("%s", items[p.Index])
		repl = sprite.CSS(name)

		p.Mark(items[p.Index-3].Pos, items[p.Index+2].Pos, repl)
		items = append(items[:p.Index-3], items[p.Index:]...)
		p.Index = p.Index - 3
		def = ""
		cmd = ""
	case "sprite-height":
		sprite := p.Sprites[fmt.Sprintf("%s", item)]
		repl = fmt.Sprintf("height: %dpx;",
			sprite.ImageHeight(items[p.Index+1].String()))
		// Walk forward to file name
		p.Index++
		p.Mark(items[p.Index-4].Pos, items[p.Index+3].Pos, repl)
		items = append(items[:p.Index-4], items[p.Index:]...)
		p.Index = p.Index - 4
		def = ""
		cmd = ""
	case "sprite-width":
		sprite := p.Sprites[fmt.Sprintf("%s", item)]
		repl = fmt.Sprintf("width: %dpx;",
			sprite.ImageWidth(items[p.Index+1].String()))
		// Walk forward to file name
		p.Index++
		p.Mark(items[p.Index-4].Pos, items[p.Index+3].Pos, repl)
		items = append(items[:p.Index-4], items[p.Index:]...)
		p.Index = p.Index - 4
		def = ""
		cmd = ""
	case "sprite-dimensions":
		sprite := p.Sprites[fmt.Sprintf("%s", item)]
		repl = sprite.Dimensions(items[p.Index+1].String())
		// Walk forward to file name
		p.Index++
		p.Mark(items[p.Index-4].Pos, items[p.Index+3].Pos, repl)
		items = append(items[:p.Index-4], items[p.Index:]...)
		p.Index = p.Index - 4
		def = ""
		cmd = ""
	default:
		items[p.Index].Value = p.Vars[item.Value]
	}
	_, _ = def, cmd
	return ""
}

// Mixin processes tokens in the format @include mixin(args...)
// and perform requested actions.
func (p *Parser) Mixin() {

	// Commands always end in ); else panic
	start := p.Index
	cmd := p.Items[start]

	var file Item
	for {
		cur := p.Items[p.Index]
		if cur.Type == RPAREN {
			p.Index++
			if p.Items[p.Index].Type != SEMIC {
				f, l := p.Items[start].Pos, p.Items[p.Index].Pos
				log.Fatal("Commands must end with semicolon",
					fmt.Sprintf(p.Input[f:l]))
			} else {
				break
			}
		} else if cur.Type == FILE {
			file = cur
		}
		p.Index++
	}
	if file.Type != FILE {
		log.Fatal("File for command was not found")
	}
	if cmd.Value == "inline-image" {
		img := ImageList{}
		img.Decode(p.ImageDir + "/" + file.Value)
		repl := img.Inline()
		p.Index-- // Preserve the final semic
		p.Mark(cmd.Pos-1, p.Items[p.Index].Pos+1, repl)
	}
	fmt.Println("Mixin", cmd.Value)
}

// Replace iterates through the list of substrings to
// cut or replace, adjusting for shift of the output
// buffer as a result of these ops.
func (p *Parser) Replace() {

	for _, c := range p.Chop {

		begin := c.Start - p.shift
		end := c.End - p.shift
		// fmt.Println(string(p.Input[begin:end]), "~>`"+string(c.Value)+"`")
		// fmt.Println("*******")
		// Adjust shift for number of bytes deleted and inserted
		p.shift += end - begin
		p.shift -= len(c.Value)
		suf := append(c.Value, p.Output[end:]...)

		p.Output = append(p.Output[:begin], suf...)
	}
}

// Mark segments of the input string for future deletion.
func (p *Parser) Mark(start, end int, val string) {
	// fmt.Println("Mark:", string(p.Input[start:end]), "~")
	p.Chop = append(p.Chop, Replace{start, end, []byte(val)})
}

// Processes file which usually mean cutting some of the input
// text.
func (p *Parser) File(cmd string, start, end int) int {
	first := p.Items[start]
	item := p.Items[end]
	i := end
	if cmd == "sprite-map" {
		// Find the next semicolon and remove it
		for ; p.Items[i].Type != RPAREN; i++ {
		}
		if p.Items[i+1].Type != SEMIC {
			panic("Statements must end in semicolon")
		}
		i++
		// Verify that the statement ends with semicolon
		interest := p.Items[i]
		// Mark the entire line plus semicolon for deletion
		p.Mark(first.Pos, interest.Pos+1, "")
		imgs := ImageList{}
		glob := fmt.Sprintf("%s", item)
		name := fmt.Sprintf("%s", p.Items[start])
		imgs.Decode(p.ImageDir + "/" + glob)
		imgs.Vertical = true
		imgs.Combine()
		p.Sprites[name] = imgs
		//TODO: Generate filename
		//imgs.Export("generated.png")
	}
	return i + 1
}

// func process(in string, items []Item, pos int) []byte {

// 	var out []byte
// 	l := len(items)

// 	if pos >= len(in) {
// 		return []byte("")
// 	}

// 	// TODO: There's an error where items[1] has an invalid
// 	// position.
// 	if l > 1 && items[1].Pos > items[0].Pos {
// 		out = append(out, in[items[0].Pos:items[1].Pos]...)
// 		out = append(out, process(in, items[1:], pos)...)
// 	} else {
// 		out = append(out, in[items[0].Pos:]...)
// 	}

// 	return out
// }

// start recursively resolves all imports.  It lexes the input
// adding the tokens to the Parser object.
// TODO: Convert this to byte slice in/out
func (p *Parser) start(pwd, input string) ([]Item, string, error) {

	var (
		status    []Item
		importing bool
		output    []byte
		pos       int
		last      *Item
	)

	lex := New(func(lex *Lexer) StateFn {
		return lex.Action()
	}, input)

	for {
		item := lex.Next()
		err := item.Error()
		//fmt.Println(item.Type, item.Value)
		if err != nil {
			return nil, string(output),
				fmt.Errorf("Error: %v (pos %d)", err, item.Pos)
		}
		switch item.Type {
		case ItemEOF:
			output = append(output, input[pos:]...)
			return status, string(output), nil
		case IMPORT:
			output = append(output, input[pos:item.Pos]...)
			last = item
			importing = true
		case INCLUDE, CMT:
			output = append(output, input[pos:item.Pos]...)
			pos = item.Pos
			status = append(status, *item)
		default:
			if importing {

				pwd, contents, err := p.ImportPath(pwd, fmt.Sprintf("%s", *item))
				if err != nil {
					log.Fatal(err)
				}
				//Eat the semicolon
				item := lex.Next()
				pos = item.Pos + len(item.Value)
				if item.Type != SEMIC {
					panic("@import statement must be followed by ;")
				}

				moreTokens, moreOutput, err := p.start(
					pwd,
					contents)
				// If importing was successful, each token must be moved forward
				// by the position of the @import call that made it available.
				for i, _ := range moreTokens {
					moreTokens[i].Pos += last.Pos
				}

				if err != nil {
					panic(err)
				}
				output = append(output, moreOutput...)
				status = append(status, moreTokens...)
				importing = false
			} else {
				output = append(output, input[pos:item.Pos]...)
				pos = item.Pos
				status = append(status, *item)
			}
		}
	}
}
<|endoftext|>


<commit_before>package lang

import "fmt"

type Parser struct {
	l *Lexer

	buf []buf
	n   int
}

func NewParser(l *Lexer) *Parser {
	return &Parser{
		l: l,
	}
}

func (p *Parser) Parse() *Program {
	prog := &Program{
		scope: &Scope{
			make(map[string]string),
		},
	}

	for {
		tok, lit := p.scanSkipWhitespace()
		if tok == EOF {
			fmt.Println("REACHED EOF!!!")
			break
		}

		switch tok {
		case Identifier:
			// Got name
			assign := &AssignmentStatement{
				Name: lit,
			}

			tok, lit = p.scanSkipWhitespace()
			if tok != String {
				fmt.Println("NOT TYPE")
				fmt.Println(tok, lit)
				break
			}

			tok, lit = p.scanSkipWhitespace()
			if tok != Assign {
				fmt.Println("NOT ASSIGN. TIME TO DIE!")
				break
			}

			tok, lit = p.scanSkipWhitespace()
			if tok != Quotes {
				fmt.Println("NOT quotes. TIME TO DIE")
				fmt.Println(tok, lit)
				break
			}

			var buf string

			for {
				tok, lit := p.scan()

				if tok == EOF {
					p.unscan()
					break
				}

				if tok == Quotes {
					break
				}

				buf += lit
			}

			fmt.Println("[DONE] got value", buf)

			assign.Value = buf

			prog.statements = append(prog.statements, assign)

			continue
		}
	}

	fmt.Println(prog)

	return prog
}

func (p *Parser) scan() (Token, string) {
	p.n++

	// check if n is in p.buf (n < len p.buf)
	if p.n < len(p.buf) {
		fmt.Println("pulling from buffer")
		b := p.buf[len(p.buf)-1]
		return b.tok, b.lit
	}

	tok, lit := p.l.Scan()
	p.buf = append(p.buf, buf{
		tok: tok,
		lit: lit,
	})

	return tok, lit
}

func (p *Parser) unscan() {
	p.n--
}

func (p *Parser) scanSkipWhitespace() (Token, string) {
	for {
		tok, lit := p.scan()

		if tok != Whitespace {
			return tok, lit
		}
	}
}

type buf struct {
	tok Token
	lit string
}
<commit_msg>Add preliminary matching<commit_after>package lang

import "fmt"

type Parser struct {
	l *Lexer

	buf []buf
	n   int
}

func NewParser(l *Lexer) *Parser {
	return &Parser{
		l: l,
	}
}

func (p *Parser) Parse() *Program {
	prog := &Program{
		scope: &Scope{
			make(map[string]string),
		},
	}

	for {
		tok, lit := p.scanSkipWhitespace()
		if tok == EOF {
			fmt.Println("REACHED EOF!!!")
			break
		}

		p.unscan()

		if p.is(MatchAssignment...) {
			tok, lit = p.scanSkipWhitespace()
			// Got name
			assign := &AssignmentStatement{
				Name: lit,
			}

			tok, lit = p.scanSkipWhitespace()
			if tok != String {
				fmt.Println("NOT TYPE")
				fmt.Println(tok, lit)
				break
			}

			tok, lit = p.scanSkipWhitespace()
			if tok != Assign {
				fmt.Println("NOT ASSIGN. TIME TO DIE!")
				break
			}

			tok, lit = p.scanSkipWhitespace()
			if tok != Quotes {
				fmt.Println("NOT quotes. TIME TO DIE")
				fmt.Println(tok, lit)
				break
			}

			var buf string

			for {
				tok, lit := p.scan()

				if tok == EOF {
					p.unscan()
					break
				}

				if tok == Quotes {
					break
				}

				buf += lit
			}

			fmt.Println("[DONE] got value", buf)

			assign.Value = buf

			prog.statements = append(prog.statements, assign)

			continue
		}

		fmt.Println("didnt match")
	}

	fmt.Println(prog)

	return prog
}

var MatchAssignment = []Token{Identifier, Whitespace, String}

func (p *Parser) is(ts ...Token) bool {
	for _, t := range ts {
		tok, _ := p.scan()

		defer func() { p.unscan() }()

		if tok != t {
			fmt.Println("Got", t, "expected", tok)
			return false
		}
	}

	return true
}

// If it can pull n from tokens, do that... else scan new tok
// and add it to the buf
func (p *Parser) scan() (Token, string) {
	defer func() {
		p.n++
	}()

	if p.n >= len(p.buf) {
		fmt.Println("Scanning new token", p.n)
		tok, lit := p.l.Scan()
		p.buf = append(p.buf, buf{
			tok: tok,
			lit: lit,
		})

		return tok, lit
	}

	b := p.buf[p.n]

	fmt.Println("Retrieving old token", p.n)
	return b.tok, b.lit
}

func (p *Parser) unscan() {
	p.n--
}

func (p *Parser) scanSkipWhitespace() (Token, string) {
	for {
		tok, lit := p.scan()

		if tok != Whitespace {
			return tok, lit
		}
	}
}

type buf struct {
	tok Token
	lit string
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"image/color"
	"os"
	"strconv"
)

// DrawingMode defines the type of each drawing mode
type DrawingMode int

const (
	// DrawLineMode is a draw argument that draws 2D lines onto the Image
	DrawLineMode DrawingMode = iota
	// DrawPolygonMode is a draw argument that draws 3D polygons onto the Image
	DrawPolygonMode
)

// Parser is a script parser
type Parser struct {
	frame  *Image
	em     *Matrix
	tm     *Matrix
	cs     *Stack
	lexer  *Lexer
	backup []Token
}

// NewParser returns a new parser
func NewParser() *Parser {
	cs := NewStack()
	cs.Push(IdentityMatrix(4))
	return &Parser{
		frame:  NewImage(DefaultHeight, DefaultWidth),
		em:     NewMatrix(4, 0),
		tm:     IdentityMatrix(4),
		cs:     cs,
		backup: make([]Token, 0, 10),
	}
}

// ParseFile parses a file for commands and executes them
func (p *Parser) ParseFile(filename string) error {
	f, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer f.Close()

	p.lexer = NewLexer()
	p.lexer.Lex(f)
	for {
		t := p.next()
		switch t.tt {
		case tError:
			return errors.New(t.value)
		case tEOF:
			return nil
		case tIdent:
			err := p.parseIdent(t)
			if err != nil {
				return err
			}
		case tComment:
			continue
		default:
			return fmt.Errorf("unexpected token %v", t)
		}
	}
}

func (p *Parser) parseIdent(t Token) error {
	var err error
	switch t.value {
	case "line":
		x0 := p.nextFloat()
		y0 := p.nextFloat()
		z0 := p.nextFloat()
		x1 := p.nextFloat()
		y1 := p.nextFloat()
		z1 := p.nextFloat()
		err = p.line(x0, y0, z0, x1, y1, z1)
	case "ident":
		p.tm = IdentityMatrix(4)
	case "scale":
		sx := p.nextFloat()
		sy := p.nextFloat()
		sz := p.nextFloat()
		err = p.scale(sx, sy, sz)
	case "move":
		x := p.nextFloat()
		y := p.nextFloat()
		z := p.nextFloat()
		err = p.move(x, y, z)
	case "rotate":
		axis := p.nextString()
		theta := p.nextFloat()
		err = p.rotate(axis, theta)
	case "save":
		filename := p.nextString()
		err = p.save(filename)
	case "display":
		err = p.display()
	case "circle":
		cx := p.nextFloat()
		cy := p.nextFloat()
		cz := p.nextFloat()
		radius := p.nextFloat()
		err = p.circle(cx, cy, cz, radius)
	case "hermite":
		x0 := p.nextFloat()
		y0 := p.nextFloat()
		x1 := p.nextFloat()
		y1 := p.nextFloat()
		dx0 := p.nextFloat()
		dy0 := p.nextFloat()
		dx1 := p.nextFloat()
		dy1 := p.nextFloat()
		err = p.hermite(x0, y0, x1, y1, dx0, dy0, dx1, dy1)
	case "bezier":
		x0 := p.nextFloat()
		y0 := p.nextFloat()
		x1 := p.nextFloat()
		y1 := p.nextFloat()
		x2 := p.nextFloat()
		y2 := p.nextFloat()
		x3 := p.nextFloat()
		y3 := p.nextFloat()
		err = p.bezier(x0, y0, x1, y1, x2, y2, x3, y3)
	case "box":
		x := p.nextFloat()
		y := p.nextFloat()
		z := p.nextFloat()
		width := p.nextFloat()
		height := p.nextFloat()
		depth := p.nextFloat()
		err = p.box(x, y, z, width, height, depth)
	case "clear":
		p.clear()
	case "sphere":
		cx := p.nextFloat()
		cy := p.nextFloat()
		cz := p.nextFloat()
		radius := p.nextFloat()
		err = p.sphere(cx, cy, cz, radius)
	case "torus":
		cx := p.nextFloat()
		cy := p.nextFloat()
		cz := p.nextFloat()
		r1 := p.nextFloat()
		r2 := p.nextFloat()
		err = p.torus(cx, cy, cz, r1, r2)
	case "push":
		top := p.cs.Peek()
		if top != nil {
			p.cs.Push(top.Copy())
		}
	case "pop":
		p.cs.Pop()
	default:
		err = fmt.Errorf("unrecognized identifier: \"%s\"", t.value)
	}
	return err
}

func (p *Parser) next() Token {
	lenBackup := len(p.backup)
	if lenBackup > 0 {
		token := p.backup[lenBackup-1]
		p.backup = p.backup[:lenBackup-1]
		return token
	}
	token := <-p.lexer.out
	return token
}

func (p *Parser) nextFloat() float64 {
	if p.requireNext(tInt) != nil && p.requireNext(tFloat) != nil {
		panic(fmt.Errorf("%s expected, got %s", tFloat, p.peek().tt))
	}
	v, _ := strconv.ParseFloat(p.next().value, 64)
	return v
}

func (p *Parser) nextString() string {
	if p.requireNext(tString) != nil {
		panic(fmt.Errorf("%s expected, got %s", tString, p.peek().tt))
	}
	return p.next().value
}

func (p *Parser) unread(token Token) {
	if p.backup == nil {
		p.backup = make([]Token, 0, 10)
	}
	p.backup = append(p.backup, token)
}

func (p *Parser) peek() Token {
	token := p.next()
	p.unread(token)
	return token
}

func (p *Parser) requireNext(tt TokenType) error {
	other := p.peek().tt
	if other != tt {
		return fmt.Errorf("unexpected %v; expected %v", other, tt)
	}
	return nil
}

func (p *Parser) line(x0, y0, z0, x1, y1, z1 float64) error {
	p.em.AddEdge(x0, y0, z0, x1, y1, z1)
	err := p.apply(DrawLineMode)
	return err
}

func (p *Parser) scale(sx, sy, sz float64) error {
	dilation := MakeDilation(sx, sy, sz)

	top := p.cs.Pop()
	top, err := dilation.Multiply(top)
	if err != nil {
		return err
	}
	p.cs.Push(top)
	return nil
}

func (p *Parser) move(x, y, z float64) error {
	translation := MakeTranslation(x, y, z)
	top := p.cs.Pop()
	top, err := translation.Multiply(top)
	if err != nil {
		return err
	}
	p.cs.Push(top)

	return nil
}

func (p *Parser) rotate(axis string, theta float64) error {
	var rotation *Matrix
	switch axis {
	case "x":
		rotation = MakeRotX(theta)
	case "y":
		rotation = MakeRotY(theta)
	case "z":
		rotation = MakeRotZ(theta)
	default:
		return errors.New("axis must be \"x\", \"y\", or \"z\"")
	}

	top := p.cs.Pop()
	top, err := rotation.Multiply(top)
	if err != nil {
		return err
	}
	p.cs.Push(top)

	return nil
}

func (p *Parser) apply(mode DrawingMode) error {
	product, err := p.cs.Peek().Multiply(p.em)
	if err != nil {
		return err
	}
	p.em = product
	p.draw(mode)
	p.clear()

	return nil
}

func (p *Parser) draw(mode DrawingMode) {
	switch mode {
	case DrawLineMode:
		p.frame.DrawLines(p.em, color.White)
	case DrawPolygonMode:
		p.frame.DrawPolygons(p.em, color.White)
	default:
	}
}

func (p *Parser) save(filename string) error {
	err := p.frame.Save(filename)
	return err
}

func (p *Parser) display() error {
	err := p.frame.Display()
	return err
}

func (p *Parser) circle(cx, cy, cz, radius float64) error {
	p.em.AddCircle(cx, cy, cz, radius)
	err := p.apply(DrawLineMode)
	return err
}

func (p *Parser) hermite(x0, y0, x1, y1, dx0, dy0, dx1, dy1 float64) error {
	p.em.AddHermite(x0, y0, x1, y1, dx0, dy0, dx1, dy1)
	err := p.apply(DrawLineMode)

	return err
}

func (p *Parser) bezier(x0, y0, x1, y1, x2, y2, x3, y3 float64) error {
	p.em.AddBezier(x0, y0, x1, y1, x2, y2, x3, y3)
	err := p.apply(DrawLineMode)
	return err
}

func (p *Parser) box(x, y, z, width, height, depth float64) error {
	p.em.AddBox(x, y, z, width, height, depth)
	err := p.apply(DrawPolygonMode)
	return err
}

func (p *Parser) clear() {
	p.em = NewMatrix(4, 0)
}

func (p *Parser) sphere(cx, cy, cz, radius float64) error {
	p.em.AddSphere(cx, cy, cz, radius)
	err := p.apply(DrawPolygonMode)
	return err
}

func (p *Parser) torus(cx, cy, cz, r1, r2 float64) error {
	p.em.AddTorus(cx, cy, cz, r1, r2)
	err := p.apply(DrawPolygonMode)

	return err
}
<commit_msg>Switch matrix multiplication order for transformations<commit_after>package main

import (
	"errors"
	"fmt"
	"image/color"
	"os"
	"strconv"
)

// DrawingMode defines the type of each drawing mode
type DrawingMode int

const (
	// DrawLineMode is a draw argument that draws 2D lines onto the Image
	DrawLineMode DrawingMode = iota
	// DrawPolygonMode is a draw argument that draws 3D polygons onto the Image
	DrawPolygonMode
)

// Parser is a script parser
type Parser struct {
	frame  *Image
	em     *Matrix
	tm     *Matrix
	cs     *Stack
	lexer  *Lexer
	backup []Token
}

// NewParser returns a new parser
func NewParser() *Parser {
	cs := NewStack()
	cs.Push(IdentityMatrix(4))
	return &Parser{
		frame:  NewImage(DefaultHeight, DefaultWidth),
		em:     NewMatrix(4, 0),
		tm:     IdentityMatrix(4),
		cs:     cs,
		backup: make([]Token, 0, 10),
	}
}

// ParseFile parses a file for commands and executes them
func (p *Parser) ParseFile(filename string) error {
	f, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer f.Close()

	p.lexer = NewLexer()
	p.lexer.Lex(f)
	for {
		t := p.next()
		switch t.tt {
		case tError:
			return errors.New(t.value)
		case tEOF:
			return nil
		case tIdent:
			err := p.parseIdent(t)
			if err != nil {
				return err
			}
		case tComment:
			continue
		default:
			return fmt.Errorf("unexpected token %v", t)
		}
	}
}

func (p *Parser) parseIdent(t Token) error {
	var err error
	switch t.value {
	case "line":
		x0 := p.nextFloat()
		y0 := p.nextFloat()
		z0 := p.nextFloat()
		x1 := p.nextFloat()
		y1 := p.nextFloat()
		z1 := p.nextFloat()
		err = p.line(x0, y0, z0, x1, y1, z1)
	case "ident":
		p.tm = IdentityMatrix(4)
	case "scale":
		sx := p.nextFloat()
		sy := p.nextFloat()
		sz := p.nextFloat()
		err = p.scale(sx, sy, sz)
	case "move":
		x := p.nextFloat()
		y := p.nextFloat()
		z := p.nextFloat()
		err = p.move(x, y, z)
	case "rotate":
		axis := p.nextString()
		theta := p.nextFloat()
		err = p.rotate(axis, theta)
	case "save":
		filename := p.nextString()
		err = p.save(filename)
	case "display":
		err = p.display()
	case "circle":
		cx := p.nextFloat()
		cy := p.nextFloat()
		cz := p.nextFloat()
		radius := p.nextFloat()
		err = p.circle(cx, cy, cz, radius)
	case "hermite":
		x0 := p.nextFloat()
		y0 := p.nextFloat()
		x1 := p.nextFloat()
		y1 := p.nextFloat()
		dx0 := p.nextFloat()
		dy0 := p.nextFloat()
		dx1 := p.nextFloat()
		dy1 := p.nextFloat()
		err = p.hermite(x0, y0, x1, y1, dx0, dy0, dx1, dy1)
	case "bezier":
		x0 := p.nextFloat()
		y0 := p.nextFloat()
		x1 := p.nextFloat()
		y1 := p.nextFloat()
		x2 := p.nextFloat()
		y2 := p.nextFloat()
		x3 := p.nextFloat()
		y3 := p.nextFloat()
		err = p.bezier(x0, y0, x1, y1, x2, y2, x3, y3)
	case "box":
		x := p.nextFloat()
		y := p.nextFloat()
		z := p.nextFloat()
		width := p.nextFloat()
		height := p.nextFloat()
		depth := p.nextFloat()
		err = p.box(x, y, z, width, height, depth)
	case "clear":
		p.clear()
	case "sphere":
		cx := p.nextFloat()
		cy := p.nextFloat()
		cz := p.nextFloat()
		radius := p.nextFloat()
		err = p.sphere(cx, cy, cz, radius)
	case "torus":
		cx := p.nextFloat()
		cy := p.nextFloat()
		cz := p.nextFloat()
		r1 := p.nextFloat()
		r2 := p.nextFloat()
		err = p.torus(cx, cy, cz, r1, r2)
	case "push":
		top := p.cs.Peek()
		if top != nil {
			p.cs.Push(top.Copy())
		}
	case "pop":
		p.cs.Pop()
	default:
		err = fmt.Errorf("unrecognized identifier: \"%s\"", t.value)
	}
	return err
}

func (p *Parser) next() Token {
	lenBackup := len(p.backup)
	if lenBackup > 0 {
		token := p.backup[lenBackup-1]
		p.backup = p.backup[:lenBackup-1]
		return token
	}
	token := <-p.lexer.out
	return token
}

func (p *Parser) nextFloat() float64 {
	if p.requireNext(tInt) != nil && p.requireNext(tFloat) != nil {
		panic(fmt.Errorf("%s expected, got %s", tFloat, p.peek().tt))
	}
	v, _ := strconv.ParseFloat(p.next().value, 64)
	return v
}

func (p *Parser) nextString() string {
	if p.requireNext(tString) != nil {
		panic(fmt.Errorf("%s expected, got %s", tString, p.peek().tt))
	}
	return p.next().value
}

func (p *Parser) unread(token Token) {
	if p.backup == nil {
		p.backup = make([]Token, 0, 10)
	}
	p.backup = append(p.backup, token)
}

func (p *Parser) peek() Token {
	token := p.next()
	p.unread(token)
	return token
}

func (p *Parser) requireNext(tt TokenType) error {
	other := p.peek().tt
	if other != tt {
		return fmt.Errorf("unexpected %v; expected %v", other, tt)
	}
	return nil
}

func (p *Parser) line(x0, y0, z0, x1, y1, z1 float64) error {
	p.em.AddEdge(x0, y0, z0, x1, y1, z1)
	err := p.apply(DrawLineMode)
	return err
}

func (p *Parser) scale(sx, sy, sz float64) error {
	dilation := MakeDilation(sx, sy, sz)

	top := p.cs.Pop()
	top, err := top.Multiply(dilation)
	if err != nil {
		return err
	}
	p.cs.Push(top)
	return nil
}

func (p *Parser) move(x, y, z float64) error {
	translation := MakeTranslation(x, y, z)
	top := p.cs.Pop()
	top, err := top.Multiply(translation)
	if err != nil {
		return err
	}
	p.cs.Push(top)

	return nil
}

func (p *Parser) rotate(axis string, theta float64) error {
	var rotation *Matrix
	switch axis {
	case "x":
		rotation = MakeRotX(theta)
	case "y":
		rotation = MakeRotY(theta)
	case "z":
		rotation = MakeRotZ(theta)
	default:
		return errors.New("axis must be \"x\", \"y\", or \"z\"")
	}

	top := p.cs.Pop()
	top, err := top.Multiply(rotation)
	if err != nil {
		return err
	}
	p.cs.Push(top)

	return nil
}

func (p *Parser) apply(mode DrawingMode) error {
	product, err := p.cs.Peek().Multiply(p.em)
	if err != nil {
		return err
	}
	p.em = product
	p.draw(mode)
	p.clear()

	return nil
}

func (p *Parser) draw(mode DrawingMode) {
	switch mode {
	case DrawLineMode:
		p.frame.DrawLines(p.em, color.White)
	case DrawPolygonMode:
		p.frame.DrawPolygons(p.em, color.White)
	default:
	}
}

func (p *Parser) save(filename string) error {
	err := p.frame.Save(filename)
	return err
}

func (p *Parser) display() error {
	err := p.frame.Display()
	return err
}

func (p *Parser) circle(cx, cy, cz, radius float64) error {
	p.em.AddCircle(cx, cy, cz, radius)
	err := p.apply(DrawLineMode)
	return err
}

func (p *Parser) hermite(x0, y0, x1, y1, dx0, dy0, dx1, dy1 float64) error {
	p.em.AddHermite(x0, y0, x1, y1, dx0, dy0, dx1, dy1)
	err := p.apply(DrawLineMode)

	return err
}

func (p *Parser) bezier(x0, y0, x1, y1, x2, y2, x3, y3 float64) error {
	p.em.AddBezier(x0, y0, x1, y1, x2, y2, x3, y3)
	err := p.apply(DrawLineMode)
	return err
}

func (p *Parser) box(x, y, z, width, height, depth float64) error {
	p.em.AddBox(x, y, z, width, height, depth)
	err := p.apply(DrawPolygonMode)
	return err
}

func (p *Parser) clear() {
	p.em = NewMatrix(4, 0)
}

func (p *Parser) sphere(cx, cy, cz, radius float64) error {
	p.em.AddSphere(cx, cy, cz, radius)
	err := p.apply(DrawPolygonMode)
	return err
}

func (p *Parser) torus(cx, cy, cz, r1, r2 float64) error {
	p.em.AddTorus(cx, cy, cz, r1, r2)
	err := p.apply(DrawPolygonMode)

	return err
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"image/color"
	"os"
	"strconv"
	"strings"
)

// Parser is a script parser
type Parser struct {
	frame *Image
	em    *Matrix
	tm    *Matrix
	cs    *Stack
}

// NewParser returns a new parser
func NewParser() *Parser {
	cs := NewStack()
	cs.Push(IdentityMatrix(4))
	return &Parser{
		frame: NewImage(DefaultHeight, DefaultWidth),
		em:    NewMatrix(4, 0),
		tm:    IdentityMatrix(4),
		cs:    cs,
	}
}

// ParseFile parses a file for commands and executes them
func (p *Parser) ParseFile(filename string) error {
	f, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		command := strings.TrimSpace(scanner.Text())
		if len(command) == 0 {
			continue
		}
		if command[0:1] == "#" {
			continue
		}
		switch command {
		case "line":
			argv := getArguments(scanner)
			err = p.line(argv)
		case "ident":
			p.tm = IdentityMatrix(4)
		case "scale":
			argv := getArguments(scanner)
			err = p.scale(argv)
		case "move":
			argv := getArguments(scanner)
			err = p.move(argv)
		case "rotate":
			argv := getArguments(scanner)
			err = p.rotate(argv)
		case "save":
			argv := getArguments(scanner)
			err = p.save(argv)
		case "display":
			err = p.display()
		case "circle":
			argv := getArguments(scanner)
			err = p.circle(argv)
		case "hermite":
			argv := getArguments(scanner)
			err = p.hermite(argv)
		case "bezier":
			argv := getArguments(scanner)
			err = p.bezier(argv)
		case "box":
			argv := getArguments(scanner)
			err = p.box(argv)
		case "clear":
			p.clear()
		case "sphere":
			argv := getArguments(scanner)
			err = p.sphere(argv)
		case "torus":
			argv := getArguments(scanner)
			err = p.torus(argv)
		case "push":
			top := p.cs.Peek()
			if top != nil {
				p.cs.Push(top.Copy())
			}
		case "pop":
			p.cs.Pop()
		default:
			err = fmt.Errorf("unrecognized command: \"%s\"", command)
		}

		if err != nil {
			return err
		}
	}

	if err := scanner.Err(); err != nil {
		return err
	}
	return nil
}

func getArguments(scanner *bufio.Scanner) []string {
	scanner.Scan()
	line := scanner.Text()
	line = strings.TrimSpace(line)
	argv := strings.Split(line, " ")
	return argv
}

func getNumerical(argv []string) ([]float64, error) {
	values := make([]float64, len(argv))
	for i, v := range argv {
		v, err := strconv.ParseFloat(v, 64)
		if err != nil {
			return nil, errors.New("arguments must be numeric")
		}
		values[i] = v
	}
	return values, nil
}

func (p *Parser) line(argv []string) error {
	if len(argv) != 6 {
		return fmt.Errorf("\"line\" expects %d arguments (%d provided)", 6, len(argv))
	}
	values, err := getNumerical(argv)
	if err != nil {
		return err
	}

	p.em.AddEdge(values[0], values[1], values[2], values[3], values[4], values[5])
	err = p.apply(DrawLineMode)

	return err
}

func (p *Parser) scale(argv []string) error {
	if len(argv) != 3 {
		return fmt.Errorf("\"scale\" expects %d arguments (%d provided)", 3, len(argv))
	}
	values, err := getNumerical(argv)
	if err != nil {
		return err
	}

	dilation := MakeDilation(values[0], values[1], values[2])

	top := p.cs.Pop()
	top, err = dilation.Multiply(top)
	if err != nil {
		return err
	}
	p.cs.Push(top)

	return nil
}

func (p *Parser) move(argv []string) error {
	if len(argv) != 3 {
		return fmt.Errorf("\"move\" expects %d arguments (%d provided)", 3, len(argv))
	}
	values, err := getNumerical(argv)
	if err != nil {
		return err
	}

	translation := MakeTranslation(values[0], values[1], values[2])
	top := p.cs.Pop()
	top, err = translation.Multiply(top)
	if err != nil {
		return err
	}
	p.cs.Push(top)

	return nil
}

func (p *Parser) rotate(argv []string) error {
	if len(argv) != 2 {
		return fmt.Errorf("\"rotate\" expects %d arguments (%d provided)", 2, len(argv))
	}
	axis := strings.ToLower(argv[0])
	theta, err := strconv.ParseFloat(argv[1], 64)
	if err != nil {
		return errors.New("arguments must be numeric")
	}

	var rotation *Matrix
	switch axis {
	case "x":
		rotation = MakeRotX(theta)
	case "y":
		rotation = MakeRotY(theta)
	case "z":
		rotation = MakeRotZ(theta)
	default:
		return errors.New("axis must be \"x\", \"y\", or \"z\"")
	}

	top := p.cs.Pop()
	top, err = rotation.Multiply(top)
	if err != nil {
		return err
	}
	p.cs.Push(top)

	return nil
}

func (p *Parser) apply(mode DrawingMode) error {
	product, err := p.cs.Peek().Multiply(p.em)
	if err != nil {
		return err
	}
	p.em = product
	p.draw(mode)
	p.clear()

	return nil
}

func (p *Parser) draw(mode DrawingMode) {
	switch mode {
	case DrawLineMode:
		p.frame.DrawLines(p.em, color.White)
	case DrawPolygonMode:
		p.frame.DrawPolygons(p.em, color.White)
	default:
	}
}

func (p *Parser) save(argv []string) error {
	if len(argv) != 1 {
		return fmt.Errorf("\"save\" expects %d argument (%d provided)", 1, len(argv))
	}
	err := p.frame.Save(argv[0])
	return err
}

func (p *Parser) display() error {
	err := p.frame.Display()
	return err
}

func (p *Parser) circle(argv []string) error {
	if len(argv) != 4 {
		return fmt.Errorf("\"circle\" expects %d arguments (%d provided)", 4, len(argv))
	}
	values, err := getNumerical(argv)
	if err != nil {
		return err
	}

	p.em.AddCircle(values[0], values[1], values[2], values[3])
	err = p.apply(DrawLineMode)

	return err
}

func (p *Parser) hermite(argv []string) error {
	if len(argv) != 8 {
		return fmt.Errorf("\"hermite\" expects %d arguments (%d provided)", 8, len(argv))
	}
	values, err := getNumerical(argv)
	if err != nil {
		return err
	}

	p.em.AddHermite(values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7])
	err = p.apply(DrawLineMode)

	return err
}

func (p *Parser) bezier(argv []string) error {
	if len(argv) != 8 {
		return fmt.Errorf("\"bezier\" expects %d arguments (%d provided)", 8, len(argv))
	}
	values, err := getNumerical(argv)
	if err != nil {
		return err
	}

	p.em.AddBezier(values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7])
	err = p.apply(DrawLineMode)

	return err
}

func (p *Parser) box(argv []string) error {
	if len(argv) != 6 {
		return fmt.Errorf("\"box\" expects %d arguments (%d provided)", 6, len(argv))
	}
	values, err := getNumerical(argv)
	if err != nil {
		return err
	}

	p.em.AddBox(values[0], values[1], values[2], values[3], values[4], values[5])
	err = p.apply(DrawPolygonMode)

	return err
}

func (p *Parser) clear() {
	p.em = NewMatrix(4, 0)
}

func (p *Parser) sphere(argv []string) error {
	if len(argv) != 4 {
		return fmt.Errorf("\"box\" expects %d arguments (%d provided)", 4, len(argv))
	}
	values, err := getNumerical(argv)
	if err != nil {
		return err
	}

	p.em.AddSphere(values[0], values[1], values[2], values[3])
	err = p.apply(DrawPolygonMode)

	return err
}

func (p *Parser) torus(argv []string) error {
	if len(argv) != 5 {
		return fmt.Errorf("\"box\" expects %d arguments (%d provided)", 5, len(argv))
	}
	values, err := getNumerical(argv)
	if err != nil {
		return err
	}

	p.em.AddTorus(values[0], values[1], values[2], values[3], values[4])
	err = p.apply(DrawPolygonMode)

	return err
}
<commit_msg>Simplify check for comments<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"image/color"
	"os"
	"strconv"
	"strings"
)

// Parser is a script parser
type Parser struct {
	frame *Image
	em    *Matrix
	tm    *Matrix
	cs    *Stack
}

// NewParser returns a new parser
func NewParser() *Parser {
	cs := NewStack()
	cs.Push(IdentityMatrix(4))
	return &Parser{
		frame: NewImage(DefaultHeight, DefaultWidth),
		em:    NewMatrix(4, 0),
		tm:    IdentityMatrix(4),
		cs:    cs,
	}
}

// ParseFile parses a file for commands and executes them
func (p *Parser) ParseFile(filename string) error {
	f, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		command := strings.TrimSpace(scanner.Text())
		if len(command) == 0 {
			continue
		}
		if command[0] == '#' {
			continue
		}
		switch command {
		case "line":
			argv := getArguments(scanner)
			err = p.line(argv)
		case "ident":
			p.tm = IdentityMatrix(4)
		case "scale":
			argv := getArguments(scanner)
			err = p.scale(argv)
		case "move":
			argv := getArguments(scanner)
			err = p.move(argv)
		case "rotate":
			argv := getArguments(scanner)
			err = p.rotate(argv)
		case "save":
			argv := getArguments(scanner)
			err = p.save(argv)
		case "display":
			err = p.display()
		case "circle":
			argv := getArguments(scanner)
			err = p.circle(argv)
		case "hermite":
			argv := getArguments(scanner)
			err = p.hermite(argv)
		case "bezier":
			argv := getArguments(scanner)
			err = p.bezier(argv)
		case "box":
			argv := getArguments(scanner)
			err = p.box(argv)
		case "clear":
			p.clear()
		case "sphere":
			argv := getArguments(scanner)
			err = p.sphere(argv)
		case "torus":
			argv := getArguments(scanner)
			err = p.torus(argv)
		case "push":
			top := p.cs.Peek()
			if top != nil {
				p.cs.Push(top.Copy())
			}
		case "pop":
			p.cs.Pop()
		default:
			err = fmt.Errorf("unrecognized command: \"%s\"", command)
		}

		if err != nil {
			return err
		}
	}

	if err := scanner.Err(); err != nil {
		return err
	}
	return nil
}

func getArguments(scanner *bufio.Scanner) []string {
	scanner.Scan()
	line := scanner.Text()
	line = strings.TrimSpace(line)
	argv := strings.Split(line, " ")
	return argv
}

func getNumerical(argv []string) ([]float64, error) {
	values := make([]float64, len(argv))
	for i, v := range argv {
		v, err := strconv.ParseFloat(v, 64)
		if err != nil {
			return nil, errors.New("arguments must be numeric")
		}
		values[i] = v
	}
	return values, nil
}

func (p *Parser) line(argv []string) error {
	if len(argv) != 6 {
		return fmt.Errorf("\"line\" expects %d arguments (%d provided)", 6, len(argv))
	}
	values, err := getNumerical(argv)
	if err != nil {
		return err
	}

	p.em.AddEdge(values[0], values[1], values[2], values[3], values[4], values[5])
	err = p.apply(DrawLineMode)

	return err
}

func (p *Parser) scale(argv []string) error {
	if len(argv) != 3 {
		return fmt.Errorf("\"scale\" expects %d arguments (%d provided)", 3, len(argv))
	}
	values, err := getNumerical(argv)
	if err != nil {
		return err
	}

	dilation := MakeDilation(values[0], values[1], values[2])

	top := p.cs.Pop()
	top, err = dilation.Multiply(top)
	if err != nil {
		return err
	}
	p.cs.Push(top)

	return nil
}

func (p *Parser) move(argv []string) error {
	if len(argv) != 3 {
		return fmt.Errorf("\"move\" expects %d arguments (%d provided)", 3, len(argv))
	}
	values, err := getNumerical(argv)
	if err != nil {
		return err
	}

	translation := MakeTranslation(values[0], values[1], values[2])
	top := p.cs.Pop()
	top, err = translation.Multiply(top)
	if err != nil {
		return err
	}
	p.cs.Push(top)

	return nil
}

func (p *Parser) rotate(argv []string) error {
	if len(argv) != 2 {
		return fmt.Errorf("\"rotate\" expects %d arguments (%d provided)", 2, len(argv))
	}
	axis := strings.ToLower(argv[0])
	theta, err := strconv.ParseFloat(argv[1], 64)
	if err != nil {
		return errors.New("arguments must be numeric")
	}

	var rotation *Matrix
	switch axis {
	case "x":
		rotation = MakeRotX(theta)
	case "y":
		rotation = MakeRotY(theta)
	case "z":
		rotation = MakeRotZ(theta)
	default:
		return errors.New("axis must be \"x\", \"y\", or \"z\"")
	}

	top := p.cs.Pop()
	top, err = rotation.Multiply(top)
	if err != nil {
		return err
	}
	p.cs.Push(top)

	return nil
}

func (p *Parser) apply(mode DrawingMode) error {
	product, err := p.cs.Peek().Multiply(p.em)
	if err != nil {
		return err
	}
	p.em = product
	p.draw(mode)
	p.clear()

	return nil
}

func (p *Parser) draw(mode DrawingMode) {
	switch mode {
	case DrawLineMode:
		p.frame.DrawLines(p.em, color.White)
	case DrawPolygonMode:
		p.frame.DrawPolygons(p.em, color.White)
	default:
	}
}

func (p *Parser) save(argv []string) error {
	if len(argv) != 1 {
		return fmt.Errorf("\"save\" expects %d argument (%d provided)", 1, len(argv))
	}
	err := p.frame.Save(argv[0])
	return err
}

func (p *Parser) display() error {
	err := p.frame.Display()
	return err
}

func (p *Parser) circle(argv []string) error {
	if len(argv) != 4 {
		return fmt.Errorf("\"circle\" expects %d arguments (%d provided)", 4, len(argv))
	}
	values, err := getNumerical(argv)
	if err != nil {
		return err
	}

	p.em.AddCircle(values[0], values[1], values[2], values[3])
	err = p.apply(DrawLineMode)

	return err
}

func (p *Parser) hermite(argv []string) error {
	if len(argv) != 8 {
		return fmt.Errorf("\"hermite\" expects %d arguments (%d provided)", 8, len(argv))
	}
	values, err := getNumerical(argv)
	if err != nil {
		return err
	}

	p.em.AddHermite(values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7])
	err = p.apply(DrawLineMode)

	return err
}

func (p *Parser) bezier(argv []string) error {
	if len(argv) != 8 {
		return fmt.Errorf("\"bezier\" expects %d arguments (%d provided)", 8, len(argv))
	}
	values, err := getNumerical(argv)
	if err != nil {
		return err
	}

	p.em.AddBezier(values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7])
	err = p.apply(DrawLineMode)

	return err
}

func (p *Parser) box(argv []string) error {
	if len(argv) != 6 {
		return fmt.Errorf("\"box\" expects %d arguments (%d provided)", 6, len(argv))
	}
	values, err := getNumerical(argv)
	if err != nil {
		return err
	}

	p.em.AddBox(values[0], values[1], values[2], values[3], values[4], values[5])
	err = p.apply(DrawPolygonMode)

	return err
}

func (p *Parser) clear() {
	p.em = NewMatrix(4, 0)
}

func (p *Parser) sphere(argv []string) error {
	if len(argv) != 4 {
		return fmt.Errorf("\"box\" expects %d arguments (%d provided)", 4, len(argv))
	}
	values, err := getNumerical(argv)
	if err != nil {
		return err
	}

	p.em.AddSphere(values[0], values[1], values[2], values[3])
	err = p.apply(DrawPolygonMode)

	return err
}

func (p *Parser) torus(argv []string) error {
	if len(argv) != 5 {
		return fmt.Errorf("\"box\" expects %d arguments (%d provided)", 5, len(argv))
	}
	values, err := getNumerical(argv)
	if err != nil {
		return err
	}

	p.em.AddTorus(values[0], values[1], values[2], values[3], values[4])
	err = p.apply(DrawPolygonMode)

	return err
}
<|endoftext|>


<commit_before>package demoinfocs

import (
	"io"

	dp "github.com/markus-wa/godispatch"

	bit "github.com/markus-wa/demoinfocs-golang/bitread"
	common "github.com/markus-wa/demoinfocs-golang/common"
	msg "github.com/markus-wa/demoinfocs-golang/msg"
	st "github.com/markus-wa/demoinfocs-golang/sendtables"
)

// TODO?: create struct GameState for all game-state relevant stuff

// Parser can parse a CS:GO demo.
// Creating a Parser is done via NewParser().
// To start off use Parser.ParseHeader() to parse the demo header.
// After parsing the header Parser.ParseNextFrame() and Parser.ParseToEnd() can be used to parse the demo.
// Use Parser.RegisterEventHandler() to receive notifications about events.
type Parser struct {
	bitReader             *bit.BitReader
	stParser              st.Parser
	msgDispatcher         dp.Dispatcher
	eventDispatcher       dp.Dispatcher
	msgQueue              chan interface{}
	currentFrame          int
	ingameTick            int
	header                *common.DemoHeader // Pointer so we can check for nil
	equipmentMapping      map[*st.ServerClass]common.EquipmentElement
	rawPlayers            [maxPlayers]*common.PlayerInfo
	players               map[int]*common.Player
	connectedPlayers      map[int]*common.Player
	additionalPlayerInfo  [maxPlayers]common.AdditionalPlayerInformation
	entities              map[int]*st.Entity
	modelPreCache         []string                      // Used to find out whether a weapon is a p250 or cz for example (same id)
	weapons               [maxEntities]common.Equipment // Used to remember what a weapon is (p250 / cz etc.)
	tState                TeamState
	ctState               TeamState
	bombsiteA             bombsiteInfo
	bombsiteB             bombsiteInfo
	triggers              []*boundingBoxInformation
	instanceBaselines     map[int][]byte
	preprocessedBaselines map[int]map[int]st.PropValue
	gehDescriptors        map[int32]*msg.CSVCMsg_GameEventListDescriptorT
	stringTables          []*msg.CSVCMsg_CreateStringTable
	cancelChan            chan struct{}
}

// Map returns the map name. E.g. de_dust2 or de_inferno.
func (p *Parser) Map() string {
	return p.header.MapName
}

// Participants returns all connected players.
// This includes spectators.
func (p *Parser) Participants() []*common.Player {
	r := make([]*common.Player, 0, len(p.connectedPlayers))
	for _, ptcp := range p.connectedPlayers {
		r = append(r, ptcp)
	}
	return r
}

// PlayingParticipants returns all players that aren't spectating.
func (p *Parser) PlayingParticipants() []*common.Player {
	r := make([]*common.Player, 0, len(p.connectedPlayers))
	for _, ptcp := range p.connectedPlayers {
		// FIXME: Why do we have to check for nil here???
		if ptcp != nil && ptcp.Team != common.TeamSpectators {
			r = append(r, ptcp)
		}
	}
	return r
}

// FrameRate returns the frame rate of the demo (frames / demo-ticks per second).
// Not necessarily the tick-rate the server ran on during the game.
// VolvoPlx128BitKTnxBye
func (p *Parser) FrameRate() float32 {
	return float32(p.header.PlaybackFrames) / p.header.PlaybackTime
}

// FrameTime returns the time a frame / demo-tick takes in seconds.
func (p *Parser) FrameTime() float32 {
	return p.header.PlaybackTime / float32(p.header.PlaybackFrames)
}

// Progress returns the parsing progress from 0 to 1.
// Where 0 means nothing has been parsed yet and 1 means the demo has been parsed to the end.
// Might not actually be reliable since it's just based on the reported tick count of the header.
func (p *Parser) Progress() float32 {
	return float32(p.currentFrame) / float32(p.header.PlaybackFrames)
}

// CurrentFrame return the number of the current frame, aka. 'demo-tick' (Since demos often have a different tick-rate than the game).
// Starts with frame 0, should go up to DemoHeader.PlaybackFrames but might not be the case (usually it's just close to it).
func (p *Parser) CurrentFrame() int {
	return p.currentFrame
}

// IngameTick returns the latest actual tick number of the server during the game.
// Watch out, I've seen this return wonky negative numbers at the start of demos.
func (p *Parser) IngameTick() int {
	return p.ingameTick
}

// CurrentTime returns the ingame time in seconds since the start of the demo.
func (p *Parser) CurrentTime() float32 {
	return float32(p.currentFrame) * p.FrameTime()
}

// RegisterEventHandler registers a handler for game events.
// Must be of type func(<EventType>) where EventType is the kind of event that is handled.
// To catch all events func(interface{}) can be used.
// Parameter handler has to be of type interface{} because lolnogenerics.
func (p *Parser) RegisterEventHandler(handler interface{}) {
	p.eventDispatcher.RegisterHandler(handler)
}

// CTState returns the TeamState of the CT team.
// Make sure you handle swapping sides properly if you keep the reference.
func (p *Parser) CTState() *TeamState {
	return &p.ctState
}

// TState returns the TeamState of the T team.
// Make sure you handle swapping sides properly if you keep the reference.
func (p *Parser) TState() *TeamState {
	return &p.tState
}

// NewParser creates a new Parser on the basis of an io.Reader
// - like os.File or bytes.Reader - that reads demo data.
func NewParser(demostream io.Reader) *Parser {
	var p Parser
	// Init parser
	p.bitReader = bit.NewLargeBitReader(demostream)
	p.msgQueue = make(chan interface{}, 8)
	p.instanceBaselines = make(map[int][]byte)
	p.preprocessedBaselines = make(map[int]map[int]st.PropValue)
	p.equipmentMapping = make(map[*st.ServerClass]common.EquipmentElement)
	p.players = make(map[int]*common.Player)
	p.connectedPlayers = make(map[int]*common.Player)
	p.entities = make(map[int]*st.Entity)
	p.cancelChan = make(chan struct{}, 1)

	// Attach proto msg handlers
	p.msgDispatcher.RegisterHandler(p.handlePacketEntities)
	p.msgDispatcher.RegisterHandler(p.handleGameEventList)
	p.msgDispatcher.RegisterHandler(p.handleGameEvent)
	p.msgDispatcher.RegisterHandler(p.handleCreateStringTable)
	p.msgDispatcher.RegisterHandler(p.handleUpdateStringTable)

	p.msgDispatcher.AddQueues(p.msgQueue)
	return &p
}
<commit_msg>Fix typo<commit_after>package demoinfocs

import (
	"io"

	dp "github.com/markus-wa/godispatch"

	bit "github.com/markus-wa/demoinfocs-golang/bitread"
	common "github.com/markus-wa/demoinfocs-golang/common"
	msg "github.com/markus-wa/demoinfocs-golang/msg"
	st "github.com/markus-wa/demoinfocs-golang/sendtables"
)

// TODO?: create struct GameState for all game-state relevant stuff

// Parser can parse a CS:GO demo.
// Creating a Parser is done via NewParser().
// To start off use Parser.ParseHeader() to parse the demo header.
// After parsing the header Parser.ParseNextFrame() and Parser.ParseToEnd() can be used to parse the demo.
// Use Parser.RegisterEventHandler() to receive notifications about events.
type Parser struct {
	bitReader             *bit.BitReader
	stParser              st.Parser
	msgDispatcher         dp.Dispatcher
	eventDispatcher       dp.Dispatcher
	msgQueue              chan interface{}
	currentFrame          int
	ingameTick            int
	header                *common.DemoHeader // Pointer so we can check for nil
	equipmentMapping      map[*st.ServerClass]common.EquipmentElement
	rawPlayers            [maxPlayers]*common.PlayerInfo
	players               map[int]*common.Player
	connectedPlayers      map[int]*common.Player
	additionalPlayerInfo  [maxPlayers]common.AdditionalPlayerInformation
	entities              map[int]*st.Entity
	modelPreCache         []string                      // Used to find out whether a weapon is a p250 or cz for example (same id)
	weapons               [maxEntities]common.Equipment // Used to remember what a weapon is (p250 / cz etc.)
	tState                TeamState
	ctState               TeamState
	bombsiteA             bombsiteInfo
	bombsiteB             bombsiteInfo
	triggers              []*boundingBoxInformation
	instanceBaselines     map[int][]byte
	preprocessedBaselines map[int]map[int]st.PropValue
	gehDescriptors        map[int32]*msg.CSVCMsg_GameEventListDescriptorT
	stringTables          []*msg.CSVCMsg_CreateStringTable
	cancelChan            chan struct{}
}

// Map returns the map name. E.g. de_dust2 or de_inferno.
func (p *Parser) Map() string {
	return p.header.MapName
}

// Participants returns all connected players.
// This includes spectators.
func (p *Parser) Participants() []*common.Player {
	r := make([]*common.Player, 0, len(p.connectedPlayers))
	for _, ptcp := range p.connectedPlayers {
		r = append(r, ptcp)
	}
	return r
}

// PlayingParticipants returns all players that aren't spectating.
func (p *Parser) PlayingParticipants() []*common.Player {
	r := make([]*common.Player, 0, len(p.connectedPlayers))
	for _, ptcp := range p.connectedPlayers {
		// FIXME: Why do we have to check for nil here???
		if ptcp != nil && ptcp.Team != common.TeamSpectators {
			r = append(r, ptcp)
		}
	}
	return r
}

// FrameRate returns the frame rate of the demo (frames / demo-ticks per second).
// Not necessarily the tick-rate the server ran on during the game.
// VolvoPlx128TixKTnxBye
func (p *Parser) FrameRate() float32 {
	return float32(p.header.PlaybackFrames) / p.header.PlaybackTime
}

// FrameTime returns the time a frame / demo-tick takes in seconds.
func (p *Parser) FrameTime() float32 {
	return p.header.PlaybackTime / float32(p.header.PlaybackFrames)
}

// Progress returns the parsing progress from 0 to 1.
// Where 0 means nothing has been parsed yet and 1 means the demo has been parsed to the end.
// Might not actually be reliable since it's just based on the reported tick count of the header.
func (p *Parser) Progress() float32 {
	return float32(p.currentFrame) / float32(p.header.PlaybackFrames)
}

// CurrentFrame return the number of the current frame, aka. 'demo-tick' (Since demos often have a different tick-rate than the game).
// Starts with frame 0, should go up to DemoHeader.PlaybackFrames but might not be the case (usually it's just close to it).
func (p *Parser) CurrentFrame() int {
	return p.currentFrame
}

// IngameTick returns the latest actual tick number of the server during the game.
// Watch out, I've seen this return wonky negative numbers at the start of demos.
func (p *Parser) IngameTick() int {
	return p.ingameTick
}

// CurrentTime returns the ingame time in seconds since the start of the demo.
func (p *Parser) CurrentTime() float32 {
	return float32(p.currentFrame) * p.FrameTime()
}

// RegisterEventHandler registers a handler for game events.
// Must be of type func(<EventType>) where EventType is the kind of event that is handled.
// To catch all events func(interface{}) can be used.
// Parameter handler has to be of type interface{} because lolnogenerics.
func (p *Parser) RegisterEventHandler(handler interface{}) {
	p.eventDispatcher.RegisterHandler(handler)
}

// CTState returns the TeamState of the CT team.
// Make sure you handle swapping sides properly if you keep the reference.
func (p *Parser) CTState() *TeamState {
	return &p.ctState
}

// TState returns the TeamState of the T team.
// Make sure you handle swapping sides properly if you keep the reference.
func (p *Parser) TState() *TeamState {
	return &p.tState
}

// NewParser creates a new Parser on the basis of an io.Reader
// - like os.File or bytes.Reader - that reads demo data.
func NewParser(demostream io.Reader) *Parser {
	var p Parser
	// Init parser
	p.bitReader = bit.NewLargeBitReader(demostream)
	p.msgQueue = make(chan interface{}, 8)
	p.instanceBaselines = make(map[int][]byte)
	p.preprocessedBaselines = make(map[int]map[int]st.PropValue)
	p.equipmentMapping = make(map[*st.ServerClass]common.EquipmentElement)
	p.players = make(map[int]*common.Player)
	p.connectedPlayers = make(map[int]*common.Player)
	p.entities = make(map[int]*st.Entity)
	p.cancelChan = make(chan struct{}, 1)

	// Attach proto msg handlers
	p.msgDispatcher.RegisterHandler(p.handlePacketEntities)
	p.msgDispatcher.RegisterHandler(p.handleGameEventList)
	p.msgDispatcher.RegisterHandler(p.handleGameEvent)
	p.msgDispatcher.RegisterHandler(p.handleCreateStringTable)
	p.msgDispatcher.RegisterHandler(p.handleUpdateStringTable)

	p.msgDispatcher.AddQueues(p.msgQueue)
	return &p
}
<|endoftext|>