Last active
January 10, 2018 10:35
-
-
Save macrat/78617f0dec7ae1974a32ddfc8dc40d76 to your computer and use it in GitHub Desktop.
simplexerとgoyaccの組み合わせサンプル。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%{ | |
package main | |
import ( | |
"fmt" | |
"io" | |
"os" | |
"strconv" | |
"strings" | |
"github.com/macrat/simplexer" | |
) | |
type Expression interface { | |
Calc() float64 | |
} | |
type Number float64 | |
func (n Number) Calc() float64 { | |
return float64(n) | |
} | |
type Operator struct { | |
Left Expression | |
Operator string | |
Right Expression | |
} | |
func (s Operator) Calc() float64 { | |
switch s.Operator { | |
case "+": | |
return s.Left.Calc() + s.Right.Calc() | |
case "-": | |
return s.Left.Calc() - s.Right.Calc() | |
case "*": | |
return s.Left.Calc() * s.Right.Calc() | |
case "/": | |
return s.Left.Calc() / s.Right.Calc() | |
} | |
return 0 | |
} | |
%} | |
%union{ | |
token *simplexer.Token | |
expr Expression | |
} | |
%type<expr> program expression operator | |
%token<token> NUMBER L1_OPERATOR L2_OPERATOR | |
%left L1_OPERATOR | |
%left L2_OPERATOR | |
%% | |
program | |
: expression | |
{ | |
$$ = $1 | |
yylex.(*Lexer).result = $$ | |
} | |
expression | |
: NUMBER | |
{ | |
f, _ := strconv.ParseFloat($1.Literal, 64) | |
$$ = Number(f) | |
} | |
| operator | |
{ | |
$$ = $1 | |
} | |
operator | |
: expression L2_OPERATOR expression | |
{ | |
$$ = Operator{ | |
Left: $1, | |
Operator: $2.Literal, | |
Right: $3, | |
} | |
} | |
| expression L1_OPERATOR expression | |
{ | |
$$ = Operator{ | |
Left: $1, | |
Operator: $2.Literal, | |
Right: $3, | |
} | |
} | |
%% | |
type Lexer struct { | |
lexer *simplexer.Lexer | |
lastToken *simplexer.Token | |
result Expression | |
} | |
func NewLexer(reader io.Reader) *Lexer { | |
l := simplexer.NewLexer(reader) | |
l.TokenTypes = []simplexer.TokenType{ | |
simplexer.NewRegexpTokenType(NUMBER, `-?[0-9]+(\.[0-9]+)?`), | |
simplexer.NewRegexpTokenType(L1_OPERATOR, `[-+]`), | |
simplexer.NewRegexpTokenType(L2_OPERATOR, `[*/]`), | |
} | |
return &Lexer{ lexer: l } | |
} | |
func (l *Lexer) Lex(lval *yySymType) int { | |
token, err := l.lexer.Scan() | |
if err != nil { | |
if e, ok := err.(simplexer.UnknownTokenError); ok { | |
fmt.Fprintln(os.Stderr, e.Error() + ":") | |
fmt.Fprintln(os.Stderr, l.lexer.GetLastLine()) | |
fmt.Fprintln(os.Stderr, strings.Repeat(" ", e.Position.Column) + strings.Repeat("^", len(e.Literal))) | |
} else { | |
l.Error(err.Error()) | |
} | |
os.Exit(1) | |
} | |
if token == nil { | |
return -1 | |
} | |
lval.token = token | |
l.lastToken = token | |
return int(token.Type.GetID()) | |
} | |
func (l *Lexer) Error(e string) { | |
fmt.Fprintln(os.Stderr, e + ":") | |
fmt.Fprintln(os.Stderr, l.lexer.GetLastLine()) | |
fmt.Fprintln(os.Stderr, strings.Repeat(" ", l.lastToken.Position.Column) + strings.Repeat("^", len(l.lastToken.Literal))) | |
} | |
func main() { | |
lexer := NewLexer(strings.NewReader("1 + 2 * 3 abc - 4 / 5")) | |
yyParse(lexer) | |
if lexer.result != nil { | |
fmt.Println(lexer.result) | |
fmt.Println("=", lexer.result.Calc()) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment