MODULE CSS2Parser;
IMPORT
KernelLog, Strings, Scanner := CSS2Scanner, XMLObjects, CSS2, Files;
TYPE
String = CSS2.String;
Parser* = OBJECT
VAR
reportError*: PROCEDURE(pos, line, row: LONGINT; msg: ARRAY OF CHAR);
scanner: Scanner.Scanner;
PROCEDURE & Init*(scanner: Scanner.Scanner);
BEGIN
reportError := DefaultReportError;
SELF.scanner := scanner;
scanner.Scan()
END Init;
PROCEDURE CheckSymbol(expectedSymbols: SET; errormsg: ARRAY OF CHAR): BOOLEAN;
BEGIN
IF scanner.sym IN expectedSymbols THEN
RETURN TRUE
ELSE
Error(errormsg);
RETURN FALSE
END
END CheckSymbol;
PROCEDURE Error(msg: ARRAY OF CHAR);
BEGIN
reportError(scanner.GetPos(), scanner.line, scanner.row, msg)
END Error;
PROCEDURE Parse*(): CSS2.StyleSheet;
VAR styleSheet: CSS2.StyleSheet; s: String;
BEGIN
NEW(styleSheet);
s := scanner.GetStr();
IF (scanner.sym = Scanner.AtKeyword) & (s^ = 'charset') THEN
scanner.Scan();
IF ~CheckSymbol({Scanner.String}, "charset expected") THEN RETURN styleSheet END;
s := scanner.GetStr(); styleSheet.SetCharSet(s^);
scanner.Scan();
IF ~CheckSymbol({Scanner.Semicolon}, "';' expected") THEN RETURN styleSheet END;
scanner.Scan()
END;
WHILE scanner.sym IN {Scanner.Cdo, Scanner.Cdc} DO scanner.Scan() END;
s := scanner.GetStr();
WHILE (scanner.sym = Scanner.AtKeyword) & (s^ = 'import') DO
ParseImport(styleSheet);
s := scanner.GetStr()
END;
WHILE scanner.sym # Scanner.Eof DO
IF scanner.sym = Scanner.AtKeyword THEN
s := scanner.GetStr();
IF s^ = 'media' THEN
ParseMedia(styleSheet)
ELSIF s^ = 'page' THEN
styleSheet.AddPage(ParsePage())
ELSIF s^ = 'font-face' THEN
styleSheet.AddFontFace(ParseFontFace())
ELSE
IgnoreKeyword()
END
ELSIF scanner.sym # Scanner.Eof THEN
styleSheet.AddRuleSet(ParseRuleSet())
END;
WHILE scanner.sym IN {Scanner.Cdo, Scanner.Cdc} DO scanner.Scan() END
END;
RETURN styleSheet
END Parse;
PROCEDURE ParseImport(styleSheet: CSS2.StyleSheet);
VAR s: String; newParser: Parser; newScanner: Scanner.Scanner; file: Files.File;
importedStyleSheet: CSS2.StyleSheet; media, media2, media3: SET; ruleSets: XMLObjects.Enumerator;
ruleSet: ANY;
BEGIN
scanner.Scan();
IF ~CheckSymbol({Scanner.String, Scanner.URI}, "URI expected") THEN RETURN END;
s := scanner.GetStr();
file := Files.Old(s^);
IF file # NIL THEN
NEW(newScanner, file);
NEW(newParser, newScanner); newParser.reportError := reportError;
importedStyleSheet := newParser.Parse()
END;
scanner.Scan();
IF scanner.sym # Scanner.Ident THEN
INCL(media, CSS2.All)
ELSE
s := scanner.GetStr();
INCL(media, GetMedium(s^));
scanner.Scan();
WHILE scanner.sym = Scanner.Comma DO
scanner.Scan();
IF ~CheckSymbol({Scanner.Ident}, "medium identifier expected") THEN RETURN END;
s := scanner.GetStr();
INCL(media, GetMedium(s^));
scanner.Scan()
END
END;
ruleSets := importedStyleSheet.GetRuleSets();
WHILE ruleSets.HasMoreElements() DO
ruleSet := ruleSets.GetNext();
media2 := ruleSet(CSS2.RuleSet).GetMedia();
media3 := media + media2;
IF (media3 - {CSS2.All} # {}) THEN media3 := media3 - {CSS2.All} END;
ruleSet(CSS2.RuleSet).SetMedia(media3);
styleSheet.AddRuleSet(ruleSet(CSS2.RuleSet))
END;
IF ~CheckSymbol({Scanner.Semicolon}, "';' expected") THEN RETURN END;
scanner.Scan()
END ParseImport;
PROCEDURE ParseMedia(styleSheet: CSS2.StyleSheet);
VAR s: String; media: SET; ruleSet: CSS2.RuleSet;
BEGIN
scanner.Scan();
IF ~CheckSymbol({Scanner.Ident}, "medium identifier expected") THEN RETURN END;
s := scanner.GetStr();
INCL(media, GetMedium(s^));
scanner.Scan();
WHILE scanner.sym = Scanner.Comma DO
scanner.Scan();
IF ~CheckSymbol({Scanner.Ident}, "medium identifier expected") THEN RETURN END;
s := scanner.GetStr();
INCL(media, GetMedium(s^));
scanner.Scan()
END;
IF ~CheckSymbol({Scanner.BraceOpen}, "'{' expected") THEN RETURN END;
scanner.Scan();
WHILE (scanner.sym # Scanner.BraceClose) & (scanner.sym # Scanner.Eof) & (scanner.sym # Scanner.Invalid) DO
ruleSet := ParseRuleSet();
ruleSet.SetMedia(media);
styleSheet.AddRuleSet(ruleSet)
END;
IF ~CheckSymbol({Scanner.BraceClose}, "'}' expected") THEN RETURN END;
scanner.Scan()
END ParseMedia;
PROCEDURE ParsePage(): CSS2.Page;
VAR page: CSS2.Page; s: String;
BEGIN
scanner.Scan();
IF ~CheckSymbol({Scanner.Ident, Scanner.Colon, Scanner.BraceOpen},
"page selector, pseudo page or '{' expected") THEN RETURN page END;
NEW(page);
IF scanner.sym = Scanner.Ident THEN
s := scanner.GetStr();
page.SetSelector(s^);
scanner.Scan()
END;
IF ~CheckSymbol({Scanner.Colon, Scanner.BraceOpen}, "pseudo page or '{' expected") THEN RETURN page END;
IF scanner.sym = Scanner.Colon THEN
scanner.Scan();
IF ~CheckSymbol({Scanner.Ident}, "pseudo page identifier expected") THEN RETURN page END;
s := scanner.GetStr();
page.SetPseudoPage(GetPseudoPage(s^));
scanner.Scan()
END;
IF ~CheckSymbol({Scanner.BraceOpen}, "'{' expected") THEN RETURN page END;
scanner.Scan();
page.AddDeclaration(ParseDeclaration());
WHILE scanner.sym = Scanner.Semicolon DO
scanner.Scan();
page.AddDeclaration(ParseDeclaration());
END;
IF ~CheckSymbol({Scanner.BraceClose}, "'}' expected") THEN RETURN page END;
scanner.Scan();
RETURN page
END ParsePage;
PROCEDURE ParseFontFace(): CSS2.FontFace;
VAR fontFace: CSS2.FontFace;
BEGIN
scanner.Scan();
IF ~CheckSymbol({Scanner.BraceOpen}, "'{' expected") THEN RETURN fontFace END;
NEW(fontFace);
scanner.Scan();
fontFace.AddDeclaration(ParseDeclaration());
WHILE scanner.sym = Scanner.Semicolon DO
scanner.Scan();
fontFace.AddDeclaration(ParseDeclaration());
END;
IF ~CheckSymbol({Scanner.BraceClose}, "'}' expected") THEN RETURN fontFace END;
scanner.Scan();
RETURN fontFace
END ParseFontFace;
PROCEDURE ParseRuleSet(): CSS2.RuleSet;
VAR ruleSet: CSS2.RuleSet;
BEGIN
NEW(ruleSet);
ruleSet.AddSelector(ParseSelector());
WHILE scanner.sym = Scanner.Comma DO
scanner.Scan();
ruleSet.AddSelector(ParseSelector())
END;
IF ~CheckSymbol({Scanner.BraceOpen}, "'{' expected") THEN RETURN ruleSet END;
scanner.Scan();
ruleSet.AddDeclaration(ParseDeclaration());
WHILE scanner.sym = Scanner.Semicolon DO
scanner.Scan();
IF scanner.sym # Scanner.BraceClose THEN ruleSet.AddDeclaration(ParseDeclaration()) END
END;
IF ~CheckSymbol({Scanner.BraceClose}, "'}' expected") THEN RETURN ruleSet END;
scanner.Scan();
RETURN ruleSet
END ParseRuleSet;
PROCEDURE ParseSelector(): CSS2.Selector;
VAR selector: CSS2.Selector;
BEGIN
NEW(selector);
selector.AddSimpleSelector(ParseSimpleSelector());
WHILE scanner.sym IN {Scanner.Ident, Scanner.Asterisk, Scanner.Hash, Scanner.Dot, Scanner.BracketOpen,
Scanner.Colon, Scanner.Greater, Scanner.Plus} DO
selector.AddSimpleSelector(ParseSimpleSelector())
END;
RETURN selector
END ParseSelector;
PROCEDURE ParseSimpleSelector(): CSS2.SimpleSelector;
VAR simpleSelector: CSS2.SimpleSelector; s: String;
BEGIN
NEW(simpleSelector);
IF scanner.sym = Scanner.Plus THEN
simpleSelector.SetCombinator(CSS2.Sibling); scanner.Scan()
ELSIF scanner.sym = Scanner.Greater THEN
simpleSelector.SetCombinator(CSS2.Child); scanner.Scan()
ELSE
simpleSelector.SetCombinator(CSS2.Descendant)
END;
IF scanner.sym = Scanner.Ident THEN
s := scanner.GetStr();
simpleSelector.SetElementName(s^); scanner.Scan()
ELSE
NEW(s, 2); s[0] := '*'; s[1] := 0X;
simpleSelector.SetElementName(s^);
IF scanner.sym = Scanner.Asterisk THEN scanner.Scan() END
END;
WHILE scanner.sym IN {Scanner.Hash, Scanner.Dot, Scanner.BracketOpen, Scanner.Colon} DO
CASE scanner.sym OF
| Scanner.Hash: simpleSelector.AddSubSelector(ParseId())
| Scanner.Dot: simpleSelector.AddSubSelector(ParseClass())
| Scanner.BracketOpen: simpleSelector.AddSubSelector(ParseAttribute())
| Scanner.Colon: simpleSelector.AddSubSelector(ParsePseudo())
ELSE
END
END;
RETURN simpleSelector
END ParseSimpleSelector;
PROCEDURE ParseId(): CSS2.Id;
VAR id: CSS2.Id; s: String;
BEGIN
IF ~CheckSymbol({Scanner.Hash}, "'#'element id expected") THEN RETURN id END;
NEW(id);
s := scanner.GetStr();
id.SetValue(s^);
scanner.Scan();
RETURN id
END ParseId;
PROCEDURE ParseClass(): CSS2.Class;
VAR class: CSS2.Class; s: String;
BEGIN
IF ~CheckSymbol({Scanner.Dot}, "'.'class value expected") THEN RETURN class END;
scanner.Scan();
IF ~CheckSymbol({Scanner.Ident}, "class value expected") THEN RETURN class END;
NEW(class);
s := scanner.GetStr();
class.SetValue(s^);
scanner.Scan();
RETURN class
END ParseClass;
PROCEDURE ParseAttribute(): CSS2.Attribute;
VAR attribute: CSS2.Attribute; s: String;
BEGIN
IF ~CheckSymbol({Scanner.BracketOpen}, "'[' expected") THEN RETURN attribute END;
scanner.Scan();
IF ~CheckSymbol({Scanner.Ident}, "attribute name expected") THEN RETURN attribute END;
NEW(attribute);
s := scanner.GetStr();
attribute.SetName(s^);
scanner.Scan();
IF scanner.sym IN {Scanner.Equal, Scanner.Includes, Scanner.Dashmatch} THEN
CASE scanner.sym OF
| Scanner.Equal: attribute.SetRelation(CSS2.Equal)
| Scanner.Includes: attribute.SetRelation(CSS2.Includes)
| Scanner.Dashmatch: attribute.SetRelation(CSS2.Dashmatch)
END;
scanner.Scan();
IF ~CheckSymbol({Scanner.Ident, Scanner.String}, "attribute value expected") THEN RETURN attribute END;
s := scanner.GetStr();
attribute.SetValue(s^);
scanner.Scan()
END;
IF ~CheckSymbol({Scanner.BracketClose}, "']' expected") THEN RETURN attribute END;
scanner.Scan();
RETURN attribute
END ParseAttribute;
PROCEDURE ParsePseudo(): CSS2.Pseudo;
VAR pseudo: CSS2.Pseudo; s: String;
BEGIN
IF ~CheckSymbol({Scanner.Colon}, "':' expected") THEN RETURN pseudo END;
scanner.Scan();
IF ~CheckSymbol({Scanner.Ident, Scanner.Function}, "':'type expected") THEN RETURN pseudo END;
s := scanner.GetStr();
NEW(pseudo);
pseudo.SetType(s^);
IF (scanner.sym = Scanner.Function) & (s^ = 'lang') THEN
scanner.Scan();
IF ~CheckSymbol({Scanner.Ident}, "language expected") THEN RETURN pseudo END;
s := scanner.GetStr();
pseudo.SetLanguage(s^);
scanner.Scan();
IF ~CheckSymbol({Scanner.ParenClose}, "')' expected") THEN RETURN pseudo END
END;
scanner.Scan();
RETURN pseudo
END ParsePseudo;
PROCEDURE ParseDeclaration(): CSS2.Declaration;
VAR declaration: CSS2.Declaration; s: String;
BEGIN
IF ~CheckSymbol({Scanner.Ident}, "declaration property expected") THEN RETURN declaration END;
NEW(declaration);
s := scanner.GetStr();
declaration.SetProperty(s^);
scanner.Scan();
IF ~CheckSymbol({Scanner.Colon}, "':' expected") THEN RETURN declaration END;
scanner.Scan();
declaration.AddTerm(ParseTerm());
WHILE ~(scanner.sym IN {Scanner.Semicolon, Scanner.BraceClose, Scanner.Important, Scanner.Eof})
& (scanner.sym # Scanner.Invalid) DO
declaration.AddTerm(ParseTerm())
END;
IF scanner.sym = Scanner.Important THEN
declaration.SetImportant(TRUE);
scanner.Scan()
END;
RETURN declaration
END ParseDeclaration;
PROCEDURE ParseTerm(): CSS2.Term;
VAR term: CSS2.Term; s: String;
BEGIN
NEW(term);
IF scanner.sym = Scanner.Slash THEN
term.SetOperator(CSS2.Slash); scanner.Scan()
ELSIF scanner.sym = Scanner.Comma THEN
term.SetOperator(CSS2.Comma); scanner.Scan()
END;
IF scanner.sym = Scanner.Minus THEN
term.SetUnaryOperator(CSS2.Minus); scanner.Scan()
ELSIF scanner.sym = Scanner.Plus THEN
term.SetUnaryOperator(CSS2.Plus); scanner.Scan()
END;
CASE scanner.sym OF
| Scanner.Number:
IF scanner.numberType = Scanner.Integer THEN
term.SetType(CSS2.IntNumber); term.SetIntVal(scanner.intVal)
ELSIF scanner.numberType = Scanner.Real THEN
term.SetType(CSS2.RealNumber); term.SetRealVal(scanner.realVal)
END
| Scanner.Percentage:
term.SetType(CSS2.Percent);
IF scanner.numberType = Scanner.Integer THEN
term.SetRealVal(scanner.intVal / 100)
ELSIF scanner.numberType = Scanner.Real THEN
term.SetRealVal(scanner.realVal / 100)
END
| Scanner.Dimension:
IF scanner.numberType = Scanner.Integer THEN
term.SetType(CSS2.IntDimension); term.SetIntVal(scanner.intVal)
ELSIF scanner.numberType = Scanner.Real THEN
term.SetType(CSS2.RealDimension); term.SetRealVal(scanner.realVal)
END;
s := scanner.GetStr();
term.SetUnit(GetTermUnit(s^))
| Scanner.Function:
s := scanner.GetStr();
IF (s^ = 'rgb') OR (s^ = 'rgba') THEN
scanner.Scan();
term.SetType(CSS2.Color); term.SetIntVal(ParseRGB(s^ = 'rgba'))
ELSE
term.SetType(CSS2.Function); term.SetStringVal(s^);
scanner.Scan();
term.AddTerm(ParseTerm());
WHILE scanner.sym IN {Scanner.Slash, Scanner.Comma} DO
term.AddTerm(ParseTerm())
END;
IF ~CheckSymbol({Scanner.ParenClose}, "')' expected") THEN RETURN term END;
END
| Scanner.String:
s := scanner.GetStr();
term.SetType(CSS2.StringVal); term.SetStringVal(s^)
| Scanner.Ident:
s := scanner.GetStr();
term.SetType(CSS2.StringIdent); term.SetStringVal(s^)
| Scanner.URI:
s := scanner.GetStr();
term.SetType(CSS2.URI); term.SetStringVal(s^)
| Scanner.Hash:
s := scanner.GetStr();
term.SetType(CSS2.Color); term.SetIntVal(ComputeRGB(s^))
ELSE
Error("unknown symbol")
END;
scanner.Scan();
RETURN term
END ParseTerm;
PROCEDURE ParseRGB(hasAlpha: BOOLEAN): LONGINT;
VAR term: CSS2.Term; r, g, b, a: LONGINT;
BEGIN
term := ParseTerm();
IF (term # NIL) & (term.GetOperator() = CSS2.Undefined) & (term.GetUnaryOperator() = CSS2.Plus) THEN
IF (term.GetType() = CSS2.Percent) THEN r := ENTIER(0.5 + term.GetRealVal() * 255)
ELSIF (term.GetType() = CSS2.IntNumber) THEN r := term.GetIntVal()
ELSIF (term.GetType() = CSS2.RealNumber) THEN r := ENTIER(0.5 + term.GetRealVal())
ELSE Error("<number>'%' expected"); RETURN 0
END
ELSE
Error("<number>'%' expected"); RETURN 0
END;
term := ParseTerm();
IF (term # NIL) & (term.GetOperator() = CSS2.Comma) & (term.GetUnaryOperator() = CSS2.Plus) THEN
IF (term.GetType() = CSS2.Percent) THEN g := ENTIER(0.5 + term.GetRealVal() * 255)
ELSIF (term.GetType() = CSS2.IntNumber) THEN g := term.GetIntVal()
ELSIF (term.GetType() = CSS2.RealNumber) THEN g := ENTIER(0.5 + term.GetRealVal())
ELSE Error("<number>'%' expected"); RETURN 0
END
ELSE
Error("<number>'%' expected"); RETURN 0
END;
term := ParseTerm();
IF (term # NIL) & (term.GetOperator() = CSS2.Comma) & (term.GetUnaryOperator() = CSS2.Plus) THEN
IF (term.GetType() = CSS2.Percent) THEN b := ENTIER(0.5 + term.GetRealVal() * 255)
ELSIF (term.GetType() = CSS2.IntNumber) THEN b := term.GetIntVal()
ELSIF (term.GetType() = CSS2.RealNumber) THEN b := ENTIER(0.5 + term.GetRealVal())
ELSE Error("<number>'%' expected"); RETURN 0
END
ELSE
Error("<number>'%' expected"); RETURN 0
END;
IF hasAlpha THEN
term := ParseTerm();
IF (term # NIL) & (term.GetOperator() = CSS2.Comma) & (term.GetUnaryOperator() = CSS2.Plus) THEN
IF (term.GetType() = CSS2.Percent) THEN a := ENTIER(0.5 + term.GetRealVal() * 255)
ELSIF (term.GetType() = CSS2.IntNumber) THEN a := term.GetIntVal()
ELSIF (term.GetType() = CSS2.RealNumber) THEN a := ENTIER(0.5 + term.GetRealVal())
ELSE Error("<number>'%' expected"); RETURN 0
END
END
ELSE
a := 0
END;
IF ~CheckSymbol({Scanner.ParenClose}, "')' expected") THEN RETURN 0 END;
RETURN ASH(a, 24) + ASH(r, 16) + ASH(g, 8) + b
END ParseRGB;
PROCEDURE IgnoreKeyword;
BEGIN
WHILE (scanner.sym # Scanner.BraceOpen) & (scanner.sym # Scanner.Semicolon) & (scanner.sym # Scanner.Eof)
& (scanner.sym # Scanner.Invalid) DO
scanner.Scan();
IF scanner.sym = Scanner.AtKeyword THEN IgnoreKeyword() END
END;
IF ~CheckSymbol({Scanner.BraceOpen, Scanner.Semicolon}, "'{' or ';' expected") THEN RETURN END;
IF scanner.sym = Scanner.BraceOpen THEN
WHILE (scanner.sym # Scanner.BraceClose) & (scanner.sym # Scanner.Eof) & (scanner.sym # Scanner.Invalid) DO
scanner.Scan();
IF scanner.sym = Scanner.AtKeyword THEN IgnoreKeyword() END
END;
IF ~CheckSymbol({Scanner.BraceClose}, "'}' expected") THEN RETURN END
END;
scanner.Scan()
END IgnoreKeyword;
END Parser;
PROCEDURE GetMedium(mediumStr: ARRAY OF CHAR): SHORTINT;
BEGIN
IF mediumStr = 'all' THEN RETURN CSS2.All
ELSIF mediumStr = 'aural' THEN RETURN CSS2.Aural
ELSIF mediumStr = 'braille' THEN RETURN CSS2.Braille
ELSIF mediumStr = 'embossed' THEN RETURN CSS2.Embossed
ELSIF mediumStr = 'handheld' THEN RETURN CSS2.Handheld
ELSIF mediumStr = 'print' THEN RETURN CSS2.Print
ELSIF mediumStr = 'projection' THEN RETURN CSS2.Projection
ELSIF mediumStr = 'screen' THEN RETURN CSS2.Screen
ELSIF mediumStr = 'tty' THEN RETURN CSS2.TTY
ELSIF mediumStr = 'tv' THEN RETURN CSS2.TV
ELSE RETURN CSS2.All
END
END GetMedium;
PROCEDURE GetPseudoPage(pseudoPageStr: ARRAY OF CHAR): SHORTINT;
BEGIN
IF pseudoPageStr = 'left' THEN RETURN CSS2.Left
ELSIF pseudoPageStr = 'right' THEN RETURN CSS2.Right
ELSIF pseudoPageStr = 'first' THEN RETURN CSS2.First
ELSE RETURN CSS2.Undefined
END
END GetPseudoPage;
PROCEDURE GetTermUnit(unitStr: ARRAY OF CHAR): SHORTINT;
BEGIN
IF unitStr = 'em' THEN RETURN CSS2.em
ELSIF unitStr = 'ex' THEN RETURN CSS2.ex
ELSIF unitStr = 'px' THEN RETURN CSS2.px
ELSIF unitStr = 'in' THEN RETURN CSS2.in
ELSIF unitStr = 'cm' THEN RETURN CSS2.cm
ELSIF unitStr = 'mm' THEN RETURN CSS2.mm
ELSIF unitStr = 'pt' THEN RETURN CSS2.pt
ELSIF unitStr = 'pc' THEN RETURN CSS2.pc
ELSIF unitStr = 'deg' THEN RETURN CSS2.deg
ELSIF unitStr = 'grad' THEN RETURN CSS2.grad
ELSIF unitStr = 'rad' THEN RETURN CSS2.rad
ELSIF unitStr = 'ms' THEN RETURN CSS2.ms
ELSIF unitStr = 's' THEN RETURN CSS2.s
ELSIF unitStr = 'Hz' THEN RETURN CSS2.Hz
ELSIF unitStr = 'kHz' THEN RETURN CSS2.kHz
ELSE RETURN CSS2.Undefined
END
END GetTermUnit;
PROCEDURE ComputeRGB(VAR s: ARRAY OF CHAR): LONGINT;
VAR col: LONGINT; r, g, b, a: LONGINT;
BEGIN
HexStrToInt(s, col);
IF (Strings.Length(s) = 6) OR (Strings.Length(s) = 8) THEN
RETURN col
ELSIF (Strings.Length(s) = 3) OR (Strings.Length(s) = 4) THEN
a := col DIV 1000H; r := (col DIV 100H) MOD 10H; g := (col DIV 10H) MOD 10H; b := col MOD 10H;
RETURN ASH(a, 28) + ASH(a, 24) + ASH(r, 20) + ASH(r, 16) + ASH(g, 12) + ASH(g, 8) + ASH(b, 4) + b
ELSE
RETURN 0
END
END ComputeRGB;
PROCEDURE HexStrToInt(VAR str: ARRAY OF CHAR; VAR val: LONGINT);
VAR i, d: LONGINT; ch: CHAR;
BEGIN
i := 0; ch := str[0];
WHILE (ch # 0X) & (ch <= " ") DO
INC(i); ch := str[i]
END;
val := 0;
WHILE (("0" <= ch) & (ch <= "9")) OR (("A" <= ch) & (ch <= "F")) DO
IF (("0" <= ch) & (ch <= "9")) THEN d := ORD(ch)-ORD("0")
ELSE d := ORD(ch) - ORD("A") + 10
END;
INC(i); ch := str[i];
val := ASH(val, 4)+d
END
END HexStrToInt;
PROCEDURE DefaultReportError(pos, line, row: LONGINT; msg: ARRAY OF CHAR);
BEGIN
KernelLog.Enter; KernelLog.Char(CHR(9H)); KernelLog.Char(CHR(9H)); KernelLog.String("pos "); KernelLog.Int(pos, 6);
KernelLog.String(", line "); KernelLog.Int(line, 0); KernelLog.String(", row "); KernelLog.Int(row, 0);
KernelLog.String(" "); KernelLog.String(msg); KernelLog.Exit;
HALT(99)
END DefaultReportError;
END CSS2Parser.