home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume38 / ibpag2 / part03 < prev    next >
Encoding:
Text File  |  1993-07-12  |  53.2 KB  |  1,667 lines

  1. Newsgroups: comp.sources.misc
  2. From: goer@midway.uchicago.edu (Richard L. Goerwitz)
  3. Subject: v38i047:  ibpag2 - Icon-Based Parser Generator, Part03/05
  4. Message-ID: <1993Jul13.044428.17151@sparky.sterling.com>
  5. X-Md4-Signature: 221d98ad8dbb09a8fa5382d7c00f1539
  6. Sender: kent@sparky.sterling.com (Kent Landfield)
  7. Organization: University of Chicago
  8. Date: Tue, 13 Jul 1993 04:44:28 GMT
  9. Approved: kent@sparky.sterling.com
  10.  
  11. Submitted-by: goer@midway.uchicago.edu (Richard L. Goerwitz)
  12. Posting-number: Volume 38, Issue 47
  13. Archive-name: ibpag2/part03
  14. Environment: Icon
  15.  
  16. #! /bin/sh
  17. # This is a shell archive.  Remove anything before this line, then feed it
  18. # into a shell via "sh file" or similar.  To overwrite existing files,
  19. # type "sh file -c".
  20. # Contents:  ibtokens.icn iiparse.lib slrtbls.icn
  21. # Wrapped by kent@sparky on Sun Jul 11 18:51:51 1993
  22. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  23. echo If this archive is complete, you will see the following message:
  24. echo '          "shar: End of archive 3 (of 5)."'
  25. if test -f 'ibtokens.icn' -a "${1}" != "-c" ; then 
  26.   echo shar: Will not clobber existing file \"'ibtokens.icn'\"
  27. else
  28.   echo shar: Extracting \"'ibtokens.icn'\" \(26595 characters\)
  29.   sed "s/^X//" >'ibtokens.icn' <<'END_OF_FILE'
  30. X############################################################################
  31. X#
  32. X#    Name:     ibtokens.icn
  33. X#
  34. X#    Title:     ibtokens (Ibpag2 source-file tokenizer)
  35. X#
  36. X#    Author:     Richard L. Goerwitz
  37. X#
  38. X#    Version: 1.10
  39. X#
  40. X############################################################################
  41. X#
  42. X#  This file contains ibtokens() - a utility for breaking Ibpag2
  43. X#  source files up into individual tokens.  Ibtokens(f) takes, as its
  44. X#  first and only argument, an open file, and suspends successive
  45. X#  ib_TOK records (declared below).  ib_TOK records contain two
  46. X#  fields.  The first field, sym, contains a string that represents
  47. X#  the name of the next token (e.g. "CSET", "STRING", etc.).  The
  48. X#  second field, str, gives that token's literal value.  E.g. the
  49. X#  ib_TOK for a literal semicolon is ib_TOK("SEMICOL", ";").  For a
  50. X#  mandatory newline, ibtokens would suspend ib_TOK("SEMICOL", "\n").
  51. X#  NB:  As a cheat, ibtokens() suspends ib_TOK(&null, "\n") for
  52. X#  nonessential newlines.
  53. X#
  54. X#  NOTE WELL: If new reserved words or operators are added to a given
  55. X#  Icon implementation, the tables below will have to be altered.
  56. X#  Note also that &keywords are implemented on the syntactic level -
  57. X#  not on the lexical one.  As a result, a keyword like &features will
  58. X#  be suspended as ib_TOK("CONJUNC", "&") and ib_TOK("IDENT",
  59. X#  "features").  In fact, this tokenizer mirrors closely the tokenizer
  60. X#  used in Icon's C implementation.
  61. X#
  62. X############################################################################
  63. X#
  64. X#  Links: slshupto
  65. X#
  66. X#  Requires: coexpressions
  67. X#
  68. X#  See also: itokens.icn, ibreader.icn, ibwriter.icn
  69. X#
  70. X############################################################################
  71. X
  72. X#link slshupto
  73. X
  74. Xglobal next_c, line_number
  75. Xrecord ib_TOK(sym, str)
  76. X
  77. X#
  78. X# ibtokens:  file      -> ib_TOK records (a generator)
  79. X#           (stream)   -> Rs
  80. X#
  81. X#     Where stream is an open file, and Rs are ib_TOK records.  Note that
  82. X#     ibtokens strips out useless newlines.  If you want to preserve
  83. X#     the line structure of the original file, see the description of
  84. X#     ibpag2_tokens() below.
  85. X#
  86. Xprocedure ibtokens(stream)
  87. X
  88. X    local T
  89. X
  90. X    every T := \ibpag2_tokens(stream) do {
  91. X    #
  92. X    # Ibpag2_tokens emits dummy tokens for nonessential newlines.
  93. X    # These have a null sym field.
  94. X    #
  95. X    \T.sym == "EOFX" & fail
  96. X        suspend T
  97. X    }
  98. X
  99. Xend
  100. X
  101. X
  102. X#
  103. X# ibpag2_tokens:  file     -> ib_TOK records (a generator)
  104. X#                 (stream) -> tokens
  105. X#
  106. X#     Where file is an open input stream, and tokens are ib_TOK records
  107. X#     holding both the token type and actual token text.
  108. X#
  109. X#     ib_TOK records contain two parts, a preterminal symbol (the first
  110. X#     "sym" field), and the actual text of the token ("str").  The
  111. X#     parser only pays attention to the sym field, although the
  112. X#     strings themselves get pushed onto the value stack.
  113. X#
  114. X#     Note the following kludge:  Unlike real Icon tokenizers, this
  115. X#     procedure returns syntactially meaningless newlines as ib_TOK
  116. X#     records with a null sym field.  Normally they would be ignored.
  117. X#     I wanted to return them so they could be printed on the output
  118. X#     stream, thus preserving the line structure of the original
  119. X#     file, and making later diagnostic messages more usable.
  120. X#
  121. Xprocedure ibpag2_tokens(stream, getchar)
  122. X
  123. X    local elem, whitespace, token, last_token, primitives, reserveds
  124. X    static be_tbl, reserved_tbl, operators
  125. X    initial {
  126. X
  127. X    #  Primitive Tokens
  128. X    #
  129. X    primitives := [
  130. X               ["identifier",      "IDENT",     "be"],
  131. X               ["integer-literal", "INTLIT",    "be"],
  132. X               ["real-literal",    "REALLIT",   "be"],
  133. X               ["string-literal",  "STRINGLIT", "be"],
  134. X               ["cset-literal",    "CSETLIT",   "be"],
  135. X               ["end-of-file",     "EOFX",      "" ]]
  136. X
  137. X    # Reserved Words
  138. X    #
  139. X    reserveds  := [
  140. X               ["break",           "BREAK",     "be"],
  141. X               ["by",              "BY",        ""  ],
  142. X               ["case",            "CASE",      "b" ],
  143. X               ["create",          "CREATE",    "b" ],
  144. X               ["default",         "DEFAULT",   "b" ],
  145. X               ["do",              "DO",        ""  ],
  146. X                       ["else",            "ELSE",      ""  ],
  147. X               ["end",             "END",       "b" ],
  148. X               ["every",           "EVERY",     "b" ],
  149. X               ["fail",            "FAIL",      "be"],
  150. X               ["global",          "GLOBAL",    ""  ],
  151. X               ["if",              "IF",        "b" ],
  152. X               ["initial",         "INITIAL",   "b" ],
  153. X               ["invocable",       "INVOCABLE", ""  ],
  154. X               ["link",            "LINK",      ""  ],
  155. X               ["local",           "LOCAL",     "b" ],
  156. X               ["next",            "NEXT",      "be"],
  157. X               ["not",             "NOT",       "b" ],
  158. X               ["of",              "OF",        ""  ],
  159. X               ["procedure",       "PROCEDURE", ""  ],
  160. X               ["record",          "RECORD",    ""  ],
  161. X               ["repeat",          "REPEAT",    "b" ],
  162. X               ["return",          "RETURN",    "be"],
  163. X               ["static",          "STATIC",    "b" ],
  164. X               ["suspend",         "SUSPEND",   "be"],
  165. X               ["then",            "THEN",      ""  ],
  166. X               ["to",              "TO",        ""  ],
  167. X               ["until",           "UNTIL",     "b" ],
  168. X               ["while",           "WHILE",     "b" ]]
  169. X
  170. X    # Operators
  171. X    #
  172. X    operators  := [
  173. X               [":=",              "ASSIGN",    ""  ],
  174. X               ["@",               "AT",        "b" ],
  175. X               ["@:=",             "AUGACT",    ""  ],
  176. X               ["&:=",             "AUGAND",    ""  ],
  177. X               ["=:=",             "AUGEQ",     ""  ],
  178. X               ["===:=",           "AUGEQV",    ""  ],
  179. X               [">=:=",            "AUGGE",     ""  ],
  180. X               [">:=",             "AUGGT",     ""  ],
  181. X               ["<=:=",            "AUGLE",     ""  ],
  182. X               ["<:=",             "AUGLT",     ""  ],
  183. X               ["~=:=",            "AUGNE",     ""  ],
  184. X               ["~===:=",          "AUGNEQV",   ""  ],
  185. X               ["==:=",            "AUGSEQ",    ""  ],
  186. X               [">>=:=",           "AUGSGE",    ""  ],
  187. X               [">>:=",            "AUGSGT",    ""  ],
  188. X               ["<<=:=",           "AUGSLE",    ""  ],
  189. X               ["<<:=",            "AUGSLT",    ""  ],
  190. X               ["~==:=",           "AUGSNE",    ""  ],
  191. X               ["\\",              "BACKSLASH", "b" ],
  192. X               ["!",               "BANG",      "b" ],
  193. X               ["|",               "BAR",       "b" ],
  194. X               ["^",               "CARET",     "b" ],
  195. X               ["^:=",             "CARETASGN", "b" ],
  196. X               [":",               "COLON",     ""  ],
  197. X               [",",               "COMMA",     ""  ],
  198. X               ["||",              "CONCAT",    "b" ],
  199. X                       ["||:=",            "CONCATASGN",""  ],
  200. X               ["&",               "CONJUNC",   "b" ],
  201. X               [".",               "DOT",       "b" ],
  202. X               ["--",              "DIFF",      "b" ],
  203. X               ["--:=",            "DIFFASGN",  ""  ],
  204. X               ["===",             "EQUIV",     "b" ],
  205. X               ["**",              "INTER",     "b" ],
  206. X               ["**:=",            "INTERASGN", ""  ],
  207. X               ["{",               "LBRACE",    "b" ],
  208. X               ["[",               "LBRACK",    "b" ],
  209. X               ["|||",             "LCONCAT",   "b" ],
  210. X               ["|||:=",           "LCONCATASGN","" ],
  211. X               ["==",              "LEXEQ",     "b" ],
  212. X               [">>=",             "LEXGE",     ""  ],
  213. X               [">>",              "LEXGT",     ""  ],
  214. X               ["<<=",             "LEXLE",     ""  ],
  215. X               ["<<",              "LEXLT",     ""  ],
  216. X               ["~==",             "LEXNE",     "b" ],
  217. X               ["(",               "LPAREN",    "b" ],
  218. X               ["-:",              "MCOLON",    ""  ],
  219. X               ["-",               "MINUS",     "b" ],
  220. X               ["-:=",             "MINUSASGN", ""  ],
  221. X               ["%",               "MOD",       ""  ],
  222. X               ["%:=",             "MODASGN",   ""  ],
  223. X               ["~===",            "NOTEQUIV",  "b" ],
  224. X               ["=",               "NUMEQ",     "b" ],
  225. X               [">=",              "NUMGE",     ""  ],
  226. X               [">",               "NUMGT",     ""  ],
  227. X               ["<=",              "NUMLE",     ""  ],
  228. X               ["<",               "NUMLT",     ""  ],
  229. X               ["~=",              "NUMNE",     "b" ],
  230. X               ["+:",              "PCOLON",    ""  ],
  231. X               ["+",               "PLUS",      "b" ],
  232. X               ["+:=",             "PLUSASGN",  ""  ],
  233. X               ["?",               "QMARK",     "b" ],
  234. X               ["<-",              "REVASSIGN", ""  ],
  235. X               ["<->",             "REVSWAP",   ""  ],
  236. X               ["}",               "RBRACE",    "e" ],
  237. X               ["]",               "RBRACK",    "e" ],
  238. X               [")",               "RPAREN",    "e" ],
  239. X               [";",               "SEMICOL",   ""  ],
  240. X               ["?:=",             "SCANASGN",  ""  ],
  241. X               ["/",               "SLASH",     "b" ],
  242. X               ["/:=",             "SLASHASGN", ""  ],
  243. X               ["*",               "STAR",      "b" ],
  244. X               ["*:=",             "STARASGN",  ""  ],
  245. X               [":=:",             "SWAP",      ""  ],
  246. X               ["~",               "TILDE",     "b" ],
  247. X               ["++",              "UNION",     "b" ],
  248. X               ["++:=",            "UNIONASGN", ""  ],
  249. X               ["$(",              "LBRACE",    "b" ],
  250. X               ["$)",              "RBRACE",    "e" ],
  251. X               ["$<",              "LBRACK",    "b" ],
  252. X               ["$>",              "RBRACK",    "e" ],
  253. X               ["$",               "RHSARG",    "b" ],
  254. X               ["%$(",             "BEGGLOB",   "b" ],
  255. X               ["%$)",             "ENDGLOB",   "e" ],
  256. X               ["%{",              "BEGGLOB",   "b" ],
  257. X               ["%}",              "ENDGLOB",   "e" ],
  258. X               ["%%",              "NEWSECT",   "be"]]
  259. X
  260. X    # static be_tbl, reserved_tbl
  261. X    reserved_tbl := table()
  262. X    every elem := !reserveds do
  263. X        insert(reserved_tbl, elem[1], elem[2])
  264. X    be_tbl := table()
  265. X    every elem := !primitives | !reserveds | !operators do {
  266. X        insert(be_tbl, elem[2], elem[3])
  267. X    }
  268. X    }
  269. X
  270. X    /getchar   := create {
  271. X    line_number := 0
  272. X    ! ( 1(!stream, line_number +:=1) || "\n" )
  273. X    }
  274. X    whitespace := ' \t'
  275. X    /next_c    := @getchar | {
  276. X    if \stream then
  277. X        return ib_TOK("EOFX")
  278. X    else fail
  279. X    }
  280. X
  281. X
  282. X    repeat {
  283. X    case next_c of {
  284. X
  285. X        "."      : {
  286. X        # Could be a real literal *or* a dot operator.  Check
  287. X        # following character to see if it's a digit.  If so,
  288. X        # it's a real literal.  We can only get away with
  289. X        # doing the dot here because it is not a substring of
  290. X        # any longer identifier.  If this gets changed, we'll
  291. X        # have to move this code into do_iboperator().
  292. X        #
  293. X        last_token := do_ibdot(getchar)
  294. X        suspend last_token
  295. X#        write(&errout, "next_c == ", image(next_c))
  296. X        next
  297. X        }
  298. X
  299. X        "\n"     : {
  300. X        # If do_ibnewline fails, it means we're at the end of
  301. X        # the input stream, and we should break out of the
  302. X        # repeat loop.
  303. X        #
  304. X        every last_token := do_ibnewline(getchar, last_token, be_tbl)
  305. X        do suspend last_token
  306. X        if next_c === &null then break
  307. X        next
  308. X        }
  309. X
  310. X        "\#"     : {
  311. X        # Just a comment.  Strip it by reading every character
  312. X        # up to the next newline.  The global var next_c
  313. X        # should *always* == "\n" when this is done.
  314. X        #
  315. X        do_ibnumber_sign(getchar)
  316. X#        write(&errout, "next_c == ", image(next_c))
  317. X        next
  318. X        }
  319. X
  320. X        "\""    : {
  321. X        # Suspend as STRINGLIT everything from here up to the
  322. X        # next non-backslashed quotation mark, inclusive
  323. X        # (accounting for the _ line-continuation convention).
  324. X        #
  325. X        last_token := do_ibquotation_mark(getchar)
  326. X        suspend last_token
  327. X#        write(&errout, "next_c == ", image(next_c))
  328. X        next
  329. X        }
  330. X
  331. X        "'"     : {
  332. X        # Suspend as CSETLIT everything from here up to the
  333. X        # next non-backslashed apostrophe, inclusive.
  334. X        #
  335. X        last_token := do_ibapostrophe(getchar)
  336. X        suspend last_token
  337. X#        write(&errout, "next_c == ", image(next_c))
  338. X        next
  339. X        }
  340. X
  341. X        &null   : iohno(4)
  342. X
  343. X        default : {
  344. X        # If we get to here, we have either whitespace, an
  345. X        # integer or real literal, an identifier or reserved
  346. X        # word (both get handled by do_ibidentifier), or an
  347. X        # operator.  The question of which we have can be
  348. X        # determined by checking the first character.
  349. X        #
  350. X        if any(whitespace, next_c) then {
  351. X            # Like all of the ib_TOK forming procedures,
  352. X            # do_ibwhitespace resets next_c.
  353. X            do_ibwhitespace(getchar, whitespace)
  354. X            # don't suspend any tokens
  355. X            next
  356. X        }
  357. X        if any(&digits, next_c) then {
  358. X            last_token := do_ibdigits(getchar)
  359. X            suspend last_token
  360. X            next
  361. X        }
  362. X        if any(&letters ++ '_', next_c) then {
  363. X            last_token := do_ibidentifier(getchar, reserved_tbl)
  364. X            suspend last_token
  365. X            next
  366. X        }
  367. X#        write(&errout, "it's an operator")
  368. X        last_token := do_iboperator(getchar, operators)
  369. X        suspend last_token
  370. X        next
  371. X        }
  372. X    }
  373. X    }
  374. X
  375. X    # If stream argument is nonnull, then we are in the top-level
  376. X    # ibpag2_tokens().  If not, then we are in a recursive call, and
  377. X    # we should not emit all this end-of-file crap.
  378. X    #
  379. X    if \stream then
  380. X    return ib_TOK("EOFX")
  381. X    else fail
  382. X
  383. Xend
  384. X
  385. X
  386. X#
  387. X#  do_ibdot:  coexpression -> ib_TOK record
  388. X#             getchar      -> t
  389. X#
  390. X#      Where getchar is the coexpression that produces the next
  391. X#      character from the input stream and t is a token record whose
  392. X#      sym field contains either "REALLIT" or "DOT".  Essentially,
  393. X#      do_ibdot checks the next char on the input stream to see if
  394. X#      it's an integer.  Since the preceding char was a dot, an
  395. X#      integer tips us off that we have a real literal.  Otherwise,
  396. X#      it's just a dot operator.  Note that do_ibdot resets next_c for
  397. X#      the next cycle through the main case loop in the calling
  398. X#      procedure.
  399. X#
  400. Xprocedure do_ibdot(getchar)
  401. X
  402. X    local token
  403. X    # global next_c
  404. X
  405. X#    write(&errout, "it's a dot")
  406. X
  407. X    # If dot's followed by a digit, then we have a real literal.
  408. X    #
  409. X    if any(&digits, next_c := @getchar) then {
  410. X#    write(&errout, "dot -> it's a real literal")
  411. X    token := "." || next_c
  412. X    while any(&digits, next_c := @getchar) do
  413. X        token ||:= next_c
  414. X    if token ||:= (next_c == ("e"|"E")) then {
  415. X        while (next_c := @getchar) == "0"
  416. X        while any(&digits, next_c) do {
  417. X        token ||:= next_c
  418. X        next_c = @getchar
  419. X        }
  420. X    }
  421. X    return ib_TOK("REALLIT", token)
  422. X    }
  423. X
  424. X    # Dot not followed by an integer; so we just have a dot operator,
  425. X    # and not a real literal.
  426. X    #
  427. X#    write(&errout, "dot -> just a plain dot")
  428. X    return ib_TOK("DOT", ".")
  429. X    
  430. Xend
  431. X
  432. X
  433. X#
  434. X#  do_ibnewline:  coexpression x ib_TOK record x table -> ib_TOK records
  435. X#                 (getchar, last_token, be_tbl)        -> Ts (a generator)
  436. X#
  437. X#      Where getchar is the coexpression that returns the next
  438. X#      character from the input stream, last_token is the last ib_TOK
  439. X#      record suspended by the calling procedure, be_tbl is a table of
  440. X#      tokens and their "beginner/ender" status, and Ts are ib_TOK
  441. X#      records.  Note that do_ibnewline resets next_c.  Do_Ibnewline
  442. X#      is a mess.  What it does is check the last token suspended by
  443. X#      the calling procedure to see if it was a beginner or ender.  It
  444. X#      then gets the next token by calling ibpag2_tokens again.  If
  445. X#      the next token is a beginner and the last token is an ender,
  446. X#      then we have to suspend a SEMICOL token.  In either event, both
  447. X#      the last and next token are suspended.
  448. X#
  449. Xprocedure do_ibnewline(getchar, last_token, be_tbl)
  450. X
  451. X    local next_token
  452. X    # global next_c
  453. X
  454. X#    write(&errout, "it's a newline")
  455. X
  456. X    # Go past any additional newlines.
  457. X    #
  458. X    while next_c == "\n" do {
  459. X        # NL can be the last char in the getchar stream; if it *is*,
  460. X    # then signal that it's time to break out of the repeat loop
  461. X    # in the calling procedure.
  462. X    #
  463. X    next_c := @getchar | {
  464. X        next_c := &null
  465. X        fail
  466. X    }
  467. X    suspend ib_TOK(&null, next_c == "\n")
  468. X    }
  469. X
  470. X    # If there was a last token (i.e. if a newline wasn't the first
  471. X    # character of significance in the input stream), then check to
  472. X    # see if it was an ender.  If so, then check to see if the next
  473. X    # token is a beginner.  If so, then suspend an ib_TOK("SEMICOL")
  474. X    # record before suspending the next token.
  475. X    #
  476. X    if find("e", be_tbl[(\last_token).sym]) then {
  477. X#    write(&errout, "calling ibpag2_tokens via do_ibnewline")
  478. X#    &trace := -1
  479. X    # First arg to ibpag2_tokens can be null here.
  480. X    \ (next_token := ibpag2_tokens(&null, getchar)).sym
  481. X    if \next_token then {
  482. X#        write(&errout, "call of ibpag2_tokens via do_ibnewline yields ",
  483. X#          ximage(next_token))
  484. X        if find("b", be_tbl[next_token.sym])
  485. X        then suspend ib_TOK("SEMICOL", "\n")
  486. X        #
  487. X        # See below.  If this were like the real Icon parser,
  488. X        # the following line would be commented out.
  489. X        #
  490. X        else suspend ib_TOK(&null, "\n")
  491. X        return next_token
  492. X    }
  493. X    else {
  494. X        #
  495. X        # If this were a *real* Icon tokenizer, it would not emit
  496. X        # any record here, but would simply fail.  Instead, we'll
  497. X        # emit a dummy record with a null sym field.
  498. X        #
  499. X        return ib_TOK(&null, "\n")
  500. X#           &trace := 0
  501. X#        fail
  502. X    }
  503. X    }
  504. X
  505. X    # See above.  Again, if this were like Icon's own tokenizer, we
  506. X    # would just fail here, and not return any ib_TOK record.
  507. X    #
  508. X#   &trace := 0
  509. X    return ib_TOK(&null, "\n")
  510. X#   fail
  511. X
  512. Xend
  513. X
  514. X
  515. X#
  516. X#  do_ibnumber_sign:  coexpression -> &null
  517. X#                     getchar      -> 
  518. X#
  519. X#      Where getchar is the coexpression that pops characters off the
  520. X#      main input stream.  Sets the global variable next_c.  This
  521. X#      procedure simply reads characters until it gets a newline, then
  522. X#      returns with next_c == "\n".  Since the starting character was
  523. X#      a number sign, this has the effect of stripping comments.
  524. X#
  525. Xprocedure do_ibnumber_sign(getchar)
  526. X
  527. X    # global next_c
  528. X
  529. X#    write(&errout, "it's a number sign")
  530. X    while next_c ~== "\n" do {
  531. X    next_c := @getchar
  532. X    }
  533. X
  534. X    # Return to calling procedure to cycle around again with the new
  535. X    # next_c already set.  Next_c should always be "\n" at this point.
  536. X    return
  537. X
  538. Xend
  539. X
  540. X
  541. X#
  542. X#  do_ibquotation_mark:  coexpression -> ib_TOK record
  543. X#                        getchar      -> t
  544. X#
  545. X#      Where getchar is the coexpression that yields another character
  546. X#      from the input stream, and t is an ib_TOK record with "STRINGLIT"
  547. X#      as its sym field.  Puts everything upto and including the next
  548. X#      non-backslashed quotation mark into the str field.  Handles the
  549. X#      underscore continuation convention.
  550. X#
  551. Xprocedure do_ibquotation_mark(getchar)
  552. X
  553. X    local token
  554. X    # global next_c
  555. X
  556. X    # write(&errout, "it's a string literal")
  557. X    token := "\""
  558. X    while next_c := @getchar do {
  559. X    if next_c == "\n" & token[-1] == "_" then {
  560. X        token := token[1:-1]
  561. X        next
  562. X    } else {
  563. X        if slshupto("\"", token ||:= next_c, 2)
  564. X        then {
  565. X        next_c := @getchar
  566. X        # resume outermost (repeat) loop in calling procedure,
  567. X        # with the new (here explicitly set) next_c
  568. X        return ib_TOK("STRINGLIT", token)
  569. X        }
  570. X    }
  571. X    }
  572. X
  573. Xend
  574. X
  575. X
  576. X#
  577. X#  do_ibapostrophe:  coexpression -> ib_TOK record
  578. X#                    getchar      -> t
  579. X#
  580. X#      Where getchar is the coexpression that yields another character
  581. X#      from the input stream, and t is an ib_TOK record with "CSETLIT"
  582. X#      as its sym field.  Puts everything upto and including the next
  583. X#      non-backslashed apostrope into the str field.
  584. X#
  585. Xprocedure do_ibapostrophe(getchar)
  586. X
  587. X    local token
  588. X    # global next_c
  589. X
  590. X#   write(&errout, "it's a cset literal")
  591. X    token := "'"
  592. X    while next_c := @getchar do {
  593. X    if slshupto("'", token ||:= next_c, 2)
  594. X    then {
  595. X        next_c := @getchar
  596. X        # Return & resume outermost containing loop in calling
  597. X        # procedure w/ new next_c.
  598. X        return ib_TOK("CSETLIT", token)
  599. X    }
  600. X    }
  601. X
  602. Xend
  603. X
  604. X
  605. X#
  606. X#  do_ibdigits:  coexpression -> ib_TOK record
  607. X#                getchar      -> t
  608. X#
  609. X#      Where getchar is the coexpression that produces the next char
  610. X#      on the input stream, and where t is an ib_TOK record containing
  611. X#      either "REALLIT" or "INTLIT" in its sym field, and the text of
  612. X#      the numeric literal in its str field.
  613. X#
  614. Xprocedure do_ibdigits(getchar)
  615. X
  616. X    local token, tok_record
  617. X    # global next_c
  618. X
  619. X    # Assume integer literal until proven otherwise....
  620. X    tok_record := ib_TOK("INTLIT")
  621. X
  622. X#   write(&errout, "it's an integer or real literal")
  623. X    token := ("0" ~== next_c) | ""
  624. X    while any(&digits, next_c := @getchar) do
  625. X    token ||:= next_c
  626. X    if token ||:= (next_c == ("R"|"r")) then {
  627. X    while any(&digits, next_c := @getchar) do
  628. X        token ||:= next_c
  629. X    } else {
  630. X    if token ||:= (next_c == ".") then {
  631. X        while any(&digits, next_c := @getchar) do
  632. X        token ||:= next_c
  633. X        tok_record := ib_TOK("REALLIT")
  634. X    }
  635. X    if token ||:= (next_c == ("e"|"E")) then {
  636. X        while any(&digits, next_c := @getchar) do
  637. X        token ||:= next_c
  638. X        tok_record := ib_TOK("REALLIT")
  639. X    }
  640. X    }
  641. X    tok_record.str := ("" ~== token) | "0"
  642. X    return tok_record
  643. X    
  644. Xend
  645. X
  646. X
  647. X#
  648. X#  do_ibwhitespace:  coexpression x cset  -> &null
  649. X#                    getchar x whitespace -> &null
  650. X#
  651. X#      Where getchar is the coexpression producing the next char on
  652. X#      the input stream.  Do_ibwhitespace just repeats until it finds
  653. X#      a non-whitespace character, whitespace being defined as
  654. X#      membership of a given character in the whitespace argument (a
  655. X#      cset).
  656. X#
  657. Xprocedure do_ibwhitespace(getchar, whitespace)
  658. X
  659. X#   write(&errout, "it's junk")
  660. X    while any(whitespace, next_c) do
  661. X    next_c := @getchar
  662. X    return
  663. X
  664. Xend
  665. X
  666. X
  667. X#
  668. X#  do_ibidentifier:  coexpression x table    -> ib_TOK record
  669. X#                    (getchar, reserved_tbl) -> t
  670. X#
  671. X#      Where getchar is the coexpression that pops off characters from
  672. X#      the input stream, reserved_tbl is a table of reserved words
  673. X#      (keys = the string values, values = the names qua symbols in
  674. X#      the grammar), and t is an ib_TOK record containing all subsequent
  675. X#      letters, digits, or underscores after next_c (which must be a
  676. X#      letter or underscore).  Note that next_c is global and gets
  677. X#      reset by do_ibidentifier.
  678. X#
  679. Xprocedure do_ibidentifier(getchar, reserved_tbl)
  680. X
  681. X    local token
  682. X    # global next_c
  683. X
  684. X#   write(&errout, "it's an indentifier")
  685. X    token := next_c
  686. X    while any(&letters ++ &digits ++ '_', next_c := @getchar)
  687. X    do token ||:= next_c
  688. X    return ib_TOK(\reserved_tbl[token], token) | ib_TOK("IDENT", token)
  689. X    
  690. Xend
  691. X
  692. X
  693. X#
  694. X#  do_iboperator:  coexpression x list      -> ib_TOK record
  695. X#                  getchar      x operators -> t
  696. X#
  697. X#      Where getchar is the coexpression that produces the next
  698. X#      character on the input stream, and t is an ib_TOK record
  699. X#      describing the operator just scanned.  Calls recognibop, which
  700. X#      creates a DFSA to recognize valid Icon operators.  Arg2
  701. X#      (operators) is the list of lists containing valid Icon operator
  702. X#      string values and names (see above).
  703. X#
  704. Xprocedure do_iboperator(getchar, operators)
  705. X
  706. X    local token, elem
  707. X
  708. X    token := next_c
  709. X
  710. X    # Go until recognibop fails.
  711. X    while elem := recognibop(operators, token, 1) do
  712. X    token ||:= (next_c := @getchar)
  713. X#   write(&errout, ximage(elem))
  714. X    if *\elem = 1 then
  715. X    return ib_TOK(elem[1][2], elem[1][1])
  716. X    else fail
  717. X
  718. Xend
  719. X
  720. X
  721. Xrecord ib_dfstn_state(b, e, tbl)
  722. Xrecord ib_start_state(b, e, tbl, master_list)
  723. X#
  724. X#  recognibop: list x string x integer -> list
  725. X#              (l, s, i)               -> l2
  726. X#
  727. X#      Where l is the list of lists created by the calling procedure
  728. X#      (each element contains a token string value, name, and
  729. X#      beginner/ender string), where s is a string possibly
  730. X#      corresponding to a token in the list, where i is the position
  731. X#      in the elements of l where the operator string values are
  732. X#      recorded, and where l2 is a list of elements from l that
  733. X#      contain operators for which string s is an exact match.
  734. X#      Fails if there are no operators that s is a prefix of, but
  735. X#      returns an empty list if there just aren't any that happen to
  736. X#      match exactly.
  737. X#
  738. X#      What this does is let the calling procedure just keep adding
  739. X#      characters to s until recognibop fails, then check the last
  740. X#      list it returned to see if it is of length 1.  If it is, then
  741. X#      it contains list with the vital stats for the operator last
  742. X#      recognized.  If it is of length 0, then string s did not
  743. X#      contain any recognizable operator.
  744. X#
  745. Xprocedure recognibop(l, s, i)
  746. X
  747. X    local   current_state, master_list, c, result, j
  748. X    static  dfstn_table
  749. X    initial dfstn_table := table()
  750. X
  751. X    /i := 1
  752. X    # See if we've created an automaton for l already.
  753. X    /dfstn_table[l] := ib_start_state(1, *l, &null, &null) & {
  754. X    dfstn_table[l].master_list := sortf(l, i)
  755. X    }
  756. X
  757. X    current_state := dfstn_table[l]
  758. X    # Save master_list, as current_state will change later on.
  759. X    master_list   := current_state.master_list
  760. X
  761. X    s ? {
  762. X    while c := move(1) do {
  763. X
  764. X        # Null means that this part of the automaton isn't
  765. X        # complete.
  766. X        #
  767. X        if /current_state.tbl then
  768. X        create_ib_arcs(master_list, i, current_state, &pos)
  769. X
  770. X        # If the table has been clobbered, then there are no arcs
  771. X        # leading out of the current state.  Fail.
  772. X        #
  773. X        if current_state.tbl === 0 then
  774. X        fail
  775. X        
  776. X#        write(&errout, "c = ", image(c))
  777. X#        write(&errout, "table for current state = ", 
  778. X#          ximage(current_state.tbl))
  779. X
  780. X        # If we get to here, the current state has arcs leading
  781. X        # out of it.  See if c is one of them.  If so, make the
  782. X        # node to which arc c is connected the current state.
  783. X        # Otherwise fail.
  784. X        #
  785. X        current_state := \current_state.tbl[c] | fail
  786. X    }
  787. X    }
  788. X
  789. X    # Return possible completions.
  790. X    #
  791. X    result := list()
  792. X    every j := current_state.b to current_state.e do {
  793. X    if *master_list[j][i] = *s then
  794. X        put(result, master_list[j])
  795. X    }
  796. X    # return empty list if nothing the right length is found
  797. X    return result
  798. X
  799. Xend
  800. X
  801. X
  802. X#
  803. X#  create_ib_arcs:  fill out a table of arcs leading out of the current
  804. X#                   state, and place that table in the tbl field for
  805. X#                   current_state
  806. X#
  807. Xprocedure create_ib_arcs(master_list, field, current_state, POS)
  808. X
  809. X    local elem, i, first_char, old_first_char
  810. X
  811. X    current_state.tbl := table()
  812. X    old_first_char := ""
  813. X    
  814. X    every elem := master_list[i := current_state.b to current_state.e][field]
  815. X    do {
  816. X    
  817. X    # Get the first character for the current position (note that
  818. X    # we're one character behind the calling routine; hence
  819. X    # POS-1).
  820. X    #
  821. X    first_char := elem[POS-1] | next
  822. X    
  823. X    # If we have a new first character, create a new arc out of
  824. X    # the current state.
  825. X    #
  826. X    if first_char ~== old_first_char then {
  827. X        # Store the start position for the current character.
  828. X        current_state.tbl[first_char] := ib_dfstn_state(i)
  829. X        # Store the end position for the old character.
  830. X        (\current_state.tbl[old_first_char]).e := i-1
  831. X        old_first_char := first_char
  832. X    }
  833. X    }
  834. X    (\current_state.tbl[old_first_char]).e := i
  835. X
  836. X    # Clobber table with 0 if no arcs were added.
  837. X    current_state.tbl := (*current_state.tbl = 0)
  838. X    return current_state
  839. X
  840. Xend
  841. END_OF_FILE
  842.   if test 26595 -ne `wc -c <'ibtokens.icn'`; then
  843.     echo shar: \"'ibtokens.icn'\" unpacked with wrong size!
  844.   fi
  845.   # end of 'ibtokens.icn'
  846. fi
  847. if test -f 'iiparse.lib' -a "${1}" != "-c" ; then 
  848.   echo shar: Will not clobber existing file \"'iiparse.lib'\"
  849. else
  850.   echo shar: Extracting \"'iiparse.lib'\" \(11774 characters\)
  851.   sed "s/^X//" >'iiparse.lib' <<'END_OF_FILE'
  852. X############################################################################
  853. X#
  854. X#    Name:     iiparse.lib
  855. X#
  856. X#    Title:     LR parser code
  857. X#
  858. X#    Author:     Richard L. Goerwitz
  859. X#
  860. X#    Version: 1.28
  861. X#
  862. X############################################################################
  863. X#
  864. X#  LR parser code for use by Ibpag2-generated files.  Entry point is
  865. X#  iiparse(infile, fail_on_error).  Infile is the stream from which
  866. X#  input is to be taken.  Infile is passed as argument 1 to the
  867. X#  user-supplied lexical analyzer, iilex_module() (where _module is
  868. X#  the string supplied with the -m option to Ibpag2).  If
  869. X#  fail_on_error is nonnull, the parser, iiparse, will fail on errors,
  870. X#  rather than abort.  Iiparse() returns the top element on its value
  871. X#  stack on a successful parse (which can be handy).
  872. X#
  873. X#  Iilex_module() must suspend integers for tokens and may also set
  874. X#  iilval_module to the actual string values.  Tokens -2, -1, and 0
  875. X#  are reserved.  -2 is epsilon, and -1 is error.  0 is EOF, and is
  876. X#  automatically appended to the token stream when iilex_module, the
  877. X#  tokenizer, fails.  These values should not normally be returned by
  878. X#  the analyzer.  In general, it is a good idea to $include
  879. X#  iilex_module from your Ibpag2 source files, so that it can use the
  880. X#  symbolic %token names declared in the original Ibpag2 source file.
  881. X#  As implied above ("suspend"), iilex_module must be a generator,
  882. X#  failing on EOF.
  883. X#
  884. X#  If desired, the user may include his or her own error-handling
  885. X#  routine.  It must be called iiparse_module (where _module is once
  886. X#  again the module name supplied to ibpag2 via the -m option).  The
  887. X#  global variable line_number_module is automatically defined below,
  888. X#  so a typical arrangement would be for the lexical analyzer to
  889. X#  initialize line_number_module to 0, and increment by 1 for each
  890. X#  line read.  The error handler, iierror_module() can then display
  891. X#  this variable.  Note that the error handler should accept a single
  892. X#  string argument (set by iiparse to describe the error just
  893. X#  encountered).
  894. X#
  895. X############################################################################
  896. X#
  897. X#  See also: ibpag2.icn
  898. X#
  899. X############################################################################
  900. X
  901. X$$line 50 "iiparse.lib"
  902. X
  903. X# These defines are output by Ibpag2 ahead of time (with the module
  904. X# name appended, if need be):
  905. X#
  906. X# $define iierrok        recover_shifts  := &null;
  907. X# $define IIERROR        iidirective := "error";
  908. X# $define IIACCEPT       iidirective := "accept";
  909. X# $define iiclearin      iidirective := "clearin";
  910. X
  911. X# Warning!  If you change the name of the value stack, change it also
  912. X# in ibreader.icn, procedure write_action_as_procedure().
  913. X#
  914. Xglobal $iilval, $errors, $line_number, $state_stack, $value_stack,
  915. X    $iidirective, $recover_shifts, $discards
  916. X
  917. X#
  918. X# iiparse: file x   anything        -> ?
  919. X#          (stream, fail_on_error)  -> ?
  920. X#
  921. X#     Where stream is an open file, where fail_on_error is a switch
  922. X#     that (if nonnull) tells the iiparse to fail, rather than abort,
  923. X#     on error, and where ? represents the user-defined result of a
  924. X#     completed parse of file, from the current location up to the
  925. X#     point where the parser executes an "accept" action.
  926. X#
  927. Xprocedure $iiparse(stream, fail_on_error)
  928. X
  929. X    local token, next_token, act, ruleno, newsym, rhsize, arglist,
  930. X    result, tmp, func
  931. X    static atbl, gtbl, ttbl
  932. X
  933. X    initial {
  934. X    atbl := $atbl_insertion_point
  935. X    gtbl := $gtbl_insertion_point
  936. X    ttbl := $ttbl_insertion_point
  937. X    $$line 86 "iiparse.lib"
  938. X    \$iilex | stop("no iilex tokenizer defined")
  939. X    }
  940. X
  941. X$$ifndef IIDEBUG
  942. X    $iidebug := 1
  943. X$$endif                # not IIDEBUG
  944. X
  945. X    $state_stack := [1]
  946. X    $value_stack := []
  947. X
  948. X    $errors := 0        # errors is global
  949. X    next_token := create $iilex(stream, fail_on_error) | 0
  950. X
  951. X    token := @next_token
  952. X    repeat {
  953. X    #
  954. X    # Begin cycle by checking whether there is a valid action
  955. X    # for state $state_stack[1] and lookahead token.  Atbl and
  956. X    # gtbl here have a "backwards" structure: t[token][state]
  957. X    # (usually they go t[state][token]).
  958. X    #
  959. X    if act := \ (\atbl[token])[$state_stack[1]] then {
  960. X    $$ifdef COMPRESSED_TABLES
  961. X        act := $uncompress_action(act)
  962. X    $$endif    #COMPRESSED TABLES
  963. X        act ? {
  964. X        # There's a valid action:  Perform it.
  965. X        case move(1) of {
  966. X            "s": {
  967. X            #
  968. X            # Shift action format, e.g. s2.1 = shift and
  969. X            # go to state 2 by rule 1.
  970. X            #
  971. X            push($state_stack, integer(tab(find("."))))
  972. X            push($value_stack, $iilval)
  973. X            ="."; ruleno := integer(tab(many(&digits)))
  974. X            $iidebug("s", ttbl, token, ruleno)
  975. X            pos(0) | stop("malformed action:  ", act)
  976. X            #
  977. X            # If, while recovering, we can manage to
  978. X            # shift 3 tokens, then we consider ourselves 
  979. X            # resynchronized.  Don't count error (-1).
  980. X            #
  981. X            if token ~= -1 then {
  982. X                if \$recover_shifts +:= 1 then {
  983. X                # 3 shifts = successful recovery
  984. X                if $recover_shifts > 4 then {
  985. X                    $recover_shifts := &null
  986. X                    $discards := 0
  987. X                }
  988. X                }
  989. X            }
  990. X            token := @next_token | break
  991. X            }
  992. X            "r": {
  993. X            #
  994. X            # Reduce action format, e.g. r1<S>2 = reduce
  995. X            # by rule 1 (LHS = S, RHS length = 2).
  996. X            #
  997. X            ruleno := integer(1(tab(find("<")), move(1)))
  998. X            newsym := 1(tab(find(">")), move(1))
  999. X            rhsize := integer(tab(many(&digits)))
  1000. X            arglist := []
  1001. X            every 1 to rhsize do {
  1002. X                pop($state_stack)
  1003. X                push(arglist, pop($value_stack))
  1004. X            }
  1005. X            # on the structure of gtbl, see above on atbl
  1006. X            push($state_stack, gtbl[newsym][$state_stack[1]])
  1007. X            #
  1008. X            # The actions are in procedures having the same
  1009. X            # name as the number of their rule, bracketed
  1010. X            # by underscores followed by the current module.
  1011. X            #
  1012. X            if func := proc("_" || ruleno || "_" || $module)
  1013. X            then {
  1014. X                result := func!arglist | arglist[-1] | &null
  1015. X                tmp := $iidirective
  1016. X                $iidirective := &null
  1017. X                #
  1018. X                # IIERROR, IIACCEPT, iierrok, and iiclearin
  1019. X                # are implemented using a switch on a global
  1020. X                # iidirective variable; see the $defines
  1021. X                # above
  1022. X                #
  1023. X                case tmp of {
  1024. X                "error"  : {
  1025. X                    # restore stacks & fake an error
  1026. X                    pop($state_stack)
  1027. X                    every 1 to rhsize do
  1028. X                    push($value_stack, !arglist)
  1029. X                    $errors +:= 1
  1030. X                    token := -1
  1031. X                    next
  1032. X                }
  1033. X                "accept" : {
  1034. X                    $iidebug("a", ttbl, token, ruleno)
  1035. X                    return arglist[-1] | &null
  1036. X                }
  1037. X                "clearin": token := @next_token
  1038. X                &null    : &null
  1039. X                default  : stop("bad iidirective")
  1040. X                }
  1041. X            }
  1042. X            # If there is no action code for this rule...
  1043. X            else {
  1044. X                # ...push the value of the last RHS arg.
  1045. X                # For 0-length e-productions, push &null.
  1046. X                result := arglist[-1] | &null
  1047. X            }
  1048. X            push($value_stack, result)
  1049. X            $iidebug("r", ttbl, token, ruleno)
  1050. X            }
  1051. X            # We're done.  Return the last-generated value.
  1052. X            "a": {
  1053. X            $iidebug("a", ttbl, token, ruleno)
  1054. X            return $value_stack[1]
  1055. X            }
  1056. X        }
  1057. X        }
  1058. X    }
  1059. X    #
  1060. X    # ...but if there is *no* action for atbl[token][$state_stack[1]],
  1061. X    # then we have an error.
  1062. X    #
  1063. X    else {
  1064. X        if \$recover_shifts := 0 then {
  1065. X        #
  1066. X        # If we're already in an error state, discard the
  1067. X        # current token, and increment the number of discards
  1068. X        # we have made.  500 is too many; abort.
  1069. X        #
  1070. X        if ($discards +:= 1) > 500 then {
  1071. X            if \$iierror
  1072. X            then $iierror("fatal error: can't resynchronize")
  1073. X            else write(&errout, "fatal error: can't resynchronize")
  1074. X            if \fail_on_error then fail
  1075. X            else stop()
  1076. X        }
  1077. X        $iidebug("e", ttbl, token)
  1078. X        } else {
  1079. X        $errors +:= 1 # global error count
  1080. X        $discards := $recover_shifts := 0
  1081. X        if \$iierror
  1082. X        then $iierror(image(\ttbl[token]) | image(token))
  1083. X        else write(&errout, "parse error")
  1084. X        #
  1085. X        # If error appears in a RHS, pop states until we get to
  1086. X        # a spot where error (-1) is a valid lookahead token:
  1087. X        #
  1088. X        if \ttbl[-1] then {
  1089. X            until *$state_stack = 0 do {
  1090. X            if \atbl[-1][$state_stack[1]] then {
  1091. X                $iidebug("e", ttbl, token)
  1092. X                token := -1
  1093. X                break next
  1094. X            } else pop($state_stack) & pop($value_stack)
  1095. X            }
  1096. X        # If we get past here, the stack is now empty.  Abort.
  1097. X        }
  1098. X        if \fail_on_error then fail
  1099. X        else stop()
  1100. X        }
  1101. X    }
  1102. X    }
  1103. X
  1104. X    #
  1105. X    # If we get to here without hitting a final state, then we aren't
  1106. X    # going to get a valid parse.  Abort.
  1107. X    #
  1108. X    if \$iierror
  1109. X    then $iierror("unexpected EOF")
  1110. X    else write(&errout, "unexpected EOF")
  1111. X
  1112. X    if \fail_on_error then fail
  1113. X    else stop()
  1114. X
  1115. Xend
  1116. X
  1117. X
  1118. X$$ifdef IIDEBUG
  1119. X
  1120. Xrecord production(LHS, RHS, POS, LOOK, no, prec, assoc)
  1121. X#
  1122. X# iidebug
  1123. X#
  1124. Xprocedure $iidebug(action, ttbl, token, ruleno)
  1125. X
  1126. X    local p, t, state
  1127. X    static rule_list
  1128. X    initial {
  1129. X    rule_list := $rule_list_insertion_point
  1130. X    $$line 279 "iiparse.lib"
  1131. X    }
  1132. X
  1133. X    case action of {
  1134. X    "a"     : writes(&errout, "accepting ")    & state := $state_stack[1]
  1135. X    "e"     : writes(&errout, "***ERROR***\n") &
  1136. X              writes(&errout, "recovery shifts = ", $recover_shifts,"\n") &
  1137. X          writes(&errout, "discarded tokens = ", $discards, "\n") &
  1138. X          writes(&errout, "total error count = ", $errors, "\n") &
  1139. X              writes(&errout, "error action ") & state := $state_stack[1]
  1140. X    "r"     : writes(&errout, "reducing ")     & state := $state_stack[2]
  1141. X    "s"     : writes(&errout, "shifting ")     & state := $state_stack[2]
  1142. X    default : stop("malformed action argument to iidebug")
  1143. X    }
  1144. X
  1145. X    t := image(token) || (" (" || (\ttbl[token] | "unknown") || ")")
  1146. X    writes(&errout, "on lookahead ", t, ", in state ", state)
  1147. X    if \ruleno then {
  1148. X    (p := !rule_list).no = ruleno |
  1149. X        stop("no rule number ", tbl[symbol][state])
  1150. X    write(&errout, "; rule ", $production_2_string(p, ttbl))
  1151. X    }
  1152. X    # for errors, ruleno is null
  1153. X    else write(&errout)
  1154. X
  1155. X    write(&errout, "    state stack now: ")
  1156. X    every write(&errout, "\t", image(!$state_stack))
  1157. X    write(&errout, "    value stack now: ")
  1158. X    if *$value_stack > 0
  1159. X    then every write(&errout, "\t", image(!$value_stack))
  1160. X    else write(&errout, "\t(empty)")
  1161. X
  1162. X    return
  1163. X
  1164. Xend
  1165. X
  1166. X
  1167. X#
  1168. X# production_2_string:  production record -> string
  1169. X#                       p                 -> s
  1170. X#
  1171. X#     Stringizes an image of the LHS and RHS of production p in
  1172. X#     human-readable form.
  1173. X#
  1174. Xprocedure $production_2_string(p, ibtoktbl)
  1175. X
  1176. X    local s, m, t
  1177. X
  1178. X    s := image(p.LHS) || " -> "
  1179. X    every m := !p.RHS do {
  1180. X    if t := \ (\ibtoktbl)[m]
  1181. X    then s ||:= t || " "
  1182. X    else s ||:= image(m) || " "
  1183. X    }
  1184. X    # if the POS field is nonnull, print it
  1185. X    s ||:= "(POS = " || image(\p.POS) || ") "
  1186. X    # if the LOOK field is nonnull, print it, too
  1187. X    s ||:= "lookahead = " || image(\p.LOOK)
  1188. X
  1189. X    return trim(s)
  1190. X
  1191. Xend
  1192. X$$endif                # IIDEBUG
  1193. X
  1194. X
  1195. X$$ifdef COMPRESSED_TABLES
  1196. X
  1197. X#
  1198. X# uncompress_action
  1199. X#
  1200. Xprocedure $uncompress_action(action)
  1201. X
  1202. X    local next_chunk, full_action
  1203. X
  1204. X    next_chunk := create ord(!action)
  1205. X    case $in_ib_bits(next_chunk, 2) of {
  1206. X    0: {
  1207. X        full_action := "s"
  1208. X        full_action ||:= $in_ib_bits(next_chunk, 11)
  1209. X        full_action ||:= "."
  1210. X        full_action ||:= $in_ib_bits(next_chunk, 11)
  1211. X    }
  1212. X    1: {
  1213. X        full_action := "r"
  1214. X        full_action ||:= $in_ib_bits(next_chunk, 11)
  1215. X        full_action ||:= "<"
  1216. X        full_action ||:= $in_ib_bits(next_chunk, 11)
  1217. X        full_action ||:= ">"
  1218. X        full_action ||:= $in_ib_bits(next_chunk, 8)
  1219. X    }
  1220. X        2: {
  1221. X        full_action := "a"
  1222. X    }
  1223. X    }
  1224. X
  1225. X    return full_action
  1226. X
  1227. Xend
  1228. X
  1229. X
  1230. X#
  1231. X# in_ib_bits:  like inbits (IPL), but with coexpression for file
  1232. X#
  1233. Xprocedure $in_ib_bits(next_chunk, len)
  1234. X
  1235. X    local i, byte, old_byte_mask
  1236. X    static old_byte, old_len, byte_length
  1237. X    initial {
  1238. X    old_byte := old_len := 0
  1239. X    byte_length := 8
  1240. X    }
  1241. X
  1242. X    old_byte_mask := (0 < 2^old_len - 1) | 0
  1243. X    old_byte := iand(old_byte, old_byte_mask)
  1244. X    i := ishift(old_byte, len-old_len)
  1245. X
  1246. X    len -:= (len > old_len) | {
  1247. X    old_len -:= len
  1248. X    return i
  1249. X    }
  1250. X    
  1251. X    while byte := @next_chunk do {
  1252. X    i := ior(i, ishift(byte, len-byte_length))
  1253. X    len -:= (len > byte_length) | {
  1254. X        old_len := byte_length-len
  1255. X        old_byte := byte
  1256. X        return i
  1257. X    }
  1258. X    }
  1259. X
  1260. Xend
  1261. X
  1262. X$$endif                # COMPRESSED_TABLES
  1263. END_OF_FILE
  1264.   if test 11774 -ne `wc -c <'iiparse.lib'`; then
  1265.     echo shar: \"'iiparse.lib'\" unpacked with wrong size!
  1266.   fi
  1267.   # end of 'iiparse.lib'
  1268. fi
  1269. if test -f 'slrtbls.icn' -a "${1}" != "-c" ; then 
  1270.   echo shar: Will not clobber existing file \"'slrtbls.icn'\"
  1271. else
  1272.   echo shar: Extracting \"'slrtbls.icn'\" \(11984 characters\)
  1273.   sed "s/^X//" >'slrtbls.icn' <<'END_OF_FILE'
  1274. X############################################################################
  1275. X#
  1276. X#    Name:     slrtbls.icn
  1277. X#
  1278. X#    Title:     slr table generation routines
  1279. X#
  1280. X#    Author:     Richard L. Goerwitz
  1281. X#
  1282. X#    Version: 1.20
  1283. X#
  1284. X############################################################################
  1285. X#
  1286. X#  Contains make_slr_tables(grammar, atbl, gtbl, noconflict,
  1287. X#  like_yacc), where grammar is an ib_grammar record (as returned by
  1288. X#  ibreader), where atbl and gtbl are initialized (default &null) hash
  1289. X#  tables, and where noconflict is a switch that, if nonnull, directs
  1290. X#  the resolver to abort on unresolvable conflicts.  Returns &null if
  1291. X#  successful in filling out atbl and gtbl.  If likeyacc is nonnull,
  1292. X#  make_slr_tables will resolve reduce/reduce conflicts by order of
  1293. X#  occurrence in the grammar, just like YACC.  Shift/reduce conflicts
  1294. X#  will be resolved in favor of shift.
  1295. X#
  1296. X#  The reason for the noconflict switch is that there are parsers that
  1297. X#  can accept tables with multiple action entries, i.e.  parsers that
  1298. X#  can use tables generated by ambiguous grammars.
  1299. X#
  1300. X#  In this routine's case, success is identified with creating a
  1301. X#  standard SLR action and goto table.  Note that both tables end up
  1302. X#  as tables of tables, with symbols being the primary or first key,
  1303. X#  and state numbers being the second.  This is the reverse of the
  1304. X#  usual arrangement, but turns out to save a lot of space.  Atbl
  1305. X#  values are of the form "s2.3", "r4<A>10", "a", etc.  The string
  1306. X#  "s2.3" means "shift the current lookahead token, and enter state 2
  1307. X#  via rule 3."  By way of contrast, "r4<A>10" means "reduce by rule
  1308. X#  number 4, which has A as its LHS symbol and 10 RHS symbols."  A
  1309. X#  single "a" means "accept."
  1310. X
  1311. X#  Atbl entries may contain more than one action.  The actions are
  1312. X#  simply concatenated: "s2.3r4<A>10a".  Conflicts may be resolved
  1313. X#  later by associativity or precedence, if available.  Unresolvable
  1314. X#  conflicts only cause error termination if the 5th and final
  1315. X#  argument is nonnull (see above on "noconflict").
  1316. X#
  1317. X#  Gtbl entries are simpler than atble entries, consisting of a single
  1318. X#  integer.
  1319. X#
  1320. X############################################################################
  1321. X#
  1322. X#  Links: follow, slritems, iohno
  1323. X#
  1324. X############################################################################
  1325. X
  1326. X# declared in ibreader.icn
  1327. X# record ib_grammar(start, rules, tbl)
  1328. X
  1329. X#link follow, slritems, iohno#, ximage
  1330. X
  1331. X#
  1332. X# make_slr_tables
  1333. X#
  1334. Xprocedure make_slr_tables(grammar, atbl, gtbl, noconflict, like_yacc)
  1335. X
  1336. X    local start_symbol, st, C, i, augmented_start_symbol, item,
  1337. X    symbol, new_item_list, j, action
  1338. X
  1339. X    # Initialize start symbol and rule list/set (either is okay).
  1340. X    start_symbol := grammar.start
  1341. X    st := grammar.rules
  1342. X
  1343. X    # Number the rules, and then construct the canonical LR(0) item sets.
  1344. X    every i := 1 to *st do st[i].no := i
  1345. X    C := make_slr_item_sets(start_symbol, st)
  1346. X
  1347. X    # Now, go through each item in each item set in C filling out the
  1348. X    # action (atbl) and goto table (gtbl) as we go.
  1349. X    #
  1350. X    augmented_start_symbol := "`_" || start_symbol || "_'"
  1351. X    every i := 1 to *C do {
  1352. X        every item := !C[i] do {
  1353. X        # if the dot's *not* at the end of the production...
  1354. X        if symbol := item.RHS[item.POS] then {
  1355. X        # if were looking at a terminal, enter a shift action
  1356. X        if type(symbol) == "integer" then {
  1357. X            if symbol = -2 then next   # Never shift epsilon!
  1358. X            new_item_list := slr_goto(C[i], symbol, st)
  1359. X            every j := 1 to *C do {
  1360. X            if equivalent_item_lists(new_item_list, C[j]) then {
  1361. X                action := "s" || j || "." || item.no
  1362. X                resolve(st, atbl, symbol, i, action,
  1363. X                    noconflict, like_yacc)
  1364. X                break next
  1365. X            }
  1366. X            }
  1367. X        # if we're looking at a nonterminal, add action to gtbl
  1368. X        } else {
  1369. X            new_item_list := slr_goto(C[i], symbol, st)
  1370. X            every j := 1 to *C do {
  1371. X            if equivalent_item_lists(new_item_list, C[j]) then {
  1372. X                /gtbl[symbol] := table()
  1373. X                /gtbl[symbol][i] := j |
  1374. X                gtbl[symbol][i] =:= j |
  1375. X                iohno(80, image(symbol), ".", image(i), ":", j)
  1376. X                break next
  1377. X            }
  1378. X            }
  1379. X        }
  1380. X        # ...else if the dot *is* at the end of the production
  1381. X        } else {
  1382. X        if item.LHS == augmented_start_symbol then {
  1383. X            action := "a"
  1384. X            # 0 = EOF 
  1385. X            resolve(st, atbl, 0, i, action, noconflict, like_yacc)
  1386. X        } else {
  1387. X            # add a reduce for every symbol in FOLLOW(item.LHS)
  1388. X            every symbol := !FOLLOW(start_symbol, st, item.LHS) do {
  1389. X            # RHS size is 0 for epsilon.
  1390. X            if item.RHS[1] === -2 then {
  1391. X                action := "r" || item.no || "<" || item.LHS ||
  1392. X                ">0"
  1393. X            } else
  1394. X                action := "r" || item.no || "<" || item.LHS ||
  1395. X                    ">" || *item.RHS
  1396. X            resolve(st, atbl, symbol, i, action,
  1397. X                noconflict, like_yacc)
  1398. X            }
  1399. X        }
  1400. X        }
  1401. X    }
  1402. X    }
  1403. X
  1404. X    return
  1405. X
  1406. Xend
  1407. X
  1408. X
  1409. X#
  1410. X# resolve: list|set x table x string|integer, integer, anything, anything
  1411. X#                                  -> string
  1412. X#          (st, tbl, symbol, state, action, noconflict, like_yacc)
  1413. X#                            -> new_action_list
  1414. X#
  1415. X#     Add action to action table, resolving conflicts by precedence
  1416. X#     and associativity, if need be.  If noconflict is nonnull, abort
  1417. X#     on unresolvable conflicts.  Fails on shift/shift "conflicts," or
  1418. X#     if an identical action is already present in the table entry to
  1419. X#     be modified.  If like_yacc is nonnull, resolve reduce/reduce
  1420. X#     conflicts by their order of occurrence in the grammar; resolve
  1421. X#     shift/reduce conflicts in favor of shift.
  1422. X#
  1423. Xprocedure resolve(st, tbl, symbol, state, action, noconflict, like_yacc)
  1424. X
  1425. X    local actions, chr, a, ruleno, p, newp
  1426. X
  1427. X    /tbl[symbol] := table()
  1428. X    /tbl[symbol][state] := ""
  1429. X    
  1430. X    # If this action is already present, then don't re-enter it.  Just
  1431. X    # fail.
  1432. X    #
  1433. X    tbl[symbol][state] ? {
  1434. X    while a := tab(any('sra')) do {
  1435. X        a ||:= tab(upto('.<'))
  1436. X        a ||:= { (="<" || tab(find(">")+1)) | ="." }
  1437. X        a ||:= tab(many(&digits))
  1438. X        if a == action then fail
  1439. X    }
  1440. X    }
  1441. X
  1442. X    # Get rule number for the new action specified as arg 5, and
  1443. X    # fetch its source production.
  1444. X    action ? {
  1445. X    case move(1) of {
  1446. X        "s": ruleno := (tab(find(".")+1), tab(many(&digits)))
  1447. X        "r": ruleno := 1(tab(find("<")), move(1))
  1448. X        "a": return tbl[symbol][state] := action || tbl[symbol][state]
  1449. X    } | iohno(70, tbl[symbol][state])
  1450. X    (newp := !st).no = ruleno |
  1451. X        iohno(72, tbl[symbol][state])
  1452. X    }
  1453. X
  1454. X    # Resolve any conflicts that might be present.
  1455. X    #
  1456. X    actions := ""
  1457. X    tbl[symbol][state] ? {
  1458. X    while a := tab(any('sra')) do {
  1459. X        # Snip out the old action, and put it into a.
  1460. X        a ||:= tab(upto('.<'))
  1461. X        a ||:= { (="<" || tab(find(">")+1)) | ="." }
  1462. X        a ||:= tab(many(&digits))
  1463. X        #
  1464. X        # Get the old action's rule number, and use it to fetch
  1465. X        # the full production that it is keyed to.
  1466. X        #
  1467. X        a ? {
  1468. X        case move(1) of {
  1469. X            "s": ruleno := (tab(find(".")+1), tab(many(&digits)))
  1470. X            "r": ruleno := 1(tab(find("<")), move(1))
  1471. X            "a": return tbl[symbol][state] := a || actions || action
  1472. X        } | iohno(70, tbl[symbol][state])
  1473. X        # Go through rule list; find the one whose number is ruleno.
  1474. X        (p := !st).no = ruleno |
  1475. X            iohno(71, tbl[symbol][state])
  1476. X        }
  1477. X
  1478. X        # Check precedences to see if we can resolve the conflict
  1479. X        # this way.
  1480. X        #
  1481. X        if \newp.prec > \p.prec then
  1482. X        # discard the old action, a
  1483. X        return tbl[symbol][state] := actions || action || tab(0)
  1484. X        else if \newp.prec < \p.prec then
  1485. X        # discard the new action, action
  1486. X        return tbl[symbol][state] := actions || a || tab(0)
  1487. X        else {
  1488. X        #
  1489. X        # If, however, both precedences are the same (i.e.
  1490. X        # newp.prec === p.prec), then we must check the
  1491. X        # associativities.  Right implies shift; left, reduce.
  1492. X        # If there is no associativity, then we have a
  1493. X        # conflict.  Nonassociative ("n") implies error.
  1494. X        #
  1495. X        case action[1] of {
  1496. X            default: iohno(70, tbl[symbol][state])    
  1497. X            # case "a" is handled above; look for "s" & "r"
  1498. X            "s" : {
  1499. X            if a[1] == "s" then fail  # no shift/shift "conflict"
  1500. X            else if a[1] == "r" then {
  1501. X                newp.assoc === p.assoc | {
  1502. X                iohno(40, "state " || state || "; token " ||
  1503. X                      symbol || "; rules " || newp.no ||
  1504. X                      "," || p.no)
  1505. X                }
  1506. X                case newp.assoc of {
  1507. X                "n"  : iohno(41, production_2_string(newp))
  1508. X                &null: { # no associativity given
  1509. X                    if \noconflict & /like_yacc then
  1510. X                    iohno(46, "state " || state ||
  1511. X                          "; token " || symbol ||
  1512. X                          "; rules " || newp.no ||
  1513. X                          "," || p.no)
  1514. X                    else {
  1515. X                    write(&errout, "warning: shift/reduce",
  1516. X                          " conflict in state " || state ||
  1517. X                          "; token " || symbol ||
  1518. X                          "; rules " || newp.no ||
  1519. X                          "," || p.no)
  1520. X                    if \like_yacc then {
  1521. X                        write(&errout, "resolving in _
  1522. X                                    favor of shift.")
  1523. X                        return tbl[symbol][state] :=
  1524. X                           actions || action || tab(0)
  1525. X                    } else {
  1526. X                        write(&errout, "creating multi-_
  1527. X                            action table entry")
  1528. X                        return tbl[symbol][state] :=
  1529. X                           actions || action || a || tab(0)
  1530. X                    }
  1531. X                    }
  1532. X                }
  1533. X                "l"  : { # left associative
  1534. X                    # discard new action, action
  1535. X                    return tbl[symbol][state] := 
  1536. X                    actions || a || tab(0)
  1537. X                }
  1538. X                "r"  : { # right associative
  1539. X                    # remove old action, a
  1540. X                    return tbl[symbol][state] := 
  1541. X                    actions || action || tab(0)
  1542. X                }
  1543. X                }
  1544. X            }
  1545. X            }
  1546. X            "r" : {
  1547. X            if a[1] == "r" then {
  1548. X                #
  1549. X                # If conflicts in general, and reduce-reduce
  1550. X                # conflicts in specific are not okay...
  1551. X                #
  1552. X                if \noconflict & /like_yacc then {
  1553. X                # ...abort, otherwise...
  1554. X                iohno(42, "state " || state || "; token " ||
  1555. X                      symbol || "; " || "; rules " ||
  1556. X                      newp.no || "," || p.no)
  1557. X                } else {
  1558. X                #
  1559. X                # ...flag reduce-reduce conficts, and
  1560. X                # then resolve them by their order of
  1561. X                # occurrence in the grammar.
  1562. X                #
  1563. X                write(&errout, "warning: reduce/reduce",
  1564. X                      " conflict in state ", state,
  1565. X                      "; token ", symbol, "; rules ",
  1566. X                      newp.no, ",", p.no)
  1567. X                if \like_yacc then {
  1568. X                    write(&errout, "resolving by order of _
  1569. X                          occurrence in the grammar")
  1570. X                    if newp.no > p.no
  1571. X                    # discard later production (newp)
  1572. X                    then return return tbl[symbol][state] := 
  1573. X                    actions || a || tab(0)
  1574. X                    # discard later production (old p)
  1575. X                    else return tbl[symbol][state] := 
  1576. X                    actions || action || tab(0)
  1577. X                } else {
  1578. X                    #
  1579. X                    # If conflicts ok, but we aren't supposed
  1580. X                    # to resolve reduce-reduce conflicts by
  1581. X                    # order of rule occurrence:
  1582. X                    #
  1583. X                    write(&errout, "creating multi-action _
  1584. X                    table entry")
  1585. X                    return tbl[symbol][state] :=
  1586. X                    actions || action || a || tab(0)
  1587. X                }
  1588. X                }
  1589. X            } else {
  1590. X                # associativities must be the same for both rules:
  1591. X                newp.assoc === p.assoc | {
  1592. X                iohno(40, "state " || state || "; token " ||
  1593. X                      symbol || "; rules " || newp.no ||
  1594. X                      "," || p.no)
  1595. X                }
  1596. X                case newp.assoc of {
  1597. X                "n"  : iohno(41, production_2_string(newp))
  1598. X                &null: {
  1599. X                    if \noconflict & /like_yacc then
  1600. X                    iohno(46, "state " || state ||
  1601. X                          "; token " || symbol ||
  1602. X                          "; rules " || newp.no ||
  1603. X                          "," || p.no)
  1604. X                    else {
  1605. X                    write(&errout, "warning: shift/reduce",
  1606. X                          " conflict in state " || state ||
  1607. X                          "; token " || symbol ||
  1608. X                          "; rules " || newp.no ||
  1609. X                          "," || p.no)
  1610. X                    if \like_yacc then {
  1611. X                        write(&errout, "resolving in _
  1612. X                                    favor of shift.")
  1613. X                        return tbl[symbol][state] :=
  1614. X                           actions || a || tab(0)
  1615. X                    } else {
  1616. X                        write(&errout, "creating multi-_
  1617. X                            action table entry")
  1618. X                        return tbl[symbol][state] :=
  1619. X                           actions || action || a || tab(0)
  1620. X                    }
  1621. X                    }
  1622. X                }
  1623. X                "r"  : {
  1624. X                    # discard new action, action
  1625. X                    return tbl[symbol][state] :=
  1626. X                    actions || a || tab(0)
  1627. X                }
  1628. X                "l"  : {
  1629. X                    # remove old action, a
  1630. X                    return tbl[symbol][state] :=
  1631. X                    actions || action || tab(0)
  1632. X                }
  1633. X                }
  1634. X            }
  1635. X            }
  1636. X        }
  1637. X        }
  1638. X    }
  1639. X    }
  1640. X
  1641. X    return tbl[symbol][state] ||:= action
  1642. X
  1643. Xend
  1644. END_OF_FILE
  1645.   if test 11984 -ne `wc -c <'slrtbls.icn'`; then
  1646.     echo shar: \"'slrtbls.icn'\" unpacked with wrong size!
  1647.   fi
  1648.   # end of 'slrtbls.icn'
  1649. fi
  1650. echo shar: End of archive 3 \(of 5\).
  1651. cp /dev/null ark3isdone
  1652. MISSING=""
  1653. for I in 1 2 3 4 5 ; do
  1654.     if test ! -f ark${I}isdone ; then
  1655.     MISSING="${MISSING} ${I}"
  1656.     fi
  1657. done
  1658. if test "${MISSING}" = "" ; then
  1659.     echo You have unpacked all 5 archives.
  1660.     rm -f ark[1-9]isdone
  1661. else
  1662.     echo You still must unpack the following archives:
  1663.     echo "        " ${MISSING}
  1664. fi
  1665. exit 0
  1666. exit 0 # Just in case...
  1667.