blob: c488ea1e56845d8888cec0786c5af0a2d252b0f8 [file] [log] [blame]
// Inferno utils/cc/macbody
// http://code.Google.Com/p/inferno-os/source/browse/utils/cc/macbody
//
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.Net)
// Portions Copyright © 1997-1999 Vita Nuova Limited
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.Vitanuova.Com)
// Portions Copyright © 2004,2006 Bruce Ellis
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.Net)
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
// Portions Copyright © 2009 The Go Authors. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package asm
import (
"bytes"
"cmd/internal/obj"
"fmt"
"os"
"strings"
)
const (
VARMAC = 0x80
)
func getnsn() int32 {
c := getnsc()
if c < '0' || c > '9' {
return -1
}
n := int32(0)
for c >= '0' && c <= '9' {
n = n*10 + int32(c) - '0'
c = getc()
}
unget(c)
return n
}
func getsym() *Sym {
c := getnsc()
if !isalpha(c) && c != '_' && c < 0x80 {
unget(c)
return nil
}
var buf bytes.Buffer
for {
buf.WriteByte(byte(c))
c = getc()
if isalnum(c) || c == '_' || c >= 0x80 {
continue
}
unget(c)
break
}
last = buf.String()
return Lookup(last)
}
func getsymdots(dots *int) *Sym {
s := getsym()
if s != nil {
return s
}
c := getnsc()
if c != '.' {
unget(c)
return nil
}
if getc() != '.' || getc() != '.' {
Yyerror("bad dots in macro")
}
*dots = 1
return Lookup("__VA_ARGS__")
}
func getcom() int {
var c int
for {
c = getnsc()
if c != '/' {
break
}
c = getc()
if c == '/' {
for c != '\n' {
c = getc()
}
break
}
if c != '*' {
break
}
c = getc()
for {
if c == '*' {
c = getc()
if c != '/' {
continue
}
c = getc()
break
}
if c == '\n' {
Yyerror("comment across newline")
break
}
c = getc()
}
if c == '\n' {
break
}
}
return c
}
func dodefine(cp string) {
var s *Sym
if i := strings.Index(cp, "="); i >= 0 {
p := cp[i+1:]
cp = cp[:i]
s = Lookup(cp)
s.Macro = &Macro{Text: p}
} else {
s = Lookup(cp)
s.Macro = &Macro{Text: "1"}
}
if debug['m'] != 0 {
fmt.Printf("#define (-D) %s %s\n", s.Name, s.Macro.Text)
}
}
var mactab = []struct {
Macname string
Macf func()
}{
{"ifdef", nil}, /* macif(0) */
{"ifndef", nil}, /* macif(1) */
{"else", nil}, /* macif(2) */
{"line", maclin},
{"define", macdef},
{"include", macinc},
{"undef", macund},
{"pragma", macprag},
{"endif", macend},
}
func domacro() {
s := getsym()
if s == nil {
s = Lookup("endif")
}
for i := 0; i < len(mactab); i++ {
if s.Name == mactab[i].Macname {
if mactab[i].Macf != nil {
mactab[i].Macf()
} else {
macif(i)
}
return
}
}
Yyerror("unknown #: %s", s.Name)
macend()
}
func macund() {
s := getsym()
macend()
if s == nil {
Yyerror("syntax in #undef")
return
}
s.Macro = nil
}
const (
NARG = 25
)
func macdef() {
var args [NARG]string
var n int
var i int
var c int
var dots int
var ischr int
var base bytes.Buffer
s := getsym()
if s == nil {
goto bad
}
if s.Macro != nil {
Yyerror("macro redefined: %s", s.Name)
}
c = getc()
n = -1
dots = 0
if c == '(' {
n++
c = getnsc()
if c != ')' {
unget(c)
var a *Sym
var c int
for {
a = getsymdots(&dots)
if a == nil {
goto bad
}
if n >= NARG {
Yyerror("too many arguments in #define: %s", s.Name)
goto bad
}
args[n] = a.Name
n++
c = getnsc()
if c == ')' {
break
}
if c != ',' || dots != 0 {
goto bad
}
}
}
c = getc()
}
if isspace(c) {
if c != '\n' {
c = getnsc()
}
}
ischr = 0
for {
if isalpha(c) || c == '_' {
var buf bytes.Buffer
buf.WriteByte(byte(c))
c = getc()
for isalnum(c) || c == '_' {
buf.WriteByte(byte(c))
c = getc()
}
symb := buf.String()
for i = 0; i < n; i++ {
if symb == args[i] {
break
}
}
if i >= n {
base.WriteString(symb)
continue
}
base.WriteByte('#')
base.WriteByte(byte('a' + i))
continue
}
if ischr != 0 {
if c == '\\' {
base.WriteByte(byte(c))
c = getc()
} else if c == ischr {
ischr = 0
}
} else {
if c == '"' || c == '\'' {
base.WriteByte(byte(c))
ischr = c
c = getc()
continue
}
if c == '/' {
c = getc()
if c == '/' {
c = getc()
for {
if c == '\n' {
break
}
c = getc()
}
continue
}
if c == '*' {
c = getc()
for {
if c == '*' {
c = getc()
if c != '/' {
continue
}
c = getc()
break
}
if c == '\n' {
Yyerror("comment and newline in define: %s", s.Name)
break
}
c = getc()
}
continue
}
base.WriteByte('/')
continue
}
}
if c == '\\' {
c = getc()
if c == '\n' {
c = getc()
continue
} else if c == '\r' {
c = getc()
if c == '\n' {
c = getc()
continue
}
}
base.WriteByte('\\')
continue
}
if c == '\n' {
break
}
if c == '#' {
if n > 0 {
base.WriteByte(byte(c))
}
}
base.WriteByte(byte(c))
c = GETC()
if c == '\n' {
Lineno++
}
if c == -1 {
Yyerror("eof in a macro: %s", s.Name)
break
}
}
s.Macro = &Macro{
Text: base.String(),
Narg: n + 1,
Dots: dots != 0,
}
if debug['m'] != 0 {
fmt.Printf("#define %s %s\n", s.Name, s.Macro.Text)
}
return
bad:
if s == nil {
Yyerror("syntax in #define")
} else {
Yyerror("syntax in #define: %s", s.Name)
}
macend()
}
func macexpand(s *Sym) []byte {
if s.Macro.Narg == 0 {
if debug['m'] != 0 {
fmt.Printf("#expand %s %s\n", s.Name, s.Macro.Text)
}
return []byte(s.Macro.Text)
}
nargs := s.Macro.Narg - 1
dots := s.Macro.Dots
c := getnsc()
var arg []string
var cp string
var out bytes.Buffer
if c != '(' {
goto bad
}
c = getc()
if c != ')' {
unget(c)
l := 0
var buf bytes.Buffer
var c int
for {
c = getc()
if c == '"' {
for {
buf.WriteByte(byte(c))
c = getc()
if c == '\\' {
buf.WriteByte(byte(c))
c = getc()
continue
}
if c == '\n' {
goto bad
}
if c == '"' {
break
}
}
}
if c == '\'' {
for {
buf.WriteByte(byte(c))
c = getc()
if c == '\\' {
buf.WriteByte(byte(c))
c = getc()
continue
}
if c == '\n' {
goto bad
}
if c == '\'' {
break
}
}
}
if c == '/' {
c = getc()
switch c {
case '*':
for {
c = getc()
if c == '*' {
c = getc()
if c == '/' {
break
}
}
}
buf.WriteByte(' ')
continue
case '/':
for {
c = getc()
if !(c != '\n') {
break
}
}
default:
unget(c)
c = '/'
}
}
if l == 0 {
if c == ',' {
if len(arg) == nargs-1 && dots {
buf.WriteByte(',')
continue
}
arg = append(arg, buf.String())
buf.Reset()
continue
}
if c == ')' {
arg = append(arg, buf.String())
break
}
}
if c == '\n' {
c = ' '
}
buf.WriteByte(byte(c))
if c == '(' {
l++
}
if c == ')' {
l--
}
}
}
if len(arg) != nargs {
Yyerror("argument mismatch expanding: %s", s.Name)
return nil
}
cp = s.Macro.Text
for i := 0; i < len(cp); i++ {
c = int(cp[i])
if c == '\n' {
c = ' '
}
if c != '#' {
out.WriteByte(byte(c))
continue
}
i++
if i >= len(cp) {
goto bad
}
c = int(cp[i])
if c == '#' {
out.WriteByte(byte(c))
continue
}
c -= 'a'
if c < 0 || c >= len(arg) {
continue
}
out.WriteString(arg[c])
}
if debug['m'] != 0 {
fmt.Printf("#expand %s %s\n", s.Name, out.String())
}
return out.Bytes()
bad:
Yyerror("syntax in macro expansion: %s", s.Name)
return nil
}
func macinc() {
var c int
var buf bytes.Buffer
var f *os.File
var hp string
var str string
var symb string
c0 := getnsc()
if c0 != '"' {
c = c0
if c0 != '<' {
goto bad
}
c0 = '>'
}
for {
c = getc()
if c == c0 {
break
}
if c == '\n' {
goto bad
}
buf.WriteByte(byte(c))
}
str = buf.String()
c = getcom()
if c != '\n' {
goto bad
}
for i := 0; i < len(include); i++ {
if i == 0 && c0 == '>' {
continue
}
symb = include[i]
symb += "/"
if symb == "./" {
symb = ""
}
symb += str
var err error
f, err = os.Open(symb)
if err == nil {
break
}
}
if f == nil {
symb = str
}
hp = symb
newio()
pushio()
newfile(hp, f)
return
bad:
unget(c)
Yyerror("syntax in #include")
macend()
}
func maclin() {
var buf bytes.Buffer
var symb string
n := getnsn()
c := getc()
if n < 0 {
goto bad
}
for {
if c == ' ' || c == '\t' {
c = getc()
continue
}
if c == '"' {
break
}
if c == '\n' {
symb = "<noname>"
goto nn
}
goto bad
}
for {
c = getc()
if c == '"' {
break
}
buf.WriteByte(byte(c))
}
symb = buf.String()
c = getcom()
if c != '\n' {
goto bad
}
nn:
obj.Linklinehist(Ctxt, int(Lineno), symb, int(n))
return
bad:
unget(c)
Yyerror("syntax in #line")
macend()
}
func macif(f int) {
var c int
var l int
var bol int
var s *Sym
if f == 2 {
goto skip
}
s = getsym()
if s == nil {
goto bad
}
if getcom() != '\n' {
goto bad
}
if (s.Macro != nil) != (f != 0) {
return
}
skip:
bol = 1
l = 0
for {
c = getc()
if c != '#' {
if !isspace(c) {
bol = 0
}
if c == '\n' {
bol = 1
}
continue
}
if !(bol != 0) {
continue
}
s = getsym()
if s == nil {
continue
}
if s.Name == "endif" {
if l != 0 {
l--
continue
}
macend()
return
}
if s.Name == "ifdef" || s.Name == "ifndef" {
l++
continue
}
if l == 0 && f != 2 && s.Name == "else" {
macend()
return
}
}
bad:
Yyerror("syntax in #if(n)def")
macend()
}
func macprag() {
var c int
s := getsym()
if s != nil && s.Name == "lib" {
c0 := getnsc()
if c0 != '"' {
c = c0
if c0 != '<' {
goto bad
}
c0 = '>'
}
var buf bytes.Buffer
for {
c = getc()
if c == c0 {
break
}
if c == '\n' {
goto bad
}
buf.WriteByte(byte(c))
}
symb := buf.String()
c = getcom()
if c != '\n' {
goto bad
}
/*
* put pragma-line in as a funny history
*/
obj.Linklinehist(Ctxt, int(Lineno), symb, -1)
return
}
if s != nil && s.Name == "pack" {
pragpack()
return
}
if s != nil && s.Name == "fpround" {
pragfpround()
return
}
if s != nil && s.Name == "textflag" {
pragtextflag()
return
}
if s != nil && s.Name == "dataflag" {
pragdataflag()
return
}
if s != nil && s.Name == "varargck" {
pragvararg()
return
}
if s != nil && s.Name == "incomplete" {
pragincomplete()
return
}
if s != nil && (strings.HasPrefix(s.Name, "cgo_") || strings.HasPrefix(s.Name, "dyn")) {
pragcgo(s.Name)
return
}
for getnsc() != '\n' {
}
return
bad:
unget(c)
Yyerror("syntax in #pragma lib")
macend()
}
func macend() {
var c int
for {
c = getnsc()
if c < 0 || c == '\n' {
return
}
}
}