Instead of in the lexer. I think this simplifies things and gives the parser more information which should hopefully make it easier to parse without shift/reduce conflicts.
375 lines
8.0 KiB
Plaintext
375 lines
8.0 KiB
Plaintext
/[ \t\n]/ { /* skip over whitespace */ }
|
|
/{/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return OPEN_CURLY
|
|
}
|
|
/}/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return CLOSE_CURLY
|
|
}
|
|
/\(/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return OPEN_PAREN
|
|
}
|
|
/\)/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return CLOSE_PAREN
|
|
}
|
|
/\[/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return OPEN_BRACK
|
|
}
|
|
/\]/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return CLOSE_BRACK
|
|
}
|
|
/if/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return IF
|
|
}
|
|
/else/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return ELSE
|
|
}
|
|
/\?:/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return ELVIS
|
|
}
|
|
/=>/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return ROCKET
|
|
}
|
|
/,/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return COMMA
|
|
}
|
|
/:/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return COLON
|
|
}
|
|
/;/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return SEMICOLON
|
|
}
|
|
/=/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return EQUALS
|
|
}
|
|
/\+/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return PLUS
|
|
}
|
|
/\-/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return MINUS
|
|
}
|
|
/\*/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return MULTIPLY
|
|
}
|
|
/\// {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return DIVIDE
|
|
}
|
|
/==/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return EQ
|
|
}
|
|
/!=/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return NEQ
|
|
}
|
|
/</ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return LT
|
|
}
|
|
/>/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return GT
|
|
}
|
|
/<=/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return LTE
|
|
}
|
|
/>=/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return GTE
|
|
}
|
|
/and/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return AND
|
|
}
|
|
/or/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return OR
|
|
}
|
|
/not/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return NOT
|
|
}
|
|
/in/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return IN
|
|
}
|
|
/\->/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return ARROW
|
|
}
|
|
/\./ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
// sanity check... these should be the same!
|
|
if x, y := lval.str, interfaces.ModuleSep; x != y {
|
|
panic(fmt.Sprintf("DOT does not match ModuleSep (%s != %s)", x, y))
|
|
}
|
|
return DOT
|
|
}
|
|
/\$/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return DOLLAR
|
|
}
|
|
/bool/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return BOOL_IDENTIFIER
|
|
}
|
|
/str/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return STR_IDENTIFIER
|
|
}
|
|
/int/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return INT_IDENTIFIER
|
|
}
|
|
/float/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return FLOAT_IDENTIFIER
|
|
}
|
|
/map/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return MAP_IDENTIFIER
|
|
}
|
|
/struct/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return STRUCT_IDENTIFIER
|
|
}
|
|
/func/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return FUNC_IDENTIFIER
|
|
}
|
|
/class/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return CLASS_IDENTIFIER
|
|
}
|
|
/include/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return INCLUDE_IDENTIFIER
|
|
}
|
|
/import/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return IMPORT_IDENTIFIER
|
|
}
|
|
/as/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return AS_IDENTIFIER
|
|
}
|
|
/variant/ {
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return VARIANT_IDENTIFIER
|
|
}
|
|
/true|false/ {
|
|
yylex.pos(lval) // our pos
|
|
s := yylex.Text()
|
|
if s == "true" {
|
|
lval.bool = true
|
|
} else if s == "false" {
|
|
lval.bool = false
|
|
} else {
|
|
// the lexer was wrong
|
|
panic(fmt.Sprintf("error lexing BOOL, got: %s", s))
|
|
}
|
|
return BOOL
|
|
}
|
|
/"(\\.|[^"])*"/
|
|
{ // This matches any number of the bracketed patterns
|
|
// that are surrounded by the two quotes on each side.
|
|
// The bracket pattern is any escaped char or something
|
|
// that is not a single quote char. See this reference:
|
|
// https://www.lysator.liu.se/c/ANSI-C-grammar-l.html#STRING-LITERAL
|
|
// old: /"[\a\b\t\n\v\f\r !#$%&'()*+,-.\/0-9:;<=>?@A-Z\[\\\]^_a-z{|}~]*"/
|
|
|
|
yylex.pos(lval) // our pos
|
|
s := yylex.Text()
|
|
|
|
if s[0:1] != "\"" || s[len(s)-1:] != "\"" {
|
|
// unhandled error
|
|
panic(fmt.Sprintf("error lexing STRING, got: %s", s))
|
|
//return ERROR // unreachable
|
|
}
|
|
|
|
lval.str = s[1:len(s)-1] // remove the two quotes
|
|
return STRING
|
|
}
|
|
/\-?[0-9]+/
|
|
{
|
|
yylex.pos(lval) // our pos
|
|
s := yylex.Text()
|
|
var err error
|
|
lval.int, err = strconv.ParseInt(s, 10, 64) // int64
|
|
if err == nil {
|
|
return INTEGER
|
|
} else if e := err.(*strconv.NumError); e.Err == strconv.ErrRange {
|
|
// this catches range errors for very large ints
|
|
lp := yylex.cast()
|
|
lp.lexerErr = &LexParseErr{
|
|
Err: ErrLexerIntegerOverflow,
|
|
Str: s,
|
|
Row: yylex.Line(),
|
|
Col: yylex.Column(),
|
|
}
|
|
return ERROR
|
|
} else {
|
|
panic(fmt.Sprintf("error lexing INTEGER, got: %v", err))
|
|
}
|
|
}
|
|
/\-?[0-9]+\.[0-9]+/
|
|
{
|
|
yylex.pos(lval) // our pos
|
|
s := yylex.Text()
|
|
var err error
|
|
lval.float, err = strconv.ParseFloat(s, 64) // float64
|
|
if err == nil {
|
|
return FLOAT
|
|
} else if e := err.(*strconv.NumError); e.Err == strconv.ErrRange {
|
|
// this catches range errors for very large floats
|
|
lp := yylex.cast()
|
|
lp.lexerErr = &LexParseErr{
|
|
Err: ErrLexerFloatOverflow,
|
|
Str: s,
|
|
Row: yylex.Line(),
|
|
Col: yylex.Column(),
|
|
}
|
|
return ERROR
|
|
} else {
|
|
panic(fmt.Sprintf("error lexing FLOAT, got: %v", err))
|
|
}
|
|
}
|
|
/[a-z]([a-z0-9_]*[a-z0-9]+)?/
|
|
{
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return IDENTIFIER
|
|
}
|
|
/[A-Z]([a-z0-9_]*[a-z0-9]+)?/
|
|
{
|
|
yylex.pos(lval) // our pos
|
|
s := yylex.Text()
|
|
lval.str = strings.ToLower(s) // uncapitalize it
|
|
return CAPITALIZED_IDENTIFIER
|
|
}
|
|
/[a-z]([a-z0-9:]*[a-z0-9]+)?/
|
|
{
|
|
yylex.pos(lval) // our pos
|
|
lval.str = yylex.Text()
|
|
return RES_IDENTIFIER
|
|
}
|
|
/#[^\n]*/
|
|
{ // this matches a (#) pound char followed by any
|
|
// number of chars that aren't the (\n) newline!
|
|
|
|
yylex.pos(lval) // our pos
|
|
s := yylex.Text()
|
|
|
|
lval.str = s[1:len(s)] // remove the leading #
|
|
//log.Printf("lang: lexer: comment: `%s`", lval.str)
|
|
//return COMMENT // skip return to avoid parsing
|
|
}
|
|
/./ {
|
|
yylex.pos(lval) // our pos
|
|
s := yylex.Text()
|
|
lp := yylex.cast()
|
|
e := ErrLexerUnrecognized
|
|
if s == "\r" { // windows!
|
|
e = ErrLexerUnrecognizedCR
|
|
}
|
|
lp.lexerErr = &LexParseErr{
|
|
Err: e,
|
|
Str: s,
|
|
Row: yylex.Line(),
|
|
Col: yylex.Column(),
|
|
}
|
|
return ERROR
|
|
}
|
|
//
|
|
|
|
// Mgmt
|
|
// Copyright (C) 2013-2023+ James Shubin and the project contributors
|
|
// Written by James Shubin <james@shubin.ca> and the project contributors
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package parser
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"github.com/purpleidea/mgmt/lang/interfaces"
|
|
)
|
|
|
|
// NOTE:
|
|
// Among rules in the same scope, the longest matching pattern takes precedence.
|
|
// In event of a tie, the first pattern wins.
|