├── .coveragerc ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── gopygo ├── __init__.py ├── ast.py ├── enums.py ├── exceptions.py ├── parser.py └── unparser.py ├── setup.cfg ├── setup.py └── tests └── test_parser.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = gopygo 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | 134 | # pytype static type analyzer 135 | .pytype/ 136 | 137 | # Cython debug symbols 138 | cython_debug/ 139 | 140 | # Editors 141 | .vscode/ 142 | 143 | # Ignore the file that are created for development 144 | *dev.* 145 | 146 | # Go related 147 | go.mod 148 | go.sum 149 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | branches: 3 | only: 4 | - master 5 | - /(\d+\.?)+/ 6 | services: 7 | - docker 8 | install: 9 | - travis_fold start "Install.Pip.Package" && pip3 install -e .[dev] && pip3 show gopygo && travis_fold end "Install.Pip.Package" 10 | script: 11 | - stty cols 120 12 | - travis_fold start "Unit.Tests" && coverage run --parallel -m pytest tests -v && travis_fold end "Unit.Tests" 13 | - flake8 14 | after_success: 15 | - coverage combine 16 | - coverage report -m 17 | - codecov 18 | deploy: 19 | edge: true # use latest v2 deploy 20 | provider: pypi 21 | # skip_cleanup: true 22 | on: 23 | tags: true 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 UP9 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gopygo 2 | 3 | Pure Python Go parser, AST and unparser library 4 | 5 | ## Installation 6 | 7 | ```shell 8 | $ pip3 install gopygo 9 | ``` 10 | 11 | ## Usage 12 | 13 | Parse and unparse the Go *Hello, World!* example: 14 | 15 | ```python 16 | >>> import gopygo 17 | >>> program = """ 18 | ... package main 19 | ... 20 | ... import "fmt" 21 | ... 22 | ... func main() { 23 | ... fmt.Println("Hello, World!") 24 | ... } 25 | ... """ 26 | >>> program = program.lstrip() 27 | >>> tree = gopygo.parse(program) 28 | >>> tree 29 | 30 | >>> tree.__dict__ 31 | {'name': , 'imports': [], 'decls': []} 32 | >>> text = gopygo.unparse(tree) 33 | >>> print(text) 34 | package main 35 | 36 | import "fmt" 37 | 38 | func main() { 39 | fmt.Println("Hello, World!") 40 | } 41 | 42 | >>> assert program == text 43 | ``` 44 | 45 | ## Roadmap 46 | 47 | Implement the AST nodes specified in [here](https://golang.org/pkg/go/ast/) and the parser, unparser libraries accordingly. 48 | 49 | Tokens are defined in [here](https://golang.org/pkg/go/token/#Token). 50 | 51 | ## Development 52 | 53 | Here is an example access Go AST using original `go/parser` and `go/ast` packages: 54 | 55 | ```go 56 | package main 57 | 58 | import "fmt" 59 | import "go/token" 60 | import "go/parser" 61 | import "go/ast" 62 | 63 | func main() { 64 | src := ` 65 | package main 66 | 67 | import "fmt" 68 | 69 | func main() { 70 | fmt.Println("Hello, World!") 71 | } 72 | ` 73 | x, err := parser.ParseFile(token.NewFileSet(), "", src, 0) 74 | fmt.Println(x.Decls[1].(*ast.FuncDecl).Body.List[0].(*ast.ExprStmt).X.(*ast.CallExpr).Args[0].(*ast.BasicLit).Value) 75 | fmt.Println(err) 76 | fmt.Printf("type: %T\n", x.Decls[1].(*ast.FuncDecl).Body.List[0].(*ast.ExprStmt).X.(*ast.CallExpr).Args[0].(*ast.BasicLit).Value) 77 | } 78 | ``` 79 | 80 | Gives this output: 81 | 82 | ```text 83 | "Hello, World!" 84 | 85 | type: string 86 | ``` 87 | -------------------------------------------------------------------------------- /gopygo/__init__.py: -------------------------------------------------------------------------------- 1 | from gopygo.parser import parse 2 | from gopygo.unparser import unparse 3 | 4 | __version__ = '0.3.2' 5 | -------------------------------------------------------------------------------- /gopygo/ast.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | .. module:: __init__ 6 | :synopsis: Go AST classes. 7 | """ 8 | 9 | from typing import List, Union 10 | 11 | 12 | class Ident(): 13 | def __init__(self, name: str): 14 | self.name = name 15 | 16 | 17 | class BasicLit(): 18 | def __init__(self, kind, value: Union[str, None]): 19 | self.kind = kind 20 | self.value = value 21 | 22 | 23 | class CompositeLit(): 24 | def __init__(self, _type, elts: list, incomplete: bool): 25 | self.type = _type 26 | self.elts = elts 27 | self.incomplete = incomplete 28 | 29 | 30 | class GenDecl(): 31 | def __init__(self, tok: str, specs: list): 32 | self.tok = tok 33 | self.specs = specs 34 | 35 | 36 | class DeclStmt(): 37 | def __init__(self, decl: GenDecl): 38 | self.decl = decl 39 | 40 | 41 | class Package(): 42 | def __init__(self, name: str): 43 | self.name = name 44 | 45 | 46 | class File(): 47 | def __init__(self, name: Package): 48 | self.name = name 49 | self.imports = [] # unused, use GenDecl in self.decls instead 50 | self.decls = [] 51 | 52 | 53 | class ImportSpec(): 54 | def __init__(self, name: Union[Ident, str, None], path: Union[BasicLit, List[BasicLit]]): 55 | self.name = name 56 | self.path = path 57 | 58 | 59 | class Field(): 60 | def __init__(self, name: str, _type): 61 | self.name = name 62 | self.type = _type 63 | 64 | 65 | class FieldList(): 66 | def __init__(self, _list: List[Field]): 67 | self.list = _list 68 | 69 | 70 | class FuncType(): 71 | def __init__(self, params: FieldList, results: FieldList): 72 | self.params = params 73 | self.results = results 74 | 75 | 76 | class BlockStmt(): 77 | def __init__(self, _list: list): 78 | self.list = _list 79 | 80 | 81 | class FuncDecl(): 82 | def __init__(self, name: str, _type: FuncType, body: BlockStmt, recv=None): 83 | self.name = name 84 | self.type = _type 85 | self.body = body 86 | self.recv = recv 87 | 88 | 89 | class SelectorExpr(): 90 | def __init__(self, x: str, sel: str): 91 | self.x = x 92 | self.sel = sel 93 | 94 | 95 | class CallExpr(): 96 | def __init__(self, fun: str, args: list, ellipsis=False): 97 | self.fun = fun 98 | self.args = args 99 | self.ellipsis = ellipsis 100 | 101 | 102 | class ArrayType(): 103 | def __init__(self, _len, elt: str): 104 | self.len = _len 105 | self.elt = elt 106 | 107 | 108 | class ValueSpec(): 109 | def __init__(self, names: list, _type: Union[str, ArrayType], values: list): 110 | self.names = names 111 | self.type = _type 112 | self.values = values 113 | 114 | 115 | class Comment(): 116 | def __init__(self, text: str): 117 | self.text = text 118 | 119 | 120 | class ExprStmt(): 121 | def __init__(self, expr): 122 | self.expr = expr 123 | 124 | 125 | class AssignStmt(): 126 | def __init__(self, lhs: list, token: str, rhs: list): 127 | self.lhs = lhs 128 | self.token = token 129 | self.rhs = rhs 130 | 131 | 132 | class FuncLit(): 133 | def __init__(self, _type: FuncType, body: BlockStmt): 134 | self.type = _type 135 | self.body = body 136 | 137 | 138 | class ReturnStmt(): 139 | def __init__(self, results: List[Union[str, FuncLit]]): 140 | self.results = results 141 | 142 | 143 | class BinaryExpr(): 144 | def __init__(self, x, op: str, y): 145 | self.x = x 146 | self.op = op 147 | self.y = y 148 | 149 | 150 | class UnaryExpr(): 151 | def __init__(self, op: str, x, right=False): 152 | self.op = op 153 | self.x = x 154 | self.right = right 155 | 156 | 157 | class ParenExpr(): 158 | def __init__(self, x): 159 | self.x = x 160 | 161 | 162 | class ForStmt(): 163 | def __init__(self, body: BlockStmt, init=None, cond=None, post=None): 164 | self.init = init 165 | self.cond = cond 166 | self.post = post 167 | self.body = body 168 | 169 | 170 | class BranchStmt(): 171 | def __init__(self, tok: str, label=None): 172 | self.tok = tok 173 | self.label = label 174 | 175 | 176 | class LabeledStmt(): 177 | def __init__(self, label: str): 178 | self.label = label 179 | 180 | 181 | class IfStmt(): 182 | def __init__(self, cond, body: BlockStmt, init=None, _else=None): 183 | self.init = init 184 | self.cond = cond 185 | self.body = body 186 | self._else = _else 187 | 188 | 189 | class SwitchStmt(): 190 | def __init__(self, body: BlockStmt, init=None, tag=None): 191 | self.init = init 192 | self.tag = tag 193 | self.body = body 194 | 195 | 196 | class CaseClause(): 197 | def __init__(self, _list: list, body: list): 198 | self.list = _list 199 | self.body = body 200 | 201 | 202 | class IndexExpr(): 203 | def __init__(self, x, index): 204 | self.x = x 205 | self.index = index 206 | 207 | 208 | class TypeAssertExpr(): 209 | def __init__(self, x, _type): 210 | self.x = x 211 | self.type = _type 212 | 213 | 214 | class SliceExpr(): 215 | def __init__(self, x, low, high, _max, slice3: bool): 216 | self.x = x 217 | self.low = low 218 | self.high = high 219 | self.max = _max 220 | self.slice3 = slice3 221 | 222 | 223 | class MapType(): 224 | def __init__(self, key, value): 225 | self.key = key 226 | self.value = value 227 | 228 | 229 | class KeyValueExpr(): 230 | def __init__(self, key, value): 231 | self.key = key 232 | self.value = value 233 | 234 | 235 | class RangeStmt(): 236 | def __init__(self, key, value, tok: str, x, body: BlockStmt): 237 | self.key = key 238 | self.value = value 239 | self.tok = tok 240 | self.x = x 241 | self.body = body 242 | 243 | 244 | class Ellipsis(): 245 | def __init__(self, _type: str): 246 | self.type = _type 247 | 248 | 249 | class StarExpr(): 250 | def __init__(self, x): 251 | self.x = x 252 | 253 | 254 | class StructType(): 255 | def __init__(self, fields: FieldList, incomplete: bool): 256 | self.fields = fields 257 | self.incomplete = incomplete 258 | 259 | 260 | class TypeSpec(): 261 | def __init__(self, name: Ident, _type): 262 | self.name = name 263 | self.type = _type 264 | 265 | 266 | class InterfaceType(): 267 | def __init__(self, methods: FieldList, incomplete: bool): 268 | self.methods = methods 269 | self.incomplete = incomplete 270 | -------------------------------------------------------------------------------- /gopygo/enums.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | .. module:: __init__ 6 | :synopsis: module that contains enums. 7 | """ 8 | 9 | 10 | from enum import Enum 11 | 12 | 13 | class Token(Enum): 14 | ILLEGAL = 1 15 | EOF = 2 16 | COMMENT = 3 17 | IDENT = 4 18 | INT = 5 19 | FLOAT = 6 20 | IMAG = 7 21 | CHAR = 8 22 | STRING = 9 23 | ADD = 10 24 | SUB = 11 25 | MUL = 12 26 | QUO = 13 27 | REM = 14 28 | AND = 15 29 | OR = 16 30 | XOR = 17 31 | SHL = 18 32 | SHR = 19 33 | AND_NOT = 20 34 | AND_ASSIGN = 21 35 | OR_ASSIGN = 22 36 | XOR_ASSIGN = 23 37 | SHL_ASSIGN = 24 38 | SHR_ASSIGN = 25 39 | AND_NOT_ASSIGN = 26 40 | LAND = 27 41 | LOR = 28 42 | ARROW = 29 43 | INC = 30 44 | DEC = 31 45 | EQL = 32 46 | LSS = 33 47 | GTR = 34 48 | ASSIGN = 35 49 | NOT = 36 50 | NEQ = 37 51 | LEQ = 38 52 | GEQ = 39 53 | DEFINE = 40 54 | ELLIPSIS = 41 55 | LPAREN = 42 56 | LBRACK = 43 57 | LBRACE = 44 58 | COMMA = 45 59 | PERIOD = 46 60 | RPAREN = 47 61 | RBRACK = 48 62 | RBRACE = 49 63 | SEMICOLON = 50 64 | COLON = 51 65 | BREAK = 52 66 | CASE = 53 67 | CHAN = 54 68 | CONST = 55 69 | CONTINUE = 56 70 | DEFAULT = 57 71 | DEFER = 58 72 | ELSE = 59 73 | FALLTHROUGH = 60 74 | FOR = 61 75 | FUNC = 62 76 | GO = 63 77 | GOTO = 64 78 | IF = 65 79 | IMPORT = 66 80 | INTERFACE = 67 81 | MAP = 68 82 | PACKAGE = 69 83 | RANGE = 70 84 | RETURN = 71 85 | SELECT = 72 86 | STRUCT = 73 87 | SWITCH = 74 88 | TYPE = 75 89 | VAR = 76 90 | TRUE = 77 91 | FALSE = 78 92 | -------------------------------------------------------------------------------- /gopygo/exceptions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | .. module:: __init__ 6 | :synopsis: module that contains the exceptions. 7 | """ 8 | 9 | 10 | class LexerError(Exception): 11 | """Raised in case of a tokenizer error. 12 | """ 13 | 14 | def __init__(self, message): 15 | super().__init__(message) 16 | -------------------------------------------------------------------------------- /gopygo/parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | .. module:: __init__ 6 | :synopsis: Go parser module. 7 | """ 8 | 9 | import os 10 | 11 | from sly import Lexer, Parser 12 | from sly.yacc import SlyLogger 13 | 14 | from gopygo.ast import ( 15 | Ident, 16 | BasicLit, 17 | CompositeLit, 18 | GenDecl, 19 | DeclStmt, 20 | Package, 21 | File, 22 | ImportSpec, 23 | FuncDecl, 24 | FuncType, 25 | FieldList, 26 | Field, 27 | BlockStmt, 28 | SelectorExpr, 29 | CallExpr, 30 | ValueSpec, 31 | Comment, 32 | ExprStmt, 33 | AssignStmt, 34 | ReturnStmt, 35 | BinaryExpr, 36 | UnaryExpr, 37 | ParenExpr, 38 | ForStmt, 39 | BranchStmt, 40 | LabeledStmt, 41 | IfStmt, 42 | SwitchStmt, 43 | CaseClause, 44 | ArrayType, 45 | IndexExpr, 46 | TypeAssertExpr, 47 | SliceExpr, 48 | MapType, 49 | KeyValueExpr, 50 | RangeStmt, 51 | Ellipsis, 52 | FuncLit, 53 | StarExpr, 54 | StructType, 55 | TypeSpec, 56 | InterfaceType 57 | ) 58 | from gopygo.exceptions import ( 59 | LexerError 60 | ) 61 | from gopygo.enums import Token 62 | 63 | 64 | def flatten(p): 65 | new = [] 66 | try: 67 | for i in p: 68 | if isinstance(i, tuple): 69 | new += i 70 | else: 71 | new.append(i) 72 | except TypeError: 73 | new.append(p) 74 | return tuple(new) 75 | 76 | 77 | class GoLexer(Lexer): 78 | tokens = { 79 | # Keywords 80 | PACKAGE, FUNC, RETURN, 81 | IMPORT, VAR, CONST, TYPE, 82 | FOR, RANGE, BREAK, CONTINUE, GOTO, FALLTHROUGH, 83 | IF, ELSE, 84 | SWITCH, CASE, DEFAULT, 85 | MAP, 86 | STRUCT, INTERFACE, 87 | 88 | # Data types 89 | BOOL, 90 | INT8, INT16, INT32, INT64, 91 | UINT8, UINT16, UINT32, UINT64, 92 | INT, UINT, RUNE, BYTE, UINTPTR, 93 | FLOAT32, FLOAT64, 94 | COMPLEX64, COMPLEX128, 95 | STRING, 96 | 97 | # Identifiers and basic type literals 98 | IDENT, IMAG_LITERAL, FLOAT_LITERAL, INT_LITERAL, CHAR_LITERAL, STRING_LITERAL, TRUE, FALSE, 99 | 100 | # Comment 101 | COMMENT, 102 | 103 | # Operators 104 | ADD_ASSIGN, SUB_ASSIGN, MUL_ASSIGN, QUO_ASSIGN, REM_ASSIGN, 105 | AND_ASSIGN, OR_ASSIGN, XOR_ASSIGN, AND_NOT_ASSIGN, SHL_ASSIGN, SHR_ASSIGN, 106 | LAND, LOR, ARROW, INC, DEC, EQL, SHL, SHR, AND_NOT, 107 | NEQ, LEQ, GEQ, DEFINE, ELLIPSIS, 108 | ADD, SUB, MUL, QUO, REM, AND, OR, XOR, LSS, GTR, ASSIGN, NOT, 109 | 110 | # Delimiters 111 | LPAREN, LBRACK, LBRACE, COMMA, PERIOD, 112 | RPAREN, RBRACK, RBRACE, SEMICOLON, COLON, 113 | NEWLINE, 114 | } 115 | 116 | ignore = ' \t' 117 | 118 | # Keywords 119 | PACKAGE = 'package' 120 | FUNC = 'func' 121 | RETURN = 'return' 122 | IMPORT = 'import' 123 | VAR = 'var' 124 | CONST = 'const' 125 | TYPE = 'type' 126 | FOR = 'for' 127 | RANGE = 'range' 128 | BREAK = 'break' 129 | CONTINUE = 'continue' 130 | GOTO = 'goto' 131 | FALLTHROUGH = 'fallthrough' 132 | IF = 'if' 133 | ELSE = 'else' 134 | SWITCH = 'switch' 135 | CASE = 'case' 136 | DEFAULT = 'DEFAULT' 137 | MAP = 'map' 138 | STRUCT = 'struct' 139 | INTERFACE = 'interface' 140 | 141 | # Data types 142 | BOOL = 'bool' 143 | INT8 = 'int8' 144 | INT16 = 'int16' 145 | INT32 = 'int32' 146 | INT64 = 'int64' 147 | UINT8 = 'uint8' 148 | UINT16 = 'uint16' 149 | UINT32 = 'uint32' 150 | UINT64 = 'uint64' 151 | INT = 'int' 152 | UINT = 'uint' 153 | RUNE = 'rune' 154 | BYTE = 'byte' 155 | UINTPTR = 'uintptr' 156 | FLOAT32 = 'float32' 157 | FLOAT64 = 'float64' 158 | COMPLEX64 = 'complex64' 159 | COMPLEX128 = 'complex128' 160 | STRING = 'string' 161 | 162 | # Identifiers and basic type literals 163 | IMAG_LITERAL = r'[0-9]+\.[0-9]+i|[0-9]+i' 164 | FLOAT_LITERAL = r'[0-9]+\.[0-9]+' 165 | INT_LITERAL = r'[0-9]+e[0-9]+|[0-9]+' 166 | CHAR_LITERAL = r'\'(\$\{.*\}|\\.|[^\'\\])*\'' 167 | STRING_LITERAL = r'\"(\$\{.*\}|\\.|[^\"\\])*\"' 168 | TRUE = r'true' 169 | FALSE = r'false' 170 | IDENT = r'[a-zA-Z_][a-zA-Z0-9_]*' 171 | 172 | # Comment 173 | COMMENT = r'//.*\n' 174 | 175 | # Operators 176 | ADD_ASSIGN = r'\+=' 177 | SUB_ASSIGN = r'-=' 178 | MUL_ASSIGN = r'\*=' 179 | QUO_ASSIGN = r'/=' 180 | REM_ASSIGN = r'%=' 181 | AND_ASSIGN = r'&=' 182 | OR_ASSIGN = r'\|=' 183 | XOR_ASSIGN = r'\^=' 184 | AND_NOT_ASSIGN = r'&\^=' 185 | SHL_ASSIGN = r'<<=' 186 | SHR_ASSIGN = r'>>=' 187 | 188 | LAND = r'&&' 189 | LOR = r'\|\|' 190 | ARROW = r'<-' 191 | INC = r'\+\+' 192 | DEC = r'--' 193 | EQL = r'==' 194 | SHL = r'<<' 195 | SHR = r'>>' 196 | AND_NOT = r'&\^' 197 | NEQ = r'!=' 198 | LEQ = r'<=' 199 | GEQ = r'>=' 200 | DEFINE = r':=' 201 | ELLIPSIS = r'\.\.\.' 202 | 203 | ADD = r'\+' 204 | SUB = r'-' 205 | MUL = r'\*' 206 | QUO = r'/' 207 | REM = r'%' 208 | AND = r'&' 209 | OR = r'\|' 210 | XOR = r'\^' 211 | LSS = r'<' 212 | GTR = r'>' 213 | ASSIGN = r'=' 214 | NOT = r'\!' 215 | 216 | # Delimiters 217 | LPAREN = r'\(' 218 | LBRACK = r'\[' 219 | LBRACE = r'{' 220 | COMMA = r'\,' 221 | PERIOD = r'\.' 222 | 223 | RPAREN = r'\)' 224 | RBRACK = r'\]' 225 | RBRACE = r'}' 226 | SEMICOLON = r';' 227 | COLON = r':' 228 | 229 | NEWLINE = r'\n' 230 | 231 | # # Ignored pattern 232 | # ignore_newline = r'\n+' 233 | 234 | # # Extra action for newlines 235 | # def ignore_newline(self, t): 236 | # self.lineno += t.value.count('\n') 237 | 238 | def error(self, t): 239 | raise LexerError("Illegal character '%s'" % t.value[0]) 240 | 241 | 242 | class GoParser(Parser): 243 | log = SlyLogger(open(os.devnull, 'w')) # To enable logging: SlyLogger(sys.stderr) 244 | 245 | tokens = GoLexer.tokens 246 | 247 | precedence = ( 248 | ('left', ADD, SUB), 249 | ('left', MUL, QUO), 250 | ('right', USUB, UXOR, UNOT, UADD), 251 | ) 252 | 253 | def __init__(self): 254 | self.names = { } 255 | 256 | @_( 257 | 'line' 258 | ) 259 | def start(self, p): 260 | if isinstance(p.line, tuple) and len(p.line) == 1: 261 | return p.line[0] 262 | else: 263 | return p.line 264 | 265 | @_( 266 | 'package NEWLINE line', 267 | 'package NEWLINE', 268 | 'NEWLINE line', 269 | '_import NEWLINE line', 270 | '_import NEWLINE', 271 | 'comment line', 272 | 'comment', 273 | 'func_decl NEWLINE line', 274 | 'func_decl NEWLINE', 275 | 'stmt line', 276 | 'stmt' 277 | ) 278 | def line(self, p): 279 | if isinstance(p[0], Package): 280 | file = File(p[0]) 281 | if len(p) > 2: 282 | for i in p.line: 283 | file.decls.append(i) 284 | return file 285 | else: 286 | if isinstance(p[0], Comment): 287 | p[0].text += '\n' 288 | if len(p) > 1: 289 | return tuple(filter(lambda x: x!= '\n', flatten(p))) 290 | else: 291 | return p[0] 292 | 293 | @_('PACKAGE IDENT') 294 | def package(self, p): 295 | return Package(p.IDENT) 296 | 297 | @_( 298 | 'IMPORT STRING_LITERAL', 299 | 'IMPORT IDENT STRING_LITERAL', 300 | 'IMPORT PERIOD STRING_LITERAL', 301 | 'IMPORT LPAREN _import_list NEWLINE RPAREN', 302 | 'IMPORT LPAREN _import_list RPAREN', 303 | 'IMPORT LPAREN NEWLINE _import_list NEWLINE RPAREN', 304 | 'IMPORT LPAREN NEWLINE _import_list RPAREN', 305 | ) 306 | def _import(self, p): 307 | if hasattr(p, 'STRING_LITERAL'): 308 | ident = None 309 | if hasattr(p, 'IDENT'): 310 | ident = p.IDENT 311 | elif hasattr(p, 'PERIOD'): 312 | ident = p.PERIOD 313 | 314 | return GenDecl( 315 | p[0], 316 | [ImportSpec(ident, BasicLit(Token.STRING, p.STRING_LITERAL[1:-1]))] 317 | ) 318 | else: 319 | return GenDecl( 320 | p[0], 321 | p._import_list 322 | ) 323 | 324 | @_( 325 | 'STRING_LITERAL', 326 | 'STRING_LITERAL NEWLINE', 327 | 'STRING_LITERAL _import_list', 328 | 'STRING_LITERAL NEWLINE _import_list', 329 | 'IDENT STRING_LITERAL', 330 | 'IDENT STRING_LITERAL NEWLINE', 331 | 'IDENT STRING_LITERAL _import_list', 332 | 'IDENT STRING_LITERAL NEWLINE _import_list', 333 | 'PERIOD STRING_LITERAL', 334 | 'PERIOD STRING_LITERAL NEWLINE', 335 | 'PERIOD STRING_LITERAL _import_list', 336 | 'PERIOD STRING_LITERAL NEWLINE _import_list' 337 | ) 338 | def _import_list(self, p): 339 | ident = None 340 | if hasattr(p, 'IDENT'): 341 | ident = p.IDENT 342 | elif hasattr(p, 'PERIOD'): 343 | ident = p.PERIOD 344 | 345 | if hasattr(p, '_import_list'): 346 | return [ImportSpec(ident, BasicLit(Token.STRING, p.STRING_LITERAL[1:-1]))] + p._import_list 347 | else: 348 | return [ImportSpec(ident, BasicLit(Token.STRING, p.STRING_LITERAL[1:-1]))] 349 | 350 | @_('FUNC IDENT func_type block_stmt') 351 | def func_decl(self, p): 352 | return FuncDecl(p.IDENT, p.func_type, p.block_stmt) 353 | 354 | @_('FUNC func_type block_stmt') 355 | def func_lit(self, p): 356 | return FuncLit(p.func_type, p.block_stmt) 357 | 358 | @_( 359 | 'func_lit', 360 | 'func_lit COMMA func_lits', 361 | ) 362 | def func_lits(self, p): 363 | if hasattr(p, 'func_lits'): 364 | return [p.func_lit] + p.func_lits 365 | else: 366 | return [p.func_lit] 367 | 368 | @_( 369 | 'LPAREN field_list RPAREN field_list', 370 | 'LPAREN field_list RPAREN LPAREN field_list RPAREN' 371 | ) 372 | def func_type(self, p): 373 | return FuncType(p.field_list0, p.field_list1) 374 | 375 | @_( 376 | '', 377 | 'field', 378 | 'field COMMA field_list', 379 | 'field NEWLINE field_list' 380 | ) 381 | def field_list(self, p): 382 | if len(p) > 2: 383 | return FieldList([p.field] + p.field_list.list) 384 | elif len(p) == 1: 385 | return FieldList([p.field]) 386 | else: 387 | return FieldList([]) 388 | 389 | @_( 390 | '_type', 391 | 'ELLIPSIS _type', 392 | 'IDENT _type', 393 | 'IDENT ELLIPSIS _type', 394 | 'FUNC LPAREN RPAREN _type', 395 | 'FUNC LPAREN RPAREN ELLIPSIS _type', 396 | 'IDENT LPAREN RPAREN _type', 397 | 'IDENT LPAREN RPAREN ELLIPSIS _type', 398 | 'MUL _type', 399 | 'ELLIPSIS MUL _type', 400 | 'IDENT MUL _type', 401 | 'IDENT ELLIPSIS MUL _type', 402 | 'FUNC LPAREN RPAREN MUL _type', 403 | 'FUNC LPAREN RPAREN ELLIPSIS MUL _type', 404 | 'IDENT LPAREN RPAREN MUL _type', 405 | 'IDENT LPAREN RPAREN ELLIPSIS MUL _type', 406 | 'MUL expr', 407 | 'ELLIPSIS MUL expr', 408 | 'IDENT MUL expr', 409 | 'IDENT ELLIPSIS MUL expr', 410 | 'FUNC LPAREN RPAREN MUL expr', 411 | 'FUNC LPAREN RPAREN ELLIPSIS MUL expr', 412 | 'IDENT LPAREN RPAREN MUL expr', 413 | 'IDENT LPAREN RPAREN ELLIPSIS MUL expr', 414 | ) 415 | def field(self, p): 416 | _type = None 417 | if not hasattr(p, '_type'): 418 | _type = StarExpr(p.expr) 419 | else: 420 | _type = StarExpr(p._type) if hasattr(p, 'MUL') else p._type 421 | 422 | if hasattr(p, 'LPAREN'): 423 | name = None 424 | if hasattr(p, 'IDENT'): 425 | name = p.IDENT 426 | return Field( 427 | name, 428 | FuncType( 429 | FieldList([]), 430 | FieldList([ 431 | Field(None, _type) 432 | ]) 433 | ) 434 | ) 435 | 436 | if hasattr(p, 'ELLIPSIS'): 437 | _type = Ellipsis(_type) 438 | 439 | if (hasattr(p, '_type') or hasattr(p, 'expr')) and hasattr(p, 'IDENT'): 440 | return Field(p.IDENT, _type) 441 | else: 442 | return Field(None, _type) 443 | 444 | @_( 445 | 'LBRACE stmts RBRACE', 446 | 'LBRACE NEWLINE stmts RBRACE', 447 | 'LBRACE case_clause_list RBRACE', 448 | 'LBRACE NEWLINE case_clause_list RBRACE', 449 | ) 450 | def block_stmt(self, p): 451 | if hasattr(p, 'stmts'): 452 | return BlockStmt(p.stmts) 453 | elif hasattr(p, 'case_clause_list'): 454 | return BlockStmt(p.case_clause_list) 455 | 456 | @_( 457 | 'case_clause', 458 | 'case_clause case_clause_list', 459 | ) 460 | def case_clause_list(self, p): 461 | if len(p) > 1: 462 | return [p.case_clause] + p.case_clause_list 463 | else: 464 | return [p.case_clause] 465 | 466 | @_( 467 | 'stmt', 468 | 'stmt stmts', 469 | 'NEWLINE stmts' 470 | ) 471 | def stmts(self, p): 472 | if p[0] == '\n': 473 | return p.stmts 474 | elif len(p) > 1: 475 | if isinstance(p.stmt, LabeledStmt) and p.stmt.label == 'default': 476 | return [CaseClause([], p.stmts)] 477 | return [p.stmt] + p.stmts 478 | else: 479 | return [p.stmt] 480 | 481 | @_( 482 | 'expr', 483 | 'expr NEWLINE' 484 | ) 485 | def stmt(self, p): 486 | return ExprStmt(p.expr) 487 | 488 | @_('COMMENT') 489 | def comment(self, p): 490 | return Comment(p.COMMENT[2:].lstrip().rstrip()) 491 | 492 | @_('call_expr') 493 | def expr(self, p): 494 | return p.call_expr 495 | 496 | @_( 497 | 'selector_expr LPAREN args RPAREN', 498 | 'IDENT LPAREN args RPAREN', 499 | '_type LPAREN args RPAREN', 500 | 'selector_expr LPAREN args ELLIPSIS RPAREN', 501 | 'IDENT LPAREN args ELLIPSIS RPAREN', 502 | '_type LPAREN args ELLIPSIS RPAREN', 503 | ) 504 | def call_expr(self, p): 505 | ellipsis = True if hasattr(p, 'ELLIPSIS') else False 506 | return CallExpr(p[0], p.args, ellipsis=ellipsis) 507 | 508 | @_('selector_expr') 509 | def expr(self, p): 510 | return p.selector_expr 511 | 512 | @_( 513 | 'IDENT PERIOD IDENT', 514 | 'selector_expr PERIOD IDENT', 515 | 'call_expr PERIOD IDENT' 516 | ) 517 | def selector_expr(self, p): 518 | return SelectorExpr(p[0], p[2]) 519 | 520 | @_( 521 | 'IDENT PERIOD LPAREN expr RPAREN', 522 | 'IDENT PERIOD LPAREN TYPE RPAREN', 523 | 'expr PERIOD LPAREN expr RPAREN', 524 | 'expr PERIOD LPAREN TYPE RPAREN', 525 | ) 526 | def expr(self, p): 527 | _type = p[3] if p[3] != GoLexer.TYPE else None 528 | return TypeAssertExpr(p[0], p[3]) 529 | 530 | @_('comment') 531 | def expr(self, p): 532 | return p.comment 533 | 534 | @_( 535 | 'assign_stmt NEWLINE', 536 | 'assign_stmt' 537 | ) 538 | def stmt(self, p): 539 | return p.assign_stmt 540 | 541 | @_('for_stmt NEWLINE') 542 | def stmt(self, p): 543 | return p.for_stmt 544 | 545 | @_('range_stmt NEWLINE') 546 | def stmt(self, p): 547 | return p.range_stmt 548 | 549 | @_('if_stmt NEWLINE') 550 | def stmt(self, p): 551 | return p.if_stmt 552 | 553 | @_('switch_stmt NEWLINE') 554 | def stmt(self, p): 555 | return p.switch_stmt 556 | 557 | @_( 558 | 'expr DEFINE expr', 559 | 'expr ASSIGN expr', 560 | 'expr ADD_ASSIGN expr', 561 | 'expr SUB_ASSIGN expr', 562 | 'expr MUL_ASSIGN expr', 563 | 'expr QUO_ASSIGN expr', 564 | 'expr REM_ASSIGN expr', 565 | 'expr AND_ASSIGN expr', 566 | 'expr OR_ASSIGN expr', 567 | 'expr XOR_ASSIGN expr', 568 | 'expr AND_NOT_ASSIGN expr', 569 | 'expr SHL_ASSIGN expr', 570 | 'expr SHR_ASSIGN expr', 571 | ) 572 | def assign_stmt(self, p): 573 | return AssignStmt(p.expr0, p[1], p.expr1) 574 | 575 | @_( 576 | 'FOR block_stmt', 577 | 'FOR expr block_stmt', 578 | 'FOR stmt SEMICOLON expr SEMICOLON stmt block_stmt' 579 | ) 580 | def for_stmt(self, p): 581 | if len(p) == 2: 582 | return ForStmt(p.block_stmt) 583 | elif len(p) == 3: 584 | return ForStmt(p.block_stmt, cond=p.expr) 585 | else: 586 | return ForStmt(p.block_stmt, init=p.stmt0, cond=p.expr, post=p.stmt1) 587 | 588 | @_( 589 | 'FOR RANGE expr block_stmt', 590 | 'FOR expr DEFINE RANGE expr block_stmt', 591 | 'FOR expr COMMA expr DEFINE RANGE expr block_stmt', 592 | 'FOR expr ASSIGN RANGE expr block_stmt', 593 | 'FOR expr COMMA expr ASSIGN RANGE expr block_stmt', 594 | ) 595 | def range_stmt(self, p): 596 | tok = Token.ILLEGAL 597 | if hasattr(p, 'DEFINE'): 598 | tok = p.DEFINE 599 | elif hasattr(p, 'ASSIGN'): 600 | tok = p.ASSIGN 601 | 602 | if tok == Token.ILLEGAL: 603 | return RangeStmt(None, None, tok, p.expr, p.block_stmt) 604 | 605 | if hasattr(p, 'COMMA'): 606 | return RangeStmt(p.expr0, p.expr1, tok, p.expr2, p.block_stmt) 607 | else: 608 | return RangeStmt(p.expr0, None, tok, p.expr1, p.block_stmt) 609 | 610 | @_( 611 | 'IF expr block_stmt', 612 | 'IF expr block_stmt ELSE block_stmt', 613 | 'IF expr block_stmt ELSE if_stmt', 614 | 'IF stmt SEMICOLON expr block_stmt', 615 | 'IF stmt SEMICOLON expr block_stmt ELSE block_stmt', 616 | 'IF stmt SEMICOLON expr block_stmt ELSE if_stmt' 617 | ) 618 | def if_stmt(self, p): 619 | init = p.stmt if hasattr(p, 'stmt') else None 620 | cond = p.expr 621 | body = p.block_stmt0 if hasattr(p, 'block_stmt0') else p.block_stmt 622 | _else = p[-1] if hasattr(p, 'ELSE') else None 623 | return IfStmt( 624 | cond, 625 | body, 626 | init=init, 627 | _else=_else 628 | ) 629 | 630 | @_( 631 | 'SWITCH block_stmt', 632 | 'SWITCH stmt block_stmt', 633 | 'SWITCH expr block_stmt', 634 | 'SWITCH stmt SEMICOLON expr block_stmt' 635 | ) 636 | def switch_stmt(self, p): 637 | init = p.stmt if hasattr(p, 'stmt') else None 638 | tag = p.expr if hasattr(p, 'expr') else None 639 | return SwitchStmt( 640 | p.block_stmt, 641 | init=init, 642 | tag=tag 643 | ) 644 | 645 | @_( 646 | 'CASE expr COLON NEWLINE stmts', 647 | 'CASE _type_list COLON NEWLINE stmts', 648 | 'DEFAULT COLON NEWLINE stmts', 649 | ) 650 | def case_clause(self, p): 651 | expr = p.expr if hasattr(p, 'expr') else [] 652 | _type_list = p._type_list if hasattr(p, '_type_list') else [] 653 | expr = [expr] if not isinstance(expr, list) else expr 654 | _type_list = [_type_list] if not isinstance(_type_list, list) else _type_list 655 | expr += _type_list 656 | return CaseClause(expr, p.stmts) 657 | 658 | @_( 659 | '_type', 660 | '_type COMMA _type_list', 661 | ) 662 | def _type_list(self, p): 663 | if len(p) > 1: 664 | return [p._type] + p._type_list 665 | else: 666 | return [p._type] 667 | 668 | @_( 669 | 'RETURN args NEWLINE', 670 | 'RETURN func_lits NEWLINE' 671 | ) 672 | def stmt(self, p): 673 | return ReturnStmt(p[1]) 674 | 675 | @_( 676 | 'TYPE IDENT STRUCT LBRACE field_list RBRACE NEWLINE', 677 | 'TYPE IDENT STRUCT LBRACE NEWLINE field_list RBRACE NEWLINE', 678 | 'TYPE IDENT STRUCT LBRACE field_list NEWLINE RBRACE NEWLINE', 679 | 'TYPE IDENT STRUCT LBRACE NEWLINE field_list NEWLINE RBRACE NEWLINE', 680 | 'TYPE IDENT INTERFACE LBRACE field_list RBRACE NEWLINE', 681 | 'TYPE IDENT INTERFACE LBRACE NEWLINE field_list RBRACE NEWLINE', 682 | 'TYPE IDENT INTERFACE LBRACE field_list NEWLINE RBRACE NEWLINE', 683 | 'TYPE IDENT INTERFACE LBRACE NEWLINE field_list NEWLINE RBRACE NEWLINE', 684 | 'VAR value_spec NEWLINE', 685 | 'CONST value_spec NEWLINE', 686 | 'IMPORT value_spec NEWLINE', 687 | 'TYPE value_spec NEWLINE', 688 | ) 689 | def stmt(self, p): 690 | if hasattr(p, 'IDENT'): 691 | _type = None 692 | if hasattr(p, 'INTERFACE'): 693 | _type = InterfaceType(p.field_list, False) 694 | else: 695 | _type = StructType(p.field_list, False) 696 | return DeclStmt( 697 | GenDecl( 698 | p[0], 699 | [ 700 | TypeSpec( 701 | p.IDENT, 702 | _type 703 | ) 704 | ] 705 | ) 706 | ) 707 | else: 708 | return DeclStmt( 709 | GenDecl( 710 | p[0], 711 | [p.value_spec] 712 | ) 713 | ) 714 | 715 | @_( 716 | 'BREAK NEWLINE', 717 | 'CONTINUE NEWLINE', 718 | 'GOTO IDENT NEWLINE', 719 | 'FALLTHROUGH NEWLINE' 720 | ) 721 | def stmt(self, p): 722 | if hasattr(p, 'GOTO'): 723 | return BranchStmt(p.GOTO, p.IDENT) 724 | else: 725 | return BranchStmt(p[0]) 726 | 727 | @_( 728 | 'IDENT COLON' 729 | ) 730 | def stmt(self, p): 731 | return LabeledStmt(p.IDENT) 732 | 733 | @_( 734 | '', 735 | 'expr', 736 | 'TYPE', 737 | 'expr COMMA args', 738 | 'TYPE COMMA args', 739 | ) 740 | def args(self, p): 741 | if len(p) > 2: 742 | return [p[0]] + p[2] 743 | elif len(p) == 1: 744 | return [p[0]] 745 | else: 746 | return [] 747 | 748 | @_( 749 | 'BOOL', 750 | 'INT8', 751 | 'INT16', 752 | 'INT32', 753 | 'INT64', 754 | 'UINT8', 755 | 'UINT16', 756 | 'UINT32', 757 | 'UINT64', 758 | 'INT', 759 | 'UINT', 760 | 'RUNE', 761 | 'BYTE', 762 | 'UINTPTR', 763 | 'FLOAT32', 764 | 'FLOAT64', 765 | 'COMPLEX64', 766 | 'COMPLEX128', 767 | 'STRING', 768 | ) 769 | def _type(self, p): 770 | return p[0] 771 | 772 | @_( 773 | 'IDENT COMMA value_spec array_type ASSIGN expr', 774 | 'IDENT COMMA value_spec _type ASSIGN expr', 775 | 'IDENT COMMA value_spec ASSIGN expr', 776 | 'IDENT array_type ASSIGN expr', 777 | 'IDENT _type ASSIGN expr', 778 | 'IDENT ASSIGN expr', 779 | 'IDENT COMMA value_spec array_type', 780 | 'IDENT COMMA value_spec _type', 781 | 'IDENT COMMA value_spec', 782 | 'IDENT array_type', 783 | 'IDENT _type', 784 | ) 785 | def value_spec(self, p): 786 | values = [] 787 | if hasattr(p, 'ASSIGN'): 788 | values = p.expr 789 | values = [values] if not isinstance(values, list) else values 790 | 791 | if hasattr(p, 'value_spec'): 792 | values += p.value_spec.values 793 | return ValueSpec([p.IDENT] + p.value_spec.names, p.value_spec.type, values) 794 | else: 795 | _type = None 796 | if hasattr(p, 'array_type'): 797 | _type = p.array_type 798 | elif hasattr(p, '_type'): 799 | _type = p._type 800 | return ValueSpec([p.IDENT], _type, values) 801 | 802 | @_( 803 | 'LBRACK expr RBRACK _type', 804 | 'LBRACK expr RBRACK array_type', 805 | 'LBRACK RBRACK _type', 806 | 'LBRACK RBRACK array_type', 807 | ) 808 | def array_type(self, p): 809 | _len = p.expr if hasattr(p, 'expr') else '' 810 | return ArrayType(_len, p[-1]) 811 | 812 | @_( 813 | 'expr LBRACK expr RBRACK' 814 | ) 815 | def expr(self, p): 816 | return IndexExpr(p.expr0, p.expr1) 817 | 818 | @_( 819 | 'expr LBRACK COLON expr RBRACK', 820 | 'expr LBRACK expr COLON RBRACK', 821 | 'expr LBRACK expr COLON expr RBRACK', 822 | 'expr LBRACK expr COLON expr COLON expr RBRACK' 823 | ) 824 | def expr(self, p): 825 | if len(p) == 5: 826 | if p[2] == GoLexer.COLON: 827 | return SliceExpr(p.expr0, None, p.expr1, None, False) 828 | else: 829 | return SliceExpr(p.expr0, p.expr1, None, None, False) 830 | if hasattr(p, 'expr3'): 831 | return SliceExpr(p.expr0, p.expr1, p.expr2, p.expr3, True) 832 | else: 833 | return SliceExpr(p.expr0, p.expr1, p.expr2, None, False) 834 | 835 | @_('map_type') 836 | def expr(self, p): 837 | return p.map_type 838 | 839 | @_( 840 | 'MAP LBRACK expr RBRACK expr', 841 | 'MAP LBRACK _type RBRACK _type', 842 | 'MAP LBRACK _type RBRACK interface_type', 843 | ) 844 | def map_type(self, p): 845 | return MapType(p[2], p[4]) 846 | 847 | @_( 848 | 'INTERFACE LBRACE field_list RBRACE' 849 | ) 850 | def interface_type(sef, p): 851 | return InterfaceType(p.field_list, False) 852 | 853 | @_( 854 | 'expr COLON expr', 855 | 'expr COLON expr COMMA NEWLINE', 856 | 'expr COLON expr COMMA key_value_list', 857 | 'expr COLON expr COMMA NEWLINE key_value_list', 858 | ) 859 | def key_value_list(self, p): 860 | if hasattr(p, 'key_value_list'): 861 | return [KeyValueExpr(p.expr0, p.expr1)] + p.key_value_list 862 | else: 863 | return [KeyValueExpr(p.expr0, p.expr1)] 864 | 865 | @_( 866 | 'map_type LBRACE key_value_list RBRACE', 867 | 'map_type LBRACE NEWLINE key_value_list RBRACE', 868 | 'map_type LBRACE RBRACE', 869 | ) 870 | def expr(self, p): 871 | expr = [] 872 | if hasattr(p, 'key_value_list'): 873 | expr = p.key_value_list if isinstance(p.key_value_list, list) else [p.key_value_list] 874 | return CompositeLit(p.map_type, expr, False) 875 | 876 | @_( 877 | 'array_type LBRACE expr RBRACE', 878 | 'array_type LBRACE NEWLINE expr RBRACE', 879 | 'array_type LBRACE expr NEWLINE RBRACE', 880 | 'array_type LBRACE NEWLINE expr NEWLINE RBRACE', 881 | 'array_type LBRACE RBRACE', 882 | ) 883 | def expr(self, p): 884 | expr = [] 885 | if hasattr(p, 'expr'): 886 | expr = p.expr if isinstance(p.expr, list) else [p.expr] 887 | return CompositeLit(p.array_type, expr, False) 888 | 889 | @_( 890 | 'array_type' 891 | ) 892 | def expr(self, p): 893 | return p.array_type 894 | 895 | @_( 896 | 'expr LAND expr', 897 | 'expr LOR expr', 898 | 'expr ARROW expr', 899 | 'expr EQL expr', 900 | 'expr SHL expr', 901 | 'expr SHR expr', 902 | 'expr AND_NOT expr', 903 | 'expr NEQ expr', 904 | 'expr LEQ expr', 905 | 'expr GEQ expr', 906 | 'expr ADD expr', 907 | 'expr SUB expr', 908 | 'expr MUL expr', 909 | 'expr QUO expr', 910 | 'expr REM expr', 911 | 'expr AND expr', 912 | 'expr OR expr', 913 | 'expr XOR expr', 914 | 'expr LSS expr', 915 | 'expr GTR expr' 916 | ) 917 | def expr(self, p): 918 | return BinaryExpr(p.expr0, p[1], p.expr1) 919 | 920 | @_( 921 | 'SUB expr %prec USUB', 922 | 'XOR expr %prec UXOR', 923 | 'NOT expr %prec UNOT', 924 | 'AND expr %prec UADD', 925 | ) 926 | def expr(self, p): 927 | return UnaryExpr(p[0], p.expr) 928 | 929 | @_( 930 | 'INC expr', 931 | 'expr INC', 932 | 'DEC expr', 933 | 'expr DEC' 934 | ) 935 | def expr(self, p): 936 | op = p.INC if hasattr(p, 'INC') else p.DEC 937 | right = True if p[1] in ('++', '--') else False 938 | return UnaryExpr(op, p.expr, right=right) 939 | 940 | @_('LPAREN expr RPAREN') 941 | def expr(self, p): 942 | return ParenExpr(p.expr) 943 | 944 | @_('IMAG_LITERAL') 945 | def expr(self, p): 946 | return BasicLit(Token.IMAG, p.IMAG_LITERAL) 947 | 948 | @_('FLOAT_LITERAL') 949 | def expr(self, p): 950 | return BasicLit(Token.FLOAT, p.FLOAT_LITERAL) 951 | 952 | @_('INT_LITERAL') 953 | def expr(self, p): 954 | return BasicLit(Token.INT, p.INT_LITERAL) 955 | 956 | @_('CHAR_LITERAL') 957 | def expr(self, p): 958 | return BasicLit(Token.CHAR, p.CHAR_LITERAL[1:-1]) 959 | 960 | @_('STRING_LITERAL') 961 | def expr(self, p): 962 | return BasicLit(Token.STRING, p.STRING_LITERAL[1:-1]) 963 | 964 | @_('TRUE') 965 | def expr(self, p): 966 | return BasicLit(Token.TRUE, None) 967 | 968 | @_('FALSE') 969 | def expr(self, p): 970 | return BasicLit(Token.FALSE, None) 971 | 972 | @_( 973 | 'expr COMMA expr', 974 | 'expr COMMA NEWLINE expr' 975 | ) 976 | def expr(self, p): 977 | return [p.expr0] + list(flatten(p.expr1)) 978 | 979 | @_( 980 | 'expr COMMA', 981 | 'expr COMMA NEWLINE' 982 | ) 983 | def expr(self, p): 984 | return p.expr 985 | 986 | @_('MUL IDENT') 987 | def expr(self, p): 988 | return StarExpr(Ident(p.IDENT)) 989 | 990 | @_('IDENT') 991 | def expr(self, p): 992 | return Ident(p.IDENT) 993 | 994 | 995 | lexer = GoLexer() 996 | parser = GoParser() 997 | 998 | def parse(text): 999 | return parser.parse(lexer.tokenize(text.lstrip().rstrip() + '\n')) 1000 | -------------------------------------------------------------------------------- /gopygo/unparser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | .. module:: __init__ 6 | :synopsis: Go unparser (code generator) module. 7 | """ 8 | 9 | import re 10 | import json 11 | 12 | from gopygo.enums import Token 13 | from gopygo.ast import Ident, FuncType 14 | 15 | INDENT = ' ' 16 | 17 | camel_to_snake_pattern = re.compile(r'(? 1: 69 | text += '(' 70 | text += '%s' % getattr(self, _get_node_type(node.results))(node.results) 71 | if len(node.results.list) > 1: 72 | text += ')' 73 | return text 74 | 75 | def field_list(self, node, separator=', ', indent=''): 76 | text = '' 77 | for field in node.list: 78 | text += '%s%s%s' % ( 79 | indent, 80 | getattr(self, _get_node_type(field))(field), 81 | separator 82 | ) 83 | if node.list: 84 | text = text[:-len(separator)] 85 | return text 86 | 87 | def field(self, node): 88 | text = '' 89 | if node.name is None: 90 | if isinstance(node.type, FuncType): 91 | text += 'func' 92 | text += '%s' % getattr(self, _get_node_type(node.type))(node.type) 93 | else: 94 | gap = ' ' 95 | if isinstance(node.type, FuncType): 96 | gap = '' 97 | text += '%s%s%s' % ( 98 | node.name, 99 | gap, 100 | getattr(self, _get_node_type(node.type))(node.type) 101 | ) 102 | return text 103 | 104 | def block_stmt(self, node): 105 | text = '{\n' 106 | self.indent += 1 107 | for stmt in node.list: 108 | text += getattr(self, _get_node_type(stmt))(stmt) 109 | self.indent -= 1 110 | text += '%s}\n' % (self.indent * INDENT) 111 | return text 112 | 113 | def selector_expr(self, node): 114 | text = '%s.%s' % ( 115 | getattr(self, _get_node_type(node.x))(node.x), 116 | getattr(self, _get_node_type(node.sel))(node.sel) 117 | ) 118 | return text 119 | 120 | def call_expr(self, node): 121 | text = '%s(' % getattr(self, _get_node_type(node.fun))(node.fun) 122 | for arg in node.args: 123 | text += '%s, ' % getattr(self, _get_node_type(arg))(arg) 124 | if node.args: 125 | text = text[:-2] 126 | if node.ellipsis and len(node.args) == 1: 127 | text += '...' 128 | text += ')' 129 | return text 130 | 131 | def value_spec(self, node): 132 | text = '' 133 | for name in node.names: 134 | text += '%s, ' % name 135 | if node.names: 136 | text = text[:-2] 137 | if node.type is not None: 138 | text += ' %s' % getattr(self, _get_node_type(node.type))(node.type) 139 | if node.values: 140 | text += ' = ' 141 | for value in node.values: 142 | text += '%s, ' % getattr(self, _get_node_type(value))(value) 143 | if node.values: 144 | text = text[:-2] 145 | return text 146 | 147 | def comment(self, node): 148 | return '// %s' % node.text 149 | 150 | def str(self, node): 151 | return node 152 | 153 | def expr_stmt(self, node): 154 | return '%s%s\n' % ( 155 | self.indent * INDENT, 156 | getattr(self, _get_node_type(node.expr))(node.expr) 157 | ) 158 | 159 | def assign_stmt(self, node): 160 | disable_lhs = True 161 | if isinstance(node.lhs, list): 162 | for _node in node.lhs: 163 | if isinstance(_node, Ident) and _node.name == '_': 164 | continue 165 | else: 166 | disable_lhs = False 167 | else: 168 | disable_lhs = False 169 | if isinstance(node.lhs, Ident) and node.lhs.name == '_': 170 | disable_lhs = True 171 | 172 | if disable_lhs: 173 | return '%s%s\n' % ( 174 | self.indent * INDENT, 175 | getattr(self, _get_node_type(node.rhs))(node.rhs), 176 | ) 177 | else: 178 | return '%s%s %s %s\n' % ( 179 | self.indent * INDENT, 180 | getattr(self, _get_node_type(node.lhs))(node.lhs), 181 | node.token, 182 | getattr(self, _get_node_type(node.rhs))(node.rhs), 183 | ) 184 | 185 | def return_stmt(self, node): 186 | text = '%sreturn ' % (self.indent * INDENT) 187 | for result in node.results: 188 | text += '%s, ' % getattr(self, _get_node_type(result))(result) 189 | if node.results: 190 | text = text[:-2] 191 | text += '\n' 192 | return text 193 | 194 | def binary_expr(self, node): 195 | x = node.x 196 | x = getattr(self, _get_node_type(x))(x) if not isinstance(x, str) else x 197 | y = node.y 198 | y = getattr(self, _get_node_type(y))(y) if not isinstance(y, str) else y 199 | return '%s %s %s' % ( 200 | x, 201 | node.op, 202 | y 203 | ) 204 | 205 | def unary_expr(self, node): 206 | x = node.x 207 | x = getattr(self, _get_node_type(x))(x) if not isinstance(x, str) else x 208 | p1 = x if node.right else node.op 209 | p2 = node.op if node.right else x 210 | return '%s%s' % ( 211 | p1, 212 | p2 213 | ) 214 | 215 | def paren_expr(self, node): 216 | x = node.x 217 | x = getattr(self, _get_node_type(x))(x) if not isinstance(x, str) else x 218 | return '(%s)' % x 219 | 220 | def list(self, node, separator=', ', indent=''): 221 | text = '' 222 | for elt in node: 223 | text += '%s%s%s' % ( 224 | indent, 225 | getattr(self, _get_node_type(elt))(elt), 226 | separator 227 | ) 228 | if node and not indent: 229 | text = text[:-2] 230 | return text 231 | 232 | def for_stmt(self, node): 233 | text = '%sfor ' % (self.indent * INDENT) 234 | if node.init is not None: 235 | text += '%s; ' % getattr(self, _get_node_type(node.init))(node.init).lstrip().rstrip() 236 | text += '%s; ' % getattr(self, _get_node_type(node.cond))(node.cond) 237 | text += '%s ' % getattr(self, _get_node_type(node.post))(node.post).lstrip().rstrip() 238 | elif node.cond is not None: 239 | text += '%s ' % getattr(self, _get_node_type(node.cond))(node.cond) 240 | text += getattr(self, _get_node_type(node.body))(node.body) 241 | return text 242 | 243 | def range_stmt(self, node): 244 | text = '%sfor' % (self.indent * INDENT) 245 | if node.key is not None: 246 | text += ' %s' % getattr(self, _get_node_type(node.key))(node.key).lstrip().rstrip() 247 | if node.value is not None: 248 | text += ', %s' % getattr(self, _get_node_type(node.value))(node.value).lstrip().rstrip() 249 | text += '%s range %s %s' % ( 250 | (' %s' % node.tok) if node.tok != Token.ILLEGAL else '', 251 | getattr(self, _get_node_type(node.x))(node.x).lstrip().rstrip(), 252 | getattr(self, _get_node_type(node.body))(node.body) 253 | ) 254 | return text 255 | 256 | def branch_stmt(self, node): 257 | text = '%s%s' % ( 258 | self.indent * INDENT, 259 | node.tok 260 | ) 261 | if node.label is not None: 262 | text += ' %s' % (node.label) 263 | text += '\n' 264 | return text 265 | 266 | def labeled_stmt(self, node): 267 | return '%s%s:\n' % ( 268 | self.indent * INDENT, 269 | node.label 270 | ) 271 | 272 | def if_stmt(self, node): 273 | text = '%sif ' % (self.indent * INDENT) 274 | if node.init is not None: 275 | text += '%s; ' % getattr(self, _get_node_type(node.init))(node.init).lstrip().rstrip() 276 | text += '%s ' % getattr(self, _get_node_type(node.cond))(node.cond) 277 | text += getattr(self, _get_node_type(node.body))(node.body) 278 | if node._else is not None: 279 | text = text.rstrip() 280 | text += ' else %s' % getattr(self, _get_node_type(node._else))(node._else).lstrip() 281 | return text 282 | 283 | def switch_stmt(self, node): 284 | text = '%sswitch ' % (self.indent * INDENT) 285 | if node.init is not None: 286 | text += '%s ' % getattr(self, _get_node_type(node.init))(node.init).lstrip().rstrip() 287 | if node.tag is not None: 288 | if node.init is not None: 289 | text = '%s; ' % text.rstrip() 290 | text += '%s ' % getattr(self, _get_node_type(node.tag))(node.tag) 291 | text += getattr(self, _get_node_type(node.body))(node.body) 292 | return text 293 | 294 | def case_clause(self, node): 295 | keyword = 'case ' if node.list else 'default' 296 | text = '%s%s' % ( 297 | (self.indent - 1) * INDENT, 298 | keyword 299 | ) 300 | for elt in node.list: 301 | text += '%s, ' % getattr(self, _get_node_type(elt))(elt) 302 | if node.list: 303 | text = text[:-2] 304 | text += ':\n' 305 | for elt in node.body: 306 | text += getattr(self, _get_node_type(elt))(elt) 307 | return text 308 | 309 | def array_type(self, node): 310 | return '[%s]%s' % ( 311 | getattr(self, _get_node_type(node.len))(node.len), 312 | getattr(self, _get_node_type(node.elt))(node.elt) 313 | ) 314 | 315 | def index_expr(self, node): 316 | return '%s[%s]' % ( 317 | getattr(self, _get_node_type(node.x))(node.x), 318 | getattr(self, _get_node_type(node.index))(node.index) 319 | ) 320 | 321 | def basic_lit(self, node): 322 | if node.kind == Token.STRING: 323 | return '%s' % json.dumps(node.value) 324 | elif node.kind == Token.CHAR: 325 | return '\'%s\'' % node.value 326 | elif node.kind == Token.TRUE: 327 | return 'true' 328 | elif node.kind == Token.FALSE: 329 | return 'false' 330 | else: 331 | return node.value 332 | 333 | def composite_lit(self, node): 334 | if node.elts: 335 | text = '%s{\n' % getattr(self, _get_node_type(node.type))(node.type) 336 | self.indent += 1 337 | text += '%s' % getattr(self, _get_node_type(node.elts))( 338 | node.elts, 339 | separator=',\n', 340 | indent=(self.indent * INDENT) 341 | ) 342 | self.indent -= 1 343 | text += '%s}' % (self.indent * INDENT) 344 | return text 345 | else: 346 | return '%s{}' % getattr(self, _get_node_type(node.type))(node.type) 347 | 348 | def decl_stmt(self, node): 349 | return getattr(self, _get_node_type(node.decl))(node.decl) 350 | 351 | def gen_decl(self, node): 352 | text = '%s%s ' % ( 353 | self.indent * INDENT, 354 | node.tok 355 | ) 356 | if len(node.specs) > 1: 357 | text += '(\n' 358 | self.indent += 1 359 | for spec in node.specs: 360 | if len(node.specs) > 1: 361 | text += '%s%s\n' % ( 362 | self.indent * INDENT, 363 | getattr(self, _get_node_type(spec))(spec).rstrip() 364 | ) 365 | else: 366 | text += getattr(self, _get_node_type(spec))(spec) 367 | if len(node.specs) > 1: 368 | self.indent -= 1 369 | text += ')\n' 370 | return text + '\n' 371 | 372 | def ident(self, node): 373 | return node.name 374 | 375 | def type_assert_expr(self, node): 376 | _type = 'type' if node.type is None else getattr(self, _get_node_type(node.type))(node.type) 377 | return '%s.(%s)' % ( 378 | getattr(self, _get_node_type(node.x))(node.x), 379 | _type 380 | ) 381 | 382 | def slice_expr(self, node): 383 | low = node.low if node.low is not None else '' 384 | high = node.high if node.high is not None else '' 385 | text = '%s[%s:%s' % ( 386 | getattr(self, _get_node_type(node.x))(node.x), 387 | getattr(self, _get_node_type(low))(low), 388 | getattr(self, _get_node_type(high))(high) 389 | ) 390 | if node.slice3: 391 | _max = node.max if node.max is not None else '' 392 | text += ':%s' % getattr(self, _get_node_type(_max))(_max) 393 | return text + ']' 394 | 395 | def map_type(self, node): 396 | return 'map[%s]%s' % ( 397 | getattr(self, _get_node_type(node.key))(node.key), 398 | getattr(self, _get_node_type(node.value))(node.value) 399 | ) 400 | 401 | def key_value_expr(self, node): 402 | return '%s: %s' % ( 403 | getattr(self, _get_node_type(node.key))(node.key), 404 | getattr(self, _get_node_type(node.value))(node.value) 405 | ) 406 | 407 | def ellipsis(self, node): 408 | return '...%s' % getattr(self, _get_node_type(node.type))(node.type) 409 | 410 | def func_lit(self, node): 411 | return 'func%s %s' % ( 412 | getattr(self, _get_node_type(node.type))(node.type), 413 | getattr(self, _get_node_type(node.body))(node.body).rstrip() 414 | ) 415 | 416 | def star_expr(self, node): 417 | return '*%s' % getattr(self, _get_node_type(node.x))(node.x) 418 | 419 | def type_spec(self, node): 420 | return '%s %s' % ( 421 | getattr(self, _get_node_type(node.name))(node.name), 422 | getattr(self, _get_node_type(node.type))(node.type) 423 | ) 424 | 425 | def struct_type(self, node): 426 | return 'struct {\n%s\n}\n' % ( 427 | getattr(self, _get_node_type(node.fields))( 428 | node.fields, 429 | separator='\n', 430 | indent=((self.indent + 1) * INDENT) 431 | ) 432 | ) 433 | 434 | def interface_type(self, node): 435 | text = 'interface' 436 | if node.methods.list: 437 | text += ' {\n%s\n}\n' % getattr(self, _get_node_type(node.methods))( 438 | node.methods, 439 | separator='\n', 440 | indent=((self.indent + 1) * INDENT) 441 | ) 442 | else: 443 | text += '{}' 444 | return text 445 | 446 | 447 | def unparse(tree): 448 | generator = Generator() 449 | if isinstance(tree, (tuple, list)): 450 | result = '' 451 | for elt in tree: 452 | result += getattr(generator, _get_node_type(elt))(elt).rstrip() + '\n' 453 | return result 454 | else: 455 | return getattr(generator, _get_node_type(tree))(tree).rstrip() + '\n' 456 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude = ./gopygo/__init__.py,./gopygo/parser.py 3 | max-line-length = 120 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | 4 | setup( 5 | name="gopygo", 6 | packages=["gopygo"], 7 | version="0.3.2", 8 | author="M. Mert Yildiran", 9 | author_email="mehmet@up9.com", 10 | url="http://github.com/up9inc/gopygo", 11 | description="Pure Python Go parser, AST and unparser library", 12 | classifiers=[ 13 | "Programming Language :: Python", 14 | "Development Status :: 4 - Beta", 15 | "Operating System :: OS Independent", 16 | "License :: OSI Approved :: MIT License", 17 | "Intended Audience :: Developers", 18 | "Topic :: Software Development :: Libraries" 19 | ], 20 | long_description="""\ 21 | ======== 22 | gopygo 23 | ======== 24 | 25 | gopygo is a Go programming language parser written in Python. 26 | AST library based on original go/ast package https://golang.org/pkg/go/ast/ 27 | 28 | """, 29 | zip_safe=False, 30 | install_requires=['sly'], 31 | extras_require={ 32 | 'dev': [ 33 | 'flake8', 34 | 'pytest', 35 | 'coverage', 36 | 'codecov' 37 | ] 38 | }, 39 | ) 40 | -------------------------------------------------------------------------------- /tests/test_parser.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from gopygo import parse, unparse 4 | from gopygo.exceptions import LexerError 5 | 6 | 7 | class TestParser(): 8 | 9 | def setup_method(self): 10 | self.program = None 11 | 12 | def parse_unparse(self, expect=None): 13 | self.program = self.program.lstrip() 14 | 15 | tree = parse(self.program) 16 | text = unparse(tree) 17 | if expect is not None: 18 | assert expect == text 19 | else: 20 | assert self.program == text 21 | 22 | def test_001_hello_world(self): 23 | self.program = """ 24 | package main 25 | 26 | import "fmt" 27 | 28 | func main() { 29 | fmt.Println("Hello, World!") 30 | } 31 | """ 32 | self.parse_unparse() 33 | 34 | def test_002_only_package(self): 35 | self.program = """ 36 | package main 37 | """ 38 | self.parse_unparse() 39 | 40 | def test_003_no_declarations(self): 41 | self.program = """ 42 | package main 43 | 44 | import "fmt" 45 | """ 46 | self.parse_unparse() 47 | 48 | def test_004_multiple_imports(self): 49 | self.program = """ 50 | package main 51 | 52 | import "fmt" 53 | 54 | import "rsc.io/quote" 55 | """ 56 | self.parse_unparse() 57 | 58 | def test_005_single_line_comment(self): 59 | self.program = """ 60 | package main 61 | 62 | import "fmt" 63 | 64 | // Main function 65 | func main() { 66 | fmt.Println("Hello, World!") 67 | } 68 | """ 69 | self.parse_unparse() 70 | 71 | def test_006_func_type(self): 72 | self.program = """ 73 | package main 74 | 75 | import "fmt" 76 | 77 | func Hello(name string) string { 78 | fmt.Println("Hello, World!") 79 | } 80 | """ 81 | self.parse_unparse() 82 | 83 | def test_007_comment_inside_function_body(self): 84 | self.program = """ 85 | package main 86 | 87 | import "fmt" 88 | 89 | // Main function 90 | func main() { 91 | // Comment inside function body 92 | fmt.Println("Hello, World!") 93 | } 94 | """ 95 | self.parse_unparse() 96 | 97 | def test_008_multiple_function_call_args(self): 98 | self.program = """ 99 | fmt.Sprintf("Hi, %v. Welcome!", name) 100 | """ 101 | self.parse_unparse() 102 | 103 | def test_009_assign_stmt(self): 104 | self.program = """ 105 | message = fmt.Sprintf("Hi, %v. Welcome!", name) 106 | message := fmt.Sprintf("Hi, %v. Welcome!", name) 107 | a += 3 108 | a -= 3 109 | a *= 3 110 | a /= 3 111 | a %= 3 112 | a &= 3 113 | a |= 3 114 | a ^= 3 115 | a &^= 3 116 | a <<= 3 117 | a >>= 3 118 | """ 119 | self.parse_unparse() 120 | 121 | def test_010_return_stmt(self): 122 | self.program = """ 123 | func Hello(name string) (string, string) { 124 | message := fmt.Sprintf("Hi, %v. Welcome!", name) 125 | message2 := fmt.Sprintf("%v, GO!", name) 126 | return message, message2 127 | } 128 | """ 129 | self.parse_unparse() 130 | 131 | def test_011_variable_declaration(self): 132 | self.program = """ 133 | var ret string 134 | var ret2 string 135 | var a = "initial" 136 | var a uint8 = 2 137 | var b, c string 138 | var b, c int = 1, 2 139 | var b, c int32 = 1, 2 140 | var a, b, c int32 = 1, 2, 3 141 | var d = true 142 | var e int 143 | f := "apple" 144 | var X uint8 = 225 145 | var Y int16 = 32767 146 | a := 20.45 147 | b := 34.89 148 | c := b - a 149 | var a complex128 = complex(6, 2) 150 | var b complex64 = complex(9, 2) 151 | str1 := "Test" 152 | var ToBe bool = false 153 | ToBe = true 154 | var absoluteZero int = -459 155 | sum := 116 - 68 156 | var maxUint32 uint32 = 4294967295 157 | var pi float64 158 | var pi float64 = 3.14 159 | var x complex128 = 1 + 2i 160 | var y complex128 = 1 + 3.14i 161 | var r rune = 'a' 162 | var b byte = 'b' 163 | """ 164 | self.parse_unparse() 165 | 166 | def test_012_lhs_list_assign_stmt(self): 167 | self.program = """ 168 | var ret string 169 | var ret2 string 170 | ret, ret2 = Hello("gopygo") 171 | fmt.Println(ret) 172 | fmt.Println(ret2) 173 | """ 174 | self.parse_unparse() 175 | 176 | def test_013_milestone_1_multiple_func_decls(self): 177 | self.program = """ 178 | package main 179 | 180 | import "fmt" 181 | 182 | // Hello returns a greeting for the named person. 183 | func Hello(name string) (string, string) { 184 | // Return a greeting that embeds the name in a message. 185 | message := fmt.Sprintf("Hi, %v. Welcome!", name) 186 | message2 := fmt.Sprintf("%v, GO!", name) 187 | return message, message2 188 | } 189 | 190 | func main() { 191 | var ret string 192 | var ret2 string 193 | ret, ret2 = Hello("gopygo") 194 | fmt.Println(ret) 195 | fmt.Println(ret2) 196 | } 197 | """ 198 | self.parse_unparse() 199 | 200 | def test_014_no_call_args(self): 201 | self.program = """ 202 | fmt.Println() 203 | """ 204 | self.parse_unparse() 205 | 206 | def test_015_operators(self): 207 | self.program = """ 208 | a := 3 + 5 209 | b := 3 - 5 210 | c := 3 * 5 211 | d := 3 / 5 212 | d := 10 % 9 213 | e := -5 214 | f := 3 * (5 + 7) 215 | a := 3 + 5 + 3 216 | b := "go" + "lang" 217 | d := true && false 218 | c := true || false 219 | c := true == false 220 | c := a << b 221 | c := a >> b 222 | c := a &^ b 223 | c := a != b 224 | c := a >= b 225 | c := a <= b 226 | c := a & b 227 | c := a | b 228 | c := a ^ b 229 | c := a > b 230 | c := a < b 231 | e := d <- c 232 | f := ++a 233 | f := a++ 234 | f := --a 235 | f := a-- 236 | a := ^0011 237 | a := !false 238 | """ 239 | self.parse_unparse() 240 | 241 | def test_016_only_import(self): 242 | self.program = """ 243 | import "fmt" 244 | """ 245 | self.parse_unparse() 246 | 247 | def test_017_only_imports(self): 248 | self.program = """ 249 | import "fmt" 250 | import "rsc.io/quote" 251 | """ 252 | self.parse_unparse() 253 | 254 | def test_018_const(self): 255 | self.program = """ 256 | const s string = "constant" 257 | const n = 500000000 258 | const d = 3e20 / n 259 | """ 260 | self.parse_unparse() 261 | 262 | def test_019_empty_line_after_block_stmt_start(self): 263 | self.program = """ 264 | package main 265 | 266 | import "fmt" 267 | 268 | func main() { 269 | 270 | fmt.Println("go" + "lang") 271 | } 272 | """ 273 | self.program = self.program[1:] 274 | 275 | tree = parse(self.program) 276 | text = unparse(tree) 277 | lines = self.program.split('\n') 278 | del lines[5] 279 | self.program = '\n'.join(lines) 280 | assert self.program == text 281 | 282 | def test_020_import_list(self): 283 | self.program = """ 284 | import ( 285 | "fmt" 286 | "math" 287 | ) 288 | """ 289 | self.parse_unparse() 290 | 291 | def test_021_type_support_in_field_list(self): 292 | self.program = """ 293 | package main 294 | 295 | import "fmt" 296 | 297 | func plus(a int, b int) int { 298 | return a + b 299 | } 300 | 301 | func plus(a float32, b float64) (rune, byte) { 302 | return a + b 303 | } 304 | 305 | func main() { 306 | res := plus(1, 2) 307 | fmt.Println("1+2 =", res) 308 | res = plusPlus(1, 2, 3) 309 | fmt.Println("1+2+3 =", res) 310 | } 311 | """ 312 | self.parse_unparse() 313 | 314 | def test_022_for(self): 315 | self.program = """ 316 | package main 317 | 318 | import "fmt" 319 | 320 | func main() { 321 | i := 1 322 | for i <= 3 { 323 | fmt.Println(i) 324 | i = i + 1 325 | } 326 | for j := 7; j <= 9; j++ { 327 | fmt.Println(j) 328 | } 329 | for { 330 | fmt.Println("loop") 331 | break 332 | } 333 | for n := 0; n <= 5; n++ { 334 | fmt.Println(n) 335 | continue 336 | } 337 | } 338 | """ 339 | self.parse_unparse() 340 | 341 | def test_023_goto(self): 342 | self.program = """ 343 | package main 344 | 345 | import "fmt" 346 | 347 | func main() { 348 | var a int = 10 349 | MAIN_START: 350 | a = 3 351 | goto MAIN_START 352 | } 353 | """ 354 | self.parse_unparse() 355 | 356 | def test_024_if_else(self): 357 | self.program = """ 358 | package main 359 | 360 | import "fmt" 361 | 362 | func main() { 363 | if 7 % 2 == 0 { 364 | fmt.Println("7 is even") 365 | } else { 366 | fmt.Println("7 is odd") 367 | } 368 | if 8 % 4 == 0 { 369 | fmt.Println("8 is divisible by 4") 370 | } 371 | if num := 9; num < 0 { 372 | fmt.Println(num, "is negative") 373 | } else if num < 10 { 374 | fmt.Println(num, "has 1 digit") 375 | } else { 376 | fmt.Println(num, "has multiple digits") 377 | } 378 | } 379 | """ 380 | self.parse_unparse() 381 | 382 | def test_025_switch_case(self): 383 | self.program = """ 384 | package main 385 | 386 | import ( 387 | "fmt" 388 | "time" 389 | ) 390 | 391 | func main() { 392 | i := 2 393 | switch i { 394 | case 1: 395 | fmt.Println("one") 396 | case 2: 397 | fmt.Println("two") 398 | case 3: 399 | fmt.Println("three") 400 | } 401 | switch time.Now().Weekday() { 402 | case time.Saturday, time.Sunday: 403 | fmt.Println("It's the weekend") 404 | default: 405 | fmt.Println("It's a weekday") 406 | } 407 | t := time.Now() 408 | switch { 409 | case t.Hour() < 12: 410 | fmt.Println("It's before noon") 411 | default: 412 | fmt.Println("It's after noon") 413 | } 414 | switch t := 2; t + 1 { 415 | case 1: 416 | fmt.Println("one") 417 | case 2: 418 | fmt.Println("two") 419 | case 3: 420 | fmt.Println("three") 421 | } 422 | switch t := i.(type) { 423 | case bool: 424 | fmt.Println("I'm a bool") 425 | case int, float32: 426 | fmt.Println("I'm an int") 427 | default: 428 | fmt.Printf("Don't know type %T", t) 429 | } 430 | } 431 | """ 432 | self.parse_unparse() 433 | 434 | def test_024_arrays(self): 435 | self.program = """ 436 | package main 437 | 438 | import "fmt" 439 | 440 | func main() { 441 | var a [5]int 442 | fmt.Println("emp:", a) 443 | a[4] = 100 444 | fmt.Println("set:", a) 445 | fmt.Println("get:", a[4]) 446 | fmt.Println("len:", len(a)) 447 | b := [5]int{ 448 | 1, 449 | 2, 450 | 3, 451 | 4, 452 | 5, 453 | } 454 | fmt.Println("dcl:", b) 455 | var twoD [2][3]int 456 | var twoD [2][3]int 457 | for i := 0; i < 2; i++ { 458 | for j := 0; j < 3; j++ { 459 | twoD[i][j] = i + j 460 | } 461 | } 462 | fmt.Println("2d: ", twoD) 463 | } 464 | """ 465 | self.parse_unparse() 466 | 467 | def test_025_slices(self): 468 | self.program = """ 469 | package main 470 | 471 | import ( 472 | "fmt" 473 | "time" 474 | ) 475 | 476 | func main() { 477 | s := make([]string, 3) 478 | fmt.Println("emp:", s) 479 | s[0] = "a" 480 | s[1] = "b" 481 | s[2] = "c" 482 | fmt.Println("set:", s) 483 | fmt.Println("get:", s[2]) 484 | fmt.Println("len:", len(s)) 485 | s = append(s, "d") 486 | s = append(s, "e", "f") 487 | fmt.Println("apd:", s) 488 | c := make([]string, len(s)) 489 | copy(c, s) 490 | fmt.Println("cpy:", c) 491 | l := s[2:5] 492 | fmt.Println("sl1:", l) 493 | l = s[:5] 494 | fmt.Println("sl2:", l) 495 | l = s[2:] 496 | fmt.Println("sl3:", l) 497 | t := []string{ 498 | "g", 499 | "h", 500 | "i", 501 | } 502 | fmt.Println("dcl:", t) 503 | l := s[2:5:7] 504 | twoD := make([][]int, 3) 505 | for i := 0; i < 3; i++ { 506 | innerLen := i + 1 507 | twoD[i] = make([]int, innerLen) 508 | for j := 0; j < innerLen; j++ { 509 | twoD[i][j] = i + j 510 | } 511 | } 512 | fmt.Println("2d: ", twoD) 513 | } 514 | """ 515 | self.parse_unparse() 516 | 517 | def test_026_maps(self): 518 | self.program = """ 519 | package main 520 | 521 | import ( 522 | "fmt" 523 | "time" 524 | ) 525 | 526 | func main() { 527 | m := make(map[string]int) 528 | m["k1"] = 7 529 | m["k2"] = 13 530 | fmt.Println("map:", m) 531 | v1 := m["k1"] 532 | fmt.Println("v1: ", v1) 533 | fmt.Println("len:", len(m)) 534 | delete(m, "k2") 535 | fmt.Println("map:", m) 536 | _, prs := m["k2"] 537 | fmt.Println("prs:", prs) 538 | n := map[string]int{ 539 | "foo": 1, 540 | "bar": 2, 541 | } 542 | fmt.Println("map:", n) 543 | } 544 | """ 545 | self.parse_unparse() 546 | 547 | def test_027_range(self): 548 | self.program = """ 549 | package main 550 | 551 | import "fmt" 552 | 553 | func main() { 554 | nums := []int{} 555 | nums := []int{ 556 | 2, 557 | 3, 558 | 4, 559 | } 560 | sum := 0 561 | for _, num := range nums { 562 | sum += num 563 | } 564 | fmt.Println("sum:", sum) 565 | for i, num := range nums { 566 | if num == 3 { 567 | fmt.Println("index:", i) 568 | } 569 | } 570 | kvs := map[string]string{ 571 | "a": "apple", 572 | "b": "banana", 573 | } 574 | for k, v := range kvs { 575 | fmt.Printf("%s -> %s", k, v) 576 | } 577 | for k := range kvs { 578 | fmt.Println("key:", k) 579 | } 580 | for i, c := range "go" { 581 | fmt.Println(i, c) 582 | } 583 | nums := []int{ 584 | 1, 585 | 2, 586 | 3, 587 | 4, 588 | } 589 | var num int 590 | for _, num = range nums { 591 | fmt.Println("num:", num) 592 | } 593 | for range nums { 594 | num++ 595 | fmt.Println("num:", num) 596 | } 597 | } 598 | """ 599 | self.parse_unparse() 600 | 601 | def test_028_ellipsis(self): 602 | self.program = """ 603 | package main 604 | 605 | import "fmt" 606 | 607 | func sum(nums ...int) { 608 | fmt.Print(nums, " ") 609 | total := 0 610 | for _, num := range nums { 611 | total += num 612 | } 613 | fmt.Println(total) 614 | } 615 | 616 | func main() { 617 | sum(1, 2) 618 | sum(1, 2, 3) 619 | nums := []int{ 620 | 1, 621 | 2, 622 | 3, 623 | 4, 624 | } 625 | sum(nums...) 626 | } 627 | """ 628 | self.parse_unparse() 629 | 630 | def test_029_closures(self): 631 | self.program = """ 632 | package main 633 | 634 | import "fmt" 635 | 636 | func closure() func() int { 637 | i := 0 638 | return func() int { 639 | i++ 640 | return i 641 | } 642 | } 643 | 644 | func main() { 645 | nextInt := closure() 646 | fmt.Println(nextInt()) 647 | fmt.Println(nextInt()) 648 | fmt.Println(nextInt()) 649 | newInts := closure() 650 | fmt.Println(newInts()) 651 | } 652 | """ 653 | self.parse_unparse() 654 | 655 | def test_030_multiple_closures(self): 656 | self.program = """ 657 | package main 658 | 659 | import "fmt" 660 | 661 | func closure() (func() int, func() int) { 662 | i := 0 663 | j := 0 664 | return func() int { 665 | i++ 666 | return i 667 | }, func() int { 668 | j-- 669 | return j 670 | } 671 | } 672 | 673 | func main() { 674 | nextInt, nextInt2 := closure() 675 | fmt.Println(nextInt()) 676 | fmt.Println(nextInt()) 677 | fmt.Println(nextInt()) 678 | fmt.Println(nextInt2()) 679 | fmt.Println(nextInt2()) 680 | fmt.Println(nextInt2()) 681 | newInts, nextInts2 := closure() 682 | fmt.Println(newInts()) 683 | fmt.Println(nextInts2()) 684 | } 685 | """ 686 | self.parse_unparse() 687 | 688 | def test_031_recursion(self): 689 | self.program = """ 690 | package main 691 | 692 | import "fmt" 693 | 694 | func fact(n int) int { 695 | if n == 0 { 696 | return 1 697 | } 698 | return n * fact(n - 1) 699 | } 700 | 701 | func main() { 702 | fmt.Println(fact(7)) 703 | } 704 | """ 705 | self.parse_unparse() 706 | 707 | def test_032_pointers(self): 708 | self.program = """ 709 | package main 710 | 711 | import "fmt" 712 | 713 | func zeroval(ival int) { 714 | ival = 0 715 | } 716 | 717 | func zeroptr(iptr *int) { 718 | *iptr = 0 719 | } 720 | 721 | func main() { 722 | i := 1 723 | fmt.Println("initial:", i) 724 | zeroval(i) 725 | fmt.Println("zeroval:", i) 726 | zeroptr(&i) 727 | fmt.Println("zeroptr:", i) 728 | fmt.Println("pointer:", &i) 729 | } 730 | """ 731 | self.parse_unparse() 732 | 733 | def test_033_structs(self): 734 | self.program = """ 735 | type person struct { 736 | name string 737 | age int 738 | } 739 | type Vertex struct { 740 | X int 741 | Y int 742 | } 743 | type Employee struct { 744 | firstName string 745 | lastName string 746 | age int 747 | salary int 748 | } 749 | func newPerson(name string) *person { 750 | fmt.Println("Hello, World!") 751 | } 752 | """ 753 | self.parse_unparse() 754 | 755 | def test_034_testify(self): 756 | self.program = """ 757 | package yours 758 | 759 | import ( 760 | "testing" 761 | "github.com/stretchr/testify/assert" 762 | ) 763 | 764 | func TestSomething(t *testing.T) { 765 | assert := assert.New(t) 766 | assert.Equal(123, 123, "they should be equal") 767 | assert.NotEqual(123, 456, "they should not be equal") 768 | assert.Nil(object) 769 | if assert.NotNil(object) { 770 | assert.Equal("Something", object.Value) 771 | } 772 | } 773 | """ 774 | self.parse_unparse() 775 | 776 | def test_035_import_with_period_1(self): 777 | self.program = """ 778 | import ( 779 | "testing" 780 | other "./somelib" 781 | . "./somelib" 782 | ) 783 | """ 784 | self.parse_unparse() 785 | 786 | def test_036_import_with_period_2(self): 787 | self.program = """ 788 | import . "./somelib" 789 | import other "./somelib" 790 | """ 791 | self.parse_unparse() 792 | 793 | def test_037_interface(self): 794 | self.program = """ 795 | m := map[string]interface{}{ 796 | "a": "apple", 797 | "b": 2, 798 | } 799 | type geometry interface { 800 | area() float64 801 | perim() float64 802 | } 803 | """ 804 | self.parse_unparse() 805 | 806 | def test_038_assign_stmt_disabled_lhs(self): 807 | self.program = """ 808 | _ := some.Call() 809 | """ 810 | expect = """ 811 | some.Call() 812 | """ 813 | self.parse_unparse(expect.lstrip()) 814 | 815 | 816 | class TestExceptions(): 817 | 818 | def test_001_lexer_error(self): 819 | program = """ 820 | package ~ 821 | """ 822 | program = program[1:] 823 | with pytest.raises( 824 | LexerError, 825 | match=r"Illegal character '~'" 826 | ): 827 | parse(program) 828 | --------------------------------------------------------------------------------