Builds out most of parser - untested.
parent
dbe94ee680
commit
5a85f9cc8f
@ -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)}
|
||||
}
|
Loading…
Reference in New Issue