diff options
Diffstat (limited to 'src/common/typespec.icn')
-rw-r--r-- | src/common/typespec.icn | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/src/common/typespec.icn b/src/common/typespec.icn new file mode 100644 index 0000000..f86ba9a --- /dev/null +++ b/src/common/typespec.icn @@ -0,0 +1,482 @@ +# +# typespec - transform Icon type specifications into C tables. +# Specifications are read from standard input; tables are written +# to standard output. +# +# The grammar for the a type specifcation is: +# +# <type-def> ::= <identifier> <opt-abrv> : <kind> <opt-return> +# +# <kind> ::= simple | +# aggregate(<component>, ... ) | +# variable <var-type-spec> +# +# <component> ::= var <identifier> <opt-abrv> | +# <identifier> +# +# <var-type-spec> ::= initially <type> | +# always <type> +# +# <type> ::= <type-name> | <type> ++ <type-name> +# +# <opt-abrv> ::= <nil> | +# { <identifier> } +# +# <opt-return> ::= <nil> | +# return block_pointer | +# return descriptor_pointer | +# return char_pointer | +# return C_integer + +# Information about an Icon type. +# +record icon_type( + id, # name of type + support_new, # supports RTL "new" construct + deref, # dereferencing needs + rtl_ret, # kind of RTL return supported if any + typ, # for variable: initial type + num_comps, # for aggregate: number of type components + compnts, # for aggregate: index of first component + abrv) # abreviation used for type tracing + +# Information about a component of an aggregate type. +# +record typ_compnt ( + id, # name of component + n, # position of component within type aggragate + var, # flag: this component is an Icon-level variable + aggregate, # index of type that owns the component + abrv) # abreviation used for type tracing + +record token(kind, image) + +global icontypes, typecompnt, type_indx, compnt_indx +global lex, line_num, saved_token, error_msg, prog_name + +procedure main() + local typ, tok, compnt, indx, x + + prog_name := "typespec" + lex := create tokenize_input() + + icontypes := [] + typecompnt := [] + + # + # Read each of the type specifications + # + while typ := icon_type(ident("may be EOF")) do { + # + # Check for abreviation + # + typ.abrv := opt_abrv(typ.id) + + if next_token().kind ~== ":" then + input_err("expected ':'") + + # + # See what kind of type this is + # + case ident() of { + "simple": { + typ.support_new := "0" + typ.deref := "DrfNone" + typ.num_comps := "0" + typ.compnts := "0" + } + + "aggregate": { + typ.support_new := "1" + typ.deref := "DrfNone" + + # + # get the component names for the type + # + typ.compnts := *typecompnt + if next_token().kind ~== "(" then + input_err("expected '('") + typ.num_comps := 0 + tok := next_token() + if tok.kind ~== "id" then + input_err("expected type component") + while tok.kind ~== ")" do { + # + # See if this component is an Icon variable. + # + if tok.image == "var" then { + compnt := typ_compnt(ident(), typ.num_comps, "1", *icontypes) + compnt.abrv := opt_abrv(compnt.id) + } + else + compnt := typ_compnt(tok.image, typ.num_comps, "0", + *icontypes) + + put(typecompnt, compnt) + typ.num_comps +:= 1 + + tok := next_token() + if tok.kind == "," then { + tok := next_token() + if tok.kind ~== "id" then + input_err("expected type component") + } + else if tok.kind ~== ")" then + input_err("expected type component") + } + } + + "variable": { + typ.support_new := "0" + typ.num_comps := "0" + typ.compnts := "0" + case ident() of { + "initially": + typ.deref := "DrfGlbl" + "always": + typ.deref := "DrfCnst" + default: + input_err("expected 'initially' or 'always'") + } + + # + # Get the initial type associated with the variable + # + typ.typ := [ident()] + tok := &null + while (tok := next_token("may be EOF")).kind == "++" do { + put(typ.typ, ident()) + tok := &null + } + saved_token := tok # put token back + } + default: + input_err("expected 'simple', 'aggregate', or 'variable'") + } + + # + # Check for an optional return clause + # + tok := &null + if (tok := next_token("may be EOF")).image == "return" then { + case next_token().image of { + "block_pointer": + typ.rtl_ret := "TRetBlkP" + "descriptor_pointer": + typ.rtl_ret := "TRetDescP" + "char_pointer": + typ.rtl_ret := "TRetCharP" + "C_integer": + typ.rtl_ret := "TRetCInt" + default: + input_err("expected vword type") + } + } + else { + typ.rtl_ret := "TRetNone" + saved_token := tok # put token back + } + + put(icontypes, typ) + } + + # + # Create tables of type and compontent indexes. + # + type_indx := table() + indx := -1 + every type_indx[(!icontypes).id] := (indx +:= 1) + compnt_indx := table() + indx := -1 + every compnt_indx[(!typecompnt).id] := (indx +:= 1) + + write("/*") + write(" * This file was generated by the program ", prog_name, ".") + write(" */") + write() + + # + # Locate the indexes of types with special semantics or which are + # explicitly needed by iconc. Output the indexes as assignments to + # variables. + # + indx := req_type("string") + icontypes[indx + 1].rtl_ret := "TRetSpcl" + write("int str_typ = ", indx, ";") + + indx := req_type("integer") + write("int int_typ = ", indx, ";") + + indx := req_type("record") + write("int rec_typ = ", indx, ";") + + indx := req_type("proc") + write("int proc_typ = ", indx, ";") + + indx := req_type("coexpr") + write("int coexp_typ = ", indx, ";") + + indx := req_type("tvsubs") + icontypes[indx + 1].deref := "DrfSpcl" + icontypes[indx + 1].rtl_ret := "TRetSpcl" + write("int stv_typ = ", indx, ";") + + indx := req_type("tvtbl") + icontypes[indx + 1].deref := "DrfSpcl" + write("int ttv_typ = ", indx, ";") + + indx := req_type("null") + write("int null_typ = ", indx, ";") + + indx := req_type("cset") + write("int cset_typ = ", indx, ";") + + indx := req_type("real") + write("int real_typ = ", indx, ";") + + indx := req_type("list") + write("int list_typ = ", indx, ";") + + indx := req_type("table") + write("int tbl_typ = ", indx, ";") + + # + # Output the type table. + # + write() + write("int num_typs = ", *icontypes, ";") + write("struct icon_type icontypes[", *icontypes, "] = {") + x := copy(icontypes) + output_typ(get(x)) + while typ := get(x) do { + write(",") + output_typ(typ) + } + write("};") + + # + # Locate the indexes of components which are explicitly needed by iconc. + # Output the indexes as assignments to variables. + # + write() + indx := req_compnt("str_var") + write("int str_var = ", indx, ";") + + indx := req_compnt("trpd_tbl") + write("int trpd_tbl = ", indx, ";") + + indx := req_compnt("lst_elem") + write("int lst_elem = ", indx, ";") + + indx := req_compnt("tbl_dflt") + write("int tbl_dflt = ", indx, ";") + + indx := req_compnt("tbl_val") + write("int tbl_val = ", indx, ";") + + # + # Output the component table. + # + write() + write("int num_cmpnts = ", *typecompnt, ";") + write("struct typ_compnt typecompnt[", *typecompnt, "] = {") + output_compnt(get(typecompnt)) + while compnt := get(typecompnt) do { + write(",") + output_compnt(compnt) + } + write("};") +end + +# +# ident - insure that next token is an identifier and return its image +# +procedure ident(may_be_eof) + local tok + + tok := next_token(may_be_eof) | fail + + if tok.kind == "id" then + return tok.image + else + input_err("expected identifier") +end + +# +# opt_abrv - look for an optional abreviation. If there is none, return the +# default value supplied by the caller. +# +procedure opt_abrv(abrv) + local tok + + tok := next_token("may be EOF") + if tok.kind == "{" then { + abrv := ident() + if next_token().kind ~== "}" then + input_err("expected '}'") + } + else + saved_token := tok # put token back + + return abrv +end + +# +# next_token - get the next token, looking to see if one was put back. +# +procedure next_token(may_be_eof) + local tok + + if \saved_token then { + tok := saved_token + saved_token := &null + return tok + } + else if tok := @lex then + return tok + else if \may_be_eof then + fail + else { + write(&errout, prog_name, ", unexpected EOF") + exit(1) + } +end + +# +# req_type - get the index of a required type. +# +procedure req_type(id) + local indx + + if indx := \type_indx[id] then + return indx + else { + write(&errout, prog_name, ", the type ", id, " is required") + exit(1) + } +end + +# +# req_compnt - get the index of a required component. +# +procedure req_compnt(id) + local indx + + if indx := \compnt_indx[id] then + return indx + else { + write(&errout, prog_name, ", the component ", id, " is required") + exit(1) + } +end + +# +# output_typ - output the table entry for a type. +# +procedure output_typ(typ) + local typ_str, s, indx + + writes(" {", image(typ.id), ", ", typ.support_new, ", ", typ.deref, ", ", + typ.rtl_ret, ", ") + if \typ.typ then { + typ_str := repl(".", *type_indx) + every s := !typ.typ do { + if s == "any_value" then { + every indx := 1 to *icontypes do { + if icontypes[indx].deref == "DrfNone" then + typ_str[indx] := icontypes[indx].abrv[1] + } + } + else if indx := \type_indx[s] + 1 then + typ_str[indx] := icontypes[indx].abrv[1] + else { + write(&errout, prog_name, ", the specification for ", typ.id, + " contains an illegal type: ", s) + exit(1) + } + } + writes(image(typ_str)) + } + else + writes("NULL") + writes(", ", typ.num_comps, ", ", typ.compnts, ", ", image(typ.abrv), ", ") + writes(image(map(typ.id[1], &lcase, &ucase) || typ.id[2:0]), "}") +end + +# +# output_compnt - output the table entry for a component. +# +procedure output_compnt(compnt) + writes(" {", image(compnt.id), ", ", compnt.n, ", ", compnt.var, ", ", + compnt.aggregate, ", ", image(\compnt.abrv) | "NULL", "}") +end + +# +# input_err - signal the lexical anaylser to print an error message about +# the last token +# +procedure input_err(msg) + error_msg := msg + @lex +end + +# +# tokenize_input - transform standard input into tokens and suspend them +# +procedure tokenize_input() + local line + + line_num := 0 + while line := read() do { + line_num +:= 1 + suspend line ? tokenize_line() + } + fail +end + +# +# tokenize_line - transform the subject of string scanning into tokens and +# suspend them +# +procedure tokenize_line() + local s, tok, save_pos + static id_chars + + initial id_chars := &letters ++ &digits ++ '_' + + repeat { + tab(many(' \t')) # skip white space + if ="#" | pos(0) then + fail # end of input on this line + + save_pos := &pos + + if any(&letters) then + tok := token("id", tab(many(id_chars))) + else if s := =(":" | "(" | "," | ")" | "++" | "{" | "}") then + tok := token(s, s) + else + err("unknown symbol") + + suspend tok + err(\error_msg, save_pos) # was the last token erroneous? + } +end + +# +# err - print an error message about the current string being scanned +# +procedure err(msg, save_pos) + local s, strt_msg + + tab(\save_pos) # error occured here + + strt_msg := prog_name || ", " || msg || "; line " || line_num || ": " + (s := image(tab(1))) & &fail # get front of line then undo tab + strt_msg ||:= s[1:-1] # strip ending quote from image + s := image(tab(0)) # get end of line + s := s[2:0] # strip first quote from image + write(&errout, strt_msg, s) + write(&errout, repl(" ", *strt_msg), "^") # show location of error + exit(1) +end |