├── .gitignore ├── LICENSE ├── README.md ├── astAddOp.go ├── astBoolOp.go ├── astExpOp.go ├── astExpr.go ├── astFnCall.go ├── astLit.go ├── astMulOp.go ├── astOp.go ├── astPart.go ├── astPredef.go ├── astRelOp.go ├── astType.go ├── astValue.go ├── astVarRef.go ├── cmdData.go ├── cmdDef.go ├── cmdDim.go ├── cmdEnd.go ├── cmdFor.go ├── cmdGo.go ├── cmdIf.go ├── cmdInput.go ├── cmdLet.go ├── cmdNext.go ├── cmdOn.go ├── cmdPrint.go ├── cmdRead.go ├── cmdRem.go ├── cmdRestore.go ├── cmdReturn.go ├── cmdRun.go ├── cmdStop.go ├── genC.go ├── go.mod ├── lexer.go ├── lib ├── basiclib.c └── basiclib.h ├── main.go ├── parser.go ├── program.go ├── solver.go ├── token.go └── visit.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jucie Dias Andrade 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 | # basic 2 | A compiler for BASIC programming language. It generates C code that you submit to the C compiler for your particular platform. 3 | 4 | This tool understands what is in David H. Ahl's own words "the gold standard of microcomputer BASICs: MITS Altair 8K BASIC, Rev. 4.0 (ix)." 5 | It has been tested against the programs from Ahl's bestseller "BASIC Computer Games". More information can be found here: 6 | 7 | https://www.atariarchives.org/basicgames 8 | 9 | You can download the entire set of games as a tarball or a ZIP archive: 10 | 11 | http://vintage-basic.net/downloads/bcg.tar.gz 12 | 13 | http://vintage-basic.net/downloads/bcg.zip 14 | 15 | Please keep in mind that those programs are extremely simple if compared to today standards. They were written to be used with TTY terminals. There are a couple programs in that compressed folders that were written in newer BASIC dialects. Those programs won't work. 16 | 17 | To clone and generate this compiler: 18 | 19 | go get -u github.com/jucie/basic 20 | 21 | To generate the C code for a game run the command like this: 22 | 23 | basic game.bas game.c 24 | 25 | The lib folder has some support files to be compiled with the generated C code. Be sure to keep them in the same folder as the game C file. Then use youc C compiler of choice to generate the executable binary: 26 | 27 | gcc game.c basiclib.c 28 | 29 | The programs have been tested on half a dozen distinct C compilers in both Windows and Linux. 30 | 31 | I would like to thank: 32 | 33 | .David Ahl for the awesome work collecting all those programs and promoting BASIC 34 | 35 | .Lyle Kopnicky for inspiration (he wrote a BASIC interpreter. Check it out at http://vintage-basic.net ) 36 | 37 | .Atari Archives for the already typed program files 38 | 39 | A similar project by Everton Marques: 40 | 41 | https://www.github.com/udhos/basgo 42 | -------------------------------------------------------------------------------- /astAddOp.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type astAddOpTail struct { 4 | oper token 5 | val *astMulOp 6 | } 7 | 8 | type astAddOp struct { 9 | head *astMulOp 10 | tail []astAddOpTail 11 | } 12 | 13 | func (p *parser) parseAddOp() *astAddOp { 14 | head := p.parseMulOp() 15 | if head == nil { 16 | return nil 17 | } 18 | result := &astAddOp{head: head} 19 | 20 | for { 21 | oper := p.lex.peek().token 22 | if !isAddOp(oper) || head.finalType() == strType && oper == '-' { 23 | break 24 | } 25 | p.lex.next() 26 | 27 | val := p.parseMulOp() 28 | if val == nil { 29 | return nil 30 | } 31 | result.tail = append(result.tail, astAddOpTail{oper: oper, val: val}) 32 | } 33 | return result 34 | } 35 | 36 | func isAddOp(b token) bool { 37 | return b == '+' || b == '-' 38 | } 39 | 40 | func (a astAddOp) receive(g guest) { 41 | g.visit(a.head) 42 | for _, t := range a.tail { 43 | g.visit(t.val) 44 | } 45 | } 46 | 47 | func (a astAddOp) finalType() astType { 48 | if len(a.tail) == 0 { 49 | return a.head.finalType() 50 | } 51 | return numType 52 | } 53 | 54 | var nextTemp = 0 55 | 56 | func createTemp() int { 57 | val := nextTemp 58 | nextTemp++ 59 | return val 60 | } 61 | -------------------------------------------------------------------------------- /astBoolOp.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type astBoolOpTail struct { 4 | oper token 5 | val *astRelOp 6 | } 7 | type astBoolOp struct { 8 | head *astRelOp 9 | tail []astBoolOpTail 10 | } 11 | 12 | func isBoolOp(op token) bool { 13 | return op == tokOr || op == tokAnd 14 | } 15 | 16 | func (p *parser) parseBoolOp() *astBoolOp { 17 | head := p.parseRelOp() 18 | if head == nil { 19 | return nil 20 | } 21 | result := &astBoolOp{head: head} 22 | 23 | for { 24 | oper := p.lex.peek().token 25 | if !isBoolOp(oper) { 26 | break 27 | } 28 | p.lex.next() 29 | 30 | val := p.parseRelOp() 31 | if val == nil { 32 | return nil 33 | } 34 | result.tail = append(result.tail, astBoolOpTail{oper: oper, val: val}) 35 | } 36 | return result 37 | } 38 | 39 | func (a astBoolOp) receive(g guest) { 40 | g.visit(a.head) 41 | for _, t := range a.tail { 42 | g.visit(t.val) 43 | } 44 | } 45 | 46 | func (a astBoolOp) finalType() astType { 47 | if len(a.tail) == 0 { 48 | return a.head.finalType() 49 | } 50 | return numType 51 | } 52 | -------------------------------------------------------------------------------- /astExpOp.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type astExpOp struct { 4 | lhs *astPart 5 | rhs *astPart 6 | } 7 | 8 | func (p *parser) parseExpOp() *astExpOp { 9 | lhs := p.parsePart() 10 | if lhs == nil { 11 | return nil 12 | } 13 | result := &astExpOp{lhs: lhs} 14 | 15 | oper := p.lex.peek().token 16 | if oper == '^' { 17 | p.lex.next() 18 | 19 | rhs := p.parsePart() 20 | if rhs == nil { 21 | return nil 22 | } 23 | result.rhs = rhs 24 | } 25 | 26 | return result 27 | } 28 | 29 | func (a astExpOp) receive(g guest) { 30 | g.visit(a.lhs) 31 | if a.rhs != nil { 32 | g.visit(a.rhs) 33 | } 34 | } 35 | 36 | func (a astExpOp) finalType() astType { 37 | if a.rhs == nil { 38 | return a.lhs.finalType() 39 | } 40 | return numType 41 | } 42 | -------------------------------------------------------------------------------- /astExpr.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type astExpr struct { 4 | boolOp *astBoolOp 5 | paren bool 6 | } 7 | 8 | func (p *parser) parseExpr(paren bool) *astExpr { 9 | boolOp := p.parseBoolOp() 10 | if boolOp == nil { 11 | return nil 12 | } 13 | return &astExpr{boolOp: boolOp, paren: paren} 14 | } 15 | 16 | func (a astExpr) receive(g guest) { 17 | g.visit(a.boolOp) 18 | } 19 | 20 | func (a astExpr) finalType() astType { 21 | return a.boolOp.finalType() 22 | } 23 | -------------------------------------------------------------------------------- /astFnCall.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type astFnCall struct { 4 | id string 5 | arg *astExpr 6 | } 7 | 8 | func (p *parser) parseFnCall() *astFnCall { 9 | l := p.lex.peek() 10 | 11 | if l.token != tokID { 12 | p.unexpected() 13 | return nil 14 | } 15 | id := l.s 16 | p.lex.next() 17 | 18 | if l.token != '(' { 19 | p.unexpected() 20 | return nil 21 | } 22 | p.lex.next() 23 | 24 | arg := p.parseExpr(false) 25 | if arg == nil { 26 | return nil 27 | } 28 | 29 | if l.token != ')' { 30 | p.unexpected() 31 | return nil 32 | } 33 | p.lex.next() 34 | 35 | return &astFnCall{id, arg} 36 | } 37 | 38 | func (a astFnCall) receive(g guest) { 39 | g.visit(a.arg) 40 | } 41 | 42 | func (a astFnCall) finalType() astType { 43 | return numType 44 | } 45 | -------------------------------------------------------------------------------- /astLit.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type astLit struct { 4 | val string 5 | _type astType 6 | } 7 | 8 | func (p *parser) parseLit() *astLit { 9 | l := p.lex.peek() 10 | val := l.s 11 | switch l.token { 12 | case tokNumber: 13 | p.lex.next() 14 | return &astLit{val: val, _type: numType} 15 | case tokString: 16 | p.lex.next() 17 | return &astLit{val: val, _type: strType} 18 | } 19 | return nil 20 | } 21 | 22 | func (a astLit) receive(g guest) { 23 | g.visit(a._type) 24 | } 25 | 26 | func (a astLit) finalType() astType { 27 | return a._type 28 | } 29 | -------------------------------------------------------------------------------- /astMulOp.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type astMulOpTail struct { 4 | oper token 5 | val *astExpOp 6 | } 7 | 8 | type astMulOp struct { 9 | head *astExpOp 10 | tail []astMulOpTail 11 | } 12 | 13 | func (p *parser) parseMulOp() *astMulOp { 14 | head := p.parseExpOp() 15 | if head == nil { 16 | return nil 17 | } 18 | result := &astMulOp{head: head} 19 | 20 | for { 21 | oper := p.lex.peek().token 22 | if !isMulOp(oper) { 23 | break 24 | } 25 | p.lex.next() 26 | 27 | val := p.parseExpOp() 28 | if val == nil { 29 | return nil 30 | } 31 | result.tail = append(result.tail, astMulOpTail{oper: oper, val: val}) 32 | } 33 | return result 34 | } 35 | 36 | func isMulOp(b token) bool { 37 | return b == '*' || b == '/' 38 | } 39 | 40 | func (a astMulOp) receive(g guest) { 41 | g.visit(a.head) 42 | for _, t := range a.tail { 43 | g.visit(t.val) 44 | } 45 | } 46 | 47 | func (a astMulOp) finalType() astType { 48 | if len(a.tail) == 0 { 49 | return a.head.finalType() 50 | } 51 | return numType 52 | } 53 | -------------------------------------------------------------------------------- /astOp.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type astOp struct { 4 | lhs *astPart 5 | rhs *astPart 6 | oper token 7 | } 8 | -------------------------------------------------------------------------------- /astPart.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type astPart struct { 4 | coord 5 | signal token 6 | val astValue 7 | } 8 | 9 | func (p *parser) parsePart() *astPart { 10 | l := p.lex.peek() 11 | result := &astPart{coord: l.pos} 12 | 13 | switch l.token { 14 | case '-': 15 | fallthrough 16 | case '+': 17 | result.signal = l.token 18 | p.lex.next() 19 | } 20 | 21 | switch l.token { 22 | case tokID: 23 | result.val = p.parseVarRef() 24 | case '(': 25 | p.lex.next() 26 | result.val = p.parseExpr(true) 27 | if l.token != ')' { 28 | break 29 | } 30 | p.lex.next() 31 | case tokFn: 32 | p.lex.next() 33 | result.val = p.parseFnCall() 34 | case tokNumber: 35 | fallthrough 36 | case tokString: 37 | result.val = p.parseLit() 38 | case tokSin: 39 | fallthrough 40 | case tokCos: 41 | fallthrough 42 | case tokAtn: 43 | fallthrough 44 | case tokTan: 45 | fallthrough 46 | case tokStr: 47 | fallthrough 48 | case tokSqr: 49 | fallthrough 50 | case tokExp: 51 | fallthrough 52 | case tokLog: 53 | fallthrough 54 | case tokAbs: 55 | fallthrough 56 | case tokInt: 57 | fallthrough 58 | case tokRnd: 59 | fallthrough 60 | case tokSgn: 61 | fallthrough 62 | case tokVal: 63 | fallthrough 64 | case tokChr: 65 | fallthrough 66 | case tokMid: 67 | fallthrough 68 | case tokLeft: 69 | fallthrough 70 | case tokRight: 71 | fallthrough 72 | case tokLen: 73 | fallthrough 74 | case tokAsc: 75 | fallthrough 76 | case tokTab: 77 | result.val = p.parsePredef() 78 | 79 | default: 80 | p.unexpected() 81 | return nil 82 | } 83 | return result 84 | } 85 | 86 | func (a astPart) receive(g guest) { 87 | g.visit(a.val) 88 | } 89 | 90 | func (a astPart) finalType() astType { 91 | return a.val.finalType() 92 | } 93 | -------------------------------------------------------------------------------- /astPredef.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type astPredef struct { 4 | function token 5 | args []*astExpr 6 | } 7 | 8 | func (p *parser) parsePredef() *astPredef { 9 | l := p.lex.peek() 10 | function := l.token 11 | p.lex.next() 12 | result := &astPredef{function: function} 13 | 14 | if l.token == '$' { 15 | p.lex.next() 16 | } 17 | 18 | if l.token != '(' { 19 | p.unexpected() 20 | return nil 21 | } 22 | p.lex.next() 23 | 24 | for { 25 | arg := p.parseExpr(false) 26 | if arg == nil { 27 | return nil 28 | } 29 | result.args = append(result.args, arg) 30 | if l.token == ',' { 31 | p.lex.next() 32 | continue 33 | } 34 | if l.token == ')' { 35 | p.lex.next() 36 | break 37 | } 38 | } 39 | return result 40 | } 41 | 42 | func (a astPredef) receive(g guest) { 43 | for _, arg := range a.args { 44 | g.visit(arg) 45 | } 46 | } 47 | 48 | func (a astPredef) finalType() astType { 49 | return predefs[a.function]._type 50 | } 51 | 52 | type predef struct { 53 | token 54 | name string 55 | _type astType 56 | params []astType 57 | } 58 | 59 | var predefs = make(map[token]predef) 60 | 61 | func init() { 62 | var predefInfo = []predef{ 63 | {tokAbs, "ABS", numType, []astType{numType}}, 64 | {tokAsc, "ASC", numType, []astType{strType}}, 65 | {tokAtn, "ATN", numType, []astType{numType}}, 66 | {tokChr, "CHR", strType, []astType{numType}}, 67 | {tokCos, "COS", numType, []astType{numType}}, 68 | {tokExp, "EXP", numType, []astType{numType}}, 69 | {tokInt, "INT", numType, []astType{numType}}, 70 | {tokLeft, "LEFT", strType, []astType{strType, numType}}, 71 | {tokLen, "LEN", numType, []astType{strType}}, 72 | {tokLog, "LOG", numType, []astType{numType}}, 73 | {tokMid, "MID", strType, []astType{strType, numType, numType}}, 74 | {tokRight, "RIGHT", strType, []astType{strType, numType}}, 75 | {tokRnd, "RND", numType, []astType{numType}}, 76 | {tokSgn, "SGN", numType, []astType{numType}}, 77 | {tokSin, "SIN", numType, []astType{numType}}, 78 | {tokSqr, "SQR", numType, []astType{numType}}, 79 | {tokStr, "STR", strType, []astType{numType}}, 80 | {tokTab, "TAB", voidType, []astType{numType}}, 81 | {tokTan, "TAN", numType, []astType{numType}}, 82 | {tokVal, "VAL", numType, []astType{strType}}, 83 | } 84 | for _, p := range predefInfo { 85 | predefs[p.token] = p 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /astRelOp.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type astRelOp struct { 4 | lhs *astAddOp 5 | rhs *astAddOp 6 | oper token 7 | } 8 | 9 | func (p *parser) parseRelOp() *astRelOp { 10 | lhs := p.parseAddOp() 11 | if lhs == nil { 12 | return nil 13 | } 14 | 15 | oper := p.lex.peek().token 16 | if !isRelOp(oper) { 17 | return &astRelOp{lhs: lhs} 18 | } 19 | p.lex.next() 20 | 21 | rhs := p.parseAddOp() 22 | if rhs == nil { 23 | return nil 24 | } 25 | return &astRelOp{lhs: lhs, rhs: rhs, oper: oper} 26 | } 27 | 28 | func isRelOp(b token) bool { 29 | switch b { 30 | case '=': 31 | fallthrough 32 | case '<': 33 | fallthrough 34 | case '>': 35 | fallthrough 36 | case tokLe: 37 | fallthrough 38 | case tokGe: 39 | fallthrough 40 | case tokNe: 41 | return true 42 | } 43 | return false 44 | } 45 | 46 | func (a astRelOp) receive(g guest) { 47 | g.visit(a.lhs) 48 | if a.rhs != nil { 49 | g.visit(a.rhs) 50 | } 51 | } 52 | 53 | func (a astRelOp) finalType() astType { 54 | if a.rhs != nil { 55 | return numType 56 | } 57 | return a.lhs.finalType() 58 | } 59 | -------------------------------------------------------------------------------- /astType.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type astType interface { 8 | host 9 | fmt.Stringer 10 | } 11 | 12 | type astNumType struct { 13 | } 14 | 15 | func (a astNumType) String() string { 16 | return "num" 17 | } 18 | 19 | func (a astNumType) receive(g guest) { 20 | } 21 | 22 | type astStrType struct { 23 | } 24 | 25 | func (a astStrType) String() string { 26 | return "str" 27 | } 28 | 29 | func (a astStrType) receive(g guest) { 30 | } 31 | 32 | type astVoidType struct { 33 | } 34 | 35 | func (a astVoidType) String() string { 36 | return "void" 37 | } 38 | 39 | func (a astVoidType) receive(g guest) { 40 | } 41 | 42 | var numType astNumType 43 | var strType astStrType 44 | var voidType astVoidType 45 | -------------------------------------------------------------------------------- /astValue.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type astValue interface { 4 | host 5 | finalType() astType 6 | } 7 | -------------------------------------------------------------------------------- /astVarRef.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | type astVarRef struct { 9 | coord 10 | id string 11 | _type astType 12 | index []*astExpr 13 | } 14 | 15 | func (p *parser) parseVarRef() *astVarRef { 16 | l := p.lex.peek() 17 | result := &astVarRef{coord: l.pos, _type: numType} 18 | 19 | if l.token != tokID { 20 | p.unexpected() 21 | return nil 22 | } 23 | result.id = l.s 24 | p.lex.next() 25 | 26 | if l.token == '$' { 27 | result._type = strType 28 | p.lex.next() 29 | } 30 | 31 | if l.token == '(' { 32 | p.lex.next() 33 | 34 | for { 35 | expr := p.parseExpr(false) 36 | if expr == nil { 37 | break 38 | } 39 | result.index = append(result.index, expr) 40 | if l.token != ',' { 41 | break 42 | } 43 | p.lex.next() 44 | } 45 | 46 | if l.token != ')' { 47 | p.unexpected() 48 | return nil 49 | } 50 | p.lex.next() 51 | } 52 | 53 | return result 54 | } 55 | 56 | func (a astVarRef) receive(g guest) { 57 | g.visit(a._type) 58 | for _, i := range a.index { 59 | g.visit(i) 60 | } 61 | } 62 | 63 | func (a astVarRef) isArray() bool { 64 | return len(a.index) != 0 65 | } 66 | 67 | func (a astVarRef) finalType() astType { 68 | return a._type 69 | } 70 | 71 | func (a astVarRef) unambiguousName() string { 72 | name := fmt.Sprintf("%s_%s", a.id, a._type) 73 | if a.isArray() { 74 | name += "_array" 75 | if len(a.index) > 1 { 76 | name += strconv.Itoa(len(a.index)) 77 | } 78 | } 79 | return name 80 | } 81 | 82 | func (a *astVarRef) equals(other *astVarRef) bool { 83 | return a.id == other.id && a._type == other._type && len(a.index) == len(other.index) 84 | } 85 | -------------------------------------------------------------------------------- /cmdData.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type cmdData struct { 4 | values []*astPart 5 | } 6 | 7 | func (p *parser) parseData() *cmdData { 8 | l := p.lex.peek() 9 | p.lex.next() 10 | result := &cmdData{} 11 | for { 12 | v := p.parsePart() 13 | if v == nil { 14 | break 15 | } 16 | p.incrementDataCounter(v.finalType()) 17 | result.values = append(result.values, v) 18 | if l.token != ',' { 19 | break 20 | } 21 | p.lex.next() 22 | } 23 | return result 24 | } 25 | 26 | func (c cmdData) receive(g guest) { 27 | } 28 | -------------------------------------------------------------------------------- /cmdDef.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type cmdFnDef struct { 4 | id string 5 | args []*astVarRef 6 | expr *astExpr 7 | } 8 | 9 | func (p *parser) parseDef() *cmdFnDef { 10 | l := p.lex.peek() 11 | result := &cmdFnDef{} 12 | 13 | if l.token != tokFn { 14 | return nil 15 | } 16 | p.lex.next() 17 | 18 | if l.token != tokID { 19 | return nil 20 | } 21 | result.id = l.s 22 | p.lex.next() 23 | 24 | if l.token != '(' { 25 | return nil 26 | } 27 | p.lex.next() 28 | 29 | if l.token != tokID { 30 | return nil 31 | } 32 | for { 33 | v := p.parseVarRef() 34 | if v == nil { 35 | break 36 | } 37 | result.args = append(result.args, v) 38 | if l.token != ',' { 39 | break 40 | } 41 | p.lex.next() 42 | } 43 | 44 | if l.token != ')' { 45 | return nil 46 | } 47 | p.lex.next() 48 | 49 | if l.token != '=' { 50 | return nil 51 | } 52 | p.lex.next() 53 | 54 | result.expr = p.parseExpr(false) 55 | 56 | return result 57 | } 58 | 59 | func (c cmdFnDef) receive(g guest) { 60 | g.visit(c.expr) 61 | } 62 | -------------------------------------------------------------------------------- /cmdDim.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type cmdDim struct { 4 | vars []*astVarRef 5 | } 6 | 7 | func (p *parser) parseDim() *cmdDim { 8 | result := &cmdDim{} 9 | l := p.lex.peek() 10 | 11 | for { 12 | v := p.parseVarRef() 13 | if v == nil { 14 | break 15 | } 16 | result.vars = append(result.vars, v) 17 | if l.token != ',' { 18 | break 19 | } 20 | p.lex.next() 21 | } 22 | if len(result.vars) == 0 { 23 | return nil 24 | } 25 | return result 26 | } 27 | 28 | func (c cmdDim) receive(g guest) { 29 | for _, v := range c.vars { 30 | g.visit(v) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cmdEnd.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type cmdEnd struct { 4 | } 5 | 6 | func (p *parser) parseEnd() *cmdEnd { 7 | return &cmdEnd{} 8 | } 9 | 10 | func (c cmdEnd) receive(g guest) { 11 | } 12 | -------------------------------------------------------------------------------- /cmdFor.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type cmdFor struct { 4 | index *astVarRef 5 | begin *astExpr 6 | end *astExpr 7 | step *astExpr 8 | next *cmdNext 9 | } 10 | 11 | var step1 = &astExpr{ 12 | boolOp: &astBoolOp{ 13 | head: &astRelOp{ 14 | lhs: &astAddOp{ 15 | head: &astMulOp{ 16 | head: &astExpOp{ 17 | lhs: &astPart{ 18 | val: &astLit{val: "1", _type: numType}, 19 | }, 20 | }, 21 | }, 22 | }, 23 | }, 24 | }, 25 | } 26 | 27 | func (p *parser) parseFor() *cmdFor { 28 | result := &cmdFor{} 29 | l := p.lex.peek() 30 | 31 | if l.token != tokID { 32 | return nil 33 | } 34 | result.index = p.parseVarRef() 35 | if result.index == nil { 36 | return nil 37 | } 38 | 39 | if l.token != '=' { 40 | return nil 41 | } 42 | p.lex.next() 43 | 44 | result.begin = p.parseExpr(false) 45 | 46 | if l.token != tokTo { 47 | return nil 48 | } 49 | p.lex.next() 50 | 51 | result.end = p.parseExpr(false) 52 | 53 | if l.token == tokStep { 54 | p.lex.next() 55 | result.step = p.parseExpr(false) 56 | } else { 57 | result.step = step1 58 | } 59 | 60 | return result 61 | } 62 | 63 | func (c cmdFor) receive(g guest) { 64 | g.visit(c.index) 65 | g.visit(c.begin) 66 | g.visit(c.end) 67 | if c.step != nil { 68 | g.visit(c.step) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /cmdGo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | type targetLine struct { 8 | nbr int 9 | adr *progLine 10 | } 11 | type cmdGo struct { 12 | dst targetLine 13 | sub bool 14 | } 15 | 16 | func (c cmdGo) cmdName() string { 17 | if c.sub { 18 | return "GOSUB" 19 | } 20 | return "GOTO" 21 | } 22 | 23 | func (p *parser) parseGo() *cmdGo { 24 | l := p.lex.peek() 25 | result := &cmdGo{} 26 | switch l.token { 27 | case tokTo: 28 | result.sub = false 29 | case tokSub: 30 | result.sub = true 31 | default: 32 | return nil 33 | } 34 | p.lex.next() 35 | 36 | if l.token != tokNumber { 37 | return nil 38 | } 39 | var err error 40 | result.dst.nbr, err = strconv.Atoi(l.s) 41 | if err != nil { 42 | return nil 43 | } 44 | p.lex.next() 45 | return result 46 | } 47 | 48 | func (c cmdGo) receive(g guest) { 49 | } 50 | 51 | var nextLabel = 0 52 | 53 | func createLabel() int { 54 | nextLabel-- 55 | return nextLabel 56 | } 57 | -------------------------------------------------------------------------------- /cmdIf.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | type cmdIf struct { 8 | expr *astExpr 9 | cmds 10 | } 11 | 12 | func (p *parser) parseIf() *cmdIf { 13 | result := &cmdIf{} 14 | l := p.lex.peek() 15 | 16 | result.expr = p.parseExpr(false) 17 | if result.expr == nil { 18 | return nil 19 | } 20 | 21 | if l.token != tokThen { 22 | return nil 23 | } 24 | p.lex.next() 25 | 26 | if l.token == tokNumber { 27 | line, err := strconv.Atoi(l.s) 28 | if err != nil { 29 | return nil 30 | } 31 | p.lex.next() 32 | _goto := &cmdGo{} 33 | _goto.dst.nbr = line 34 | result.cmds = append(result.cmds, _goto) 35 | } else { 36 | result.cmds = p.parseLineTail() 37 | } 38 | 39 | return result 40 | } 41 | 42 | func (c *cmdIf) receive(g guest) { 43 | g.visit(c.expr) 44 | for _, cmd := range c.cmds { 45 | g.visit(cmd) 46 | } 47 | } 48 | 49 | // condBranchTarget return the target address of the conditional branch 50 | // or zero if this command is not a conditional branch. 51 | func (c *cmdIf) condBranchTarget() int { 52 | if len(c.cmds) == 1 { // has only 1 conditional command 53 | switch v := c.cmds[0].(type) { 54 | case *cmdGo: // it's a GO command 55 | if !v.sub { // it's not a GOSUB, so it's a GOTO 56 | return v.dst.adr.switchID 57 | } 58 | } 59 | } 60 | return 0 61 | } 62 | -------------------------------------------------------------------------------- /cmdInput.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type cmdInput struct { 4 | label string // optional 5 | vars []*astVarRef 6 | } 7 | 8 | func (p *parser) parseInput() *cmdInput { 9 | result := &cmdInput{} 10 | l := p.lex.peek() 11 | 12 | if l.token == tokString { 13 | result.label = l.s 14 | p.lex.next() // separador 15 | p.lex.next() // pula o separador 16 | } 17 | for { 18 | v := p.parseVarRef() 19 | if v == nil { 20 | break 21 | } 22 | result.vars = append(result.vars, v) 23 | if l.token != ',' { 24 | break 25 | } 26 | p.lex.next() 27 | } 28 | if len(result.vars) == 0 { 29 | return nil 30 | } 31 | return result 32 | } 33 | 34 | func (c cmdInput) receive(g guest) { 35 | for _, v := range c.vars { 36 | g.visit(v) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /cmdLet.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type cmdLet struct { 4 | dst *astVarRef 5 | src *astExpr 6 | } 7 | 8 | func (p *parser) parseLet() *cmdLet { 9 | dst := p.parseVarRef() 10 | 11 | if p.lex.peek().token != '=' { 12 | return nil 13 | } 14 | p.lex.next() 15 | 16 | src := p.parseExpr(false) 17 | if src == nil { 18 | return nil 19 | } 20 | 21 | return &cmdLet{dst: dst, src: src} 22 | } 23 | 24 | func (c cmdLet) receive(g guest) { 25 | g.visit(c.dst) 26 | g.visit(c.src) 27 | } 28 | -------------------------------------------------------------------------------- /cmdNext.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type cmdNext struct { 4 | vars []*astVarRef 5 | labelExit int 6 | } 7 | 8 | func (p *parser) parseNext() *cmdNext { 9 | result := &cmdNext{} 10 | if p.isEndOfCommand() { 11 | return result 12 | } 13 | 14 | l := p.lex.peek() 15 | for { 16 | v := p.parseVarRef() 17 | if v == nil { 18 | break 19 | } 20 | result.vars = append(result.vars, v) 21 | if l.token != ',' { 22 | break 23 | } 24 | p.lex.next() 25 | } 26 | return result 27 | } 28 | 29 | func (c cmdNext) receive(g guest) { 30 | for _, v := range c.vars { 31 | g.visit(v) 32 | } 33 | } 34 | 35 | func (c *cmdNext) createLabel() { 36 | c.labelExit = createLabel() 37 | } 38 | -------------------------------------------------------------------------------- /cmdOn.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | type cmdOn struct { 8 | expr *astExpr 9 | dsts []targetLine 10 | sub bool 11 | } 12 | 13 | func (p *parser) parseOn() *cmdOn { 14 | result := &cmdOn{} 15 | l := p.lex.peek() 16 | 17 | result.expr = p.parseExpr(false) 18 | if result.expr == nil { 19 | return nil 20 | } 21 | 22 | if l.token != tokGo { 23 | return nil 24 | } 25 | p.lex.next() 26 | if l.token == tokSub { 27 | result.sub = true 28 | } else if l.token != tokTo { 29 | return nil 30 | } 31 | p.lex.next() 32 | 33 | for l.token == tokNumber { 34 | dst, err := strconv.Atoi(l.s) 35 | if err != nil { 36 | return nil 37 | } 38 | t := targetLine{} 39 | t.nbr = dst 40 | result.dsts = append(result.dsts, t) 41 | p.lex.next() 42 | if l.token != ',' { 43 | break 44 | } 45 | p.lex.next() 46 | } 47 | return result 48 | } 49 | 50 | func (c cmdOn) receive(g guest) { 51 | g.visit(c.expr) 52 | } 53 | -------------------------------------------------------------------------------- /cmdPrint.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type subCmd interface { 4 | host 5 | } 6 | type printSubCmds []subCmd 7 | 8 | type cmdPrint struct { 9 | printSubCmds 10 | } 11 | 12 | func (p *parser) parsePrint() *cmdPrint { 13 | result := &cmdPrint{} 14 | l := p.lex.peek() 15 | 16 | Loop: 17 | for { 18 | var subCmd subCmd 19 | switch l.token { 20 | case ';': 21 | fallthrough 22 | case ',': 23 | subCmd = l.token 24 | p.lex.next() 25 | case tokEOL: 26 | fallthrough 27 | case tokEOF: 28 | fallthrough 29 | case ':': 30 | break Loop 31 | default: 32 | expr := p.parseExpr(false) 33 | if expr == nil { 34 | break Loop 35 | } 36 | subCmd = expr 37 | } 38 | result.printSubCmds = append(result.printSubCmds, subCmd) 39 | } 40 | return result 41 | } 42 | 43 | func (c cmdPrint) receive(g guest) { 44 | for _, subCmd := range c.printSubCmds { 45 | g.visit(subCmd) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /cmdRead.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type cmdRead struct { 4 | vars []*astVarRef 5 | } 6 | 7 | func (p *parser) parseRead() *cmdRead { 8 | result := &cmdRead{} 9 | l := p.lex.peek() 10 | 11 | for { 12 | v := p.parseVarRef() 13 | result.vars = append(result.vars, v) 14 | if l.token != ',' { 15 | break 16 | } 17 | p.lex.next() 18 | } 19 | if len(result.vars) == 0 { 20 | return nil 21 | } 22 | return result 23 | } 24 | 25 | func (c cmdRead) receive(g guest) { 26 | for _, v := range c.vars { 27 | g.visit(v) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /cmdRem.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type cmdRem struct { 4 | text string 5 | } 6 | 7 | func (p *parser) parseRem() *cmdRem { 8 | text := p.lex.peek().s 9 | if len(text) > 0 { 10 | if text[0] != ' ' { 11 | text = "REM" + text 12 | } 13 | } 14 | return &cmdRem{text: text} 15 | } 16 | 17 | func (c cmdRem) receive(g guest) { 18 | } 19 | -------------------------------------------------------------------------------- /cmdRestore.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type cmdRestore struct { 4 | } 5 | 6 | func (p *parser) parseRestore() *cmdRestore { 7 | return &cmdRestore{} 8 | } 9 | 10 | func (c cmdRestore) receive(g guest) { 11 | } 12 | -------------------------------------------------------------------------------- /cmdReturn.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type cmdReturn struct { 4 | } 5 | 6 | func (p *parser) parseReturn() *cmdReturn { 7 | return &cmdReturn{} 8 | } 9 | 10 | func (c cmdReturn) receive(g guest) { 11 | } 12 | -------------------------------------------------------------------------------- /cmdRun.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type cmdRun struct { 4 | arg string 5 | } 6 | 7 | func (p *parser) parseRun() *cmdRun { 8 | l := p.lex.peek() 9 | result := &cmdRun{} 10 | 11 | if l.token != tokString { 12 | return nil 13 | } 14 | result.arg = l.s 15 | p.lex.next() 16 | 17 | return result 18 | } 19 | 20 | func (c cmdRun) receive(g guest) { 21 | } 22 | -------------------------------------------------------------------------------- /cmdStop.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type cmdStop struct { 4 | } 5 | 6 | func (p *parser) parseStop() *cmdStop { 7 | return &cmdStop{} 8 | } 9 | 10 | func (c cmdStop) receive(g guest) { 11 | } 12 | -------------------------------------------------------------------------------- /genC.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "sort" 7 | "strconv" 8 | ) 9 | 10 | type generatorForC struct { 11 | wr *bufio.Writer 12 | } 13 | 14 | func newGeneratorForC(wr *bufio.Writer) *generatorForC { 15 | return &generatorForC{wr: wr} 16 | } 17 | 18 | func (g *generatorForC) generate(prog *program) { 19 | g.genProgram(prog) 20 | /* 21 | scan(p, func(h host) { 22 | switch v := h.(type) { 23 | case *cmdFor: 24 | case *cmdNext: 25 | } 26 | } 27 | */ 28 | } 29 | 30 | func (g *generatorForC) genProgram(p *program) { 31 | fmt.Fprintf(g.wr, "#include \"basiclib.h\"\n\n") 32 | g.genPrologue(p) 33 | fmt.Fprintf(g.wr, "int main(){\n") 34 | for _, s := range p.loopVars { 35 | fmt.Fprintf(g.wr, "int %s_target;\n", s) 36 | } 37 | fmt.Fprintf(g.wr, "int target = 0;\n\nsrand((unsigned)time(0));\nfor(;;){switch (target){\ncase 0:\n") 38 | g.genLines(p.lines) 39 | fmt.Fprintf(g.wr, "case %d: exit(0);\n", createLabel()) 40 | fmt.Fprintf(g.wr, "default: fprintf(stderr, \"Undefined target %s\", target); abort(); \n}}\n} /* main */\n\n", "%d") 41 | 42 | g.genFunctionDefinitions(p) 43 | g.genDataDefinitions(p, strType) 44 | g.genDataDefinitions(p, numType) 45 | 46 | temp := createTemp() 47 | if temp != 0 { 48 | fmt.Fprintf(g.wr, "static str temp_str_area[%d], *temp_str = temp_str_area;\n", temp) 49 | } 50 | } 51 | 52 | func (g *generatorForC) genDataDefinitions(p *program, _type astType) { 53 | size := p.dataCounter[_type] 54 | fmt.Fprintf(g.wr, "const size_t data_area_for_%s_cnt=%d;\n", _type, size) 55 | if size == 0 { 56 | fmt.Fprintf(g.wr, "const %s data_area_for_%s[1]={0};\n\n", _type, _type) 57 | return 58 | } 59 | fmt.Fprintf(g.wr, "const %s data_area_for_%s[%d]={\n", _type, _type, size) 60 | scan(p, func(h host) { 61 | c, ok := h.(*cmdData) 62 | if ok { 63 | g.generateDataDefinition(c, _type) 64 | } 65 | }) 66 | fmt.Fprintf(g.wr, "};\n\n") 67 | } 68 | 69 | func (g *generatorForC) genFunctionDeclarations(p *program) { 70 | b := false 71 | scan(p, func(h host) { 72 | switch v := h.(type) { 73 | case *cmdFnDef: 74 | g.genDeclaration(v) 75 | b = true 76 | } 77 | }) 78 | if b { 79 | g.wr.WriteRune('\n') 80 | } 81 | } 82 | 83 | func (g *generatorForC) genFunctionDefinitions(p *program) { 84 | scan(p, func(h host) { 85 | switch v := h.(type) { 86 | case *cmdFnDef: 87 | g.genDefinition(v) 88 | } 89 | }) 90 | } 91 | 92 | func (g *generatorForC) genPrologue(p *program) { 93 | g.genFunctionDeclarations(p) 94 | g.genVarDefinitions(p) 95 | fmt.Fprintf(g.wr, "static str *temp_str;\n\n") 96 | } 97 | 98 | func (g *generatorForC) genVarDefinitions(p *program) { 99 | m := make(map[string]*astVarRef) 100 | scan(p, func(h host) { 101 | switch v := h.(type) { 102 | case *astVarRef: 103 | m[v.unambiguousName()] = v 104 | } 105 | }) 106 | var names []string 107 | for k := range m { 108 | names = append(names, k) 109 | } 110 | sort.Strings(names) 111 | for _, k := range names { 112 | v := m[k] 113 | if v.isArray() { 114 | fmt.Fprintf(g.wr, "static arr %s_var;\n", k) 115 | } else { 116 | fmt.Fprintf(g.wr, "static %s %s;\n", v.finalType(), k) 117 | } 118 | } 119 | g.wr.WriteRune('\n') 120 | 121 | for _, k := range names { 122 | v := m[k] 123 | if v.isArray() { 124 | typeString := v.finalType().String() 125 | fmt.Fprintf(g.wr, "static %s *%s(", typeString, k) 126 | for i := 0; i != len(v.index); i++ { 127 | if i != 0 { 128 | g.wr.WriteRune(',') 129 | } 130 | fmt.Fprintf(g.wr, "num index%d", i) 131 | } 132 | fmt.Fprintf(g.wr, "){ return %s_in_array(&%s_var,%d", typeString, k, len(v.index)) 133 | for i := 0; i != len(v.index); i++ { 134 | fmt.Fprintf(g.wr, ",(size_t)index%d", i) 135 | } 136 | fmt.Fprintf(g.wr, ");}\n") 137 | } 138 | } 139 | g.wr.WriteRune('\n') 140 | } 141 | 142 | func (g *generatorForC) genFunctionHeader(c *cmdFnDef) { 143 | fmt.Fprintf(g.wr, "static %s fn_%s(", c.expr.finalType(), c.id) 144 | for i, v := range c.args { 145 | if i != 0 { 146 | g.wr.WriteRune(',') 147 | } 148 | fmt.Fprintf(g.wr, "num %s", v.unambiguousName()) 149 | } 150 | fmt.Fprintf(g.wr, ")") 151 | } 152 | 153 | func (g *generatorForC) genDeclaration(c *cmdFnDef) { 154 | g.genFunctionHeader(c) 155 | fmt.Fprintf(g.wr, ";\n") 156 | } 157 | 158 | func (g *generatorForC) genDefinition(c *cmdFnDef) { 159 | g.genFunctionHeader(c) 160 | fmt.Fprintf(g.wr, "{\n") 161 | for _, v := range c.args { 162 | fmt.Fprintf(g.wr, "\t%s=%s;\n", v.unambiguousName(), v.unambiguousName()) 163 | } 164 | fmt.Fprintf(g.wr, "\treturn ") 165 | g.genAstExpr(c.expr) 166 | fmt.Fprintf(g.wr, ";\n}\n\n") 167 | } 168 | 169 | func (g *generatorForC) genAstExpr(a *astExpr) { 170 | if a.paren { 171 | g.wr.WriteRune('(') 172 | g.genAstBoolOp(a.boolOp) 173 | g.wr.WriteRune(')') 174 | return 175 | } 176 | g.genAstBoolOp(a.boolOp) 177 | } 178 | 179 | func (g *generatorForC) genAstBoolOp(a *astBoolOp) { 180 | g.genAstRelOp(a.head) 181 | for _, tail := range a.tail { 182 | g.genAstBoolOpTail(tail) 183 | } 184 | } 185 | 186 | func (g *generatorForC) genAstBoolOpTail(a astBoolOpTail) { 187 | switch a.oper { 188 | case tokOr: 189 | fmt.Fprintf(g.wr, "||") 190 | case tokAnd: 191 | fmt.Fprintf(g.wr, "&&") 192 | } 193 | g.genAstRelOp(a.val) 194 | } 195 | 196 | func (g *generatorForC) genAstRelOp(a *astRelOp) { 197 | if a.lhs.finalType() == strType && a.rhs != nil { 198 | g.genAstRelOpForStr(a) 199 | return 200 | } 201 | g.genAstAddOp(a.lhs) 202 | if a.rhs == nil { 203 | return 204 | } 205 | g.genOperator(a) 206 | g.genAstAddOp(a.rhs) 207 | } 208 | 209 | func (g *generatorForC) genAstRelOpForStr(a *astRelOp) { 210 | fmt.Fprintf(g.wr, "compare_str(") 211 | g.genAstAddOp(a.lhs) 212 | g.wr.WriteRune(',') 213 | g.genAstAddOp(a.rhs) 214 | g.wr.WriteRune(')') 215 | g.genOperator(a) 216 | g.wr.WriteRune('0') 217 | } 218 | 219 | func (g *generatorForC) genOperator(a *astRelOp) { 220 | switch a.oper { 221 | case '=': 222 | fmt.Fprintf(g.wr, "==") 223 | case tokNe: 224 | fmt.Fprintf(g.wr, "!=") 225 | case tokLe: 226 | fmt.Fprintf(g.wr, "<=") 227 | case tokGe: 228 | fmt.Fprintf(g.wr, ">=") 229 | default: 230 | fmt.Fprintf(g.wr, "%c", a.oper) 231 | } 232 | } 233 | 234 | func (g *generatorForC) genAstAddOp(a *astAddOp) { 235 | if a.head.finalType() == strType && len(a.tail) != 0 { 236 | g.genAspAddOpForStr(a) 237 | return 238 | } 239 | g.genAstMulOp(a.head) 240 | for _, t := range a.tail { 241 | g.genAstAddOpTail(t) 242 | } 243 | } 244 | 245 | func (g *generatorForC) genAstAddOpTail(a astAddOpTail) { 246 | fmt.Fprintf(g.wr, "%c", a.oper) 247 | g.genAstMulOp(a.val) 248 | } 249 | 250 | func (g *generatorForC) genAspAddOpForStr(a *astAddOp) { 251 | fmt.Fprintf(g.wr, "concat_str(&temp_str[%d],%d,", createTemp(), len(a.tail)+1) 252 | g.genAstMulOp(a.head) 253 | for _, t := range a.tail { 254 | g.wr.WriteRune(',') 255 | g.genAstMulOp(t.val) 256 | } 257 | g.wr.WriteRune(')') 258 | } 259 | 260 | func (g *generatorForC) genAstMulOp(a *astMulOp) { 261 | g.genAstExpOp(a.head) 262 | for _, t := range a.tail { 263 | g.genAstMulOpTail(t) 264 | } 265 | } 266 | 267 | func (g *generatorForC) genAstMulOpTail(a astMulOpTail) { 268 | fmt.Fprintf(g.wr, "%c ", a.oper) 269 | g.genAstExpOp(a.val) 270 | } 271 | 272 | func (g *generatorForC) genAstExpOp(a *astExpOp) { 273 | if a.rhs == nil { 274 | g.genAstPart(a.lhs) 275 | return 276 | } 277 | fmt.Fprintf(g.wr, "((num)pow(") 278 | g.genAstPart(a.lhs) 279 | g.wr.WriteRune(',') 280 | g.genAstPart(a.rhs) 281 | fmt.Fprintf(g.wr, "))") 282 | } 283 | 284 | func (g *generatorForC) genAstPart(a *astPart) { 285 | if a.signal != 0 { 286 | fmt.Fprintf(g.wr, "%c", a.signal) 287 | } 288 | g.genAstValue(a.val) 289 | } 290 | 291 | func (g *generatorForC) genAstValue(a astValue) { 292 | switch v := a.(type) { 293 | case *astVarRef: 294 | g.genAstVarRef(v) 295 | case *astExpr: 296 | g.genAstExpr(v) 297 | case *astFnCall: 298 | g.genAstFnCall(v) 299 | case *astLit: 300 | g.genAstLit(v) 301 | case *astPredef: 302 | g.genAstPredef(v) 303 | } 304 | } 305 | 306 | func (g *generatorForC) genAstVarRef(a *astVarRef) { 307 | g.generateCVarRef(a, true) 308 | } 309 | 310 | /* 311 | array not array const lit 312 | As an R value 313 | str *s(1) s "abc" 314 | num *n(1) n 1.0000f 315 | 316 | As an L value 317 | str let_str(s(1), let_str(&s, 318 | num let_num(n(1), let_num(&n, 319 | */ 320 | 321 | // generateCVarRef emits C code for access to a variable. 322 | // The intricacies are due to the subtleties of several variable types. 323 | func (g *generatorForC) generateCVarRef(a *astVarRef, shouldDeref bool) { 324 | if !a.isArray() { 325 | fmt.Fprintf(g.wr, "%s", a.unambiguousName()) 326 | return 327 | } 328 | if shouldDeref { 329 | g.wr.WriteRune('*') 330 | } 331 | fmt.Fprintf(g.wr, "%s", a.unambiguousName()) 332 | g.wr.WriteRune('(') 333 | for i, v := range a.index { 334 | if i != 0 { 335 | g.wr.WriteRune(',') 336 | } 337 | g.genAstExpr(v) 338 | } 339 | g.wr.WriteRune(')') 340 | } 341 | 342 | // gererateCLValue generates code for the left side of an assignment. 343 | // returns a bool indicating wether a closing parenthesis is needed 344 | // The intricacies are due to the subtleties of several variable types. 345 | func (g *generatorForC) generateCLValue(a *astVarRef, fname string) { 346 | fmt.Fprintf(g.wr, "%s_%s(", fname, a.finalType()) 347 | if !a.isArray() { 348 | g.wr.WriteRune('&') 349 | } 350 | g.generateCVarRef(a, false) 351 | } 352 | 353 | func (g *generatorForC) genAstFnCall(a *astFnCall) { 354 | fmt.Fprintf(g.wr, "fn_%s(", a.id) 355 | g.genAstExpr(a.arg) 356 | fmt.Fprintf(g.wr, ")") 357 | } 358 | 359 | func (g *generatorForC) genAstLit(a *astLit) { 360 | switch a._type { 361 | case numType: 362 | f, err := strconv.ParseFloat(a.val, 32) 363 | if err != nil { 364 | panic(err) 365 | } 366 | fmt.Fprintf(g.wr, "%.4ff", f) 367 | case strType: 368 | g.wr.WriteRune('"') 369 | for _, r := range a.val { 370 | if r == '\'' || r == '\\' { 371 | g.wr.WriteRune('\\') 372 | } 373 | g.wr.WriteRune(r) 374 | } 375 | g.wr.WriteRune('"') 376 | } 377 | } 378 | 379 | func (g *generatorForC) genAstPredef(a *astPredef) { 380 | predef := predefs[a.function] 381 | fmt.Fprintf(g.wr, "%s_%s(", predef.name, predef._type) 382 | if predef._type == strType { 383 | fmt.Fprintf(g.wr, "&temp_str[%d],", createTemp()) 384 | } 385 | for i, arg := range a.args { 386 | if i != 0 { 387 | g.wr.WriteRune(',') 388 | } 389 | g.genAstExpr(arg) 390 | } 391 | g.wr.WriteRune(')') 392 | } 393 | 394 | func (g *generatorForC) genLines(pl progLines) { 395 | for _, l := range pl { 396 | g.genProgLine(l) 397 | } 398 | } 399 | 400 | func (g *generatorForC) genProgLine(l *progLine) { 401 | if l.isDst { 402 | fmt.Fprintf(g.wr, "case %d: ", l.switchID) 403 | } 404 | fmt.Fprintf(g.wr, "/* line %d */\n", l.id) 405 | if len(l.cmds) != 0 { 406 | g.genCommands(l.cmds) 407 | } 408 | } 409 | 410 | func (g *generatorForC) genCommands(cms cmds) { 411 | for _, cmd := range cms { 412 | g.genCmd(cmd) 413 | } 414 | } 415 | 416 | func (g *generatorForC) genCmd(cmd cmd) { 417 | switch c := cmd.(type) { 418 | case *cmdData: 419 | //does nothing. It's not generated inline. 420 | case *cmdFnDef: 421 | //does nothing. It's not generated inline. 422 | case *cmdDim: 423 | g.genCmdDim(c) 424 | case *cmdEnd: 425 | g.genCmdEnd(c) 426 | case *cmdFor: 427 | g.genCmdFor(c) 428 | case *cmdGo: 429 | g.genCmdGo(c) 430 | case *cmdIf: 431 | g.genCmdIf(c) 432 | case *cmdInput: 433 | g.genCmdInput(c) 434 | case *cmdLet: 435 | g.genCmdLet(c) 436 | case *cmdNext: 437 | g.genCmdNext(c) 438 | case *cmdOn: 439 | g.genCmdOn(c) 440 | case *cmdPrint: 441 | g.genCmdPrint(c) 442 | case *cmdRead: 443 | g.genCmdRead(c) 444 | case *cmdRem: 445 | g.genCmdRem(c) 446 | case *cmdRestore: 447 | g.genCmdRestore(c) 448 | case *cmdReturn: 449 | g.genCmdReturn(c) 450 | case *cmdRun: 451 | g.genCmdRun(c) 452 | case *cmdStop: 453 | g.genCmdStop(c) 454 | } 455 | } 456 | 457 | func (g *generatorForC) generateDataDefinition(c *cmdData, _type astType) { 458 | emittedSome := false 459 | //does nothing. Data is not generated here. 460 | for _, v := range c.values { 461 | if v.finalType() == _type { 462 | g.genAstPart(v) 463 | g.wr.WriteRune(',') 464 | emittedSome = true 465 | } 466 | } 467 | if emittedSome { 468 | g.wr.WriteRune('\n') 469 | } 470 | } 471 | 472 | func (g *generatorForC) genCmdDim(c *cmdDim) { 473 | for _, v := range c.vars { 474 | fmt.Fprintf(g.wr, "\tdim_%s(&%s_var,%d,", v._type, v.unambiguousName(), len(v.index)) 475 | for i, index := range v.index { 476 | if i != 0 { 477 | g.wr.WriteRune(',') 478 | } 479 | fmt.Fprintf(g.wr, "(size_t)") 480 | g.genAstExpr(index) 481 | } 482 | fmt.Fprintf(g.wr, ");\n") 483 | } 484 | } 485 | 486 | func (g *generatorForC) genCmdEnd(c *cmdEnd) { 487 | fmt.Fprintf(g.wr, "\texit(0);\n") 488 | } 489 | 490 | func (g *generatorForC) genCmdFor(c *cmdFor) { 491 | labelInc := createLabel() 492 | labelCond := createLabel() 493 | 494 | fmt.Fprintf(g.wr, "\t%s_target = %d;\n", c.index.unambiguousName(), labelInc) 495 | 496 | // first index attribution 497 | g.wr.WriteRune('\t') 498 | g.generateCLValue(c.index, "let") 499 | g.wr.WriteRune(',') 500 | g.genAstExpr(c.begin) 501 | fmt.Fprintf(g.wr, ");\n") 502 | fmt.Fprintf(g.wr, "\ttarget = %d; break;\n", labelCond) 503 | 504 | // Increment 505 | fmt.Fprintf(g.wr, "case %d:\n", labelInc) 506 | g.wr.WriteRune('\t') 507 | g.generateCLValue(c.index, "let") 508 | g.wr.WriteRune(',') 509 | g.genAstVarRef(c.index) 510 | g.wr.WriteRune('+') 511 | g.genAstExpr(c.step) 512 | fmt.Fprintf(g.wr, ");\n") 513 | 514 | // index value bounds checking 515 | fmt.Fprintf(g.wr, "case %d:\n", labelCond) 516 | if c.step == step1 { 517 | fmt.Fprintf(g.wr, "\tif (") 518 | g.genAstVarRef(c.index) 519 | fmt.Fprintf(g.wr, " > ") 520 | g.genAstExpr(c.end) 521 | fmt.Fprintf(g.wr, ") { target=%d; break; }\n", c.next.labelExit) 522 | return 523 | } 524 | fmt.Fprintf(g.wr, "\tif (") 525 | g.genAstExpr(c.step) 526 | fmt.Fprintf(g.wr, " > 0 && ") 527 | g.genAstVarRef(c.index) 528 | fmt.Fprintf(g.wr, " > ") 529 | g.genAstExpr(c.end) 530 | fmt.Fprintf(g.wr, ") { target=%d; break; }\n", c.next.labelExit) 531 | fmt.Fprintf(g.wr, "\telse if (") 532 | g.genAstExpr(c.step) 533 | fmt.Fprintf(g.wr, " < 0 && ") 534 | g.genAstVarRef(c.index) 535 | fmt.Fprintf(g.wr, " < ") 536 | g.genAstExpr(c.end) 537 | fmt.Fprintf(g.wr, ") { target=%d; break; }\n", c.next.labelExit) 538 | } 539 | 540 | func (g *generatorForC) genCmdGo(c *cmdGo) { 541 | if c.sub { 542 | returnAddress := createLabel() 543 | fmt.Fprintf(g.wr, "\tpush_address(%d);\n", returnAddress) 544 | fmt.Fprintf(g.wr, "\ttarget = %d; break;\n", c.dst.adr.switchID) 545 | fmt.Fprintf(g.wr, "case %d:\n", returnAddress) 546 | } else { 547 | fmt.Fprintf(g.wr, "\ttarget = %d; break;\n", c.dst.adr.switchID) 548 | } 549 | } 550 | 551 | func (g *generatorForC) genCmdIf(c *cmdIf) { 552 | label := c.condBranchTarget() 553 | if label != 0 { 554 | g.genConditionalBranch(c, label) 555 | } else { 556 | g.genRegularIf(c) 557 | } 558 | } 559 | 560 | func (g *generatorForC) genConditionalBranch(c *cmdIf, label int) { 561 | fmt.Fprintf(g.wr, "\tif (") 562 | g.genAstExpr(c.expr) 563 | fmt.Fprintf(g.wr, "){ target = %d; break; }\n", label) 564 | } 565 | 566 | func (g *generatorForC) genRegularIf(c *cmdIf) { 567 | label := createLabel() 568 | fmt.Fprintf(g.wr, "\tif (!(") 569 | g.genAstExpr(c.expr) 570 | fmt.Fprintf(g.wr, ")){ target = %d; break; }\n", label) 571 | g.genCommands(c.cmds) 572 | fmt.Fprintf(g.wr, "case %d:\n", label) 573 | } 574 | 575 | func (g *generatorForC) genCmdInput(c *cmdInput) { 576 | if c.label != "" { 577 | fmt.Fprintf(g.wr, "\tprint_str(\"%s\");\n", c.label) 578 | } 579 | fmt.Fprintf(g.wr, "\tinput_to_buffer();\n") 580 | 581 | for _, v := range c.vars { 582 | fmt.Fprintf(g.wr, "\t") 583 | g.generateCLValue(v, "input") 584 | fmt.Fprintf(g.wr, ");\n") 585 | } 586 | } 587 | 588 | func (g *generatorForC) genCmdLet(c *cmdLet) { 589 | g.wr.WriteRune('\t') 590 | g.generateCLValue(c.dst, "let") 591 | g.wr.WriteRune(',') 592 | g.genAstExpr(c.src) 593 | fmt.Fprintf(g.wr, ");\n") 594 | } 595 | 596 | func (g *generatorForC) genCmdNext(c *cmdNext) { 597 | fmt.Fprintf(g.wr, "\ttarget=%s_target; break;\n", c.vars[0].unambiguousName()) 598 | fmt.Fprintf(g.wr, "case %d:\n", c.labelExit) 599 | } 600 | 601 | func (g *generatorForC) genCmdOn(c *cmdOn) { 602 | labelExit := createLabel() 603 | fmt.Fprintf(g.wr, "\ttarget = (int)(") 604 | g.genAstExpr(c.expr) 605 | fmt.Fprintf(g.wr, ");\n") 606 | fmt.Fprintf(g.wr, "\tif (target < 1 || target > %d) {target = %d; break;}\n", len(c.dsts), labelExit) 607 | if c.sub { 608 | fmt.Fprintf(g.wr, "\tpush_address(%d);\n", labelExit) 609 | } 610 | fmt.Fprintf(g.wr, "\t{static const int tab[]={") 611 | for i, line := range c.dsts { 612 | if i != 0 { 613 | g.wr.WriteRune(',') 614 | } 615 | fmt.Fprintf(g.wr, "%d", line.adr.switchID) 616 | } 617 | fmt.Fprintf(g.wr, "}; target = tab[target -1]; break;}\n") 618 | fmt.Fprintf(g.wr, "case %d:\n", labelExit) 619 | } 620 | 621 | func (g *generatorForC) genCmdPrint(c *cmdPrint) { 622 | g.genPrintSubCmds(c.printSubCmds) 623 | } 624 | 625 | func (g *generatorForC) genPrintSubCmds(scs printSubCmds) { 626 | var _type astType 627 | shouldNL := true 628 | for _, subCmd := range scs { 629 | switch cmd := subCmd.(type) { 630 | case token: 631 | if cmd == ';' { 632 | shouldNL = false 633 | } else if cmd == ',' { 634 | shouldNL = false 635 | fmt.Fprintf(g.wr, "\tprint_char('\\t');\n") 636 | } 637 | case *astExpr: 638 | shouldNL = true 639 | _type = cmd.finalType() 640 | if _type == voidType { 641 | fmt.Fprintf(g.wr, "\t") 642 | g.genAstExpr(cmd) 643 | fmt.Fprintf(g.wr, ";\n") 644 | } else { 645 | fmt.Fprintf(g.wr, "\tprint_%s(", _type) 646 | g.genAstExpr(cmd) 647 | fmt.Fprintf(g.wr, ");\n") 648 | } 649 | } 650 | } 651 | if shouldNL { 652 | fmt.Fprintf(g.wr, "\tprint_char('\\n');\n") 653 | } 654 | } 655 | 656 | func (g *generatorForC) genCmdRead(c *cmdRead) { 657 | for _, v := range c.vars { 658 | g.wr.WriteRune('\t') 659 | g.generateCLValue(v, "read") 660 | fmt.Fprintf(g.wr, ");\n") 661 | } 662 | } 663 | 664 | func (g *generatorForC) genCmdRem(c *cmdRem) { 665 | fmt.Fprintf(g.wr, "\t/*%s*/\n", c.text) 666 | } 667 | 668 | func (g *generatorForC) genCmdRestore(c *cmdRestore) { 669 | fmt.Fprintf(g.wr, "\trestore();\n") 670 | } 671 | 672 | func (g *generatorForC) genCmdReturn(c *cmdReturn) { 673 | fmt.Fprintf(g.wr, "\tpop_address(&target); break;\n") 674 | } 675 | 676 | func (g *generatorForC) genCmdRun(c *cmdRun) { 677 | fmt.Fprintf(g.wr, "\tsystem(\"%s\");\n", c.arg) 678 | } 679 | 680 | func (g *generatorForC) genCmdStop(c *cmdStop) { 681 | fmt.Fprintf(g.wr, "\texit(0);\n") 682 | } 683 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/jucie/basic 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /lexer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "strings" 7 | "unicode" 8 | ) 9 | 10 | type coord struct { 11 | row int 12 | col int 13 | } 14 | 15 | type lexeme struct { 16 | token token 17 | pos coord 18 | s string 19 | } 20 | 21 | type lexer struct { 22 | rd *bufio.Reader 23 | pos coord 24 | begin coord 25 | buf bytes.Buffer 26 | lexeme 27 | unreadBuf bytes.Buffer 28 | c chan lexeme 29 | } 30 | 31 | func newLexer(rd *bufio.Reader) *lexer { 32 | lex := &lexer{rd: rd} 33 | lex.c = make(chan lexeme) 34 | go func() { 35 | for { 36 | lex.next0() 37 | } 38 | }() 39 | lex.next() 40 | return lex 41 | } 42 | 43 | func (lex *lexer) get() *lexeme { 44 | l := lex.peek() 45 | lex.next() 46 | return l 47 | } 48 | 49 | func (lex *lexer) peek() *lexeme { 50 | return &lex.lexeme 51 | } 52 | 53 | func (lex *lexer) next() { 54 | lex.lexeme = <-lex.c 55 | } 56 | 57 | func (lex *lexer) next0() { 58 | var tok token 59 | for { 60 | tok = lex.walk() 61 | if tok == tokRem { 62 | lex.consumeLine() 63 | } 64 | lex.pos.col += lex.buf.Len() 65 | if tok != tokSpace { 66 | break 67 | } 68 | } 69 | text := string(lex.buf.Bytes()) 70 | lex.c <- lexeme{token: tok, pos: lex.pos, s: text} 71 | } 72 | 73 | func (lex *lexer) nextLine() { 74 | lex.consumeLine() 75 | if lex.peek().token == tokEOL { 76 | lex.walk() 77 | } 78 | } 79 | 80 | func (lex *lexer) consumeLine() { 81 | lex.buf.Reset() 82 | 83 | for { 84 | 85 | b, err := lex.readByte() 86 | if err != nil { 87 | break 88 | } 89 | 90 | if b == '\n' || b == '\r' { 91 | lex.unreadByte(b) 92 | break 93 | } 94 | lex.buf.WriteByte(b) 95 | } 96 | } 97 | 98 | func (lex *lexer) handleSpace(b byte) token { 99 | for { 100 | lex.buf.WriteByte(b) 101 | b, err := lex.readByte() 102 | if err != nil { 103 | break 104 | } 105 | if b == '\n' || !unicode.IsSpace(rune(b)) { 106 | lex.unreadByte(b) 107 | break 108 | } 109 | } 110 | return tokSpace 111 | } 112 | 113 | func (lex *lexer) handleString(b byte) token { 114 | for { 115 | b, err := lex.readByte() 116 | if err != nil { 117 | break 118 | } 119 | if b == '\n' { 120 | lex.unreadByte(b) 121 | break 122 | } 123 | if b == '"' { 124 | break 125 | } 126 | lex.buf.WriteByte(b) 127 | } 128 | return tokString 129 | } 130 | 131 | func (lex *lexer) handleID(b byte) token { 132 | lex.buf.WriteByte(byte(unicode.ToUpper(rune(b)))) 133 | 134 | var err error 135 | b, err = lex.readByte() 136 | if err != nil { 137 | return tokID 138 | } 139 | 140 | // for an id, if the second character is a digit, then we are done. 141 | if unicode.IsNumber(rune(b)) { 142 | lex.buf.WriteByte(byte(unicode.ToUpper(rune(b)))) 143 | return tokID // we have a single character id 144 | } 145 | 146 | // for an id, the second character must be a letter or a digit 147 | if !unicode.IsLetter(rune(b)) { 148 | lex.unreadByte(b) 149 | return tokID // we have a single character id 150 | } 151 | 152 | var lastByte byte 153 | // read full word 154 | for { 155 | lex.buf.WriteByte(byte(unicode.ToUpper(rune(b)))) 156 | b, err = lex.readByte() 157 | if err != nil { 158 | break 159 | } 160 | if !unicode.IsLetter(rune(b)) { 161 | lastByte = b 162 | break 163 | } 164 | } 165 | 166 | // trying to find keywords inside the string. 167 | s := string(lex.buf.Bytes()) 168 | min := len(s) 169 | var rwmin reservedWord 170 | for _, rw := range reservedWords { 171 | pos := strings.Index(s, rw.s) 172 | if pos < 0 { 173 | continue 174 | } else if pos == 0 { 175 | min = 0 176 | rwmin = rw 177 | break 178 | } else { 179 | if pos < min { 180 | min = pos // the earlier find is what we want. 181 | rwmin = rw 182 | } 183 | } 184 | } 185 | var tok token = tokID 186 | if min == 0 { // the string begins with a keyword 187 | lex.buf.Reset() 188 | lex.buf.WriteString(rwmin.s) 189 | lex.unreadString(s[len(rwmin.s):]) 190 | tok = rwmin.token 191 | } else if min > 0 && min < len(s) { // we have an id before the keyword 192 | lex.buf.Reset() 193 | lex.buf.WriteString(s[:min]) 194 | lex.unreadString(s[min:]) 195 | } 196 | lex.unreadByte(lastByte) // the non letter character after the string 197 | return tok 198 | } 199 | 200 | func (lex *lexer) handleDigraph(b byte) token { 201 | lex.buf.WriteByte(b) 202 | 203 | second, err := lex.readByte() 204 | if err != nil { 205 | return token(b) 206 | } 207 | 208 | lex.buf.WriteByte(second) 209 | switch string(lex.buf.Bytes()) { 210 | case "<=": 211 | return tokLe 212 | case ">=": 213 | return tokGe 214 | case "<>": 215 | return tokNe 216 | } 217 | 218 | lex.unreadByte(second) // unread second 219 | lex.buf.Reset() 220 | lex.buf.WriteByte(b) 221 | return token(b) 222 | } 223 | 224 | func (lex *lexer) handleNumber(b byte) token { 225 | var err error 226 | hasPoint := false 227 | hasE := false 228 | hasExpSignal := false 229 | Loop: 230 | for { 231 | switch b { 232 | case '.': 233 | if hasPoint { 234 | lex.unreadByte(b) 235 | break Loop 236 | } 237 | hasPoint = true 238 | lex.buf.WriteByte(b) 239 | case 'E': 240 | if hasE { 241 | lex.unreadByte(b) 242 | break Loop 243 | } 244 | hasE = true 245 | lex.buf.WriteByte(b) 246 | case '+': 247 | fallthrough 248 | case '-': 249 | if !hasE { 250 | lex.unreadByte(b) 251 | break Loop 252 | } 253 | if hasExpSignal { 254 | lex.unreadByte(b) 255 | break Loop 256 | } 257 | hasExpSignal = true 258 | lex.buf.WriteByte(b) 259 | default: 260 | if !unicode.IsDigit(rune(b)) { 261 | lex.unreadByte(b) 262 | break Loop 263 | } 264 | lex.buf.WriteByte(b) 265 | } 266 | b, err = lex.readByte() 267 | if err != nil { 268 | break Loop 269 | } 270 | } 271 | return tokNumber 272 | } 273 | 274 | func canBeNumber(b byte) bool { 275 | return b == '.' || unicode.IsDigit(rune(b)) 276 | } 277 | 278 | func (lex *lexer) walk() token { 279 | lex.buf.Reset() 280 | 281 | b, err := lex.readByte() 282 | if err != nil { 283 | return tokEOF 284 | } 285 | 286 | if b == '\n' { 287 | lex.pos.row++ 288 | lex.pos.col = 0 289 | return tokEOL 290 | } 291 | 292 | if unicode.IsSpace(rune(b)) { 293 | return lex.handleSpace(b) 294 | } 295 | 296 | if canBeNumber(b) { 297 | return lex.handleNumber(b) 298 | } 299 | 300 | if unicode.IsLetter(rune(b)) { 301 | return lex.handleID(b) 302 | } 303 | 304 | if b == '"' { 305 | return lex.handleString(b) 306 | } 307 | 308 | if b == '>' || b == '<' { 309 | return lex.handleDigraph(b) 310 | } 311 | 312 | lex.buf.WriteByte(b) 313 | return token(b) 314 | } 315 | 316 | func (lex *lexer) readByte() (byte, error) { 317 | if lex.unreadBuf.Len() > 0 { 318 | return lex.unreadBuf.ReadByte() 319 | } 320 | return lex.rd.ReadByte() 321 | } 322 | 323 | func (lex *lexer) unreadByte(b byte) { 324 | lex.unreadBuf.WriteByte(b) 325 | } 326 | 327 | func (lex *lexer) unreadString(s string) { 328 | lex.unreadBuf.WriteString(s) 329 | } 330 | -------------------------------------------------------------------------------- /lib/basiclib.c: -------------------------------------------------------------------------------- 1 | #ifdef _MSC_VER 2 | #define _CRT_SECURE_NO_WARNINGS 1 3 | #endif 4 | 5 | #include "basiclib.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | static int current_column; 12 | 13 | static void ops(const char *format, ...) { 14 | va_list ap; 15 | va_start(ap, format); 16 | fputc('\n', stderr); 17 | vfprintf(stderr, format, ap); 18 | fputc('\n', stderr); 19 | va_end(ap); 20 | abort(); 21 | } 22 | 23 | static void *realloc_mem(void *ptr, size_t size) { 24 | void *result = realloc(ptr, size); 25 | if (!result) { 26 | ops("Out of memory"); 27 | } 28 | return result; 29 | } 30 | 31 | static void dim(arr *a, size_t elem_size, int argcnt, va_list ap0, va_list ap1) { 32 | size_t total, multiplier; 33 | int i; 34 | size_t *p; 35 | 36 | multiplier = 1; 37 | for (i = 0; i < argcnt; i++) { 38 | size_t size = va_arg(ap0, size_t); 39 | if (size < 1) { 40 | ops("DIMension must be greater than zero"); 41 | } 42 | multiplier *= size +1; 43 | } 44 | total = argcnt * sizeof(size_t) + multiplier * elem_size; 45 | 46 | p = *a = realloc_mem(*a, total); 47 | memset(p, 0, total); 48 | 49 | for (i = 0; i < argcnt; i++) { 50 | size_t size = va_arg(ap1, size_t); 51 | p[i] = size +1; 52 | } 53 | } 54 | 55 | void dim_num(arr *a, int argcnt, ...) { 56 | va_list ap0, ap1; 57 | va_start(ap0, argcnt); 58 | va_start(ap1, argcnt); 59 | dim(a, sizeof(num), argcnt, ap0, ap1); 60 | va_end(ap0); 61 | va_end(ap1); 62 | } 63 | 64 | void dim_str(arr *a, int argcnt, ...) { 65 | va_list ap0, ap1; 66 | va_start(ap0, argcnt); 67 | va_start(ap1, argcnt); 68 | dim(a, sizeof(str), argcnt, ap0, ap1); 69 | va_end(ap0); 70 | va_end(ap1); 71 | } 72 | 73 | 74 | static void *element_in_array(arr *a, size_t elem_size, int argcnt, va_list ap) { 75 | size_t *p = *a; 76 | size_t offset, multiplier; 77 | int i; 78 | 79 | offset = 0; 80 | multiplier = 1; 81 | for (i = 0; i < argcnt; i++, p++) { 82 | size_t pos = va_arg(ap, size_t); 83 | if (pos >= *p) { 84 | ops("Index out of bounds for dimension %d: %u. It should be a value from 0 up to %u", i+1, pos, *p -1); 85 | } 86 | offset += multiplier * pos; 87 | multiplier *= *p; 88 | } 89 | offset *= elem_size; 90 | return ((char*)p)+offset; 91 | } 92 | 93 | num *num_in_array(arr *a, int argcnt, ...) { 94 | num *result; 95 | va_list ap; 96 | 97 | if (*a == NULL) { /* Did the programmer forget to DIMension this array? */ 98 | dim_num(a, 1, 10); /* No problem. We can DIM it! It's a BASIC thing */ 99 | } 100 | 101 | va_start(ap, argcnt); 102 | result = (num*)element_in_array(a, sizeof(num), argcnt, ap); 103 | va_end(ap); 104 | return result; 105 | } 106 | 107 | str *str_in_array(arr *a, int argcnt, ...) { 108 | str *result; 109 | va_list ap; 110 | 111 | if (*a == NULL) { /* Did the programmer forget to DIMension this array? */ 112 | dim_str(a, 1, 10); /* No problem. We can DIM it! It's a BASIC thing */ 113 | } 114 | 115 | va_start(ap, argcnt); 116 | result = (str*)element_in_array(a, sizeof(str), argcnt, ap); 117 | va_end(ap); 118 | return result; 119 | } 120 | 121 | void let_str(str *dst, str src) { 122 | size_t size; 123 | if (!src) { 124 | free(*dst); 125 | *dst = NULL; 126 | return; 127 | } 128 | size = strlen(src)+1; 129 | *dst = realloc_mem(*dst, size); 130 | strcpy(*dst, src); 131 | } 132 | 133 | static char input_buffer[4 * 1024], *input_ptr; 134 | 135 | void input_to_buffer() { 136 | char *p; 137 | 138 | print_str("? "); 139 | if (!fgets(input_buffer, sizeof input_buffer, stdin)) { 140 | ops("INPUT failed"); 141 | } 142 | 143 | p = strchr(input_buffer, '\n'); 144 | if (p) { 145 | *p = '\0'; 146 | current_column = 0; 147 | } 148 | input_ptr = input_buffer; 149 | } 150 | 151 | void input_num(num *dst) { 152 | char *p; 153 | if (!input_ptr || *input_ptr == '\0') { 154 | *dst = 0; 155 | return; 156 | } 157 | 158 | *dst = (num)atof(input_ptr); 159 | p = strchr(input_ptr, ','); 160 | if (p) { 161 | input_ptr = p +1; 162 | } else { 163 | input_ptr = NULL; 164 | } 165 | } 166 | 167 | void input_str(str *dst) { 168 | char *p; 169 | size_t size; 170 | 171 | if (!input_ptr || *input_ptr == '\0') { 172 | free(*dst); 173 | *dst = NULL; 174 | return; 175 | } 176 | 177 | p = strchr(input_ptr, ','); 178 | if (p) { 179 | size = p - input_ptr; 180 | *dst = realloc_mem(*dst, size +1); 181 | memcpy(*dst, input_ptr, size); 182 | (*dst)[size] = '\0'; 183 | input_ptr = p+1; 184 | } else { 185 | size = strlen(input_ptr); 186 | *dst = realloc_mem(*dst, size +1); 187 | strcpy(*dst, input_ptr); 188 | input_ptr = NULL; /* input is depleted */ 189 | } 190 | } 191 | 192 | void print_char(char c) { 193 | switch (c) { 194 | case '\n': 195 | current_column = 0; 196 | break; 197 | case '\t': 198 | { 199 | const int TAB_WIDTH = 14; 200 | int pos = ((current_column / TAB_WIDTH) + 1) * TAB_WIDTH; 201 | while (current_column < pos) { 202 | print_char(' '); 203 | } 204 | } 205 | return; 206 | } 207 | if (c == '\n') { 208 | current_column = 0; 209 | } else { 210 | current_column++; 211 | } 212 | putchar(c); 213 | } 214 | 215 | void print_num(num val) { 216 | char buffer[64], *p = buffer; 217 | if (val == (int)val) { 218 | sprintf(buffer, " %d ", (int)val); 219 | } else { 220 | sprintf(buffer, " %f ", val); 221 | } 222 | while (*p) { 223 | print_char(*p++); 224 | } 225 | } 226 | 227 | void print_str(str val) { 228 | if (!val) { 229 | return; 230 | } 231 | for (; *val; val++) { 232 | print_char(*val); 233 | } 234 | } 235 | 236 | void TAB_void(num val) { 237 | int column = (int)val; 238 | while (current_column < column) { 239 | print_char(' '); 240 | } 241 | } 242 | 243 | #define ADDRESS_STACK_SIZE 64 244 | static int address_stack[ADDRESS_STACK_SIZE], *address_stack_ptr; 245 | 246 | void push_address(int address) { 247 | if (!address_stack_ptr) { 248 | address_stack_ptr = address_stack; 249 | } else { 250 | if (address_stack_ptr - address_stack >= ADDRESS_STACK_SIZE) { 251 | ops("Too many nested GOSUBs"); 252 | } 253 | address_stack_ptr++; 254 | } 255 | *address_stack_ptr = address; 256 | } 257 | 258 | void pop_address(int *address) { 259 | if (!address_stack_ptr) { 260 | ops("RETURN without GOSUB"); 261 | } 262 | *address = *address_stack_ptr; 263 | if (address_stack_ptr == address_stack) { 264 | address_stack_ptr = NULL; 265 | } else { 266 | address_stack_ptr--; 267 | } 268 | } 269 | 270 | static size_t data_area_for_num_index; 271 | 272 | void read_num(num *val) { 273 | extern const size_t data_area_for_num_cnt; 274 | extern const num data_area_for_num[]; 275 | 276 | if (data_area_for_num_index >= data_area_for_num_cnt) { 277 | ops("READ number past DATA"); 278 | } 279 | *val = data_area_for_num[data_area_for_num_index++]; 280 | } 281 | 282 | static size_t data_area_for_str_index; 283 | 284 | void read_str(str *val) { 285 | extern const size_t data_area_for_str_cnt; 286 | extern const str data_area_for_str[]; 287 | 288 | size_t size; 289 | str src; 290 | str dst; 291 | 292 | if (data_area_for_str_index >= data_area_for_str_cnt) { 293 | ops("READ string past DATA"); 294 | } 295 | src = data_area_for_str[data_area_for_str_index++]; 296 | size = strlen(src); 297 | dst = *val = realloc_mem(*val, size +1); 298 | memcpy(dst, src, size); 299 | dst[size] = '\0'; 300 | } 301 | 302 | void restore(void) { 303 | data_area_for_num_index = 0; 304 | data_area_for_str_index = 0; 305 | } 306 | 307 | num ASC_num(str val) { 308 | if (!val) { 309 | ops("Trying to perform ASC function on an empty string"); 310 | } 311 | return *val; 312 | } 313 | 314 | str CHR_str(str *dst, num val) { 315 | str s = *dst = realloc_mem(*dst, 2); 316 | s[0] = (unsigned char)val; 317 | s[1] = '\0'; 318 | return *dst; 319 | } 320 | 321 | num LEN_num(str val) { 322 | if (!val) { 323 | return 0; 324 | } 325 | return (num)strlen(val); 326 | } 327 | 328 | str RIGHT_str(str *dst, str s, num length_num) { 329 | size_t size; 330 | size_t length = (size_t)length_num; 331 | 332 | if (!s) { 333 | free(*dst); 334 | return *dst = NULL; 335 | } 336 | 337 | size = strlen(s); 338 | if (size <= length) { 339 | length = size; 340 | } 341 | 342 | if (length == 0) { 343 | free(*dst); 344 | return *dst = NULL; 345 | } 346 | 347 | *dst = realloc_mem(*dst, length +1); 348 | memcpy(*dst, s +size -length, length); 349 | (*dst)[length] = '\0'; 350 | return *dst; 351 | } 352 | 353 | str LEFT_str(str *dst, str s, num length_num) { 354 | size_t size; 355 | size_t length = (size_t)length_num; 356 | 357 | if (!s) { 358 | free(*dst); 359 | return *dst = NULL; 360 | } 361 | 362 | size = strlen(s); 363 | if (size < length) { 364 | length = size; 365 | } 366 | 367 | if (length == 0) { 368 | free(*dst); 369 | return *dst = NULL; 370 | } 371 | 372 | *dst = realloc_mem(*dst, length +1); 373 | memcpy(*dst, s, length); 374 | (*dst)[length] = '\0'; 375 | return *dst; 376 | } 377 | 378 | str MID_str(str *dst, str s, num start_num, num length_num) { 379 | size_t size; 380 | size_t start = (size_t)start_num; 381 | size_t length = (size_t)length_num; 382 | 383 | if (!s) { 384 | free(*dst); 385 | return *dst = NULL; 386 | } 387 | 388 | start--; 389 | size = strlen(s); 390 | if (size <= start) { 391 | free(*dst); 392 | return *dst = NULL; 393 | } 394 | return LEFT_str(dst, s + start, (num)length); 395 | } 396 | 397 | num RND_num(num val) { 398 | double dummy; 399 | val = val; 400 | 401 | /* see https://stackoverflow.com/questions/13408990/how-to-generate-random-float-number-in-c#13409133 */ 402 | return (num)modf(((num)rand()/(num)(RAND_MAX)), &dummy); 403 | } 404 | 405 | num SGN_num(num val) { 406 | if (val > 0) return 1; 407 | if (val < 0) return -1; 408 | return 0; 409 | } 410 | 411 | str STR_str(str *dst, num val) { 412 | char buffer[64]; 413 | size_t size = sprintf(buffer, "%f", val); 414 | *dst = realloc_mem(*dst, size +1); 415 | memcpy(*dst, buffer, size); 416 | (*dst)[size] = '\0'; 417 | return *dst; 418 | } 419 | 420 | str concat_str(str *dst, int argcnt, ...) { 421 | va_list ap; 422 | size_t size; 423 | int i; 424 | str p; 425 | 426 | va_start(ap, argcnt); 427 | size = 0; 428 | for (i = 0; i < argcnt; i++) { 429 | str s = va_arg(ap, str); 430 | if (s) { 431 | size += strlen(s); 432 | } 433 | } 434 | p = *dst = realloc_mem(*dst, size +1); 435 | 436 | va_start(ap, argcnt); 437 | for (i = 0; i < argcnt; i++) { 438 | str s = va_arg(ap, str); 439 | if (s) { 440 | size = strlen(s); 441 | memcpy(p, s, size); 442 | p += size; 443 | } 444 | } 445 | *p = '\0'; 446 | va_end(ap); 447 | 448 | return *dst; 449 | } 450 | 451 | int compare_str(str lhs, str rhs) { 452 | if (!lhs) { 453 | lhs = ""; 454 | } 455 | if (!rhs) { 456 | rhs = ""; 457 | } 458 | return strcmp(lhs, rhs); 459 | } 460 | -------------------------------------------------------------------------------- /lib/basiclib.h: -------------------------------------------------------------------------------- 1 | #ifndef BASICLIB_H 2 | #define BASICLIB_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef double num; 10 | typedef unsigned char* str; 11 | typedef size_t* arr; 12 | 13 | void dim_num(arr *a, int argcnt, ...); 14 | void dim_str(arr *a, int argcnt, ...); 15 | 16 | num *num_in_array(arr *a, int argcnt, ...); 17 | str *str_in_array(arr *a, int argcnt, ...); 18 | 19 | #define let_num(dst,src) (*(dst) = (src)) 20 | void let_str(str *dst, str src); 21 | 22 | void input_to_buffer(); 23 | void input_num(num *dst); 24 | void input_str(str *dst); 25 | 26 | void print_num(num val); 27 | void print_str(str val); 28 | void print_char(char c); 29 | 30 | void push_address(int address); 31 | void pop_address(int *address); 32 | 33 | void read_num(num *val); 34 | void read_str(str *val); 35 | void restore(void); 36 | 37 | #define ABS_num(val) ((num)fabs(val)) 38 | num ASC_num(str val); 39 | #define ATN_num(val) ((num)atan(val)) 40 | str CHR_str(str *dst, num val); 41 | #define COS_num(val) ((num)cos(val)) 42 | #define EXP_num(val) ((num)exp(val)) 43 | #define INT_num(val) ((num)(int)(val)) 44 | str LEFT_str(str *dst, str s, num length); 45 | num LEN_num(str val); 46 | #define LOG_num(val) ((num)log(val)) 47 | str MID_str(str *dst, str s, num start, num length); 48 | str RIGHT_str(str *dst, str s, num length); 49 | num RND_num(num val); 50 | num SGN_num(num val); 51 | #define SIN_num(val) ((num)sin(val)) 52 | #define SQR_num(val) ((num)sqrt(val)) 53 | str STR_str(str *dst, num val); 54 | void TAB_void(num val); 55 | #define TAN_num(val) ((num)tan(val)) 56 | #define VAL_num(val) ((num)atof(val)) 57 | 58 | str concat_str(str *dst, int argcnt, ...); 59 | int compare_str(str lhs, str rhs); 60 | 61 | #endif /* BASICLIB_H */ 62 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "os" 8 | ) 9 | 10 | type generator interface { 11 | generate(prog *program) 12 | } 13 | 14 | func help() { 15 | fmt.Println( 16 | `basic v1.0.1 Copyright(C) 2019 Jucie Dias Andrade 17 | Use: 18 | basic input.bas output.c`) 19 | } 20 | 21 | func main() { 22 | var outputFormat = flag.String("gen", "c", "output format") 23 | flag.Parse() 24 | 25 | if len(flag.Args()) < 2 { 26 | help() 27 | return 28 | } 29 | 30 | srcFile, err := os.Open(flag.Arg(0)) 31 | if err != nil { 32 | panic(err) 33 | } 34 | defer srcFile.Close() 35 | 36 | dstFile, err := os.Create(flag.Arg(1)) 37 | if err != nil { 38 | panic(err) 39 | } 40 | defer dstFile.Close() 41 | 42 | prog := newProgram() 43 | rd := bufio.NewReader(srcFile) 44 | prog.loadFrom(rd) 45 | 46 | wr := bufio.NewWriter(dstFile) 47 | defer wr.Flush() 48 | 49 | var gen generator 50 | switch *outputFormat { 51 | case "c": 52 | gen = newGeneratorForC(wr) 53 | default: 54 | panic(fmt.Sprintf("unknown output format: \"%s\"", *outputFormat)) 55 | } 56 | gen.generate(prog) 57 | } 58 | -------------------------------------------------------------------------------- /parser.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | ) 9 | 10 | type parser struct { 11 | lex *lexer 12 | prog *program 13 | } 14 | 15 | func newParser(rd *bufio.Reader) *parser { 16 | lex := newLexer(rd) 17 | return &parser{lex: lex} 18 | } 19 | 20 | func (p *parser) parseCmd() (cmd, bool) { 21 | l := p.lex.peek() 22 | tok := l.token 23 | 24 | switch tok { 25 | case tokLet: 26 | p.lex.next() 27 | fallthrough 28 | case tokID: 29 | { 30 | c := p.parseLet() 31 | return c, c != nil 32 | } 33 | case tokData: 34 | { 35 | c := p.parseData() 36 | return c, c != nil 37 | } 38 | case tokDef: 39 | { 40 | p.lex.next() 41 | c := p.parseDef() 42 | return c, c != nil 43 | } 44 | case tokDim: 45 | { 46 | p.lex.next() 47 | c := p.parseDim() 48 | return c, c != nil 49 | } 50 | case tokEnd: 51 | { 52 | p.lex.next() 53 | c := p.parseEnd() 54 | return c, c != nil 55 | } 56 | case tokFor: 57 | { 58 | p.lex.next() 59 | c := p.parseFor() 60 | return c, c != nil 61 | } 62 | case tokGo: 63 | { 64 | p.lex.next() 65 | c := p.parseGo() 66 | return c, c != nil 67 | } 68 | case tokIf: 69 | { 70 | p.lex.next() 71 | c := p.parseIf() 72 | return c, c != nil 73 | } 74 | case tokInput: 75 | { 76 | p.lex.next() 77 | c := p.parseInput() 78 | return c, c != nil 79 | } 80 | case tokNext: 81 | { 82 | p.lex.next() 83 | c := p.parseNext() 84 | return c, c != nil 85 | } 86 | case tokOn: 87 | { 88 | p.lex.next() 89 | c := p.parseOn() 90 | return c, c != nil 91 | } 92 | case tokPrint: 93 | { 94 | p.lex.next() 95 | c := p.parsePrint() 96 | return c, c != nil 97 | } 98 | case tokRead: 99 | { 100 | p.lex.next() 101 | c := p.parseRead() 102 | return c, c != nil 103 | } 104 | case tokRem: 105 | { 106 | c := p.parseRem() 107 | p.lex.next() 108 | return c, c != nil 109 | } 110 | case tokRestore: 111 | { 112 | p.lex.next() 113 | c := p.parseRestore() 114 | return c, c != nil 115 | } 116 | case tokReturn: 117 | { 118 | p.lex.next() 119 | c := p.parseReturn() 120 | return c, c != nil 121 | } 122 | case tokRun: 123 | { 124 | p.lex.next() 125 | c := p.parseRun() 126 | return c, c != nil 127 | } 128 | case tokStop: 129 | { 130 | p.lex.next() 131 | c := p.parseStop() 132 | return c, c != nil 133 | } 134 | case tokEOL: 135 | fallthrough 136 | case tokEOF: 137 | return nil, true 138 | default: 139 | p.unexpected() 140 | p.lex.consumeLine() 141 | } 142 | return nil, false 143 | } 144 | 145 | func (p *parser) isEndOfCommand() bool { 146 | switch p.lex.peek().token { 147 | case ':': 148 | fallthrough 149 | case tokEOL: 150 | fallthrough 151 | case tokEOF: 152 | return true 153 | } 154 | return false 155 | } 156 | 157 | func (p *parser) consumeCmd() { 158 | for !p.isEndOfCommand() { 159 | p.lex.next() 160 | } 161 | } 162 | 163 | func (p *parser) unexpected() { 164 | l := p.lex.lexeme 165 | 166 | fmt.Fprintf(os.Stderr, "%d:%d: Unexpected token (%d) \"%s\"\n", 167 | l.pos.row+1, l.pos.col+1, l.token, l.s) 168 | } 169 | 170 | func appendCmds(tail []cmd, cmd cmd) []cmd { 171 | c, ok := cmd.(*cmdNext) 172 | if ok { // it's a NEXT command 173 | if len(c.vars) >= 2 { // it's multiple. NEXT A,B,C 174 | // let's substitute for several single var NEXT 175 | for _, v := range c.vars { 176 | n := &cmdNext{vars: []*astVarRef{v}} 177 | tail = append(tail, n) 178 | } 179 | return tail 180 | } 181 | } 182 | tail = append(tail, cmd) 183 | return tail 184 | } 185 | 186 | func (p *parser) parseLineTail() []cmd { 187 | var result []cmd 188 | l := p.lex.peek() 189 | for { 190 | if l.token == tokEOL { 191 | break 192 | } 193 | cmd, ok := p.parseCmd() 194 | if !ok { 195 | p.unexpected() 196 | break 197 | } 198 | result = appendCmds(result, cmd) 199 | if l.token == ':' { 200 | p.lex.next() // skip separator 201 | continue 202 | } else if l.token == tokEOL { 203 | break 204 | } else { 205 | return nil 206 | } 207 | } 208 | return result 209 | } 210 | 211 | func (p *parser) parseLine() *progLine { 212 | l := p.lex.peek() 213 | if l.token != tokNumber { 214 | return nil 215 | } 216 | id, err := strconv.Atoi(l.s) 217 | if err != nil { 218 | panic(err) 219 | } 220 | p.lex.next() 221 | 222 | line := &progLine{id: id} 223 | line.cmds = p.parseLineTail() 224 | if l.token == tokEOL { 225 | p.lex.next() 226 | } 227 | return line 228 | } 229 | 230 | func (p *parser) parseProgram(prog *program) { 231 | p.prog = prog 232 | var previous *progLine 233 | for { 234 | line := p.parseLine() 235 | if line == nil { 236 | break 237 | } 238 | p.prog.lines = append(p.prog.lines, line) 239 | if previous != nil && line.id <= previous.id { 240 | fmt.Fprintf(os.Stderr, "%d: out of sequence. Previous is %d\n", line.id, previous.id) 241 | } 242 | previous = line 243 | } 244 | } 245 | 246 | func (p *parser) incrementDataCounter(_type astType) { 247 | p.prog.incrementDataCounter(_type) 248 | } 249 | -------------------------------------------------------------------------------- /program.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | ) 6 | 7 | type cmd interface { 8 | host 9 | } 10 | type cmds []cmd 11 | 12 | type progLine struct { 13 | id int 14 | cmds 15 | isDst bool 16 | switchID int 17 | } 18 | type progLines []*progLine 19 | 20 | func (l *progLine) receive(g guest) { 21 | for _, cmd := range l.cmds { 22 | g.visit(cmd) 23 | } 24 | } 25 | 26 | type program struct { 27 | lines progLines 28 | ids map[int]int 29 | dataCounter map[astType]int 30 | loopVars []string 31 | } 32 | 33 | func newProgram() *program { 34 | return &program{ids: make(map[int]int), dataCounter: make(map[astType]int)} 35 | } 36 | 37 | func (p *program) loadFrom(src *bufio.Reader) { 38 | parser := newParser(src) 39 | parser.parseProgram(p) 40 | p.resolve() 41 | } 42 | 43 | func (p *program) resolve() { 44 | solver := newSolver(p) 45 | scan(p, func(h host) { 46 | solver.consider(h) 47 | }) 48 | solver.linkForNext(p) 49 | p.lines = solver.linkLines(p.lines) 50 | } 51 | 52 | func (p program) receive(g guest) { 53 | for _, l := range p.lines { 54 | g.visit(l) 55 | } 56 | } 57 | 58 | func (p *program) incrementDataCounter(_type astType) { 59 | p.dataCounter[_type]++ 60 | } 61 | -------------------------------------------------------------------------------- /solver.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sort" 7 | ) 8 | 9 | type variable struct { 10 | def *astVarRef 11 | dims int 12 | ref []*astVarRef 13 | isForIndex bool 14 | } 15 | 16 | type function struct { 17 | id string 18 | def *cmdFnDef 19 | ref []*astFnCall 20 | } 21 | 22 | type solver struct { 23 | p *program 24 | dsts []*targetLine 25 | types map[string]int 26 | funcs map[string]*function 27 | predefs map[token]int 28 | vars map[string]*variable 29 | } 30 | 31 | func newSolver(p *program) *solver { 32 | return &solver{ 33 | p: p, 34 | types: make(map[string]int), 35 | funcs: make(map[string]*function), 36 | predefs: make(map[token]int), 37 | vars: make(map[string]*variable), 38 | } 39 | } 40 | 41 | var mt = make(map[string]int) 42 | 43 | func (s *solver) consider(h host) { 44 | mt[fmt.Sprintf("%T", h)]++ 45 | switch v := h.(type) { 46 | case *astPredef: 47 | s.predefs[v.function]++ 48 | case astType: 49 | t := fmt.Stringer(v) 50 | s.types[t.String()]++ 51 | case *astVarRef: 52 | vv, ok := s.vars[v.id] 53 | if !ok { 54 | vv = &variable{} 55 | s.vars[v.id] = vv 56 | } 57 | vv.ref = append(vv.ref, v) 58 | vv.dims = len(v.index) 59 | case *cmdDim: 60 | for _, def := range v.vars { 61 | vv, ok := s.vars[def.unambiguousName()] 62 | if !ok { 63 | vv = &variable{} 64 | s.vars[def.unambiguousName()] = vv 65 | } 66 | if vv.def != nil { 67 | fmt.Fprintf(os.Stderr, "Multiple definition for variable %s.", def.id) 68 | } 69 | vv.def = def 70 | vv.dims = len(def.index) 71 | } 72 | case *cmdFor: 73 | vv, ok := s.vars[v.index.id] 74 | if !ok { 75 | vv = &variable{} 76 | s.vars[v.index.id] = vv 77 | } 78 | vv.isForIndex = true 79 | case *cmdFnDef: 80 | vv, ok := s.funcs[v.id] 81 | if !ok { 82 | vv = &function{id: v.id} 83 | s.funcs[v.id] = vv 84 | } 85 | if vv.def != nil { 86 | fmt.Fprintf(os.Stderr, "Multiple definition for function %s.", v.id) 87 | } 88 | vv.def = v 89 | case *astFnCall: 90 | vv, ok := s.funcs[v.id] 91 | if !ok { 92 | vv = &function{id: v.id} 93 | s.funcs[v.id] = vv 94 | } 95 | vv.ref = append(vv.ref, v) 96 | case *cmdGo: 97 | s.dsts = append(s.dsts, &v.dst) 98 | case *cmdOn: 99 | for i := 0; i < len(v.dsts); i++ { 100 | s.dsts = append(s.dsts, &v.dsts[i]) 101 | } 102 | } 103 | } 104 | 105 | func (s *solver) linkLines(lines progLines) progLines { 106 | m := make(map[int]*progLine) 107 | for _, l := range lines { 108 | m[l.id] = l 109 | } 110 | for i := range s.dsts { 111 | dst := s.dsts[i] 112 | l, ok := m[dst.nbr] 113 | if !ok { // target line doesn't exist 114 | l = &progLine{id: dst.nbr} 115 | m[dst.nbr] = l 116 | lines = append(lines, l) 117 | } 118 | dst.adr = l 119 | l.isDst = true 120 | } 121 | sort.Slice(lines, func(i, j int) bool { return lines[i].id < lines[j].id }) 122 | cnt := 0 123 | for _, l := range lines { 124 | if l.isDst { 125 | cnt++ 126 | l.switchID = cnt 127 | } 128 | } 129 | return lines 130 | } 131 | 132 | func (s *solver) linkForNext(p *program) { 133 | m := make(map[string]struct{}) 134 | stack := make([]*cmdFor, 64) // max levels deep 135 | sp := -1 136 | scan(p, func(h host) { 137 | switch v := h.(type) { 138 | case *cmdFor: 139 | sp++ 140 | if sp >= len(stack) { 141 | panic("Too many nested FOR loops.") 142 | } 143 | stack[sp] = v 144 | m[v.index.unambiguousName()] = struct{}{} 145 | case *cmdNext: 146 | v.createLabel() 147 | if sp < 0 { 148 | panic("NEXT without FOR") 149 | } 150 | if len(v.vars) == 0 { // NEXT without a var only matches the most recent FOR 151 | f := stack[sp] 152 | f.next = v 153 | v.vars = append(v.vars, f.index) 154 | sp-- 155 | } 156 | // NEXT with a var 157 | for sp >= 0 { 158 | f := stack[sp] 159 | if !v.vars[0].equals(f.index) { // FOR doesn't match NEXT 160 | break 161 | } 162 | f.next = v 163 | v.vars = append(v.vars, f.index) 164 | sp-- 165 | } 166 | } 167 | }) 168 | v := make([]string, 0, len(m)) 169 | for k := range m { 170 | v = append(v, k) 171 | } 172 | sort.Strings(v) 173 | p.loopVars = v 174 | } 175 | -------------------------------------------------------------------------------- /token.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sort" 5 | ) 6 | 7 | const ( 8 | tokEOF = iota + 128 9 | tokEOL 10 | tokSpace 11 | tokComment 12 | tokNumber 13 | tokString 14 | tokID 15 | 16 | // commands 17 | tokData 18 | tokDef 19 | tokDim 20 | tokEnd 21 | tokFor 22 | tokGo 23 | tokIf 24 | tokInput 25 | tokLet 26 | tokNext 27 | tokOn 28 | tokPrint 29 | tokRead 30 | tokRem 31 | tokRestore 32 | tokReturn 33 | tokRun 34 | tokStop 35 | 36 | // predefined functions 37 | tokAbs 38 | tokAsc 39 | tokAtn 40 | tokChr 41 | tokCos 42 | tokExp 43 | tokInt 44 | tokLeft 45 | tokLen 46 | tokLog 47 | tokMid 48 | tokRight 49 | tokRnd 50 | tokSgn 51 | tokSin 52 | tokSqr 53 | tokStr 54 | tokTab 55 | tokTan 56 | tokVal 57 | 58 | // general 59 | tokThen 60 | tokTo 61 | tokSub 62 | tokStep 63 | tokLe 64 | tokGe 65 | tokNe 66 | tokFn 67 | tokAnd 68 | tokOr 69 | ) 70 | 71 | type token int 72 | 73 | func (t token) receive(g guest) { 74 | } 75 | 76 | type reservedWord struct { 77 | token token 78 | s string 79 | } 80 | 81 | var reservedWordList = [...]reservedWord{ 82 | {tokAbs, "ABS"}, 83 | {tokAnd, "AND"}, 84 | {tokAsc, "ASC"}, 85 | {tokAtn, "ATN"}, 86 | {tokChr, "CHR"}, 87 | {tokCos, "COS"}, 88 | {tokData, "DATA"}, 89 | {tokDef, "DEF"}, 90 | {tokDim, "DIM"}, 91 | {tokEnd, "END"}, 92 | {tokExp, "EXP"}, 93 | {tokFn, "FN"}, 94 | {tokFor, "FOR"}, 95 | {tokGo, "GO"}, 96 | {tokIf, "IF"}, 97 | {tokInput, "INPUT"}, 98 | {tokInt, "INT"}, 99 | {tokLeft, "LEFT"}, 100 | {tokLen, "LEN"}, 101 | {tokLet, "LET"}, 102 | {tokLog, "LOG"}, 103 | {tokMid, "MID"}, 104 | {tokNext, "NEXT"}, 105 | {tokOn, "ON"}, 106 | {tokOr, "OR"}, 107 | {tokPrint, "PRINT"}, 108 | {tokRead, "READ"}, 109 | {tokRem, "REM"}, 110 | {tokRestore, "RESTORE"}, 111 | {tokReturn, "RETURN"}, 112 | {tokRight, "RIGHT"}, 113 | {tokRnd, "RND"}, 114 | {tokRun, "RUN"}, 115 | {tokSgn, "SGN"}, 116 | {tokSin, "SIN"}, 117 | {tokSqr, "SQR"}, 118 | {tokStep, "STEP"}, 119 | {tokStop, "STOP"}, 120 | {tokStr, "STR"}, 121 | {tokSub, "SUB"}, 122 | {tokTab, "TAB"}, 123 | {tokTan, "TAN"}, 124 | {tokThen, "THEN"}, 125 | {tokTo, "TO"}, 126 | {tokVal, "VAL"}, 127 | } 128 | 129 | type rws []reservedWord 130 | 131 | func (l rws) Len() int { return len(l) } 132 | func (l rws) Less(i, j int) bool { 133 | lis := l[i].s 134 | ljs := l[j].s 135 | if len(lis) > len(ljs) { 136 | return true 137 | } 138 | if len(lis) < len(ljs) { 139 | return false 140 | } 141 | return lis > ljs 142 | } 143 | func (l rws) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 144 | 145 | var reservedWords rws 146 | 147 | func init() { 148 | reservedWords = reservedWordList[:] 149 | sort.Sort(reservedWords) 150 | } 151 | -------------------------------------------------------------------------------- /visit.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type guest interface { 4 | visit(h host) 5 | } 6 | 7 | type host interface { 8 | receive(g guest) 9 | } 10 | 11 | type visitor struct { 12 | consider func(h host) 13 | } 14 | 15 | func (v *visitor) visit(h host) { 16 | h.receive(v) 17 | v.consider(h) 18 | } 19 | 20 | func scan(h host, consider func(h host)) { 21 | v := visitor{consider: consider} 22 | v.visit(h) 23 | } 24 | --------------------------------------------------------------------------------