mirror of
https://github.com/johnkerl/miller.git
synced 2026-07-17 16:38:54 +00:00
Replace GOCC parser-generator with PGPG (#2015)
* Porting * Update some tests that depend on AST-print output * iterating on GOCC -> PGPG * iterating * iterating * iterating * iterating * iterating * Modify expout files that need only AST-print-syntax updates * iterating * iterating * iterating * iterating * iterating * iterating * iterating * iterating * iterating * iterating * iterating * sync test cases from mlr-6.17.0, except AST-prints ... * sync test cases from mlr-6.17.0, except AST-prints ... * Fix lashed emit for $* and @* * Fix --1 and ++1 chained unary ops * Emit with function callsite * test cases * dot operator * M_PI and M_E * Iterating on lashed emit cases * error-wording differences * rm some should-fail files * Fix issue with leading semicolon * trailing comma in func params; most AST-print deltas * error-wording delta * fix unset all * AST-print deltas * go mod tidy: forced `go 1.25` to `go 1.25.0` * Depend on PGPG v1.0.0 * Fix Windows CI failure * Remove cmd/experiments/dsl_parser * GOCC -> PGPG * neaten * Fix regex issue found in doc gen
This commit is contained in:
parent
40f3a72de2
commit
af1adf80ad
732 changed files with 924001 additions and 1122928 deletions
|
|
@ -1 +0,0 @@
|
|||
map \f :w<C-m>:!clear;echo Building ...; echo; build; echo; main<C-m>
|
||||
|
|
@ -1 +0,0 @@
|
|||
Running the GOCC parser-generator for the full Miller grammar takes a few minutes. That's a bit painful for experimentation; hence this.
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Setup
|
||||
us=$(basename $0)
|
||||
set -euo pipefail
|
||||
|
||||
verbose="false"
|
||||
|
||||
if [ $# -eq 1 ]; then
|
||||
bnf="$1"
|
||||
elif [ $# -eq 2 ]; then
|
||||
if [ "$1" = "-v" ]; then
|
||||
verbose="true"
|
||||
else
|
||||
echo "Usage: $0 {.bnf file}" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
bnf="$2"
|
||||
else
|
||||
echo "Usage: $0 {.bnf file}" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dir=src
|
||||
mkdir -p $dir
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Run the parser-generator
|
||||
|
||||
# Build the bin/gocc executable (use my fork for performance):
|
||||
# get github.com/goccmack/gocc
|
||||
go get github.com/johnkerl/gocc
|
||||
bingocc="$GOPATH/bin/gocc"
|
||||
|
||||
if [ ! -x "$bingocc" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f $dir/*.txt
|
||||
if [ "$verbose" = "true" ]; then
|
||||
lr1="$dir/LR1_conflicts.txt"
|
||||
# The -o specifies the package name within the autogen
|
||||
$bingocc -v -o $dir $bnf || expand -2 $lr1
|
||||
else
|
||||
$bingocc -o $dir $bnf
|
||||
fi
|
||||
|
||||
echo "Parser-autogen OK"
|
||||
|
||||
# Code-gen directories:
|
||||
# $dir/errors/
|
||||
# $dir/lexer/
|
||||
# $dir/parser/
|
||||
# $dir/token/
|
||||
# $dir/util/
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Compile the main and the parser-autogen
|
||||
|
||||
go build main.go
|
||||
echo "Compile OK"
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
module one
|
||||
|
||||
go 1.24
|
||||
|
||||
toolchain go1.24.5
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"one/src/lexer"
|
||||
"one/src/parser"
|
||||
)
|
||||
|
||||
func parseOne(input string) {
|
||||
theLexer := lexer.NewLexer([]byte(input))
|
||||
theParser := parser.NewParser()
|
||||
_, err := theParser.Parse(theLexer)
|
||||
|
||||
green := "\033[32;01m"
|
||||
red := "\033[31;01m"
|
||||
textdefault := "\033[0m"
|
||||
|
||||
if err != nil {
|
||||
//fmt.Println(err)
|
||||
fmt.Printf("%sFail%s %s\n", red, textdefault, input)
|
||||
} else {
|
||||
fmt.Printf("%sOK%s %s\n", green, textdefault, input)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) == 1 {
|
||||
|
||||
fmt.Println("EXPECT OK")
|
||||
goods := []string{
|
||||
"",
|
||||
";",
|
||||
";;",
|
||||
"x",
|
||||
"x;x",
|
||||
"x;x;x",
|
||||
"x;x;x;x",
|
||||
"x;",
|
||||
"x;;",
|
||||
";x",
|
||||
";;x",
|
||||
"x ; {}",
|
||||
"{} ; x",
|
||||
"{} x",
|
||||
"{ x }",
|
||||
"{ x; x }",
|
||||
"x; { x; x }",
|
||||
"{ x; x } x",
|
||||
"{ x; x } ; x",
|
||||
"{};{}",
|
||||
"{} {}",
|
||||
}
|
||||
for _, input := range goods {
|
||||
parseOne(input)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println("EXPECT FAIL")
|
||||
bads := []string{
|
||||
"x x",
|
||||
"x {}",
|
||||
}
|
||||
for _, input := range bads {
|
||||
parseOne(input)
|
||||
}
|
||||
|
||||
} else {
|
||||
for _, arg := range os.Args[1:] {
|
||||
parseOne(arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Usage: $0 {.bnf file} [expressions]" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
bnf="$1"
|
||||
shift
|
||||
./build $bnf && echo && main "$@"
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
!whitespace : ' ' | '\t' | '\n' | '\r' ;
|
||||
|
||||
Root
|
||||
: StatementBlock
|
||||
;
|
||||
|
||||
StatementBlock
|
||||
: ";"
|
||||
| BracelessStatement
|
||||
| ";" BracelessStatement
|
||||
| BracefulStatement
|
||||
| BracelessStatement ";" NonEmptyStatementBlock
|
||||
| BracefulStatement NonEmptyStatementBlock
|
||||
;
|
||||
|
||||
NonEmptyStatementBlock
|
||||
: BracelessStatement
|
||||
| BracelessStatement ";" StatementBlock
|
||||
| BracefulStatement
|
||||
| BracefulStatement StatementBlock
|
||||
;
|
||||
|
||||
BracelessStatement
|
||||
: "x"
|
||||
;
|
||||
|
||||
BracefulStatement
|
||||
: "{" StatementBlock "}"
|
||||
;
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
!whitespace : ' ' | '\t' | '\n' | '\r' ;
|
||||
|
||||
Root
|
||||
: StatementBlock
|
||||
;
|
||||
|
||||
StatementBlock
|
||||
: empty
|
||||
| ";" StatementBlock
|
||||
| BracelessStatement
|
||||
| BracefulStatement
|
||||
| BracelessStatement ";" StatementBlock
|
||||
| BracefulStatement ";" BracefulStatement
|
||||
| BracefulStatement BracefulStatement
|
||||
| BracefulStatement ";" BracelessStatement
|
||||
| BracefulStatement BracelessStatement
|
||||
;
|
||||
|
||||
BracelessStatement
|
||||
: "x"
|
||||
;
|
||||
|
||||
BracefulStatement
|
||||
: "{" StatementBlock "}"
|
||||
;
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
// ================================================================
|
||||
// LEXER
|
||||
|
||||
_string_literal_element
|
||||
: 'A'-'Z' | 'a'-'z' | '0'-'9'
|
||||
| ' ' | '!' | '#' | '$' | '%' | '&' | '\'' | '\\'
|
||||
| '(' | ')' | '*' | '+' | ',' | '-' | '.' | '/'
|
||||
| ':' | ';' | '<' | '=' | '>' | '?' | '@' | '['
|
||||
| ']' | '^' | '_' | '`' | '{' | '|' | '}' | '~'
|
||||
| ( '\\' '"' )
|
||||
| ( '\\' '[' )
|
||||
| ( '\\' ']' )
|
||||
| ( '\\' 'b' )
|
||||
| ( '\\' 'f' )
|
||||
| ( '\\' 'n' )
|
||||
| ( '\\' 'r' )
|
||||
| ( '\\' 't' )
|
||||
| ( '\\' '\\' )
|
||||
| ( '\\' '0' )
|
||||
| ( '\\' 'x' )
|
||||
| '\u0100'-'\U0010FFFF'
|
||||
;
|
||||
string_literal : '"' {_string_literal_element} '"' ;
|
||||
|
||||
// ================================================================
|
||||
// IMPORT
|
||||
|
||||
<< import "two/src/dsl" >>
|
||||
|
||||
// ================================================================
|
||||
// PARSER
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
Root
|
||||
: StringLiteral
|
||||
<< dsl.NewASTWithErrorReturn($0) >>
|
||||
;
|
||||
|
||||
StringLiteral
|
||||
: string_literal
|
||||
<< dsl.NewASTNodeStripDoubleQuotePair($0, dsl.NodeTypeStringLiteral) >>
|
||||
;
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Setup
|
||||
us=$(basename $0)
|
||||
set -euo pipefail
|
||||
|
||||
verbose="false"
|
||||
|
||||
if [ $# -eq 1 ]; then
|
||||
bnf="$1"
|
||||
elif [ $# -eq 2 ]; then
|
||||
if [ "$1" = "-v" ]; then
|
||||
verbose="true"
|
||||
else
|
||||
echo "Usage: $0 {.bnf file}" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
bnf="$2"
|
||||
else
|
||||
echo "Usage: $0 {.bnf file}" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dir=src/parsing
|
||||
mkdir -p $dir
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Run the parser-generator
|
||||
|
||||
# Build the bin/gocc executable (use my fork for performance):
|
||||
# go get github.com/goccmack/gocc
|
||||
go get github.com/johnkerl/gocc
|
||||
bingocc="$GOPATH/bin/gocc"
|
||||
if [ ! -x "$bingocc" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f $dir/*.txt
|
||||
if [ "$verbose" = "true" ]; then
|
||||
lr1="$dir/LR1_conflicts.txt"
|
||||
# The -o specifies the package name within the autogen
|
||||
$bingocc -v -o $dir $bnf || expand -2 $lr1
|
||||
else
|
||||
$bingocc -o $dir $bnf
|
||||
fi
|
||||
|
||||
echo "Parser-autogen OK"
|
||||
|
||||
# Code-gen directories:
|
||||
# $dir/errors/
|
||||
# $dir/lexer/
|
||||
# $dir/parser/
|
||||
# $dir/token/
|
||||
# $dir/util/
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Override GOCC codegen with customized error handling
|
||||
cp ../../../go/src/parsing/errors.go.template src/parsing/errors/errors.go
|
||||
sed -i .bak 's:miller/src:two/src:' src/parsing/errors/errors.go
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Copy AST files from the main Miller tree
|
||||
|
||||
rm -rf ./src/lib/
|
||||
rm -rf ./src/dsl/
|
||||
|
||||
mkdir -p ./src/lib/
|
||||
mkdir -p ./src/dsl/
|
||||
|
||||
cp ../../../go/src/lib/*.go ./src/lib/
|
||||
cp ../../../go/src/dsl/ast*.go ./src/dsl/
|
||||
|
||||
# Different path to autogen between main Miller tree and here
|
||||
sed -i .bak 's:miller/src:two/src:' src/dsl/ast*go
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Compile the main and the parser-autogen
|
||||
|
||||
go build main.go
|
||||
echo "Compile OK"
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
// ================================================================
|
||||
// LEXER
|
||||
|
||||
!whitespace : ' ' | '\t' | '\n' | '\r' ;
|
||||
!comment : '#' {.} '\n' ;
|
||||
|
||||
_letter : 'a'-'z' | 'A'-'Z' ;
|
||||
_decdig : '0'-'9' ;
|
||||
_idchar : _letter | _decdig | '_' ;
|
||||
|
||||
emit : 'e' 'm' 'i' 't' ;
|
||||
|
||||
// ================================================================
|
||||
// IMPORT
|
||||
|
||||
<< import "miller/src/dsl" >>
|
||||
|
||||
// ================================================================
|
||||
// PARSER
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
Root
|
||||
: EmitStatement
|
||||
<< dsl.NewASTWithErrorReturn($0) >>
|
||||
;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Examples:
|
||||
// emit @a
|
||||
// emit (@a, @b)
|
||||
// emit @a, "x", "y"
|
||||
// emit (@a, @b), "x", "y"
|
||||
// First argument (single or in parentheses) must be non-indexed
|
||||
// oosvar/localvar/fieldname, so we can use their names as keys in the emitted
|
||||
// record. These restrictions are enforced in the CST logic, to keep this
|
||||
// parser/AST logic simpler.
|
||||
EmitStatement
|
||||
: emit Rvalue
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeEmitStatement,
|
||||
[]interface{}{
|
||||
$1,
|
||||
}
|
||||
) >>
|
||||
|
||||
| emit Rvalue "," EmitArgs
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeEmitStatement,
|
||||
[]interface{}{
|
||||
$1,
|
||||
$3,
|
||||
}
|
||||
) >>
|
||||
|
||||
| emit "(" EmitArgs ")"
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeEmitStatement,
|
||||
[]interface{}{
|
||||
$2,
|
||||
}
|
||||
) >>
|
||||
|
||||
| emit "(" EmitArgs ")" "," EmitArgs
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeEmitStatement,
|
||||
[]interface{}{
|
||||
$2,
|
||||
$5,
|
||||
}
|
||||
) >>
|
||||
;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
EmitArgs
|
||||
: Rvalue
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeFunctionCallsite,
|
||||
[]interface{}{
|
||||
$0,
|
||||
}
|
||||
) >>
|
||||
|
||||
| Rvalue "," EmitArgs
|
||||
<< dsl.WithChildPrepended(
|
||||
$2,
|
||||
$0,
|
||||
) >>
|
||||
;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
FcnArgs
|
||||
: Rvalue
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeFunctionCallsite,
|
||||
[]interface{}{
|
||||
$0,
|
||||
}
|
||||
) >>
|
||||
|
||||
// Allow trailing final comma, especially for multiline statements
|
||||
| Rvalue ","
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeFunctionCallsite,
|
||||
[]interface{}{
|
||||
$0,
|
||||
}
|
||||
) >>
|
||||
|
||||
// Allow trailing final comma, especially for multiline statements
|
||||
| Rvalue "," FcnArgs
|
||||
<< dsl.WithChildPrepended(
|
||||
$2,
|
||||
$0,
|
||||
) >>
|
||||
;
|
||||
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
Rvalue
|
||||
: Literal
|
||||
| "(" Literal ")"
|
||||
;
|
||||
|
||||
Literal
|
||||
: "x" << dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeStringLiteral,
|
||||
[]interface{}{},
|
||||
) >>
|
||||
| "y" << dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeStringLiteral,
|
||||
[]interface{}{},
|
||||
) >>
|
||||
| "z" << dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeStringLiteral,
|
||||
[]interface{}{},
|
||||
) >>
|
||||
;
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
// ================================================================
|
||||
// LEXER
|
||||
|
||||
!whitespace : ' ' | '\t' | '\n' | '\r' ;
|
||||
!comment : '#' {.} '\n' ;
|
||||
|
||||
_letter : 'a'-'z' | 'A'-'Z' ;
|
||||
_decdig : '0'-'9' ;
|
||||
_idchar : _letter | _decdig | '_' ;
|
||||
|
||||
emit : 'e' 'm' 'i' 't' ;
|
||||
|
||||
// ================================================================
|
||||
// IMPORT
|
||||
|
||||
<< import "two/src/dsl" >>
|
||||
|
||||
// ================================================================
|
||||
// PARSER
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
Root
|
||||
: EmitStatement
|
||||
<< dsl.NewASTWithErrorReturn($0) >>
|
||||
;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Examples:
|
||||
// emit @a
|
||||
// emit (@a, @b)
|
||||
// emit @a, "x", "y"
|
||||
// emit (@a, @b), "x", "y"
|
||||
// First argument (single or in parentheses) must be non-indexed
|
||||
// oosvar/localvar/fieldname, so we can use their names as keys in the emitted
|
||||
// record. These restrictions are enforced in the CST logic, to keep this
|
||||
// parser/AST logic simpler.
|
||||
|
||||
EmitStatement
|
||||
: emit Emittable
|
||||
<<
|
||||
dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeEmitStatement,
|
||||
[]interface{}{
|
||||
$1,
|
||||
},
|
||||
)
|
||||
>>
|
||||
|
||||
| emit "(" EmittableList ")"
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeEmitStatement,
|
||||
[]interface{}{
|
||||
$2,
|
||||
},
|
||||
) >>
|
||||
|
||||
| emit Emittable "," EmitKeys
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeEmitStatement,
|
||||
[]interface{}{
|
||||
$1,
|
||||
$3,
|
||||
},
|
||||
) >>
|
||||
|
||||
| emit "(" EmittableList ")" "," EmitKeys
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeEmitStatement,
|
||||
[]interface{}{
|
||||
$2,
|
||||
$5,
|
||||
},
|
||||
) >>
|
||||
;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
EmittableList
|
||||
: Emittable
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeEmittableList,
|
||||
[]interface{}{
|
||||
$0,
|
||||
}
|
||||
) >>
|
||||
|
||||
// Allow trailing final comma, especially for multiline statements
|
||||
| Emittable "," EmittableList
|
||||
<< dsl.WithChildPrepended(
|
||||
$2,
|
||||
$0,
|
||||
) >>
|
||||
;
|
||||
|
||||
Emittable
|
||||
: Literal
|
||||
;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
EmitKeys
|
||||
|
||||
: Rvalue
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeEmitKeys,
|
||||
[]interface{}{
|
||||
$0,
|
||||
},
|
||||
) >>
|
||||
|
||||
| Rvalue "," EmitKeys
|
||||
<< dsl.WithChildPrepended(
|
||||
$2,
|
||||
$0,
|
||||
) >>
|
||||
;
|
||||
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
Rvalue
|
||||
: Literal
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeStringLiteral,
|
||||
[]interface{}{
|
||||
$0,
|
||||
},
|
||||
) >>
|
||||
| "(" Literal ")"
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeStringLiteral,
|
||||
[]interface{}{
|
||||
$1,
|
||||
},
|
||||
) >>
|
||||
| "[" Literal "]"
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeStringLiteral,
|
||||
[]interface{}{
|
||||
$1,
|
||||
},
|
||||
)) >>
|
||||
| "[" Literal "," Literal "]"
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeStringLiteral,
|
||||
[]interface{}{
|
||||
$1,
|
||||
$2,
|
||||
},
|
||||
)) >>
|
||||
;
|
||||
|
||||
Literal
|
||||
: "x" << dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeStringLiteral,
|
||||
[]interface{}{},
|
||||
) >>
|
||||
| "y" << dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeStringLiteral,
|
||||
[]interface{}{},
|
||||
) >>
|
||||
| "z" << dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeStringLiteral,
|
||||
[]interface{}{},
|
||||
)) >>
|
||||
;
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
module two
|
||||
|
||||
go 1.24
|
||||
|
||||
toolchain go1.24.5
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"two/src/dsl"
|
||||
|
||||
"two/src/parsing/lexer"
|
||||
"two/src/parsing/parser"
|
||||
)
|
||||
|
||||
const GREEN = "\033[32;01m"
|
||||
const RED = "\033[31;01m"
|
||||
const TEXTDEFAULT = "\033[0m"
|
||||
|
||||
func parseOne(input string, printError bool) bool {
|
||||
theLexer := lexer.NewLexer([]byte(input))
|
||||
theParser := parser.NewParser()
|
||||
iast, err := theParser.Parse(theLexer)
|
||||
if err == nil {
|
||||
fmt.Printf("%sOK%s %s\n", GREEN, TEXTDEFAULT, input)
|
||||
iast.(*dsl.AST).Print()
|
||||
fmt.Println()
|
||||
return true
|
||||
}
|
||||
if printError {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Printf("%sFail%s %s\n", RED, TEXTDEFAULT, input)
|
||||
fmt.Println()
|
||||
return false
|
||||
}
|
||||
|
||||
func main() {
|
||||
printError := false
|
||||
args := os.Args[1:] // os.Args[0] is program name in Go
|
||||
if len(args) >= 1 && args[0] == "-v" {
|
||||
printError = true
|
||||
args = args[1:]
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
ok := true
|
||||
|
||||
fmt.Println("----------------------------------------------------------------")
|
||||
fmt.Println("EXPECT OK")
|
||||
goods := []string{
|
||||
"",
|
||||
";",
|
||||
";;",
|
||||
";;;",
|
||||
"x",
|
||||
"x;x",
|
||||
"x;x;x",
|
||||
"x;x;x;x",
|
||||
"x;",
|
||||
"x;;",
|
||||
";x",
|
||||
";;x",
|
||||
"x ; {}",
|
||||
"{} ; x",
|
||||
"{} x",
|
||||
"{ x }",
|
||||
"{ x; x }",
|
||||
"x; { x; x }",
|
||||
"{ x; x } x",
|
||||
"{ x; x } ; x",
|
||||
"{};{}",
|
||||
"{} {}",
|
||||
"{} {};",
|
||||
"{};{};",
|
||||
"{} {} {}",
|
||||
"{};{}; {}",
|
||||
"{} ; ; {}",
|
||||
"x; x;",
|
||||
"{x; x;}",
|
||||
"{x; x;} x",
|
||||
"{x; x;} x;",
|
||||
"{x; x;} {};",
|
||||
"{} x; {}",
|
||||
"{} x; {};",
|
||||
"{} x; x; {}",
|
||||
"{} x; x; {};",
|
||||
"{} x; x; x; {}",
|
||||
"{} x; x; x; {};",
|
||||
"{} {} ;;; {;;} x; x; x; x; x; {}",
|
||||
}
|
||||
for _, input := range goods {
|
||||
if parseOne(input, printError) == false {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println("----------------------------------------------------------------")
|
||||
fmt.Println("EXPECT FAIL")
|
||||
bads := []string{
|
||||
"x x",
|
||||
"x {}",
|
||||
}
|
||||
for _, input := range bads {
|
||||
if parseOne(input, printError) == true {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println("----------------------------------------------------------------")
|
||||
if ok {
|
||||
fmt.Printf("%sALL AS EXPECTED%s\n", GREEN, TEXTDEFAULT)
|
||||
} else {
|
||||
fmt.Printf("%sNOT ALL AS EXPECTED%s\n", RED, TEXTDEFAULT)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
} else {
|
||||
for _, arg := range args {
|
||||
parseOne(arg, printError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ~/.ctags needs to look like
|
||||
# --langdef=Go
|
||||
# --langmap=Go:.go
|
||||
# --regex-Go=/func([ \t]+\([^)]+\))?[ \t]+([a-zA-Z0-9_]+)/\2/d,func/
|
||||
# --regex-Go=/var[ \t]+([a-zA-Z_][a-zA-Z0-9_]+)/\1/d,var/
|
||||
# --regex-Go=/type[ \t]+([a-zA-Z_][a-zA-Z0-9_]+)/\1/d,type/
|
||||
#
|
||||
# See also https://stackoverflow.com/questions/8204367/ctag-database-for-go
|
||||
|
||||
ctags -f gosource.tags -R `pwd`
|
||||
mv gosource.tags tags
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Usage: $0 {.bnf file} [expressions]" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
bnf="$1"
|
||||
shift
|
||||
./build $bnf && echo && main "$@"
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
// ================================================================
|
||||
// LEXER
|
||||
|
||||
!whitespace : ' ' | '\t' | '\n' | '\r' ;
|
||||
|
||||
// ================================================================
|
||||
// IMPORT
|
||||
|
||||
<< import "two/src/dsl" >>
|
||||
|
||||
// ================================================================
|
||||
// PARSER
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
Root
|
||||
: StatementBlock
|
||||
<< dsl.NewASTWithErrorReturn($0) >>
|
||||
;
|
||||
|
||||
// ================================================================
|
||||
StatementBlock
|
||||
|
||||
// Empty statement. This allows for 'mlr put ""', as well as repeated semicolons.
|
||||
: empty
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeStatementBlock,
|
||||
[]interface{}{},
|
||||
) >>
|
||||
|
||||
| NonEmptyStatementBlock
|
||||
<< dsl.WithErrorReturn($0) >>
|
||||
;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
NonEmptyStatementBlock
|
||||
// ---------------------- Terminal rules
|
||||
|
||||
// Things not ending in a curly brace, like assignments -- and also do-while.
|
||||
: BracelessStatement
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeStatementBlock,
|
||||
[]interface{}{
|
||||
$0,
|
||||
},
|
||||
) >>
|
||||
|
||||
// Things ending in a curly brace, like for/do/while, begin/end, and pattern-action blocks
|
||||
| BracefulStatement
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeStatementBlock,
|
||||
[]interface{}{
|
||||
$0,
|
||||
},
|
||||
) >>
|
||||
|
||||
// ---------------------- Recursive rules
|
||||
|
||||
// So statements can start with a semicolon
|
||||
| ";" StatementBlock
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
|
||||
// Normal case for sequential statements like '$x=1; $y=2'
|
||||
| BracelessStatement ";" StatementBlock
|
||||
<<dsl.WithChildPrepended($2, $0) >>
|
||||
|
||||
// For 'begin {...} ; $x=1'
|
||||
| BracefulStatement ";" StatementBlock
|
||||
<<dsl.WithChildPrepended($2, $0) >>
|
||||
|
||||
// These are for things like 'begin {...} begin {...} ...' -- where people
|
||||
// shouldn't have to put semicolons after the closing curly braces.
|
||||
//
|
||||
// We get LR-1 conflicts with the following, so we need a pair of more
|
||||
// explicit lookahead-by-more production rules instead. (By using ternaries
|
||||
// we are effectively getting lookahead-by-two.)
|
||||
//
|
||||
// | BracefulStatement StatementBlock
|
||||
// <<dsl.WithChildPrepended($1, $0) >>
|
||||
|
||||
// E.g. 'begin {...} begin {...} $x=1'
|
||||
| BracefulStatement BracefulStatement StatementBlock
|
||||
<<dsl.WithTwoChildrenPreprended($2, $0, $1) >>
|
||||
|
||||
// E.g. 'begin {...} $x=1'
|
||||
| BracefulStatement BracelessStatement
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeStatementBlock,
|
||||
[]interface{}{
|
||||
$0,
|
||||
$1,
|
||||
},
|
||||
) >>
|
||||
|
||||
// E.g. 'begin {...} $x=1 ;'
|
||||
| BracefulStatement BracelessStatement ";"
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeStatementBlock,
|
||||
[]interface{}{
|
||||
$0,
|
||||
$1,
|
||||
},
|
||||
) >>
|
||||
|
||||
| BracefulStatement BracelessStatement ";" NonEmptyStatementBlock
|
||||
<<dsl.WithTwoChildrenPreprended($3, $0, $1) >>
|
||||
;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
BracelessStatement
|
||||
: "x"
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeBareBoolean,
|
||||
[]interface{}{},
|
||||
) >>
|
||||
;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
BracefulStatement
|
||||
: "{" StatementBlock "}"
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
;
|
||||
|
|
@ -1,302 +0,0 @@
|
|||
// ================================================================
|
||||
// LEXER
|
||||
|
||||
!whitespace : ' ' | '\t' | '\n' | '\r' ;
|
||||
!comment : '#' {.} '\n' ;
|
||||
|
||||
_letter : 'a'-'z' | 'A'-'Z' ;
|
||||
_decdig : '0'-'9' ;
|
||||
_idchar : _letter | _decdig | '_' ;
|
||||
|
||||
emitf : 'e' 'm' 'i' 't' 'f';
|
||||
emit : 'e' 'm' 'i' 't' ;
|
||||
|
||||
stdout : 's' 't' 'd' 'o' 'u' 't' ;
|
||||
stderr : 's' 't' 'd' 'e' 'r' 'r' ;
|
||||
|
||||
// ================================================================
|
||||
// IMPORT
|
||||
|
||||
<< import "two/src/dsl" >>
|
||||
|
||||
// ================================================================
|
||||
// PARSER
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
Root
|
||||
: EmitFStatement
|
||||
<< dsl.NewASTWithErrorReturn($0) >>
|
||||
| EmitStatement
|
||||
<< dsl.NewASTWithErrorReturn($0) >>
|
||||
;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
Redirector
|
||||
: ">" RedirectTarget
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeRedirectWrite,
|
||||
[]interface{}{
|
||||
$1,
|
||||
}
|
||||
) >>
|
||||
| ">>" RedirectTarget
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeRedirectAppend,
|
||||
[]interface{}{
|
||||
$1,
|
||||
}
|
||||
) >>
|
||||
| "|" RedirectTarget
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeRedirectPipe,
|
||||
[]interface{}{
|
||||
$1,
|
||||
}
|
||||
) >>
|
||||
;
|
||||
|
||||
RedirectTarget
|
||||
: stdout
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeRedirectTargetStdout,
|
||||
[]interface{}{}
|
||||
) >>
|
||||
| stderr
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeRedirectTargetStderr,
|
||||
[]interface{}{}
|
||||
) >>
|
||||
| Rvalue
|
||||
;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
EmitStatement
|
||||
: emit Emittable
|
||||
<<
|
||||
dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeEmitStatement,
|
||||
[]interface{}{
|
||||
$1,
|
||||
dsl.NewASTNodeTerminal(nil, dsl.NodeTypeNoOp),
|
||||
dsl.NewASTNodeTerminal(nil, dsl.NodeTypeNoOp),
|
||||
}
|
||||
))
|
||||
>>
|
||||
|
||||
| emit Redirector "," Emittable
|
||||
<<
|
||||
dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeEmitStatement,
|
||||
[]interface{}{
|
||||
$3,
|
||||
dsl.NewASTNodeTerminal(nil, dsl.NodeTypeNoOp),
|
||||
$1,
|
||||
}
|
||||
))
|
||||
>>
|
||||
|
||||
| emit "(" EmittableList ")"
|
||||
<<
|
||||
dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeEmitStatement,
|
||||
[]interface{}{
|
||||
$2,
|
||||
dsl.NewASTNodeTerminal(nil, dsl.NodeTypeNoOp),
|
||||
dsl.NewASTNodeTerminal(nil, dsl.NodeTypeNoOp),
|
||||
}
|
||||
))
|
||||
>>
|
||||
|
||||
| emit Redirector "," "(" EmittableList ")"
|
||||
<<
|
||||
dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeEmitStatement,
|
||||
[]interface{}{
|
||||
$4,
|
||||
dsl.NewASTNodeTerminal(nil, dsl.NodeTypeNoOp),
|
||||
$1,
|
||||
}
|
||||
))
|
||||
>>
|
||||
|
||||
| emit Emittable "," EmitKeys
|
||||
<<
|
||||
dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeEmitStatement,
|
||||
[]interface{}{
|
||||
$1,
|
||||
$3,
|
||||
dsl.NewASTNodeTerminal(nil, dsl.NodeTypeNoOp),
|
||||
}
|
||||
))
|
||||
>>
|
||||
|
||||
| emit Redirector "," Emittable "," EmitKeys
|
||||
<<
|
||||
dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeEmitStatement,
|
||||
[]interface{}{
|
||||
$3,
|
||||
$5,
|
||||
$1,
|
||||
}
|
||||
))
|
||||
>>
|
||||
|
||||
| emit "(" EmittableList ")" "," EmitKeys
|
||||
<<
|
||||
dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeEmitStatement,
|
||||
[]interface{}{
|
||||
$2,
|
||||
$5,
|
||||
dsl.NewASTNodeTerminal(nil, dsl.NodeTypeNoOp),
|
||||
}
|
||||
))
|
||||
>>
|
||||
|
||||
| emit Redirector "," "(" EmittableList ")" "," EmitKeys
|
||||
<<
|
||||
dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeEmitStatement,
|
||||
[]interface{}{
|
||||
$4,
|
||||
$7,
|
||||
$1,
|
||||
}
|
||||
))
|
||||
>>
|
||||
;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Examples:
|
||||
// emitf @a
|
||||
// emitf @a, b, $c
|
||||
// Each argument must be a non-indexed oosvar/localvar/fieldname, so we can use
|
||||
// their names as keys in the emitted record.
|
||||
EmitFStatement
|
||||
|
||||
: emitf EmittableList
|
||||
<< dsl.WithChildrenAdopted(
|
||||
dsl.NewASTNodeTerminal(
|
||||
$0,
|
||||
dsl.NodeTypeEmitFStatement,
|
||||
),
|
||||
$1,
|
||||
) >>
|
||||
|
||||
| emitf Redirector "," EmittableList
|
||||
// TODO
|
||||
|
||||
;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
EmittableList
|
||||
|
||||
: Emittable
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeEmittableList,
|
||||
[]interface{}{
|
||||
$0,
|
||||
}
|
||||
) >>
|
||||
|
||||
// Allow trailing final comma, especially for multiline statements
|
||||
| Emittable "," EmittableList
|
||||
<< dsl.WithChildPrepended(
|
||||
$2,
|
||||
$0,
|
||||
) >>
|
||||
;
|
||||
|
||||
Emittable
|
||||
: Literal
|
||||
;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
EmitKeys
|
||||
|
||||
: Rvalue
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeEmitKeys,
|
||||
[]interface{}{
|
||||
$0,
|
||||
}
|
||||
) >>
|
||||
|
||||
| Rvalue "," EmitKeys
|
||||
<< dsl.WithChildPrepended(
|
||||
$2,
|
||||
$0,
|
||||
) >>
|
||||
;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
Rvalue
|
||||
: Literal
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeStringLiteral,
|
||||
[]interface{}{
|
||||
$0,
|
||||
}
|
||||
) >>
|
||||
| "(" Literal ")"
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeStringLiteral,
|
||||
[]interface{}{
|
||||
$1,
|
||||
}
|
||||
) >>
|
||||
| "[" Literal "]"
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeStringLiteral,
|
||||
[]interface{}{
|
||||
$1,
|
||||
}
|
||||
) >>
|
||||
| "[" Literal "," Literal "]"
|
||||
<< dsl.NewASTNodeWithErrorReturn(
|
||||
nil,
|
||||
dsl.NodeTypeStringLiteral,
|
||||
[]interface{}{
|
||||
$1,
|
||||
$2,
|
||||
}
|
||||
) >>
|
||||
;
|
||||
|
||||
Literal
|
||||
: "x" << dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeStringLiteral,
|
||||
[]interface{}{}
|
||||
) >>
|
||||
| "y" << dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeStringLiteral,
|
||||
[]interface{}{}
|
||||
) >>
|
||||
| "z" << dsl.NewASTNodeWithErrorReturn(
|
||||
$0,
|
||||
dsl.NodeTypeStringLiteral,
|
||||
[]interface{}{}
|
||||
) >>
|
||||
;
|
||||
Loading…
Add table
Add a link
Reference in a new issue