From 5a85f9cc8fa0e62a1f7e4e66d3bcd0078d836ea0 Mon Sep 17 00:00:00 2001 From: Brian Evans Date: Tue, 16 Jul 2019 16:36:06 -0700 Subject: [PATCH] Builds out most of parser - untested. --- parser/lexer.go | 38 ++++++-- parser/parser.go | 226 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 255 insertions(+), 9 deletions(-) diff --git a/parser/lexer.go b/parser/lexer.go index 2c850be..7ae27a1 100644 --- a/parser/lexer.go +++ b/parser/lexer.go @@ -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()) diff --git a/parser/parser.go b/parser/parser.go index e69de29..40531ae 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -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)} +}