Browse Source

huge refactor

I am a fool and should have found a better way to break this into many commits
shame... shame on the poor choices I have made
pull/16/head
Brett Langdon 11 years ago
parent
commit
3911921151
18 changed files with 634 additions and 395 deletions
  1. +0
    -9
      config/boolean.go
  2. +0
    -39
      config/config.go
  3. +0
    -9
      config/float.go
  4. +0
    -9
      config/integer.go
  5. +0
    -9
      config/null.go
  6. +0
    -132
      config/section.go
  7. +0
    -9
      config/string.go
  8. +88
    -0
      conversions.go
  9. +7
    -8
      example/main.go
  10. +2
    -2
      example_test.go
  11. +19
    -15
      forge.go
  12. +52
    -70
      parser.go
  13. +89
    -0
      primative.go
  14. +32
    -30
      scanner.go
  15. +244
    -0
      section.go
  16. +53
    -0
      token/token.go
  17. +0
    -54
      token/tokenid.go
  18. +48
    -0
      value.go

+ 0
- 9
config/boolean.go View File

@ -1,9 +0,0 @@
package config
type BooleanValue struct {
Name string
Value bool
}
func (this BooleanValue) GetType() ConfigType { return BOOLEAN }
func (this BooleanValue) GetValue() interface{} { return this.Value }

+ 0
- 39
config/config.go View File

@ -1,39 +0,0 @@
package config
type ConfigType int
const (
SECTION ConfigType = iota
INTEGER
BOOLEAN
FLOAT
STRING
NULL
)
var configTypes = [...]string{
SECTION: "SECTION",
BOOLEAN: "BOOLEAN",
INTEGER: "INTEGER",
FLOAT: "FLOAT",
STRING: "STRING",
NULL: "NULL",
}
func (this ConfigType) String() string {
s := ""
if 0 <= this && this < ConfigType(len(configTypes)) {
s = configTypes[this]
}
if s == "" {
s = "UNKNOWN"
}
return s
}
type ConfigValue interface {
GetType() ConfigType
GetValue() interface{}
}

+ 0
- 9
config/float.go View File

@ -1,9 +0,0 @@
package config
type FloatValue struct {
Name string
Value float64
}
func (this FloatValue) GetType() ConfigType { return INTEGER }
func (this FloatValue) GetValue() interface{} { return this.Value }

+ 0
- 9
config/integer.go View File

@ -1,9 +0,0 @@
package config
type IntegerValue struct {
Name string
Value int64
}
func (this IntegerValue) GetType() ConfigType { return INTEGER }
func (this IntegerValue) GetValue() interface{} { return this.Value }

+ 0
- 9
config/null.go View File

@ -1,9 +0,0 @@
package config
type NullValue struct {
Name string
Value interface{}
}
func (this NullValue) GetType() ConfigType { return NULL }
func (this NullValue) GetValue() interface{} { return nil }

+ 0
- 132
config/section.go View File

