/[ \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 DEFAULT } /=>/ { 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() // sanity check... these should be the same! if x, y := lval.str, interfaces.ClassSep; x != y { panic(fmt.Sprintf("COLON does not match ClassSep (%s != %s)", x, y)) } 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 } /\*/ { // This is used as the multiplication symbol, but also // (for now) the bare import feature, eg: `import as *`. yylex.pos(lval) // our pos lval.str = yylex.Text() // sanity check... these should be the same! if x, y := lval.str, interfaces.BareSymbol; x != y { panic(fmt.Sprintf("MULTIPLY does not match BareSymbol (%s != %s)", x, y)) } 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 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 } /panic/ { yylex.pos(lval) // our pos lval.str = yylex.Text() return PANIC_IDENTIFIER } /"(\\.|[^"])*"/ { // 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 } /#[^\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-2024+ James Shubin and the project contributors // Written by James Shubin 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 . // // Additional permission under GNU GPL version 3 section 7 // // If you modify this program, or any covered work, by linking or combining it // with embedded mcl code and modules (and that the embedded mcl code and // modules which link with this program, contain a copy of their source code in // the authoritative form) containing parts covered by the terms of any other // license, the licensors of this program grant you additional permission to // convey the resulting work. Furthermore, the licensors of this program grant // the original author, James Shubin, additional permission to update this // additional permission if he deems it necessary to achieve the goals of this // additional permission. 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.