package raku import ( "fmt" "strings" "gitlab.com/beoran/woe/graphviz" "gitlab.com/beoran/woe/monolog" "gitlab.com/beoran/woe/tree" ) type ParseAction func(parser *ManualParser) bool type RuleType int const ( RuleTypeNone = RuleType(iota) RuleTypeAlternate RuleTypeSequence ) type Rule struct { tree.Node Name string RuleType ParseAction } func NewRule(name string, ruty RuleType) *Rule { res := &Rule{} res.RuleType = ruty res.Name = name return res } func (me *Rule) NewChild(action ParseAction) *Rule { child := NewRule("foo", RuleTypeNone) tree.AppendChild(me, child) return child } func (me *Rule) Walk(walker func(rule *Rule) *Rule) *Rule { node_res := tree.Walk(me, func(node tree.Noder) tree.Noder { rule_res := walker(node.(*Rule)) if rule_res == nil { return nil } else { return rule_res } }) return node_res.(*Rule) } type ManualParser struct { *Ast *Tokenizer Classifier now *Ast position int tokens []*Token lookahead *Token } func (me *ManualParser) SetupRules() { } func (me *ManualParser) Expect(types ...TokenType) bool { monolog.Debug("Expecting: ", types, " from ", me.now.AstType, " have ", me.LookaheadType(), " \n") for _, t := range types { if me.LookaheadType() == t { monolog.Debug("Found: ", t, "\n") return true } } monolog.Debug("Not found.\n") return false } type Parsable interface { isParsable() } func (me TokenType) isParsable() { } func (me ParseAction) isParsable() { } /* Advance the lexer but only of there is no lookahead token already available in me.lookahead. */ func (me *ManualParser) Advance() *Token { if me.lookahead == nil { me.lookahead = me.tokens[me.position] me.position++ } return me.lookahead } func (me *ManualParser) DropLookahead() { me.lookahead = nil } func (me *ManualParser) Lookahead() *Token { return me.lookahead } func (me *ManualParser) LookaheadType() TokenType { if me.lookahead == nil { return TokenError } return me.Lookahead().TokenType } func (me *ManualParser) Consume(atyp AstType, types ...TokenType) bool { me.Advance() res := me.Expect(types...) if res { me.NewAstChild(atyp) me.DropLookahead() } return res } func (me *ManualParser) ConsumeWithoutAst(types ...TokenType) bool { me.Advance() res := me.Expect(types...) if res { me.DropLookahead() } return res } /* func (me * ManualParser) OneOf(restype AstType, options ...Parsable) bool { res := false k, v := range options { switch option := v.Type { case TokenType: res := Consume(restype, option) case ParseAction: res := option(me) } } return res } */ func (me *ManualParser) ParseEOX() bool { return me.ConsumeWithoutAst(TokenEOL, TokenPeriod) } func (me *ManualParser) ParseValue() bool { return me.Consume(AstTypeValue, TokenString, TokenNumber, TokenSymbol) } func (me *ManualParser) ParseWord() bool { return me.Consume(AstTypeWord, TokenWord, TokenArticle) } func (me *ManualParser) ParseWordValue() bool { me.NewAstChildDescend(AstTypeWordValue) res := me.ParseValue() || me.ParseWord() me.AstAscend(res) return res } func (me *ManualParser) ParseParametersNonempty() bool { res := false for me.ParseParameter() { res = true } return res } func (me *ManualParser) ParseCallArgs() bool { me.NewAstChildDescend(AstTypeCallArgs) res := me.ParseParameters() && me.ParseEOX() me.AstAscend(res) return res } func (me *ManualParser) ParseOperator() bool { return me.Consume(AstTypeOperator, TokenOperator) } func (me *ManualParser) NewAstChild(tyty AstType) *Ast { return me.now.NewChild(tyty, me.lookahead) } func (me *ManualParser) NewAstChildDescend(tyty AstType) { node := me.NewAstChild(tyty) me.now = node } func (me *ManualParser) AstAscend(keep bool) { if me.now.Parent() != nil { now := me.now me.now = now.Parent().(*Ast) if !keep { now.Remove() } } } func (me TokenType) BlockCloseForOpen() (TokenType, bool) { switch me { case TokenOpenBrace: return TokenCloseBrace, true case TokenDo: return TokenEnd, true default: return TokenError, false } } func (me TokenType) ParenthesisCloseForOpen() (TokenType, bool) { switch me { case TokenOpenBracket: return TokenCloseBracket, true case TokenOpenParen: return TokenCloseParen, true default: return TokenError, false } } func (me *ManualParser) ParseBlock() bool { me.Advance() open := me.LookaheadType() done, ok := open.BlockCloseForOpen() if !ok { /* Not an opening of a block, so no block found. */ return false } me.DropLookahead() me.NewAstChildDescend(AstTypeBlock) res := me.ParseStatements() me.AstAscend(res) if res { me.Advance() if me.LookaheadType() != done { return me.ParseError() } me.DropLookahead() } return res } func (me *ManualParser) ParseParenthesis() bool { me.Advance() open := me.LookaheadType() done, ok := open.ParenthesisCloseForOpen() if !ok { /* Not an opening of a parenthesis, so no parenthesis found. */ return false } me.DropLookahead() me.NewAstChildDescend(AstTypeParenthesis) res := me.ParseExpression() me.AstAscend(res) if res { me.Advance() if me.LookaheadType() != done { return me.ParseError() } me.DropLookahead() } return res } func (me *ManualParser) ParseWords() bool { me.NewAstChildDescend(AstTypeWords) res := me.ParseWord() for me.ParseWord() { } me.AstAscend(res) return res } func (me *ManualParser) ParseDefinition() bool { me.Advance() res := me.Consume(AstTypeDefinition, TokenDef) if !res { return false } res = res && (me.ParseWord() || me.ParseOperator()) if !res { _ = me.ParseError() } res = res && me.ParseParametersNonempty() if !res { _ = me.ParseError() } me.AstAscend(res) return res } func (me *ManualParser) ParseParameter() bool { me.NewAstChildDescend(AstTypeParameter) res := me.ParseWordValue() || me.ParseOperator() || me.ParseParenthesis() || me.ParseBlock() me.AstAscend(res) return res } func (me *ManualParser) ParseParameters() bool { for me.ParseParameter() { } return true } func (me *ManualParser) ParseError() bool { me.now.NewChild(AstTypeError, me.lookahead) fmt.Printf("Parse error: at %s\n", me.lookahead) return false } func (me *ManualParser) ParseExpression() bool { return (me.ParseWordValue() || me.ParseOperator()) && me.ParseParameters() } func (me *ManualParser) ParseStatement() bool { me.NewAstChildDescend(AstTypeStatement) /* First case is for an empty expression/statement. */ res := me.ParseEOX() || me.ParseDefinition() || (me.ParseExpression() && me.ParseEOX()) || me.ParseBlock() me.AstAscend(res) return res } func (me *ManualParser) ParseEOF() bool { return me.Consume(AstTypeEox, TokenEOF) } func (me *ManualParser) ParseStatements() bool { me.NewAstChildDescend(AstTypeStatements) res := me.ParseStatement() for me.ParseStatement() { } me.AstAscend(res) return res } func (me *ManualParser) ParseProgram() bool { return me.ParseStatements() && me.ParseEOF() } func (me *Ast) DotID() string { return fmt.Sprintf("ast_%p", me) } func (me *Ast) ToGraph() *graphviz.Digraph { g := graphviz.NewDigraph("rankdir", "LR") me.Walk(func(ast *Ast) *Ast { label := ast.AstType.String() if ast.Token != nil { token := ast.Token.ShortString() label = label + "\n" + token } g.AddNode(ast.DotID(), "label", label) if ast.Parent() != nil { g.AddEdgeByName(ast.Parent().(*Ast).DotID(), ast.DotID()) } return nil }) return g } func (me *Ast) Dotty() { g := me.ToGraph() g.Dotty() } func (me *Ast) ToAscii() { me.Walk(func(ast *Ast) *Ast { depth := tree.Depth(ast) nchild:= tree.CountChildren(ast) label := ast.AstType.String() indent:= strings.Repeat("--", depth) if ast.Token != nil { token := ast.Token.ShortString() fmt.Printf("%s>%s: %s\n", indent, label, token); } else { fmt.Printf("%s>%s: (%d)\n", indent, label, nchild ); } return nil }) } /* PROGRAM -> STATEMENTS. STATEMENTS -> STATEMENT STATEMENTS | . STATEMENT -> EXPRESSION EOX | DEFINITION | BLOCK . DEFINITION -> define WORDOP WORDOPS BLOCK. WORDOPS -> WORDOP WORDOPS | . EXPRESSION -> WORDVALUE PARAMETERS. PARAMETERS -> PARAMETER PARAMETERS | . PARAMETER -> WORDVALUE | PARENTHESIS | BLOCK | operator. PARENTHESIS -> '(' EXPRESSION ')' | ot EXPRESSION ct. BLOCK -> oe STATEMENTS ce | do STATEMENTS end . WORDOP -> word | operator | a | the. WORDVALUE -> word | VALUE | a | the. VALUE -> string | number | symbol. EOX -> eol | period. ) */