@ -1,132 +0,0 @@
package config
import (
"encoding/json"
"errors"
"fmt"
"strings"
)
type SectionValue struct {
Name string
Value map[string]ConfigValue
Comments []string
Includes []string
}
func NewNamedSection(name string) *SectionValue {
return &SectionValue{
Name: name,
Value: make(map[string]ConfigValue),
Comments: make([]string, 0),
Includes: make([]string, 0),
}
}
func NewAnonymousSection() *SectionValue {
return &SectionValue{
Value: make(map[string]ConfigValue),
Comments: make([]string, 0),
Includes: make([]string, 0),
}
}
func (this SectionValue) GetType() ConfigType { return SECTION }
func (this SectionValue) GetValue() interface{} { return this.Value }
func (this *SectionValue) AddComment(comment string) {
this.Comments = append(this.Comments, comment)
}
func (this *SectionValue) AddInclude(include string) {
this.Includes = append(this.Includes, include)
}
func (this *SectionValue) Set(name string, value ConfigValue) {
this.Value[name] = value
}
func (this *SectionValue) Get(name string) ConfigValue {
return this.Value[name]
}
func (this *SectionValue) GetSection(name string) SectionValue {
value := this.Value[name]
return value.(SectionValue)
}
func (this *SectionValue) GetString(name string) StringValue {
value := this.Value[name]
return value.(StringValue)
}
func (this *SectionValue) GetInteger(name string) IntegerValue {
value := this.Value[name]
return value.(IntegerValue)
}
func (this *SectionValue) GetFloat(name string) FloatValue {
value := this.Value[name]
return value.(FloatValue)
}
func (this *SectionValue) Contains(name string) bool {
_, ok := this.Value[name]
return ok
}
func (this *SectionValue) Resolve(setting string) (ConfigValue, error) {
parts := strings.Split(setting, ".")
var reference ConfigValue
reference = this
visited := []string{}
for {
if len(parts) == 0 {
break
}
if reference.GetType() != SECTION {
name := strings.Join(visited, ".")
return nil, errors.New(fmt.Sprintf("'%s' is a %s not a SECTION", name, reference.GetType()))
}
part := parts[0]
parts = parts[1:]
section := reference.(*SectionValue)
if section.Contains(part) == false {
name := strings.Join(visited, ".")
if len(name) > 0 {
return nil, errors.New(fmt.Sprintf("'%s' does not have setting '%s'", name, part))
} else {
return nil, errors.New(fmt.Sprintf("setting '%s' does not exist", part))
}
}
reference = section.Get(part)
visited = append(visited, part)
}
return reference, nil
}
func (this *SectionValue) ToJSON() ([]byte, error) {
data, err := this.ToMap()
if err != nil {
return nil, err
}
return json.Marshal(data)
}
func (this *SectionValue) ToMap() (map[string]interface{}, error) {
settings := make(map[string]interface{})
for name, value := range this.Value {
if value.GetType() == SECTION {
data, err := value.(*SectionValue).ToMap()
if err != nil {
return nil, err
}
settings[name] = data
} else {
settings[name] = value.GetValue()
}
}
return settings, nil
}

+ 0
- 9
config/string.go View File

@ -1,9 +0,0 @@
package config
type StringValue struct {
Name string
Value string
}
func (this StringValue) GetType() ConfigType { return STRING }
func (this StringValue) GetValue() interface{} { return this.Value }

+ 88
- 0
conversions.go View File

@ -0,0 +1,88 @@
package forge
import (
"errors"
"fmt"
"math"
"strconv"
)
func asBoolean(value interface{}) (bool, error) {
switch val := value.(type) {
case bool:
return val, nil
case float64:
return val != 0, nil
case int64:
return val != 0, nil
case nil:
return false, nil
case string:
return val != "", nil
}
msg := fmt.Sprintf("Could not convert value %s to type BOOLEAN", value)
return false, errors.New(msg)
}
func asFloat(value interface{}) (float64, error) {
switch val := value.(type) {
case bool:
if val {
return float64(1), nil
} else {
return float64(0), nil
}
case float64:
return val, nil
case int64:
return float64(val), nil
case string:
return strconv.ParseFloat(val, 64)
}
msg := fmt.Sprintf("Could not convert value %s to type FLOAT", value)
return 0, errors.New(msg)
}
func asInteger(value interface{}) (int64, error) {
switch val := value.(type) {
case bool:
if val {
return int64(1), nil
} else {
return int64(0), nil
}
case float64:
return int64(math.Trunc(val)), nil
case int64:
return val, nil
case string:
return strconv.ParseInt(val, 10, 64)
}
msg := fmt.Sprintf("Could not convert value %s to type INTEGER", value)
return 0, errors.New(msg)
}
func asString(value interface{}) (string, error) {
switch val := value.(type) {
case bool:
if val {
return "True", nil
} else {
return "False", nil
}
case float64:
return strconv.FormatFloat(val, 10, -1, 64), nil
case int64:
return strconv.FormatInt(val, 10), nil
case nil:
return "Null", nil
case string:
return val, nil
}
msg := fmt.Sprintf("Could not convert value %s to type STRING", value)
return "", errors.New(msg)
}

+ 7
- 8
example/main.go View File

