Browse Source

Make semicolons optional, require newline instead

fixes #20
pull/22/head
Brett Langdon 11 years ago
parent
commit
6233f93478
7 changed files with 54 additions and 33 deletions
  1. +8
    -5
      README.md
  2. +5
    -4
      forge.go
  3. +9
    -6
      forge_test.go
  4. +13
    -6
      parser.go
  5. +8
    -6
      scanner.go
  6. +9
    -6
      test.cfg
  7. +2
    -0
      token/token.go

+ 8
- 5
README.md View File

@ -27,11 +27,14 @@ global = "global value";
primary {
string = "primary string value";
single = 'single quotes are allowed too';
integer = 500;
float = 80.80;
boolean = true;
negative = FALSE;
nothing = NULL;
# Semicolons are optional
integer = 500
float = 80.80
boolean = true
negative = FALSE
nothing = NULL
# Include external files
include "./include*.cfg";
# Primary-sub section


+ 5
- 4
forge.go View File

@ -27,6 +27,7 @@
//
// IDENTIFIER: [_a-zA-Z]+
// NUMBERS: [0-9]+
// END: ';' | '\n'
//
// BOOL: 'true' | 'false'
// NULL: 'null'
@ -36,10 +37,10 @@
// REFERENCE: (IDENTIFIER)? ('.' IDENTIFIER)+
// VALUE: BOOL | NULL | INTEGER | FLOAT | STRING | REFERENCE
//
// INCLUDE: 'include ' STRING ';'
// DIRECTIVE: (IDENTIFIER '=' VALUE | INCLUDE) ';'
// INCLUDE: 'include ' STRING END
// DIRECTIVE: (IDENTIFIER '=' VALUE | INCLUDE) END
// SECTION: IDENTIFIER '{' (DIRECTIVE | SECTION)* '}'
// COMMENT: '#' .* NEWLINE '\n'
// COMMENT: '#' .* '\n'
//
// CONFIG_FILE: (COMMENT | DIRECTIVE | SECTION)*
//
@ -70,7 +71,7 @@
// until after the newline.
// * Directive:
// A directive is a setting, a identifier and a value. They are in the format '<identifier> = <value>;'
// All directives must end in a semicolon. The value can be any of the types defined above.
// All directives must end in either a semicolon or newline. The value can be any of the types defined above.
// * Section:
// A section is a grouping of directives under a common name. They are in the format '<section_name> { <directives> }'.
// All sections must be wrapped in brackets ('{', '}') and must all have a name. They do not end in a semicolon.


+ 9
- 6
forge_test.go View File

@ -17,12 +17,15 @@ primary {
string_with_quote = "some \"quoted\" str\\ing";
single = 'hello world';
single_with_quote = '\'hello\' "world"';
integer = 500;
float = 80.80;
negative = -50;
boolean = true;
not_true = FALSE;
nothing = NULL;
# Semicolons are optional
integer = 500
float = 80.80
negative = -50
boolean = true
not_true = FALSE
nothing = NULL
# Reference secondary._under (which hasn't been defined yet)
sec_ref = secondary._under;
# Primary-sub stuff


+ 13
- 6
parser.go View File

@ -11,6 +11,10 @@ import (
"github.com/brettlangdon/forge/token"
)
func isSemicolonOrNewline(id token.TokenID) bool {
return id == token.SEMICOLON || id == token.NEWLINE
}
// Parser is a struct to hold data necessary for parsing a config from a scanner
type Parser struct {
files []string
@ -88,10 +92,10 @@ func (parser *Parser) parseReference(startingSection *Section, period bool) (Val
}
name += parser.curTok.Literal
period = false
} else if parser.curTok.ID == token.SEMICOLON {
} else if isSemicolonOrNewline(parser.curTok.ID) {
break
} else {
msg := fmt.Sprintf("expected ';' instead found '%s'", parser.curTok.Literal)
msg := fmt.Sprintf("expected ';' or '\n' instead found '%s'", parser.curTok.Literal)
return nil, parser.syntaxError(msg)
}
}
@ -159,8 +163,8 @@ func (parser *Parser) parseSetting(name string) error {
if readNext {
parser.readToken()
}
if parser.curTok.ID != token.SEMICOLON {
msg := fmt.Sprintf("expected ';' instead found '%s'", parser.curTok.Literal)
if isSemicolonOrNewline(parser.curTok.ID) == false {
msg := fmt.Sprintf("expected ';' or '\n' instead found '%s'", parser.curTok.Literal)
return parser.syntaxError(msg)
}
parser.readToken()
@ -177,8 +181,8 @@ func (parser *Parser) parseInclude() error {
pattern := parser.curTok.Literal
parser.readToken()
if parser.curTok.ID != token.SEMICOLON {
msg := fmt.Sprintf("expected ';' instead found '%s'", parser.curTok.Literal)
if isSemicolonOrNewline(parser.curTok.ID) == false {
msg := fmt.Sprintf("expected ';' or '\n' instead found '%s'", parser.curTok.Literal)
return parser.syntaxError(msg)
}
@ -260,6 +264,9 @@ func (parser *Parser) parse() error {
if err != nil {
return err
}
case token.NEWLINE:
// Ignore extra newlines
continue
default:
return parser.syntaxError(fmt.Sprintf("unexpected token %s", tok))
}


+ 8
- 6
scanner.go View File

@ -18,8 +18,8 @@ func isDigit(ch rune) bool {
return ('0' <= ch && ch <= '9')
}
func isWhitespace(ch rune) bool {
return (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
func isNonNewlineWhitespace(ch rune) bool {
return (ch == ' ' || ch == '\t' || ch == '\r')
}
func isBoolean(str string) bool {
@ -171,10 +171,10 @@ func (scanner *Scanner) parseComment() {
scanner.readRune()
}
func (scanner *Scanner) skipWhitespace() {
func (scanner *Scanner) skipNonNewlineWhitespace() {
for {
scanner.readRune()
if !isWhitespace(scanner.curCh) {
if !isNonNewlineWhitespace(scanner.curCh) {
break
}
}
@ -182,8 +182,8 @@ func (scanner *Scanner) skipWhitespace() {
// NextToken will read in the next valid token from the Scanner
func (scanner *Scanner) NextToken() token.Token {
if isWhitespace(scanner.curCh) {
scanner.skipWhitespace()
if isNonNewlineWhitespace(scanner.curCh) {
scanner.skipNonNewlineWhitespace()
}
scanner.curTok = token.Token{
@ -217,6 +217,8 @@ func (scanner *Scanner) NextToken() token.Token {
scanner.curTok.ID = token.RBRACKET
case ';':
scanner.curTok.ID = token.SEMICOLON
case '\n':
scanner.curTok.ID = token.NEWLINE
case '.':
scanner.curTok.ID = token.PERIOD
case '-':


+ 9
- 6
test.cfg View File

@ -6,12 +6,15 @@ primary {
string_with_quote = "some \"quoted\" str\\ing";
single = 'hello world';
single_with_quote = '\'hello\' "world"';
integer = 500;
float = 80.80;
negative = -50;
boolean = true;
not_true = FALSE;
nothing = NULL;
# Semicolons are optional
integer = 500
float = 80.80
negative = -50
boolean = true
not_true = FALSE
nothing = NULL
# Reference secondary._under (which hasn't been defined yet)
sec_ref = secondary._under;
# Primary-sub stuff


+ 2
- 0
token/token.go View File

@ -26,6 +26,7 @@ const (
RBRACKET
EQUAL
SEMICOLON
NEWLINE
PERIOD
IDENTIFIER
@ -45,6 +46,7 @@ var tokenNames = [...]string{
RBRACKET: "RBRACKET",
EQUAL: "EQUAL",
SEMICOLON: "SEMICOLON",
NEWLINE: "NEWLINE",
PERIOD: "PERIOD",
IDENTIFIER: "IDENTIFIER",
BOOLEAN: "BOOLEAN",


Loading…
Cancel
Save