Builds out most of parser - untested.

This commit is contained in:
Brian Evans 2019-07-16 16:36:06 -07:00
parent dbe94ee680
commit 5a85f9cc8f
2 changed files with 255 additions and 9 deletions

View File

@ -1,10 +1,9 @@
package parser
package parse
import (
"bufio"
"bytes"
"io"
"strings"
)
type Token struct {
@ -19,14 +18,14 @@ type scanner struct {
var eof rune = rune(0)
const (
Number int = iota
Number int = iota + 1
Text
Opperator
Whitespace
Comment
Newline
Separator
End
Illegal
)
@ -63,15 +62,17 @@ func (s *scanner) scan() Token {
} else if char == '#' {
s.unread()
return s.scanComment()
} else if isOpperator() {
} else if isOpperator(char) {
return Token{Opperator, string(char)}
} else if char == '\n' {
return Token{Newline,""}
} else if char == ':' {
return Token{Separator,""}
} else if char == eof {
return Token{End, ""}
}
return Token{illegal, string(char)}
return Token{Illegal, string(char)}
}
@ -94,6 +95,25 @@ func (s *scanner) scanWhitespace() Token {
return Token{Whitespace, buf.String()}
}
func (s *scanner) scanNumber() Token {
var buf bytes.Buffer
buf.WriteRune(s.read())
for {
if ch := s.read(); ch == eof {
s.unread()
break
} else if !isNumber(ch) {
s.unread()
break
} else {
_, _ = buf.WriteRune(ch)
}
}
return Token{Number, buf.String()}
}
func (s *scanner) scanText() Token {
var buf bytes.Buffer
@ -109,13 +129,13 @@ func (s *scanner) scanText() Token {
} else {
_, _ = buf.WriteRune(ch)
}
return Token{Text, buf.String()}
}
return Token{Text, buf.String()}
}
func (s *scanner) scanComment() {
func (s *scanner) scanComment() Token {
var buf bytes.Buffer
buf.WriteRune(s.read())

View File

@ -0,0 +1,226 @@
package parse
import (
"io"
"fmt"
"strings"
"strconv"
)
type Parser struct {
s *scanner
row int
buffer struct {
token Token
size int
}
}
type Expression struct {
Opperator rune
Opperand int
Variable string
}
type Procedure struct {
Kind string
Expressions []Expression
}
type Loop struct {
Counter int
Procedures []Procedure
}
type AST struct {
Loops []Loop
}
func (p *Parser) scan() (current Token) {
if p.buffer.size != 0 {
p.buffer.size = 0
return p.buffer.token
}
for {
if current = p.s.scan(); current.kind != Whitespace && current.kind != Comment {
break
}
}
current = p.s.scan()
p.buffer.token = current
return
}
func (p *Parser) unscan() {
p.buffer.size = 1
}
func (p *Parser) parseExpressions(equals bool) []Expression {
var exprs []Expression = make([]Expression, 0, 4)
for {
t := p.scan()
ex := Expression{}
if t.kind == Opperator {
ex.Opperator = rune(t.val[0])
t = p.scan()
} else {
ex.Opperator = '='
}
if t.kind == Number {
ex.Opperand, _ = strconv.Atoi(t.val)
} else if t.kind == Text {
if isVariable(t.val) {
ex.Variable = strings.ToUpper(t.val)
} else {
panic(fmt.Sprintf("Parse error: Invalid variable %q on \033[1mline %d\033[0m", t.val, p.row))
}
}
exprs = append(exprs, ex)
if t.kind == Separator {
continue
}
if t.kind == Newline || t.kind == End {
p.unscan()
break
}
}
return exprs
}
func (p *Parser) parseProcedure() Procedure {
out := Procedure{}
t := p.scan()
t.val = strings.ToUpper(t.val)
if isValidProcedure(t.val) {
out.Kind = t.val
} else {
panic(fmt.Sprintf("Parse error: Invalid procedure call %q on \033[1mline %d\033[0m", t.val, p.row))
}
t = p.scan()
switch t.kind {
case Number, Text:
p.unscan()
out.Expressions = p.parseExpressions(true)
case Opperator:
p.unscan()
out.Expressions = p.parseExpressions(false)
case End, Newline:
p.unscan()
default:
panic(fmt.Sprintf("Parse error: Invalid token %q on \033[1mline %d\033[0m", t.val, p.row))
}
// isVariable(t.val)
return out
}
func (p *Parser) parseLoop() Loop {
t := p.scan()
l := Loop{}
if strings.ToUpper(t.val) == "LOOP" {
t = p.scan()
if t.kind == Number {
l.Counter, _ = strconv.Atoi(t.val)
} else {
panic(fmt.Sprintf("Parse error: Loop declared, but not followed by a number on \033[1mline %d\033[0m", p.row))
}
} else {
p.unscan()
l.Counter = 1
}
l.Procedures = make([]Procedure,0, 5)
L:
for {
t = p.scan()
switch t.kind {
case Newline:
p.row++
case Illegal:
panic(fmt.Sprintf("Parse error: Illegal token %q on \033[1mline %d\033[0m", t.val, p.row))
case Opperator:
panic(fmt.Sprintf("Parse error: Opperator %q outside of a procedure call on \033[1mline %d\033[0m", t.val, p.row))
case Separator:
panic(fmt.Sprintf("Parse error: Illegal field separator ':' on \033[1mline %d\033[0m", p.row))
case End:
break L
case Number:
panic(fmt.Sprintf("Parse error: Number %q outside of procedure call on \033[1mline %d\033[0m", t.val, p.row))
case Text:
p.unscan()
procedure := p.parseProcedure()
l.Procedures = append(l.Procedures, procedure)
}
}
return l
}
func (p *Parser) Parse() AST {
p.row = 1
tree := AST{}
L:
for {
t := p.scan()
switch t.kind {
case Newline:
p.row++
case Illegal:
panic(fmt.Sprintf("Parse error: Illegal token %q on \033[1mline %d\033[0m", t.val, p.row))
case Opperator:
panic(fmt.Sprintf("Parse error: Opperator %q outside of a procedure call on \033[1mline %d\033[0m", t.val, p.row))
case Separator:
panic(fmt.Sprintf("Parse error: Illegal field separator ':' on \033[1mline %d\033[0m", p.row))
case End:
break L
case Number:
panic(fmt.Sprintf("Parse error: Number %q outside of procedure call on \033[1mline %d\033[0m", t.val, p.row))
case Text:
p.unscan()
loop := p.parseLoop()
tree.Loops = append(tree.Loops, loop)
}
}
return tree
}
func isValidProcedure(p string) bool {
switch p {
case "REG", "LOC", "BEG", "END", "RED",
"GRN", "BLU", "APH", "COL", "APY", "MOD",
"FAD":
return true
}
return false
}
func isVariable(v string) bool {
v = strings.ToUpper(v)
switch v {
case "WID", "HIG", "REG":
return true
}
return false
}
func NewParser(r io.Reader) *Parser {
return &Parser{s: NewScanner(r)}
}