@ -13,19 +13,18 @@ func main() {
panic(err)
}
// Get a single value
if settings.Contains("global") {
// Get `global` casted as `StringValue`
value := settings.GetString("global")
fmt.Printf("global = \"%s\"\r\n", value.GetValue())
str_val, err := settings.GetString("global")
if err != nil {
panic(err)
}
fmt.Printf("global = \"%s\"\r\n", str_val)
// Get a nested value
value, err := settings.Resolve("primary.included_setting")
fmt.Printf("primary.included_setting = \"%s\"\r\n", value.GetValue())
// value, err := settings.Resolve("primary.included_setting")
// fmt.Printf("primary.included_setting = \"%s\"\r\n", value.GetValue())
// Convert settings to a map
settingsMap, err := settings.ToMap()
settingsMap := settings.ToMap()
fmt.Printf("global = \"%s\"\r\n", settingsMap["global"])
// Convert settings to JSON


+ 2
- 2
example_test.go View File

@ -15,7 +15,7 @@ func Example() {
}
// Get a single value
if settings.Contains("global") {
if settings.Exists("global") {
// Get `global` casted as `StringValue`
value := settings.GetString("global")
fmt.Printf("global = \"%s\"\r\n", value.GetValue())
@ -28,7 +28,7 @@ func Example() {
// You can also traverse down the sections manually
primary, err := settings.GetSection("primary")
value, err := primary.GetString("included_setting")
fmt.Printf("primary.included_setting = \"%s\"\r\n", value.GetValue())
fmt.Printf("primary.included_setting = \"%s\"\r\n", value)
// Convert settings to a map
settingsMap, err := settings.ToMap()


+ 19
- 15
forge.go View File

@ -83,28 +83,32 @@ package forge
import (
"bytes"
"io"
"os"
"strings"
"github.com/brettlangdon/forge/config"
"github.com/brettlangdon/forge/parser"
)
// ParseString will parse a forge SectionValue from a string
func ParseString(data string) (*config.SectionValue, error) {
return parser.ParseReader(strings.NewReader(data))
func ParseBytes(data []byte) (*Section, error) {
return ParseReader(bytes.NewReader(data))
}
// ParseBytes will parse a forge SectionValue from a byte array
func ParseBytes(data []byte) (*config.SectionValue, error) {
return parser.ParseReader(bytes.NewReader(data))
func ParseFile(filename string) (*Section, error) {
reader, err := os.Open(filename)
if err != nil {
return nil, err
}
return ParseReader(reader)
}
// ParseFile will parse a forge SectionValue from a filename
func ParseFile(filename string) (*config.SectionValue, error) {
return parser.ParseFile(filename)
func ParseReader(reader io.Reader) (*Section, error) {
parser := NewParser(reader)
err := parser.Parse()
if err != nil {
return nil, err
}
return parser.GetSettings(), nil
}
// ParseReader will parse a forge SectionValue from a io.Reader
func ParseReader(reader io.Reader) (*config.SectionValue, error) {
return parser.ParseReader(reader)
func ParseString(data string) (*Section, error) {
return ParseReader(strings.NewReader(data))
}

parser/parser.go → parser.go View File


+ 89
- 0
primative.go View File

@ -0,0 +1,89 @@
package forge
import (
"errors"
"fmt"
)
type Primative struct {
valueType ValueType
value interface{}
}
func NewPrimative(valueType ValueType, value interface{}) *Primative {
return &Primative{
valueType: valueType,
value: value,
}
}
func NewBoolean(value bool) *Primative {
return NewPrimative(BOOLEAN, value)
}
func NewFloat(value float64) *Primative {
return NewPrimative(FLOAT, value)
}
func NewInteger(value int64) *Primative {
return NewPrimative(INTEGER, value)
}
func NewNull() *Primative {
return NewPrimative(NULL, nil)
}
func NewString(value string) *Primative {
return NewPrimative(STRING, value)
}
func (this *Primative) GetType() ValueType {
return this.valueType
}
func (this *Primative) GetValue() interface{} {
return this.value
}
func (this *Primative) UpdateValue(value interface{}) error {
// Valid types
switch value.(type) {
case bool:
this.valueType = BOOLEAN
case float64:
this.valueType = FLOAT
case int64:
this.valueType = INTEGER
case nil:
this.valueType = NULL
case string:
this.valueType = STRING
default:
msg := fmt.Sprintf("Unsupported type, %s must be of (bool, float64, int64, nil, string)", value)
return errors.New(msg)
}
this.value = value
return nil
}
func (this *Primative) AsBoolean() (bool, error) {
return asBoolean(this.value)
}
func (this *Primative) AsFloat() (float64, error) {
return asFloat(this.value)
}
func (this *Primative) AsInteger() (int64, error) {
return asInteger(this.value)
}
func (this *Primative) AsString() (string, error) {
return asString(this.value)
}
func (this *Primative) String() string {
str, _ := this.AsString()
return str
}

token/tokenizer.go → scanner.go View File


+ 244
- 0
section.go View File

@ -0,0 +1,244 @@
package forge
import (
"encoding/json"
"errors"
"fmt"
"strings"
)
type Section struct {
parent *Section
values map[string]Value
}
func NewSection() *Section {
return &Section{
values: make(map[string]Value),
}
}
func NewChildSection(parent *Section) *Section {
return &Section{
parent: parent,
values: make(map[string]Value),
}
}
func (this *Section) GetType() ValueType {
return SECTION
}
func (this *Section) GetValue() interface{} {
return this.values
}
func (this *Section) UpdateValue(value interface{}) error {
switch value.(type) {
case map[string]Value:
this.values = value.(map[string]Value)
return nil
}
msg := fmt.Sprintf("Unsupported type, %s must be of type `map[string]Value`", value)
return errors.New(msg)
}
func (this *Section) AddSection(name string) *Section {
section := NewChildSection(this)
this.values[name] = section
return section
}
func (this *Section) Exists(name string) bool {
_, err := this.Get(name)
return err == nil
}
func (this *Section) Get(name string) (Value, error) {
value, ok := this.values[name]
var err error
if ok == false {
err = errors.New("Value does not exist")
}
return value, err
}
func (this *Section) GetBoolean(name string) (bool, error) {
value, err := this.Get(name)
if err != nil {
return false, err
}
switch value.(type) {
case *Primative:
return value.(*Primative).AsBoolean()
case *Section:
return true, nil
}
return false, errors.New("Could not convert unknown value to boolean")
}
func (this *Section) GetFloat(name string) (float64, error) {
value, err := this.Get(name)
if err != nil {
return float64(0), err
}
switch value.(type) {
case *Primative:
return value.(*Primative).AsFloat()
}
return float64(0), errors.New("Could not convert non-primative value to float")
}
func (this *Section) GetInteger(name string) (int64, error) {
value, err := this.Get(name)
if err != nil {
return int64(0), err
}
switch value.(type) {
case *Primative:
return value.(*Primative).AsInteger()
}
return int64(0), errors.New("Could not convert non-primative value to integer")
}
func (this *Section) GetSection(name string) (*Section, error) {
value, err := this.Get(name)
if err != nil {
return nil, err
}
if value.GetType() == SECTION {
return value.(*Section), nil
}
return nil, errors.New("Could not fetch value as section")
}
func (this *Section) GetString(name string) (string, error) {
value, err := this.Get(name)
if err != nil {
return "", err
}
switch value.(type) {
case *Primative:
return value.(*Primative).AsString()
}
return "", errors.New("Could not convert non-primative value to string")
}
func (this *Section) GetParent() *Section {
return this.parent
}
func (this *Section) HasParent() bool {
return this.parent != nil
}
func (this *Section) Set(name string, value Value) {
this.values[name] = value
}
func (this *Section) SetBoolean(name string, value bool) {
current, err := this.Get(name)
// Exists just update the value/type
if err == nil {
current.UpdateValue(value)
} else {
this.values[name] = NewBoolean(value)
}
}
func (this *Section) SetFloat(name string, value float64) {
current, err := this.Get(name)
// Exists just update the value/type
if err == nil {
current.UpdateValue(value)
} else {
this.values[name] = NewFloat(value)
}
}
func (this *Section) SetInteger(name string, value int64) {
current, err := this.Get(name)
// Exists just update the value/type
if err == nil {
current.UpdateValue(value)
} else {
this.values[name] = NewInteger(value)
}
}
func (this *Section) SetNull(name string) {
current, err := this.Get(name)
// Already is a Null, nothing to do
if err == nil && current.GetType() == NULL {
return
}
this.Set(name, NewNull())
}
func (this *Section) SetString(name string, value string) {
current, err := this.Get(name)
// Exists just update the value/type
if err == nil {
current.UpdateValue(value)
} else {
this.Set(name, NewString(value))
}
}
func (this *Section) Resolve(name string) (Value, error) {
// Used only in error state return value
var value Value
parts := strings.Split(name, ".")
if len(parts) == 0 {
return value, errors.New("No name provided")
}
var current Value
current = this
for _, part := range parts {
if current.GetType() != SECTION {
return value, errors.New("Trying to resolve value from non-section")
}
next_current, err := current.(*Section).Get(part)
if err != nil {
return value, errors.New("Could not find value in section")
}
current = next_current
}
return current, nil
}
func (this *Section) ToJSON() ([]byte, error) {
data := this.ToMap()
return json.Marshal(data)
}
func (this *Section) ToMap() map[string]interface{} {
output := make(map[string]interface{})
for key, value := range this.values {
if value.GetType() == SECTION {
output[key] = value.(*Section).ToMap()
} else {
output[key] = value.GetValue()
}
}
return output
}

+ 53
- 0
token/token.go View File

@ -15,3 +15,56 @@ func (this Token) String() string {
this.ID, this.Literal, this.Line, this.Column,
)
}
type TokenID int
const (
ILLEGAL TokenID = iota
EOF
LBRACKET
RBRACKET
EQUAL
SEMICOLON
PERIOD
IDENTIFIER
BOOLEAN
INTEGER
FLOAT
STRING
NULL
COMMENT
INCLUDE
)
var tokenNames = [...]string{
ILLEGAL: "ILLEGAL",
EOF: "EOF",
LBRACKET: "LBRACKET",
RBRACKET: "RBRACKET",
EQUAL: "EQUAL",
SEMICOLON: "SEMICOLON",
PERIOD: "PERIOD",
IDENTIFIER: "IDENTIFIER",
BOOLEAN: "BOOLEAN",
INTEGER: "INTEGER",
FLOAT: "FLOAT",
STRING: "STRING",
NULL: "NULL",
COMMENT: "COMMENT",
INCLUDE: "INCLUDE",
}
func (this TokenID) String() string {
s := ""
if 0 <= this && this < TokenID(len(tokenNames)) {
s = tokenNames[this]
}
if s == "" {
s = "UNKNOWN"
}
return s
}

+ 0
- 54
token/tokenid.go View File

@ -1,54 +0,0 @@
package token
type TokenID int
const (
ILLEGAL TokenID = iota
EOF
LBRACKET
RBRACKET
EQUAL
SEMICOLON
PERIOD
IDENTIFIER
BOOLEAN
INTEGER
FLOAT
STRING
NULL
COMMENT
INCLUDE
)
var tokenNames = [...]string{
ILLEGAL: "ILLEGAL",
EOF: "EOF",
LBRACKET: "LBRACKET",
RBRACKET: "RBRACKET",
EQUAL: "EQUAL",
SEMICOLON: "SEMICOLON",
PERIOD: "PERIOD",
IDENTIFIER: "IDENTIFIER",
BOOLEAN: "BOOLEAN",
INTEGER: "INTEGER",
FLOAT: "FLOAT",
STRING: "STRING",
NULL: "NULL",
COMMENT: "COMMENT",
INCLUDE: "INCLUDE",
}
func (this TokenID) String() string {
s := ""
if 0 <= this && this < TokenID(len(tokenNames)) {
s = tokenNames[this]
}
if s == "" {
s = "UNKNOWN"
}
return s
}

+ 48
- 0
value.go View File

@ -0,0 +1,48 @@
package forge
type ValueType int
const (
UNKNOWN ValueType = iota
// Primative values
BOOLEAN
FLOAT
INTEGER
NULL
STRING
// Complex values
REFERENCE
SECTION
)
var valueTypes = [...]string{
BOOLEAN: "BOOLEAN",
FLOAT: "FLOAT",
INTEGER: "INTEGER",
NULL: "NULL",
STRING: "STRING",
REFERENCE: "REFERENCE",
SECTION: "SECTION",
}
func (this ValueType) String() string {
str := ""
if 0 <= this && this < ValueType(len(valueTypes)) {
str = valueTypes[this]
}
if str == "" {
str = "UNKNOWN"
}
return str
}
type Value interface {
GetType() ValueType
GetValue() interface{}
UpdateValue(interface{}) error
}

Loading…
Cancel
Save