├── py ├── piraha │ ├── py.typed │ ├── version.py │ ├── here.py │ └── colored.py ├── examples │ ├── calc2 │ │ ├── calc.in │ │ ├── calc.peg │ │ └── calc.py │ └── calc │ │ ├── calc.peg │ │ └── calc.py ├── pyproject.toml ├── README.md ├── setup.py └── LICENSE ├── LICENSE ├── src └── edu │ └── lsu │ └── cct │ └── piraha │ ├── BreakException.java │ ├── MatchException.java │ ├── ParseException.java │ ├── Visitor.java │ ├── Mapping.java │ ├── Nothing.java │ ├── Start.java │ ├── End.java │ ├── Break.java │ ├── examples │ ├── xml.peg │ ├── SimpleWave-original.m │ ├── SimpleWave.kranc │ ├── BasicCalc.java │ ├── Calc.java │ ├── Generic.java │ ├── McLachlan_ADMQuantities.kranc │ ├── Test.java │ ├── McLachlan_BSSN.kranc │ └── Kranc.java │ ├── Dot.java │ ├── ExtraVisitor.java │ ├── Ex.java │ ├── Pattern.java │ ├── Boundary.java │ ├── NegLookAhead.java │ ├── LookAhead.java │ ├── Near.java │ ├── BodyWhite.java │ ├── ILiteral.java │ ├── Range.java │ ├── GrammarToXML.java │ ├── OpenWhite.java │ ├── CloseWhite.java │ ├── Literal.java │ ├── Expected.java │ ├── Name.java │ ├── PackRat.java │ ├── DebugOutput.java │ ├── BackRef.java │ ├── DebugVisitor.java │ ├── Multi.java │ ├── Seq.java │ ├── Lookup.java │ ├── CheckVisitor.java │ ├── Or.java │ ├── Matcher.java │ ├── Find.java │ ├── Bracket.java │ ├── Grammar.java │ ├── Group.java │ ├── AutoGrammar.java │ └── ReParse.java ├── .classpath ├── .settings └── org.eclipse.jdt.core.prefs ├── README.md └── doc ├── ref.html ├── QuickStart.html ├── GrammarFiles.html ├── Calculator.html └── CScript.html /py/piraha/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /py/piraha/version.py: -------------------------------------------------------------------------------- 1 | __version__ = "1.1.7" 2 | -------------------------------------------------------------------------------- /py/examples/calc2/calc.in: -------------------------------------------------------------------------------- 1 | # assign a 2 | a = 3 + 4 3 | 4 | # assign b 5 | b = 2 + 2*a 6 | 7 | # The answer 8 | a*b + 1 9 | -------------------------------------------------------------------------------- /py/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ "setuptools>=42", "wheel" ] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Piraha is licensed with version 3 of the GNU Lesser General Public License 2 | http://www.gnu.org/licenses/lgpl-3.0.en.html 3 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/BreakException.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | public class BreakException extends RuntimeException { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/MatchException.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | public class MatchException extends RuntimeException { 4 | public MatchException(String msg) { 5 | super(msg); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/ParseException.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | public class ParseException extends RuntimeException { 4 | 5 | public ParseException(String string) { 6 | super(string); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /py/README.md: -------------------------------------------------------------------------------- 1 | # piraha-peg Python3 2 | Piraha is a [Parsing Expression Grammar](https://en.wikipedia.org/wiki/Parsing_expression_grammar) library written in Python3 3 | 4 | See the top level README for this repo, and the example files. 5 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Visitor.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | public class Visitor { 4 | 5 | public Visitor startVisit(Pattern p) { 6 | return this; 7 | } 8 | public void finishVisit(Pattern p) { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Mapping.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | public class Mapping { 4 | int from, delta; 5 | Mapping(int from,int delta) { 6 | this.from = from; 7 | this.delta = delta; 8 | } 9 | public String toString() { 10 | return "["+from+" += "+delta+"]"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Nothing.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | public class Nothing extends Pattern { 4 | 5 | @Override 6 | public boolean match(Matcher m) { 7 | return true; 8 | } 9 | 10 | @Override 11 | public String decompile() { 12 | return ""; 13 | } 14 | 15 | @Override 16 | public boolean eq(Object obj) { 17 | return true; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Start.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | public class Start extends Pattern { 4 | 5 | @Override 6 | public boolean match(Matcher m) { 7 | return m.getTextPos() == 0; 8 | } 9 | 10 | @Override 11 | public String decompile() { 12 | return "^"; 13 | } 14 | 15 | @Override 16 | public boolean eq(Object obj) { 17 | return true; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/End.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | 4 | public class End extends Pattern { 5 | 6 | @Override 7 | public boolean match(Matcher m) { 8 | return m.getTextPos() == m.text.length(); 9 | } 10 | 11 | @Override 12 | public String decompile() { 13 | return "$"; 14 | } 15 | 16 | @Override 17 | public boolean eq(Object obj) { 18 | return true; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Break.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | public class Break extends Pattern { 4 | 5 | @Override 6 | public boolean match(Matcher m) { 7 | throw new BreakException(); 8 | } 9 | 10 | @Override 11 | public String decompile() { 12 | return "{brk}"; 13 | } 14 | 15 | @Override 16 | public boolean eq(Object obj) { 17 | return obj instanceof Break; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/examples/xml.peg: -------------------------------------------------------------------------------- 1 | uses = (?i:uses|) 2 | #comment 3 | skipper = \b{-w}* 4 | xname = [a-zA-Z0-9\-:._]+ 5 | w = [\ \t\r\n\b] 6 | text = [^<>]+ 7 | dquote = "[^"]*" 8 | squote = '[^']*' 9 | stag = <{xname}({-w}+{xname}=({dquote}|{squote}))*{w}/> 10 | comment = )[^])*--> 11 | cdata = )[^])*\]\]> 12 | tag = <{xname}({-w}+{xname} = ({dquote}|{squote}))* (/>|>({text}|{-comment}|{cdata}|{stag}|{tag})*) 13 | doc = ( ]*>|) {tag} $ -------------------------------------------------------------------------------- /py/examples/calc/calc.peg: -------------------------------------------------------------------------------- 1 | # This is the grammar file for 2 | # our simple calculator. 3 | 4 | # Skipper's define the pattern 5 | # that applies between lexical 6 | # elements. 7 | skipper = ([ \t\r\n]|\#.*)* 8 | 9 | val = -?[0-9]+ 10 | 11 | # Parenthetical expression 12 | paren = \( {add} \) 13 | 14 | term = ({val}|{paren}) 15 | 16 | # patterns for the operators 17 | mulop = [*/] 18 | addop = [+-] 19 | 20 | mul = {term}( {mulop} {term})* 21 | add = {mul}( {addop} {mul})* 22 | 23 | expr = ^ {add} $ 24 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Dot.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | public class Dot extends Pattern { 4 | 5 | @Override 6 | public boolean match(Matcher m) { 7 | if(m.getTextPos() < m.text.length() && m.text.charAt(m.getTextPos()) != '\n') { 8 | m.incrTextPos(1); 9 | return true; 10 | } else { 11 | return false; 12 | } 13 | } 14 | 15 | @Override 16 | public String decompile() { 17 | return "."; 18 | } 19 | 20 | @Override 21 | public boolean eq(Object obj) { 22 | return true; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/ExtraVisitor.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | import java.util.Map; 4 | 5 | public class ExtraVisitor extends Visitor { 6 | final Map visited; 7 | 8 | ExtraVisitor(Map visited) { 9 | this.visited = visited; 10 | } 11 | 12 | public void finishVisit(Pattern p) { 13 | if(p instanceof Lookup) { 14 | Lookup ll = (Lookup)p; 15 | String name = ll.lookup; 16 | if(!visited.containsKey(name)) 17 | visited.put(name,Boolean.FALSE); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Ex.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | public class Ex { 4 | public static void main(String[] args) { 5 | Grammar g = new Grammar(); 6 | String patternName = "bx"; 7 | String pattern = "<{bx}>|x"; 8 | String textToMatch = "<>"; 9 | g.compile(patternName,pattern); 10 | Matcher matcher = g.matcher(patternName,textToMatch); 11 | if(matcher.matches()) { 12 | for(Group m : matcher.subMatches) { 13 | System.out.println(m.patternName+", "+m.substring(matcher.text)); 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /py/piraha/here.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | from .colored import colored 3 | import os 4 | 5 | _here = os.path.realpath(os.getcwd()) 6 | 7 | def here(*args : Any)->None: 8 | import inspect 9 | stack = inspect.stack() 10 | frame = stack[1] 11 | fname = os.path.realpath(frame.filename) 12 | if fname.startswith(_here): 13 | fname = fname[len(_here)+1:] 14 | print(colored("HERE:","cyan"),fname+":"+colored(frame.lineno,"yellow"), *args, flush=True) 15 | frame = None 16 | stack = None 17 | 18 | if __name__ == "__main__": 19 | here(_here) 20 | -------------------------------------------------------------------------------- /py/examples/calc2/calc.peg: -------------------------------------------------------------------------------- 1 | # This is the grammar file for 2 | # our simple calculator. 3 | 4 | # Skipper's define the pattern 5 | # that applies between lexical 6 | # elements. 7 | skipper = ([ \t\r\n]|\#.*)* 8 | 9 | val = -?[0-9]+ 10 | 11 | # A c-language identifier 12 | name = [a-zA-Z][a-zA-Z0-9_]* 13 | 14 | # Parenthetical expression 15 | paren = \( {add} \) 16 | 17 | term = ({val}|{paren}|{name}) 18 | 19 | # patterns for the operators 20 | mulop = [*/] 21 | addop = [+-] 22 | 23 | mul = {term}( {mulop} {term})* 24 | add = {mul}( {addop} {mul})* 25 | assign = {name} = {add} 26 | 27 | expr = ^( {assign})* {add} $ 28 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate 4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 6 | org.eclipse.jdt.core.compiler.compliance=1.7 7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 12 | org.eclipse.jdt.core.compiler.source=1.7 13 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Pattern.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Base class for all kinds of 8 | * pattern elements. 9 | */ 10 | public abstract class Pattern { 11 | public abstract boolean match(Matcher m); 12 | 13 | public void visit(Visitor v) { 14 | v.startVisit(this); 15 | v.finishVisit(this); 16 | } 17 | 18 | public abstract String decompile(); 19 | 20 | public abstract boolean eq(Object obj); 21 | 22 | @Override 23 | public final boolean equals(Object obj) { 24 | if(obj != null && getClass() == obj.getClass()) { 25 | return eq(obj); 26 | } 27 | return false; 28 | } 29 | 30 | public List expected(int n) { return new ArrayList(); } 31 | } 32 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Boundary.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | public class Boundary extends Pattern { 4 | 5 | @Override 6 | public boolean match(Matcher m) { 7 | int n = m.getTextPos(); 8 | if(n==0 || n==m.text.length()) { 9 | return true; 10 | } 11 | if(!identc(m.text.charAt(n)) || !identc(m.text.charAt(n-1))) { 12 | return true; 13 | } 14 | return false; 15 | } 16 | 17 | private boolean identc(char c) { 18 | if(c >= 'a' && c <= 'z') 19 | return true; 20 | if(c >= 'A' && c <= 'Z') 21 | return true; 22 | if(c >= '0' && c <= '9') 23 | return true; 24 | return c == '_'; 25 | } 26 | 27 | @Override 28 | public String decompile() { 29 | return "\\b"; 30 | } 31 | 32 | @Override 33 | public boolean eq(Object obj) { 34 | return true; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /py/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | import re 3 | 4 | vfile="piraha/version.py" 5 | verstrline = open(vfile, "rt").read() 6 | VSRE = r"^__version__\s*=\s*(['\"])(.*)\1" 7 | g = re.search(VSRE, verstrline, re.M) 8 | if g: 9 | __version__ = g.group(2) 10 | else: 11 | raise RuntimeError(f"Unable to find version in file '{vfile}") 12 | 13 | setup( 14 | name='Piraha', 15 | version=__version__, 16 | description='Parsing Expression Grammar for Python', 17 | long_description='Parsing Expression Grammar for Python', 18 | url='https://github.com/stevenrbrandt/piraha-peg.git', 19 | author='Steven R. Brandt', 20 | author_email='steven@stevenrbrandt.com', 21 | license='LGPL', 22 | packages=['piraha'], 23 | package_data = { 24 | 'piraha': ['py.typed'], 25 | } 26 | ) 27 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/NegLookAhead.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | 4 | public class NegLookAhead extends Pattern { 5 | Pattern pattern; 6 | 7 | public NegLookAhead(Pattern pattern) { 8 | this.pattern = pattern; 9 | } 10 | 11 | @Override 12 | public boolean match(Matcher m) { 13 | int pos = m.getTextPos(); 14 | boolean b = !Matcher.matchAll(pattern, m); 15 | m.setTextPos(pos); 16 | return b; 17 | } 18 | 19 | @Override public void visit(Visitor v) { 20 | Visitor vv = v.startVisit(this); 21 | pattern.visit(vv); 22 | v.finishVisit(this); 23 | } 24 | 25 | @Override 26 | public String decompile() { 27 | return "(?!"+pattern.decompile()+")"; 28 | } 29 | 30 | @Override 31 | public boolean eq(Object obj) { 32 | NegLookAhead nla = (NegLookAhead)obj; 33 | return nla.pattern.equals(pattern); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/LookAhead.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | 4 | public class LookAhead extends Pattern { 5 | 6 | Pattern pattern; 7 | 8 | public LookAhead(Pattern pattern) { 9 | this.pattern = pattern; 10 | } 11 | 12 | @Override 13 | public boolean match(Matcher m) { 14 | int pos = m.getTextPos(); 15 | boolean b = Matcher.matchAll(pattern, m); 16 | if(b) { 17 | m.setTextPos(pos); 18 | } 19 | return b; 20 | } 21 | 22 | @Override public void visit(Visitor v) { 23 | Visitor vv = v.startVisit(this); 24 | pattern.visit(vv); 25 | v.finishVisit(this); 26 | } 27 | 28 | @Override 29 | public String decompile() { 30 | return "(?="+pattern.decompile()+")"; 31 | } 32 | 33 | @Override 34 | public boolean eq(Object obj) { 35 | LookAhead lh = (LookAhead)obj; 36 | return pattern.equals(lh.pattern); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Near.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | 4 | public class Near { 5 | public String text,rule; 6 | public Expected expected; 7 | public int lineNum=1, pos, startOfLine, endOfLine; 8 | public Near() {} 9 | public String toString() { 10 | int lstart = startOfLine - 50; 11 | if(lstart < 0) lstart = 0; 12 | int npos = pos + startOfLine - lstart; 13 | String line = text.substring(lstart,endOfLine); 14 | String str = line.substring(0,npos)+"|"+line.substring(npos); 15 | String base = ""; 16 | if(expected != null) { 17 | if(expected.possibilities.size()==1) { 18 | base = "Expected: "+expected+" "; 19 | } else { 20 | base = "Expected one of: "+expected+" "; 21 | } 22 | } 23 | if(rule != null) 24 | return base+lineNum+" in {"+rule+"}: '"+str.trim()+"'"; 25 | else 26 | return base+lineNum+": '"+str.trim()+"'"; 27 | } 28 | } -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/examples/SimpleWave-original.m: -------------------------------------------------------------------------------- 1 | 2 | << "KrancThorn.m"; 3 | 4 | groups = {{"evolved_group", {phi, pi}}}; 5 | 6 | derivatives = { 7 | PDstandard2nd[i_] -> StandardCenteredDifferenceOperator[1,1,i], 8 | PDstandard2nd[i_, i_] -> StandardCenteredDifferenceOperator[2,1,i] 9 | }; 10 | 11 | PD = PDstandard2nd; 12 | 13 | initialSineCalc = { 14 | Name -> "initial_sine", 15 | Schedule -> {"AT INITIAL"}, 16 | Equations -> 17 | { 18 | phi -> Sin[2 Pi (x - t)], 19 | pi -> -2 Pi Cos[2 Pi (x - t)] 20 | } 21 | }; 22 | 23 | evolveCalc = { 24 | Name -> "calc_rhs", 25 | Schedule -> {"in MoL_CalcRHS"}, 26 | Equations -> 27 | { 28 | dot[phi] -> pi, 29 | dot[pi] -> Euc[ui,uj] PD[phi,li,lj] 30 | } 31 | }; 32 | 33 | CreateKrancThornTT[groups, ".", 34 | "SimpleWave", 35 | Calculations -> {initialSineCalc, evolveCalc}, 36 | PartialDerivatives -> derivatives, 37 | DeclaredGroups -> {"evolved_group"}]; 38 | -------------------------------------------------------------------------------- /py/piraha/colored.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | import sys 3 | 4 | def not_colored(a,_): 5 | return str(a) 6 | 7 | colors = { 8 | "red":"\033[31m", 9 | "green":"\033[32m", 10 | "yellow":"\033[33m", 11 | "blue":"\033[34m", 12 | "magenta":"\033[35m", 13 | "cyan":"\033[36m", 14 | } 15 | reset = "\033[0m" 16 | 17 | def colored(arg:Any,c:str)->str: 18 | assert type(c) == str 19 | assert c in colors 20 | s = str(arg) 21 | return colors[c] + s + reset 22 | 23 | if hasattr(sys.stdout,"isatty"): 24 | is_tty = sys.stdout.isatty() 25 | else: 26 | is_tty = False 27 | 28 | is_jupyter = type(sys.stdout).__name__ == 'OutStream' and type(sys.stdout).__module__ == 'ipykernel.iostream' 29 | if (not is_tty) and (not is_jupyter): 30 | colored = not_colored 31 | 32 | if __name__ == "__main__": 33 | if installed: 34 | print(colored("Colored was installed","green")) 35 | else: 36 | print("Colored was NOT installed") 37 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/examples/SimpleWave.kranc: -------------------------------------------------------------------------------- 1 | @THORN MLP_SimpleWave 2 | 3 | @DERIVATIVES 4 | PDstandard2nd[i_] -> StandardCenteredDifferenceOperator[1,1,i], 5 | PDstandard2nd[i_, i_] -> StandardCenteredDifferenceOperator[2,1,i], 6 | PDstandard2th[i_, j_] -> StandardCenteredDifferenceOperator[1,1,i] * 7 | StandardCenteredDifferenceOperator[1,1,j], 8 | @END_DERIVATIVES 9 | 10 | @TENSORS 11 | phi, pi 12 | @END_TENSORS 13 | 14 | @GROUPS 15 | phi -> phi_group, 16 | pi -> pi_group, 17 | @END_GROUPS 18 | 19 | @DEFINE PD = PDstandard2nd 20 | 21 | @CALCULATION initial_sine 22 | @Schedule {"AT INITIAL"} 23 | @EQUATIONS 24 | phi -> Sin[2 Pi (x - t)], 25 | pi -> -2 Pi Cos[2 Pi (x - t)], 26 | @END_EQUATIONS 27 | @END_CALCULATION 28 | 29 | @CALCULATION calc_rhs 30 | @Schedule {"in MoL_CalcRHS"} 31 | @EQUATIONS 32 | dot[phi] -> pi, 33 | dot[pi] -> Euc[ui,uj] PD[phi,li,lj], 34 | @END_EQUATIONS 35 | @END_CALCULATION 36 | 37 | @END_THORN 38 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/BodyWhite.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | //import edu.lsu.cct.util.Here; 4 | 5 | public class BodyWhite extends Pattern { 6 | 7 | @Override 8 | public boolean match(Matcher m) { 9 | int pos = m.getTextPos(); 10 | if(pos==0 || m.text.charAt(pos-1)=='\n') { 11 | int newPos = pos; 12 | for(;newPos expected(int n) { 37 | List ex = new ArrayList(); 38 | ex.add(decompile()); 39 | return ex; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Range.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | import java.util.Set; 4 | 5 | public class Range extends Pattern { 6 | char lo,hi; 7 | Range(int lo,int hi) { 8 | this.lo = (char) lo; 9 | this.hi = (char) hi; 10 | } 11 | @Override 12 | public boolean match(Matcher m) { 13 | char c = m.text.charAt(m.getTextPos()); 14 | if(lo <= c && c <= hi) { 15 | m.incrTextPos(1); 16 | return true; 17 | } 18 | return false; 19 | } 20 | @Override 21 | public String decompile() { 22 | throw new RuntimeException(); 23 | } 24 | @Override 25 | public boolean eq(Object obj) { 26 | if(obj instanceof Range) { 27 | Range r = (Range)obj; 28 | return r.lo == lo && r.hi == hi; 29 | } 30 | return false; 31 | } 32 | public Range overlap(Range rr) { 33 | if(rr.hi+1 >= lo && rr.hi <= hi) 34 | return new Range(Math.min(lo, rr.lo),Math.max(hi,rr.hi)); 35 | if(hi+1 >= rr.lo && hi <= rr.hi) 36 | return new Range(Math.min(lo, rr.lo),Math.max(hi,rr.hi)); 37 | return null; 38 | } 39 | public String toString() { 40 | return lo+"-"+hi; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/GrammarToXML.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.File; 5 | import java.io.FileWriter; 6 | import java.io.PrintWriter; 7 | 8 | public class GrammarToXML { 9 | public static void main(String[] args) throws Exception { 10 | if(args.length < 4 || !args[0].endsWith(".peg") || !args[2].endsWith(".xml")) { 11 | System.err.println("usage: GrammarToXML pegfile rule outputxmlfile srcfiles"); 12 | System.exit(1); 13 | } 14 | Grammar g = new Grammar(); 15 | g.compileFile(new File(args[0])); 16 | //g.diag(new DebugOutput()); 17 | String rule = args[1]; 18 | String xmlFile = args[2]; 19 | DebugOutput out = new DebugOutput(new PrintWriter(new BufferedWriter(new FileWriter(xmlFile)))); 20 | for(int i=3;i 0) { 15 | thresh = m.whiteThresholds.get(nt-1); 16 | } 17 | int newPos = pos; 18 | for(;newPos= 0) 27 | return "\\"+c; 28 | else 29 | return DebugOutput.outc(c); 30 | } 31 | 32 | @Override 33 | public String decompile() { 34 | return outc(ch); 35 | } 36 | @Override 37 | public boolean eq(Object obj) { 38 | Literal lit = (Literal)obj; 39 | return lit.ch == ch; 40 | } 41 | 42 | @Override 43 | public List expected(int n) { 44 | List ex = new ArrayList(); 45 | ex.add(decompile()); 46 | return ex; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Expected.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | public class Expected { 7 | Seq seq; 8 | int n; 9 | Pattern pat; 10 | int pos; 11 | Set possibilities = new HashSet(); 12 | int epos; 13 | Or or; 14 | 15 | void build(int maxTextPos) { 16 | pos = maxTextPos; 17 | if(pat != null) { 18 | possibilities.add(pat.decompile()); 19 | } else if(seq != null) { 20 | StringBuffer sb = new StringBuffer(); 21 | for(int j=n;j0) 33 | possibilities.add(sb.toString()); 34 | } 35 | } 36 | 37 | public Expected() { 38 | } 39 | 40 | public Expected(Pattern pat) { 41 | this.pat = pat; 42 | } 43 | 44 | public Expected(Seq seq,int n) { 45 | this.seq = seq; 46 | this.n = n; 47 | } 48 | 49 | public Expected(Or or,int pos) { 50 | this.or = or; 51 | this.epos = pos; 52 | } 53 | 54 | public String toString() { return possibilities.toString(); } 55 | } 56 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Name.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | import java.util.LinkedList; 4 | 5 | public class Name extends Pattern { 6 | String name; 7 | 8 | public Name(String name) { 9 | this.name = name; 10 | } 11 | 12 | @Override 13 | public boolean match(Matcher m) { 14 | for(Group match : m.subMatches) { 15 | boolean b = comp(m,match); 16 | if(b) 17 | return b; 18 | } 19 | for(LinkedList list : m.savedMatches) { 20 | for (Group match : list) { 21 | boolean b = comp(m, match); 22 | if (b) 23 | return b; 24 | } 25 | } 26 | return false; 27 | } 28 | 29 | private boolean comp(Matcher m, Group match) { 30 | if(!match.getPatternName().equals(name)) 31 | return false; 32 | for(int i=0;true;i++) { 33 | if(i+match.getBegin() >= match.getEnd()) { 34 | m.setTextPos(i+m.getTextPos()); 35 | return true; 36 | } 37 | char c1 = m.text.charAt(i+match.getBegin()); 38 | char c2 = m.text.charAt(i+m.getTextPos()); 39 | if(c1 != c2) 40 | return false; 41 | if(i+m.getTextPos() >= m.text.length()) 42 | return false; 43 | } 44 | } 45 | 46 | @Override 47 | public String decompile() { 48 | return "{$"+name+"}"; 49 | } 50 | 51 | @Override 52 | public boolean eq(Object obj) { 53 | Name n = (Name)obj; 54 | return n.name.equals(name); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/PackRat.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | import java.util.LinkedList; 4 | 5 | /* 6 | public static void main(String[] args) { 7 | Grammar g = new Grammar(); 8 | g.compile("test","short"); 9 | Matcher m = g.matcher("test", "short short short"); 10 | m.startReplacement(); 11 | while(m.find()) { 12 | m.appendReplacement("long"); 13 | } 14 | String result = m.appendTail(); 15 | System.out.println(result); 16 | System.out.println(m.mappings); 17 | for(int i=0;i "+result.charAt(m.mapping(i))); 19 | } 20 | } 21 | */ 22 | public class PackRat { 23 | final String name; 24 | final int pos; 25 | final int hc; 26 | public boolean matched; 27 | public int after; 28 | public boolean filled; 29 | public LinkedList subMatches; 30 | PackRat(String name,int pos) { 31 | this.name = name; 32 | this.pos = pos; 33 | this.hc = pos ^ name.hashCode(); 34 | } 35 | 36 | @Override 37 | public int hashCode() { return hc; } 38 | 39 | @Override 40 | public boolean equals(Object o) { 41 | PackRat pr = (PackRat)o; 42 | return pr.pos == pos && pr.name.equals(name); 43 | } 44 | 45 | public String toString() { 46 | int n = -1; 47 | String m = ""; 48 | if(subMatches != null) { 49 | n = subMatches.size(); 50 | } 51 | return "PackRat("+name+","+pos+","+matched+","+after+","+n+")"; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/examples/BasicCalc.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha.examples; 2 | 3 | import edu.lsu.cct.piraha.Grammar; 4 | import edu.lsu.cct.piraha.Group; 5 | import edu.lsu.cct.piraha.Matcher; 6 | 7 | public class BasicCalc { 8 | public static Grammar makeMath() { 9 | Grammar math = new Grammar(); 10 | // All the various was a double precision number (or integer) can be 11 | // written 12 | math.compile("num","-?[0-9]+"); 13 | 14 | // The basic operators 15 | math.compile("addop", "\\+|-"); 16 | 17 | math.compile("addexpr", "{num}({addop}{num})*"); 18 | 19 | return math; 20 | } 21 | 22 | Grammar math = makeMath(); 23 | 24 | public static void main(String[] args) { 25 | Grammar math = makeMath(); 26 | 27 | Matcher m = math.matcher("addexpr", "3+4-2*(9-4)"); 28 | 29 | boolean b = m.matches(); 30 | System.out.println("match? " + b); 31 | if (b) { 32 | m.dumpMatches(); 33 | System.out.println("eval=" + evalExpr(m)); 34 | } else { 35 | System.out.println(m.near()); // report errors 36 | } 37 | } 38 | 39 | private static double evalExpr(Group match) { 40 | double answer = Double.parseDouble(match.group(0).substring()); 41 | for(int i=1;i= 128) { 25 | String hex = Integer.toHexString(c); 26 | while(hex.length() < 4) 27 | hex = "0"+hex; 28 | return "\\u"+hex; 29 | } else { 30 | return Character.toString(c); 31 | } 32 | } 33 | public void print(Object o) { 34 | if(o != null) { 35 | if(newline) { 36 | for(int i=0;i "+c2); 31 | if (c1 != c2) { 32 | return false; 33 | } 34 | } 35 | } else { 36 | for (int i = 0; i < n; i++) { 37 | int nn = pos + i; 38 | char c1 = m.text.charAt(i+begin); 39 | char c2 = m.text.charAt(nn); 40 | //System.out.println("!ign: "+c1+" <=> "+c2); 41 | if (c1 != c2) { 42 | return false; 43 | } 44 | } 45 | } 46 | m.setTextPos(pos+n); 47 | return true; 48 | } 49 | 50 | @Override 51 | public String decompile() { 52 | return "\\"+(1+num); 53 | } 54 | 55 | @Override 56 | public boolean eq(Object obj) { 57 | BackRef ref = (BackRef)obj; 58 | return ref.num == num; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/DebugVisitor.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | public class DebugVisitor extends Visitor { 4 | DebugOutput out; 5 | 6 | public DebugVisitor(DebugOutput out) { 7 | this.out = out; 8 | } 9 | public DebugVisitor() { 10 | this.out = DebugOutput.out; 11 | } 12 | 13 | private void printChar(char c) { 14 | if(c == ' ') 15 | out.print("[ ]"); 16 | else 17 | out.print(c); 18 | } 19 | 20 | @Override 21 | public Visitor startVisit(Pattern p) { 22 | String cn = p.getClass().getName(); 23 | int n = cn.lastIndexOf('.'); 24 | cn = cn.substring(n+1); 25 | out.print(cn); 26 | out.print(": "); 27 | out.indent += 2; 28 | if(p instanceof Literal) { 29 | Literal ll = (Literal)p; 30 | printChar(ll.ch); 31 | } else if(p instanceof ILiteral) { 32 | ILiteral il = (ILiteral)p; 33 | printChar(il.lch); 34 | } else if(p instanceof Name) { 35 | Name nm = (Name)p; 36 | out.print(nm.name); 37 | } else if(p instanceof Range) { 38 | Range r = (Range)p; 39 | if(r.lo == r.hi) 40 | printChar(r.lo); 41 | else { 42 | printChar(r.lo); 43 | out.print(" to "); 44 | printChar(r.hi); 45 | } 46 | } else if(p instanceof Multi) { 47 | Multi m = (Multi)p; 48 | if(m.min == m.max) 49 | out.print(m.min); 50 | else 51 | out.print(m.min+" to "+m.max); 52 | } else if(p instanceof Lookup) { 53 | Lookup lo = (Lookup)p; 54 | out.print(lo.lookup); 55 | } else if(p instanceof Bracket) { 56 | Bracket br = (Bracket)p; 57 | out.print("neg="+br.neg); 58 | } 59 | out.println(); 60 | return this; 61 | } 62 | @Override 63 | public void finishVisit(Pattern p) { 64 | out.indent -= 2; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /py/examples/calc/calc.py: -------------------------------------------------------------------------------- 1 | # A simple calculator based on Piraha 2 | 3 | import sys 4 | from piraha import Grammar, compileFile, Matcher 5 | 6 | g = Grammar() 7 | 8 | # Compile a grammar from a file 9 | compileFile(g,"calc.peg") 10 | 11 | # Process the parse tree 12 | def calc(gr): 13 | 14 | # Get the name of the rule that produced this parse tree element 15 | n = gr.getPatternName() 16 | 17 | # Process a single numeric value 18 | if n == "val": 19 | return float(gr.substring()) 20 | 21 | # Process an operation 22 | elif n in ["add", "mul"]: 23 | v = calc(gr.group(0)) 24 | # This could be 3 + 4 - 2 + 6 ... 25 | for i in range(1,gr.groupCount(),2): 26 | op = gr.group(i).substring() 27 | v2 = calc(gr.group(i+1)) 28 | if op == "+": 29 | v += v2 30 | elif op == '-': 31 | v -= v2 32 | elif op == '*': 33 | v *= v2 34 | elif op == '/': 35 | v /= v2 36 | return v 37 | 38 | elif n in ["expr", "term", "paren"]: 39 | # Process the root element or a term in an expression 40 | # or a parenthetical expression 41 | return calc(gr.group(0)) 42 | 43 | else: 44 | raise Exception(">"+n+"<") 45 | 46 | # Create a matcher 47 | if __name__ == "__main__": 48 | if len(sys.argv) == 1: 49 | input = "3*2 + (2+2)*1" 50 | print("Using sample input:",input) 51 | else: 52 | input = sys.argv[1] 53 | m = Matcher(g,g.default_rule, input) 54 | if m.matches(): 55 | print("Success! Dump parse tree...") 56 | print(m.gr.dump()) 57 | print("answer:",calc(m.gr)) 58 | else: 59 | # Show a helpful error message 60 | m.showError() 61 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Multi.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | 4 | 5 | public class Multi extends Pattern { 6 | final int min,max; 7 | Pattern pattern; 8 | 9 | public Multi(int min,int max) { 10 | this.min = min; 11 | this.max = max; 12 | } 13 | public Multi(Pattern p,int min,int max) { 14 | this.pattern = p; 15 | this.min = min; 16 | this.max = max; 17 | } 18 | 19 | @Override 20 | public boolean match(Matcher m) { 21 | for(int i=0;i sz) { 29 | m.subMatches.removeLast(); 30 | nsz--; 31 | } 32 | return i >= min; 33 | } 34 | } catch (BreakException e) { 35 | return true; 36 | } 37 | } 38 | return true; 39 | } 40 | 41 | @Override public void visit(Visitor v) { 42 | Visitor vv = v.startVisit(this); 43 | pattern.visit(vv); 44 | v.finishVisit(this); 45 | } 46 | 47 | @Override 48 | public String decompile() { 49 | if(min == 1 && max == Integer.MAX_VALUE) 50 | return pre()+"+"; 51 | if(min == 0 && max == Integer.MAX_VALUE) 52 | return pre()+"*"; 53 | if(min == 0 && max == 1) 54 | return pre()+"?"; 55 | if(max == Integer.MAX_VALUE) 56 | return pre()+"{"+min+",}"; 57 | if(min == max) 58 | return pre()+"{"+min+"}"; 59 | return pattern.decompile()+"{"+min+","+max+"}"; 60 | } 61 | 62 | String pre() { 63 | if(pattern instanceof Seq) { 64 | Seq seq = (Seq)pattern; 65 | if(!seq.igcShow) 66 | return "("+pattern.decompile()+")"; 67 | } else if(pattern instanceof Or) { 68 | Or or = (Or)pattern; 69 | if(!or.igcShow) 70 | return "("+pattern.decompile()+")"; 71 | } 72 | return pattern.decompile(); 73 | } 74 | 75 | @Override 76 | public boolean eq(Object obj) { 77 | Multi m = (Multi)obj; 78 | return m.min == min && pattern.equals(m.pattern); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /py/examples/calc2/calc.py: -------------------------------------------------------------------------------- 1 | # A simple calculator based on Piraha 2 | 3 | import sys 4 | from piraha import Grammar, compileFile, Matcher 5 | 6 | g = Grammar() 7 | 8 | # Compile a grammar from a file 9 | compileFile(g,"calc.peg") 10 | 11 | values = {} 12 | 13 | # Process the parse tree 14 | def calc(gr): 15 | global values 16 | 17 | # Get the name of the rule that produced this parse tree element 18 | n = gr.getPatternName() 19 | 20 | # Process the read of a variable 21 | if n in ["name"]: 22 | return values[gr.substring()] 23 | 24 | # Process a single numeric value 25 | elif n == "val": 26 | return float(gr.substring()) 27 | 28 | # Process an operation 29 | elif n in ["add", "mul"]: 30 | v = calc(gr.group(0)) 31 | # This could be 3 + 4 - 2 + 6 ... 32 | for i in range(1,gr.groupCount(),2): 33 | op = gr.group(i).substring() 34 | v2 = calc(gr.group(i+1)) 35 | if op == "+": 36 | v += v2 37 | elif op == '-': 38 | v -= v2 39 | elif op == '*': 40 | v *= v2 41 | elif op == '/': 42 | v /= v2 43 | return v 44 | 45 | elif n in ["expr"]: 46 | v = None 47 | for i in range(gr.groupCount()): 48 | v = calc(gr.group(i)) 49 | return v 50 | 51 | elif n in ["term", "paren"]: 52 | # Process a term in an expression 53 | # or a parenthetical expression 54 | return calc(gr.group(0)) 55 | 56 | elif n in ["assign"]: 57 | varname = gr.group(0).substring() 58 | value = calc(gr.group(1)) 59 | values[varname] = value 60 | 61 | else: 62 | raise Exception(">"+n+"<") 63 | 64 | # Create a matcher 65 | if __name__ == "__main__": 66 | if len(sys.argv) == 1: 67 | input_file = "calc.in" 68 | print("Using sample input file:",input_file) 69 | else: 70 | input_file = sys.argv[1] 71 | with open(input_file, "r") as fd: 72 | input = fd.read() 73 | m = Matcher(g,g.default_rule, input) 74 | if m.matches(): 75 | print("Success! Dump parse tree...") 76 | print(m.gr.dump()) 77 | print("answer:",calc(m.gr)) 78 | else: 79 | # Show a helpful error message 80 | m.showError() 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # piraha-peg 2 | Piraha is a [Parsing Expression Grammar](https://en.wikipedia.org/wiki/Parsing_expression_grammar) library written in Java 3 | 4 | The name comes from the spoken language called [Pirahã](https://en.wikipedia.org/wiki/Pirah%C3%A3_language), which by some measure is 5 | considered the simplest language in the world. 6 | 7 | The syntax is loosely based on regular expressions as they are found in Perl or Java. 8 | The Reference link below provides a detailed description of the pattern elements. The 9 | Quick Start describes how to use the API. See the Calculator and CSscript examples to 10 | get a more detailed idea on how to write programs with Piraha. 11 | 12 | Note, however, that since Piraha does not describe a 13 | Regular Language the pattern expressions used to describe it are not properly called 14 | "regular expressions." However, because of the similarity in form between Parsing Expression 15 | Grammars (PEGs) and regular expressions, I use the term "pegular expressions" instead. 16 | 17 | [Reference](https://stevenrbrandt.github.io/piraha-peg/doc/ref.html) - This 18 | reference card provides a description of the pattern elements that can be used in a 19 | pegular expression. 20 | 21 | [Quick Start](https://stevenrbrandt.github.io/piraha-peg/doc/QuickStart.html) - 22 | This guide shows you how to call the Piraha engine from Java. It also explains the differences 23 | between the Piraha matcher and a regular expression matcher. 24 | 25 | [Grammar Files](https://stevenrbrandt.github.io/piraha-peg/doc/GrammarFiles.html) - 26 | This document explains how to construct and use Piraha expressions from inside a Grammar File, 27 | i.e. a file containing multiple pegular expressions. 28 | 29 | [Calculator](https://stevenrbrandt.github.io/piraha-peg/doc/Calculator.html) - 30 | What grammar engine is complete without a calculator example? 31 | 32 | [CScript](https://stevenrbrandt.github.io/piraha-peg/doc/CScript.html) - 33 | Calculators are boring! Here's how you can write a complete language using Piraha. 34 | 35 | [Citing Piraha](http://ieeexplore.ieee.org/document/5698011/) This is a link to the 36 | Piraha Paper. You can also cite the digital object identifier: 37 | [https://dx.doi.org/10.6084/m9.figshare.3837840](DOI: https://dx.doi.org/10.6084/m9.figshare.3837840) 38 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Seq.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Set; 6 | 7 | 8 | public class Seq extends Pattern { 9 | 10 | List patternList; 11 | boolean ignCase, igcShow; 12 | public Seq(List pattern,boolean ignCase,boolean igcShow) { 13 | this.patternList = pattern; 14 | this.ignCase = ignCase; 15 | this.igcShow = igcShow; 16 | } 17 | public Seq(Pattern...patterns) { 18 | this(false,false,patterns); 19 | } 20 | public Seq(boolean ignCase,boolean igcShow,Pattern...patterns) { 21 | patternList = new ArrayList(patterns.length); 22 | for(Pattern p : patterns) 23 | patternList.add(p); 24 | } 25 | 26 | @Override 27 | public boolean match(Matcher m) { 28 | //for(Pattern x = pattern;x != null;x = x.next) { 29 | for(int i=0;i expected(int n) { 80 | List ex = new ArrayList(); 81 | for(int i=n;i nex = new ArrayList(); 83 | List li = patternList.get(i).expected(0); 84 | for(int j=0;j0;i-=2) { 93 | d = Math.pow(evalExpr(match.group(i-1)), d); 94 | } 95 | return d; 96 | } 97 | return evalExpr(match.group(0)); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Lookup.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | 6 | 7 | public class Lookup extends Pattern { 8 | final Grammar g; 9 | final String lookup; 10 | final String grammarName; 11 | final boolean capture; 12 | Pattern pattern = null; 13 | Grammar grammar = null; 14 | final String fullName; 15 | 16 | public Lookup(String lookup,Grammar g) { 17 | this.g = g; 18 | if(lookup.startsWith("-")) { 19 | lookup = lookup.substring(1); 20 | capture = false; 21 | } else { 22 | capture = true; 23 | } 24 | this.fullName = lookup; 25 | int n = lookup.indexOf(':'); 26 | if(n >= 0) { 27 | grammarName = lookup.substring(0,n); 28 | lookup = lookup.substring(n+1); 29 | } else { 30 | grammarName = null; 31 | grammar = g; 32 | } 33 | this.lookup = lookup; 34 | } 35 | 36 | private void setup() { 37 | if(grammar == null) { 38 | if(g.grammars != null && g.grammars.containsKey(grammarName)) { 39 | grammar = g.grammars.get(grammarName); 40 | } else { 41 | throw new MatchException("No such grammar: "+grammarName); 42 | } 43 | } 44 | if(pattern == null) { 45 | if(grammar.patterns.containsKey(lookup)) 46 | pattern = grammar.patterns.get(lookup); 47 | else 48 | throw new MatchException("No such grammar rule: "+lookup+" in "+grammar.patterns.keySet()); 49 | } 50 | } 51 | 52 | @Override 53 | public boolean match(Matcher m) { 54 | setup(); 55 | int before = m.getTextPos(); 56 | PackRat pr = m.find((capture ? "" : "-") + fullName,before); 57 | if(pr.filled) { 58 | // System.out.println("use: "+pr); 59 | if(!pr.matched) 60 | return false; 61 | m.subMatches.addAll(pr.subMatches); 62 | m.setTextPos(pr.after); 63 | return true; 64 | } 65 | m.savedMatches.push(m.subMatches); 66 | m.subMatches = new LinkedList(); 67 | //String lookupSave = m.lookup 68 | m.lookStack.push(lookup); 69 | try { 70 | m.lookup = fullName; 71 | boolean b = Matcher.matchAll(pattern, m); 72 | final int after = m.getTextPos(); 73 | if (b) { 74 | if(capture) { 75 | LinkedList lm = new LinkedList(); 76 | lm.add(new Group(fullName, before, after, m.subMatches,m.text)); 77 | m.subMatches = lm; 78 | } 79 | m.savedMatches.peek().addAll(m.subMatches); 80 | } 81 | m.addPackRat(pr, b, after, m.subMatches); 82 | // System.out.println("store: "+pr); 83 | return b; 84 | } finally { 85 | m.subMatches = m.savedMatches.pop(); 86 | //m.lookup = lookupSave; 87 | m.lookStack.pop(); 88 | } 89 | } 90 | 91 | @Override public void visit(Visitor v) { 92 | setup(); 93 | v.startVisit(this); 94 | v.finishVisit(this); 95 | } 96 | 97 | @Override 98 | public String decompile() { 99 | if(capture) { 100 | return "{"+lookup+"}"; 101 | } else { 102 | return "{-"+lookup+"}"; 103 | } 104 | } 105 | @Override 106 | public boolean eq(Object obj) { 107 | Lookup lh = (Lookup)obj; 108 | return lookup.equals(lh.lookup) && capture == lh.capture; 109 | } 110 | 111 | @Override 112 | public List expected(int n) { 113 | setup(); 114 | return pattern.expected(n); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/examples/Generic.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha.examples; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.File; 5 | import java.io.FileWriter; 6 | import java.io.IOException; 7 | import java.io.PrintWriter; 8 | 9 | import edu.lsu.cct.piraha.DebugOutput; 10 | import edu.lsu.cct.piraha.Grammar; 11 | import edu.lsu.cct.piraha.Matcher; 12 | 13 | public class Generic { 14 | public static void usage() { 15 | System.err.println("usage: Generic [--perl|--python|--xml] grammar text"); 16 | System.exit(2); 17 | } 18 | public static void main(String[] args) throws IOException { 19 | if(args.length < 2) 20 | usage(); 21 | Grammar g = new Grammar(); 22 | int n = 0; 23 | int count = 0; 24 | boolean checkOnly = false; 25 | String suffix = ".pegout"; 26 | while(args[n].startsWith("--")) { 27 | if(args[n].equals("--python")) 28 | suffix = ".py"; 29 | else if(args[n].equals("--perl")) 30 | suffix = ".pm"; 31 | else if(args[n].equals("--xml")) 32 | suffix = ".xml"; 33 | else if(args[n].equals("--check-only")) 34 | checkOnly = true; 35 | n++; 36 | } 37 | String grammarFile = args[n++]; 38 | if(!grammarFile.endsWith(".peg")) 39 | usage(); 40 | g.compileFile(new File(grammarFile)); 41 | for(;n= 0) outputFile = outputFile.substring(0,nn); 45 | outputFile = outputFile+suffix; 46 | if(!checkOnly) { 47 | System.out.println("reading file: "+args[n]); 48 | System.out.println("writing file: "+outputFile); 49 | 50 | long inMtime = new File(args[n]).lastModified(); 51 | long outMtime = new File(outputFile).lastModified(); 52 | long pegMtime = new File(grammarFile).lastModified(); 53 | 54 | boolean olderThanSource = inMtime < outMtime; 55 | boolean olderThanPeg = pegMtime < outMtime; 56 | 57 | if(olderThanPeg && olderThanSource) { 58 | System.out.println(" ---> Up to date"); 59 | continue; 60 | } 61 | } 62 | if(outputFile.equals(args[n])) throw new IOException("won't over-write input file "+args[n]); 63 | if(outputFile.equals(grammarFile)) throw new IOException("won't over-write grammar file "+grammarFile); 64 | Matcher m = g.matcher(Grammar.readContents(new File(args[n]))); 65 | if(m.match(0)) { 66 | count++; 67 | if(!checkOnly) { 68 | FileWriter fw = new FileWriter(outputFile); 69 | BufferedWriter bw = new BufferedWriter(fw); 70 | PrintWriter pw = new PrintWriter(bw); 71 | DebugOutput dout = new DebugOutput(pw); 72 | if(suffix.equals(".py")) { 73 | m.dumpMatchesPython("VAR",dout); 74 | dout.print("CONTENTS=\""+m.escText(m.substring())+"\""); 75 | } else if(suffix.equals(".pm")) { 76 | m.dumpMatchesPerl("$VAR",dout); 77 | dout.print("$CONTENTS=\""+m.escText(m.substring())+"\""); 78 | } else if(suffix.equals(".xml")) { 79 | m.dumpMatchesXML(dout,true); 80 | } else if(suffix.equals(".pegout")) { 81 | m.dumpMatches(dout); 82 | } 83 | dout.flush(); 84 | pw.close(); 85 | } 86 | } else { 87 | System.err.println("FAILED: grammar=["+grammarFile+"] file=["+args[n]+"]: "+" "+m.near()); 88 | System.exit(2); 89 | } 90 | } 91 | System.out.println("SUCCESS: files-checked=["+count+"] grammar=["+grammarFile+"]"); 92 | System.exit(0); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /doc/ref.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Piraha Reference Card 8 | 17 | 18 | 19 |

Piraha Reference Card

20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 47 |
Pattern ElementMeaning
[a-fx]Square brackets denote a character class. The pattern at left can match the letters a-f or x. Piraha has no pre-defined character classes and does not support the notions of unions or intersections insided character classes available in Java's regular expression package.
[^a-fx]This is a negated character entity. It will match any character except a-f or x.
[^]This pattern will match any character.
x{n,m}The quantifier pattern will match the pattern x a minimum of n and a maximum of m times.
x{n,}This version of the quantifier pattern will match n or more occurrences of x
x{,m}This version of the quantifier pattern will match at most m occurrences of x
x+A shorthand for x{1,}
x*A shorthand for x{0,}
x?A shorthand for x{0,1}
\1Match the first backreference within this rule. Any of backreferences 1 to 9 may be matched by using \2, \3, etc.
{name}Match the pattern named by name. Save the match in a backreference.
{-name}Match the pattern named by name, but do not capture backreferences.|
(?=x)Match the pattern x as a zero-width lookahead assertion
(?!x)A negative zero-width lookahead assertion for pattern x
(x|y|z)An ordered set of preferred ways to match. If pattern x does not succeed, try pattern y, and then z. There can be any number of alternatives in this set.
\bA word boundary. A word is composed of letters, numbers, and the underscore.
$Match the end of the string.
^Match the beginning of the string.
(?i:x)Ignore case when matching x.
(?-i:x)Do not ignore case when matching x.|
{OpenWhite}A zero-width lookahead assertion that a new indentation level has opened, starting at the current position.
{CloseWhite}A zero-width lookahead assertion that an indentation level has closed, starting at the current position.
{BodyWhite} 45 | Match the current indentation level. Initially this will be zero. It will increase when {OpenWhite} successfully matches, and decrease when {CloseWhite} successfully matches. 46 |
48 | 49 | 50 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/CheckVisitor.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | public class CheckVisitor extends Visitor { 9 | Map patterns; 10 | CheckVisitor child = null; 11 | List canBeZero = new ArrayList(); 12 | String checking; 13 | boolean retry = true; 14 | public Map defaults; 15 | 16 | public CheckVisitor() { 17 | patterns = new HashMap(); 18 | } 19 | private CheckVisitor(Map patterns) { 20 | this.patterns = patterns; 21 | } 22 | 23 | Boolean getDefault(String key) { 24 | if(defaults != null && defaults.containsKey(key)) { 25 | return defaults.get(key); 26 | } 27 | return Boolean.FALSE; 28 | } 29 | 30 | @Override 31 | public Visitor startVisit(Pattern p) { 32 | if(p instanceof Multi) { 33 | CheckVisitor cv = new CheckVisitor(patterns); 34 | child = cv; 35 | cv.checking = checking; 36 | return cv; 37 | } else if(p instanceof Or) { 38 | CheckVisitor cv = new CheckVisitor(patterns); 39 | child = cv; 40 | cv.checking = checking; 41 | return cv; 42 | } else if(p instanceof Seq) { 43 | CheckVisitor cv = new CheckVisitor(patterns); 44 | child = cv; 45 | cv.checking = checking; 46 | return cv; 47 | } else if(p instanceof Lookup) { 48 | CheckVisitor cv = new CheckVisitor(patterns); 49 | child = cv; 50 | Lookup ll = (Lookup)p; 51 | cv.checking = ll.lookup; 52 | return cv; 53 | } 54 | return this; 55 | } 56 | 57 | private boolean andZero() { 58 | for(Boolean b : child.canBeZero) { 59 | if(!b) 60 | return false; 61 | } 62 | return true; 63 | } 64 | 65 | private boolean orZero() { 66 | for(Boolean b : child.canBeZero) { 67 | if(b) 68 | return true; 69 | } 70 | return false; 71 | } 72 | 73 | @Override 74 | public void finishVisit(Pattern p) { 75 | if(p instanceof Multi) { 76 | Multi m = (Multi)p; 77 | if(m.max > 1 && andZero()) { 78 | System.out.println(child.canBeZero); 79 | System.out.println(patterns); 80 | //p.visit(new DebugVisitor()); 81 | throw new ParseException(checking+ 82 | ": cannot have zero length pattern in quantifier: "+p.decompile()); 83 | } 84 | if(m.min==0) 85 | canBeZero.add(Boolean.TRUE); 86 | else 87 | canBeZero.add(andZero()); 88 | } else if(p instanceof Nothing) { 89 | canBeZero.add(Boolean.TRUE); 90 | } else if(p instanceof LookAhead) { 91 | canBeZero.add(Boolean.TRUE); 92 | } else if(p instanceof NegLookAhead) { 93 | canBeZero.add(Boolean.TRUE); 94 | } else if(p instanceof End) { 95 | canBeZero.add(Boolean.TRUE); 96 | } else if(p instanceof Start) { 97 | canBeZero.add(Boolean.TRUE); 98 | } else if(p instanceof Seq) { 99 | //p.visit(new DebugVisitor()); 100 | canBeZero.add(andZero()); 101 | } else if(p instanceof Or) { 102 | canBeZero.add(orZero()); 103 | } else if(p instanceof Lookup) { 104 | Lookup l = (Lookup)p; 105 | if(patterns.containsKey(l.lookup)) { 106 | canBeZero.add(patterns.get(l.lookup)); 107 | } else { 108 | Boolean defaultValue = getDefault(l.lookup); 109 | patterns.put(l.lookup,defaultValue); 110 | l.pattern.visit(child); 111 | boolean res = andZero(); 112 | if(res) { 113 | patterns.put(l.lookup,Boolean.TRUE); 114 | } 115 | if(defaultValue ^ res) 116 | retry = true; 117 | canBeZero.add(res); 118 | } 119 | } else { 120 | canBeZero.add(Boolean.FALSE); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /doc/QuickStart.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Quick Start 8 | 21 | 22 | 23 | 24 |

piraha-peg - QuickStart.wiki

25 | 26 |
27 | 28 |

Quick Introduction to Piraha

29 | 30 |

Piraha uses a subset of the syntax familiar from regular expressions in the java library: 31 | java.util.regex.Pattern doc

32 | 33 |

This document assumes you are already familiar with writing regular expressions using the regular expression engine that comes with java 1.4+.

34 | 35 |

The Piraha API is similar, but instead of simply compiling patterns with a static method, each pattern is named and is compiled into a grammar.

36 | 37 |
 38 | import edu.lsu.cct.piraha.*;
 39 | 
 40 | public class Test {
 41 |   public static void main(String[] args) {
 42 |     // Instantiate a grammar
 43 |     Grammar g = new Grammar();
 44 | 
 45 |     // compile a pattern and name it HELLO
 46 |     g.compile("HELLO","(?i:hello world)");
 47 | 
 48 |     // get a matcher for the pattern named HELLO
 49 |     Matcher m = g.matcher("HELLO","Hello World!");
 50 | 
 51 |     // Look for a match!
 52 |     if(m.matches()) {
 53 |       System.out.println(m.group());
 54 |     }
 55 | 
 56 |   }
 57 | }
 58 | 
59 | 60 |

Basic differences are as follows: 61 |

    62 |
  1. All quantifiers are possessive. What does this mean? It means that Piraha will automatically fail any pattern of the form "(a*)a" regardless of what string of characters you supply for text:

    63 |
     64 | import edu.lsu.cct.piraha.*;
     65 | 
     66 | public class Test2 {
     67 |   public static void main(String[] args) {
     68 |     Grammar g = new Grammar();
     69 |     g.compile("alist","(a*)a");
     70 |     String text = " ... any text ...";
     71 |     Matcher m = g.matcher("alist",text);
     72 |     if(m.matches()) {
     73 |       // can't happen
     74 |       System.out.println(m.group());
     75 |     }
     76 |   }
     77 | }
     78 | 
    79 |
  2. 80 |
  3. 81 |

    All groups are independent non-capturing groups. What does this mean? It means that Piraha will fail when it gets a pattern and text like the one below. The reason is that the first version of the pattern "aaa" will match the first three characters of "aaaa", and that will leave only one "a" unmatched. Neither the sub-pattern "aaa" nor "aa" can match a single "a". However, the string "aaaaa" will succeed and the whole pattern will match.

    82 |
     83 | import edu.lsu.cct.piraha.*;
     84 | 
     85 | public class Test3 {
     86 |   public static void main(String[] args) {
     87 |     Grammar g = new Grammar();
     88 |     g.compile("alist","(aaa|aa)$");
     89 |     Matcher m = g.matcher("alist","aaaa");
     90 |     if(m.matches()) {
     91 |       System.out.println(m.group());
     92 |     }
     93 |   }
     94 | }
     95 | 
    96 |
  4. 97 |
  5. 98 |

    The Pattern element {name} references a pattern element by name, and a pattern can reference itself recursively. This means that it's easy to matched balanced parenthesis in Piraha. In this example, we match a balanced angle bracket.

    99 |
    100 | import edu.lsu.cct.piraha.*;
    101 | 
    102 | public class Test4 {
    103 |   public static void main(String[] args) {
    104 |     Grammar g = new Grammar();
    105 |     g.compile("d","<{d}>|[a-z]+");
    106 |     Matcher m = g.matcher("d","<<bar>>extra");
    107 |     if(m.find()) {
    108 |       System.out.println(m.group()); // prints <<bar>>
    109 |     }
    110 |   }
    111 | }
    112 |
  6. 113 |
114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Or.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | import java.lang.reflect.Array; 4 | import java.util.ArrayList; 5 | import java.util.LinkedList; 6 | import java.util.List; 7 | 8 | public class Or extends Pattern { 9 | List patterns; 10 | boolean ignCase,igcShow; 11 | 12 | public Or(boolean ignCase,boolean igcShow) { 13 | patterns = new ArrayList(); 14 | this.ignCase = ignCase; 15 | this.igcShow = igcShow; 16 | } 17 | public Or(Pattern...pats) { 18 | this(false,false,pats); 19 | } 20 | public Or(boolean ignCase,boolean igcShow,Pattern...pats) { 21 | patterns = new ArrayList(pats.length); 22 | for(int i=0;i expecteds = new ArrayList(); 33 | StringBuilder whSave = new StringBuilder(); 34 | List whListSave = new ArrayList(); 35 | whSave.append(m.white); 36 | for(int n : m.whiteThresholds) 37 | whListSave.add(n); 38 | for(int i=0;i sz) { 42 | m.subMatches.removeLast(); 43 | nsz--; 44 | } 45 | 46 | // Possibly this can be optimized. 47 | m.white.setLength(0); 48 | m.white.append(whSave); 49 | m.whiteThresholds.clear(); 50 | for(int n : whListSave) 51 | m.whiteThresholds.add(n); 52 | 53 | boolean b = Matcher.matchAll(patterns.get(i),m); 54 | if(b) 55 | return true; 56 | else if(m.expected != null) 57 | expecteds.add(m.expected); 58 | } 59 | int max = -1; 60 | for(Expected e : expecteds) { 61 | if(e.pos > max && e.possibilities.size()>0) { 62 | max = e.pos; 63 | } 64 | } 65 | Expected ex = new Expected(); 66 | ex.epos = max; 67 | for(Expected e : expecteds) { 68 | if(e.pos == max) { 69 | for(String s : e.possibilities) { 70 | ex.possibilities.add(s); 71 | } 72 | } 73 | } 74 | m.expected(ex); 75 | // new version 76 | // LinkedList save = m.subMatches; 77 | // m.subMatches = new LinkedList(); 78 | // LinkedList best = m.subMatches; 79 | // int bestPos = -1; 80 | // for(int i=0;i(); 83 | // boolean b = Matcher.matchAll(patterns.get(i), m); 84 | // if(b) { 85 | // int np = m.getTextPos(); 86 | // if(np > bestPos) { 87 | // best = m.subMatches; 88 | // bestPos = np; 89 | // } 90 | // } 91 | // } 92 | // if(bestPos >= 0) { 93 | // save.addAll(best); 94 | // m.subMatches = save; 95 | // m.setTextPos(bestPos); 96 | // return true; 97 | // } 98 | return false; 99 | } 100 | 101 | 102 | @Override 103 | public void visit(Visitor v) { 104 | Visitor vv = v.startVisit(this); 105 | for(Pattern x : patterns) { 106 | x.visit(vv); 107 | } 108 | v.finishVisit(this); 109 | } 110 | 111 | 112 | @Override 113 | public String decompile() { 114 | if(!igcShow && patterns.size()==1) { 115 | return patterns.get(0).decompile(); 116 | } 117 | StringBuffer sb = new StringBuffer(); 118 | sb.append("("); 119 | if (igcShow) { 120 | if (ignCase) 121 | sb.append("?i:"); 122 | else 123 | sb.append("?-i:"); 124 | } 125 | for(int i=0;i 0) sb.append("|"); 127 | sb.append(patterns.get(i).decompile()); 128 | } 129 | sb.append(")"); 130 | return sb.toString(); 131 | } 132 | 133 | @Override 134 | public boolean eq(Object obj) { 135 | Or or = (Or)obj; 136 | if(or.patterns.size() != patterns.size()) 137 | return false; 138 | for(int i=0;i expected(int n) { 147 | List ex = new ArrayList(); 148 | for(int i=0;i li = patterns.get(i).expected(0); 150 | for(String s : li) 151 | ex.add(s); 152 | } 153 | return ex; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Matcher.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.Iterator; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.Stack; 10 | 11 | public class Matcher extends Group { 12 | int maxId; 13 | private int textPos; 14 | int maxTextPos; 15 | Pattern pattern; 16 | public String lookup, maxLookup; 17 | public boolean didMatch; 18 | Stack lookStack = new Stack(); 19 | 20 | public boolean find() { 21 | if(end == 0) 22 | return find(0); 23 | else if(begin == end) 24 | return find(end+1); 25 | else 26 | return find(end); 27 | } 28 | public boolean find(int pos) { 29 | for(int i=pos;i maxTextPos) { 43 | maxTextPos = newTextPos; 44 | maxLookup = lookStack.toString(); 45 | expected = null; 46 | } 47 | textPos = newTextPos; 48 | } 49 | public final void incrTextPos(int incr) { 50 | setTextPos(getTextPos()+incr); 51 | } 52 | public boolean match(int pos) { 53 | expected = null; 54 | textPos = pos; 55 | maxTextPos = pos; 56 | savedMatches = new Stack>(); 57 | rats = new HashMap(); 58 | subMatches = new LinkedList(); 59 | if(matchAll(pattern,this)) { 60 | begin = pos; 61 | end = textPos; 62 | didMatch = true; 63 | return true; 64 | } 65 | begin = -1; 66 | end = -1; 67 | didMatch = false; 68 | return false; 69 | } 70 | static boolean matchAll(Pattern pattern, Matcher m) { 71 | /*for(Pattern x = pattern;x != null;x = x.next) { 72 | if(!x.match(m)) 73 | return false; 74 | } 75 | return true;*/ 76 | return pattern.match(m); 77 | } 78 | 79 | /** 80 | * Just here to make things more similar to java.util.Regex 81 | * @return 82 | */ 83 | public Group group() { 84 | return new Group(patternName,getBegin(),getEnd(),subMatches,text); 85 | } 86 | 87 | public String getText() { 88 | return text; 89 | } 90 | 91 | public Near near() { 92 | Near near = new Near(); 93 | near.text = text; 94 | near.rule = maxLookup; 95 | near.expected = expected; 96 | for(int i=0;i mappings = null; 111 | StringBuffer sb = null; 112 | int lastAppend = 0, lastDelta = 0; 113 | public void startReplacement() { 114 | sb = new StringBuffer(); 115 | mappings = new ArrayList(); 116 | lastAppend = 0; 117 | lastDelta = 0; 118 | } 119 | private void addMapping(int from,int to) { 120 | if(from < 0) return; 121 | int delta = to - from; 122 | if(delta != lastDelta) { 123 | mappings.add(new Mapping(from,delta)); 124 | lastDelta = delta; 125 | } 126 | } 127 | public void appendReplacement(String s) { 128 | sb.append(text.substring(lastAppend,begin)); 129 | addMapping(begin,sb.length()); 130 | addMapping(end-1,sb.length()+s.length()-1); 131 | sb.append(s); 132 | lastAppend = end; 133 | } 134 | public String appendTail() { 135 | if(lastAppend < text.length()) 136 | sb.append(text.substring(lastAppend)); 137 | lastAppend = text.length(); 138 | return sb.toString(); 139 | } 140 | public int mapping(int from) { 141 | int delta = 0; 142 | for(Mapping m : mappings) { 143 | if(m.from > from) 144 | break; 145 | delta = m.delta; 146 | } 147 | //System.out.println("delta["+from+"]="+delta); 148 | return from+delta; 149 | } 150 | public Pattern getPattern() { 151 | return pattern; 152 | } 153 | Map rats; 154 | 155 | public PackRat find(String name,int pos) { 156 | PackRat pr = new PackRat(name,pos); 157 | PackRat res = rats.get(pr); 158 | if(res == null) { 159 | rats.put(pr, pr); 160 | return pr; 161 | } else { 162 | res.filled = true; 163 | return res; 164 | } 165 | } 166 | 167 | public void addPackRat(PackRat pr,boolean b, int after, 168 | LinkedList subMatches) { 169 | pr.matched = b; 170 | if(b) { 171 | pr.subMatches = subMatches; 172 | } 173 | pr.after = after; 174 | } 175 | 176 | // data for white space patterns 177 | public StringBuilder white = new StringBuilder(); 178 | public List whiteThresholds = new ArrayList(); 179 | 180 | Expected expected; 181 | 182 | public void expected(Expected ex) { 183 | if(textPos == maxTextPos || ex.epos == maxTextPos) { 184 | ex.build(maxTextPos); 185 | if(ex.possibilities.size()>0) 186 | expected = ex; 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Find.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | //import edu.lsu.cct.piraha.examples.Calc; 7 | 8 | public class Find { 9 | static Grammar g = new Grammar(); 10 | static { 11 | g.compile("index","[0-9]+"); 12 | g.compile("quoted", "([^'\\\\]|\\\\[^\n])+"); 13 | g.compile("value","[a-zA-Z0-9_]+|'{quoted}'"); 14 | g.compile("op","![=~]|=~?|[<>]=?"); 15 | g.compile("name","[a-zA-Z0-9_*]+"); 16 | g.compile("elem","{name}(\\[{index}\\]|)({op}{value}|)"); 17 | g.compile("elemseq","\\.{elem}(\\|{elem})*"); 18 | g.compile("quant","\\({elemseq}\\)\\*"); 19 | g.compile("pat", "({quant}|{elemseq})({pat}|{quant}|)"); 20 | g.compile("full", "{pat}$"); 21 | } 22 | Group patc; 23 | public Find(String pattern) { 24 | Matcher m = g.matcher(pattern); 25 | if(!m.matches()) 26 | throw new RuntimeException(m.near().toString()); 27 | patc = m.group().group(0); 28 | } 29 | public List find(Group g) { 30 | List li = new ArrayList(); 31 | find(patc,g,li); 32 | return li; 33 | } 34 | private void find(Group p,Group g,List li) { 35 | String pn = p.getPatternName(); 36 | if("pat".equals(pn)) { 37 | List li2 = p.groupCount()==1 ? li :new ArrayList(); 38 | find(p.group(0),g,li2); 39 | if(p.groupCount()==2) { 40 | for(Group gg : li2) { 41 | for(int i=0;i0 ? p.group(2).group(0).substring() : p.group(2).substring(); 61 | if("=".equals(op)) { 62 | if(!g.substring().equals(val)) { 63 | add = false; 64 | } 65 | } else if("!=".equals(op)) { 66 | if(g.substring().equals(val)) { 67 | add = false; 68 | } 69 | } else if("=~".equals(op)) { 70 | if(wildmatch(val,g.substring()) != g.substring().length()) { 71 | add = false; 72 | } 73 | } else if("!~".equals(op)) { 74 | if(wildmatch(val,g.substring()) == g.substring().length()) { 75 | add = false; 76 | } 77 | } else { 78 | if("<".equals(op)) { 79 | int intstr = Integer.parseInt(g.substring()); 80 | if(intstr >= Integer.parseInt(val)) { 81 | add = false; 82 | } 83 | } else if(">".equals(op)) { 84 | int intstr = Integer.parseInt(g.substring()); 85 | if(intstr <= Integer.parseInt(val)) { 86 | add = false; 87 | } 88 | } else if("<=".equals(op)) { 89 | int intstr = Integer.parseInt(g.substring()); 90 | if(intstr > Integer.parseInt(val)) { 91 | add = false; 92 | } 93 | } else if(">=".equals(op)) { 94 | int intstr = Integer.parseInt(g.substring()); 95 | if(intstr < Integer.parseInt(val)) { 96 | add = false; 97 | } 98 | } 99 | } 100 | } 101 | if(add) 102 | li.add(g); 103 | } else if("quant".equals(pn)) { 104 | List lg = new ArrayList(); 105 | find(p.group(0),g,lg); 106 | if(lg.size()>0) { 107 | if(p.groupCount()==1) { 108 | for(Group g2 : lg) { 109 | li.add(g2); 110 | } 111 | } 112 | for(int i=0;i= 0) return n; 132 | j++; 133 | } else if(pattern.charAt(i) == text.charAt(j)) { 134 | i++; j++; 135 | } else { 136 | return -1; 137 | } 138 | } 139 | if(i==pattern.length()) 140 | return j; 141 | else 142 | return -1; 143 | } 144 | 145 | 146 | public static void check(boolean b) { 147 | if(!b) throw new Error(); 148 | } 149 | /* 150 | public static void main(String[] args) { 151 | Grammar tg = Calc.makeMath(); 152 | Matcher mg = tg.matcher("3+9*(1+9)-4"); 153 | mg.matches(); 154 | Group res = mg.group(); 155 | res.dumpMatches(); 156 | 157 | Find f = new Find("(.*)*.num<=3"); 158 | List fgl = f.find(res); 159 | check(fgl.size()==2); 160 | 161 | f = new Find("(.*)*.num"); 162 | fgl = f.find(res); 163 | check(fgl.size()==5); 164 | 165 | f = new Find("(.*)*.addop|mulop"); 166 | fgl = f.find(res); 167 | check(fgl.size()==4); 168 | 169 | System.out.println("test complete"); 170 | } 171 | */ 172 | } 173 | 174 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/examples/McLachlan_ADMQuantities.kranc: -------------------------------------------------------------------------------- 1 | @THORN MLP_ADMQuantities 2 | 3 | # Global settings 4 | 5 | @DEFINE derivOrder = 4 6 | @DEFINE useGlobalDerivs = False 7 | @DEFINE evolutionTimelevels = 3 8 | @DEFINE addMatter = 1 9 | 10 | 11 | 12 | # Finite differencing operators 13 | 14 | @DERIVATIVES 15 | # this list requires commas 16 | PDstandardNth[i_] -> StandardCenteredDifferenceOperator[1,derivOrder/2,i], 17 | PDstandardNth[i_,i_] -> StandardCenteredDifferenceOperator[2,derivOrder/2,i], 18 | PDstandardNth[i_,j_] -> StandardCenteredDifferenceOperator[1,derivOrder/2,i] * 19 | StandardCenteredDifferenceOperator[1,derivOrder/2,j], 20 | @END_DERIVATIVES 21 | 22 | @DEFINE PD = PDstandardNth 23 | 24 | 25 | 26 | # Tensors 27 | 28 | @TENSORS 29 | x, 30 | phi, {gt[la,lb],la,lb}, Xt, trK, {At[la,lb],la,lb}, alpha, beta, 31 | T00, T0, {T[la,lb],la,lb}, 32 | detgt, {gtu[ua,ub],ua,ub}, {dgtu[ua,ub,lc],ua,ub}, 33 | {Gtl[la,lb,lc],lb,lc}, Gtlu, {Gt[ua,lb,lc],lb,lc}, 34 | Xtn, {Rt[la,lb],la,lb}, trRt, ephi, Atm, 35 | rho, S, 36 | Madm, Jadm 37 | @END_TENSORS 38 | 39 | @GROUPS 40 | Madm -> ML_Madm 41 | Jadm[la] -> ML_Jadm 42 | @END_GROUPS 43 | 44 | @EXTRA_GROUPS 45 | grid::coordinates -> {x, y, z, r} 46 | ML_BSSN::ML_log_confac -> {phi} 47 | ML_BSSN::ML_metric -> {gt11, gt12, gt13, gt22, gt23, gt33} 48 | ML_BSSN::ML_Gamma -> {Xt1, Xt2, Xt3} 49 | ML_BSSN::ML_trace_curv -> {trK} 50 | ML_BSSN::ML_curv -> {At11, At12, At13, At22, At23, At33} 51 | ML_BSSN::ML_lapse -> {alpha} 52 | ML_BSSN::ML_shift -> {beta1, beta2, beta3} 53 | TmunuBase::stress_energy_scalar -> {eTtt} 54 | TmunuBase::stress_energy_vector -> {eTtx, eTty, eTtz} 55 | TmunuBase::stress_energy_tensor -> {eTxx, eTxy, eTxz, eTyy, eTyz, eTzz} 56 | @END_EXTRA_GROUPS 57 | 58 | # Use the CartGrid3D variable names 59 | @DEFINE x1 = x 60 | @DEFINE x2 = y 61 | @DEFINE x3 = z 62 | 63 | # Use the TmunuBase variable names 64 | @DEFINE T00 = eTtt 65 | @DEFINE T01 = eTtx 66 | @DEFINE T02 = eTty 67 | @DEFINE T03 = eTtz 68 | @DEFINE T11 = eTxx 69 | @DEFINE T12 = eTxy 70 | @DEFINE T22 = eTyy 71 | @DEFINE T13 = eTxz 72 | @DEFINE T23 = eTyz 73 | @DEFINE T33 = eTzz 74 | 75 | 76 | 77 | # Calculations 78 | 79 | @DEFINE pi = N[Pi,40] 80 | @DEFINE detgtExpr = Det [MatrixOfComponents [gt[la,lb]]] 81 | 82 | @CALCULATION ML_ADMQuantities 83 | @Schedule Automatic 84 | @Where Interior 85 | @SHORTHANDS 86 | detgt, gtu[ua,ub], dgtu[ua,ub,lc], 87 | Gtl[la,lb,lc], Gtlu[la,lb,uc], Gt[ua,lb,lc], 88 | Xtn[ua], Rt[la,lb], trRt, 89 | Atm[ua,lb], 90 | ephi, 91 | rho, S[la], 92 | @END_SHORTHANDS 93 | @EQUATIONS 94 | # this list requires commas 95 | detgt -> 1 (* detgtExpr *), 96 | gtu[ua,ub] -> 1/detgt detgtExpr MatrixInverse [gt[ua,ub]], 97 | dgtu[ua,ub,lc] -> - gtu[ua,ud] gtu[ub,ue] PD[gt[ld,le],lc], 98 | Gtl[la,lb,lc] -> 1/2 99 | (PD[gt[lb,la],lc] + PD[gt[lc,la],lb] - PD[gt[lb,lc],la]), 100 | Gtlu[la,lb,uc] -> gtu[uc,ud] Gtl[la,lb,ld], 101 | Gt[ua,lb,lc] -> gtu[ua,ud] Gtl[ld,lb,lc], 102 | 103 | (* The conformal connection functions calculated from the conformal metric, 104 | used instead of Xt where no derivatives of Xt are taken *) 105 | Xtn[ui] -> gtu[uj,uk] Gt[ui,lj,lk], 106 | 107 | (* PRD 62, 044034 (2000), eqn. (18) *) 108 | Rt[li,lj] -> - (1/2) gtu[ul,um] PD[gt[li,lj],ll,lm] 109 | + (1/2) gt[lk,li] PD[Xt[uk],lj] 110 | + (1/2) gt[lk,lj] PD[Xt[uk],li] 111 | + (1/2) Xtn[uk] Gtl[li,lj,lk] 112 | + (1/2) Xtn[uk] Gtl[lj,li,lk] 113 | + (+ Gt[uk,li,ll] Gtlu[lj,lk,ul] 114 | + Gt[uk,lj,ll] Gtlu[li,lk,ul] 115 | + Gt[uk,li,ll] Gtlu[lk,lj,ul]), 116 | 117 | trRt -> gtu[ua,ub] Rt[la,lb], 118 | 119 | ephi -> IfThen [conformalMethod, 1/Sqrt[phi], Exp[phi]], 120 | 121 | Atm[ua,lb] -> gtu[ua,uc] At[lc,lb], 122 | 123 | (* Matter terms *) 124 | 125 | (* rho = n^a n^b T_ab *) 126 | rho -> addMatter 127 | 1/alpha^2 (T00 - 2 beta[ui] T0[li] + beta[ui] beta[uj] T[li,lj]), 128 | 129 | (* S_i = -p^a_i n^b T_ab, where p^a_i = delta^a_i + n^a n_i *) 130 | S[li] -> addMatter (-1/alpha (T0[li] - beta[uj] T[li,lj])), 131 | 132 | (* ADM quantities *) 133 | (* See PRD 66, 084026 (2002) *) 134 | 135 | Madm -> 1/(16 pi) 136 | (+ ephi^5 (+ 16 pi addMatter rho 137 | + Atm[ua,lb] Atm[ub,la] 138 | - 2/3 trK^2) 139 | - gtu[ua,ub] Gt[uc,la,ld] Gtlu[lb,lc,ud] 140 | + (1 - ephi) trRt), 141 | 142 | Jadm[li] -> 1/(16 pi) Eps[li,lj,uk] ephi^6 143 | (+ 2 Atm[uj,lk] 144 | + 16 pi x[uj] S[lk] 145 | + 4/3 x[uj] PD[trK,lk] 146 | - x[uj] dgtu[ul,um,lk] At[ll,lm]), 147 | @END_EQUATIONS 148 | @END_CALCULATION 149 | 150 | 151 | 152 | # Parameters etc. 153 | 154 | @INHERITED_IMPLEMENTATION ML_BSSN 155 | @INHERITED_IMPLEMENTATION TmunuBase 156 | 157 | @INT_PARAMETER conformalMethod 158 | @Description "Treatment of conformal factor" 159 | @ALLOWED_VALUES 160 | "0" -> "phi method", 161 | "1" -> "W method", 162 | @END_ALLOWED_VALUES 163 | @Default 0 164 | @END_INT_PARAMETER 165 | 166 | 167 | 168 | @END_THORN 169 | -------------------------------------------------------------------------------- /doc/GrammarFiles.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Grammar Files 8 | 21 | 22 | 23 | 24 |

piraha-peg - Grammar Files

25 | 26 |
27 | 28 |

The basic pattern elements were documented in the Reference Card, and the basic 29 | API was documented in the Quick Start. These documents show you how to compile 30 | and use Piraha pattern expressions one by one. However, if you are describing a complex grammar, it is often 31 | more convenient to use a Grammar File.

32 |

A Grammar File consists of pattern definitions of the form "name=value," where the name is a c-identifier 33 | (i.e. a sequence of letters, numbers, or the underscore), and value is a pattern or pegular expression. 34 |

    35 |
  1. 36 | The Grammar file uses the hash (#) as a comment character, so if you use it within a pattern you should 37 | escape it (i.e. precede it with a backslash, \#).
  2. 38 |
  3. 39 | The Grammar file requires the definition of a special pattern called "skipper." The skipper typically reads 40 | whitespace and/or comment characters. The skipper is inserted whenever there is a whitespace in your pattern. 41 | Therefore, if you want a literal space in a Grammar File, you need to escape it as well.
  4. 42 |
  5. 43 | The last pattern in a Grammar File is the default. While you can ask the pattern matcher to use any pattern 44 | in the file for matching, it is generally assumed the last one will be used.
  6. 45 |
46 | 47 |

It is not necessary to write Java code to make use of a Grammar File. Piraha comes with a generic grammar 48 | compiler. You may invoke it as follows:

49 |
 50 |   $ java -cp piraha.jar edu.lsu.cct.piraha.examples.Generic peg-file src-file1 src-file2 ...
 51 | 
52 |

The result of running the command will be a series of files with the suffix "pegout." They will provide 53 | a parse tree for your grammar in outline form. If you would prefer to see xml, you can ask for that. 54 | In the example below, we provide eqn.peg as an input file:

55 |
 56 | skipper = [ \t\n]*
 57 | num = [0-9]+
 58 | mulop = [*/]
 59 | mul = {num}( {mulop} {num})*
 60 | addop = [+-]
 61 | add = {mul}( {addop} {mul})*
 62 | math = ( {add} )$
 63 | 
64 |

We also provide eqn.in as an example source for the input file. 65 |

 66 | 10+9-2*3-1
 67 | 
68 |

We now run the command with --xml:

69 |
 70 |  $ java -cp piraha.jar edu.lsu.cct.piraha.examples.Generic --xml eqn.peg eqn.in
 71 |  reading file: eqn.in
 72 |  writing file: eqn.xml
 73 |  SUCCESS: files-checked=[1] grammar=[eqn.peg]
 74 | 
75 |

And obtain the output:

76 |
 77 | <math start='0' end='11' line='1'>
 78 |  <add start='0' end='10' line='1'>
 79 |   <mul start='0' end='2' line='1'>
 80 |    <num start='0' end='2' line='1'>10</num>
 81 |   </mul>
 82 |   <addop start='2' end='3' line='1'>+</addop>
 83 |   <mul start='3' end='4' line='1'>
 84 |    <num start='3' end='4' line='1'>9</num>
 85 |   </mul>
 86 |   <addop start='4' end='5' line='1'>-</addop>
 87 |   <mul start='5' end='8' line='1'>
 88 |    <num start='5' end='6' line='1'>2</num>
 89 |    <mulop start='6' end='7' line='1'>*</mulop>
 90 |    <num start='7' end='8' line='1'>3</num>
 91 |   </mul>
 92 |   <addop start='8' end='9' line='1'>-</addop>
 93 |   <mul start='9' end='10' line='1'>
 94 |    <num start='9' end='10' line='1'>1</num>
 95 |   </mul>
 96 |  </add>
 97 | <text>10+9-2*3-1
</text>
 98 | </math>
 99 | 
100 |

Note that pattern names become xml node names, and each node has 101 | a start position in the text, and end position, a line number, 102 | and either child nodes, or the text matched by the node. 103 | In addition, the full input text is captured in the text node 104 | at the end.

105 |

Other flags to Generic include --perl, which generates Perl5 data 106 | structures as output, or --python, which generates Python data 107 | structures.

108 | 109 |

If you want to compile a grammar file within Java, you can do it 110 | like this: 111 |

112 | import edu.lsu.cct.piraha.*;
113 | import java.io.*;
114 | 
115 | public class Gram {
116 |   public static void main(String[] args) throws Exception {
117 |     Grammar g = new Grammar();
118 |     g.compileFile(new File("eqn.peg"));
119 |     String contents = Grammar.readContents(new File("eqn.in"));
120 |     Matcher m = g.matcher(contents);
121 |     if(m.match(0)) { // test for match at 0
122 |       m.dumpMatchesXML(); // Write matches to the screen in XML
123 |     }
124 |   }
125 | }
126 | 
127 | 128 | 129 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/examples/Test.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha.examples; 2 | 3 | import edu.lsu.cct.piraha.DebugOutput; 4 | import edu.lsu.cct.piraha.DebugVisitor; 5 | import edu.lsu.cct.piraha.Grammar; 6 | import edu.lsu.cct.piraha.Matcher; 7 | import edu.lsu.cct.piraha.ParseException; 8 | import edu.lsu.cct.piraha.Pattern; 9 | 10 | /** 11 | * Just a handful of basic tests 12 | * @author sbrandt 13 | * 14 | */ 15 | public class Test { 16 | 17 | 18 | public static void test(String pat, String text, int matchsize) { 19 | Grammar g = new Grammar(); 20 | Pattern p = g.compile("d", pat);//"a(0|1|2|3|4|5|6a|7|8|9)*x"); 21 | String pdc = p.decompile(); 22 | Pattern pe = null; 23 | DebugVisitor dv = new DebugVisitor(); 24 | try { 25 | pe = g.compile("e",pdc); 26 | } catch (ParseException e) { 27 | p.visit(dv); 28 | DebugOutput.out.println("pat="+pat); 29 | DebugOutput.out.println("pdc="+pdc); 30 | throw new RuntimeException("parser decompile error "+pat+" != "+pdc,e); 31 | } 32 | if(!pe.equals(p)) { 33 | p.visit(dv); 34 | pe.visit(dv); 35 | throw new RuntimeException("decompile error "+pat+" != "+pe.decompile()); 36 | } 37 | Matcher m = g.matcher(text);//"a6ax"); 38 | //m.getPattern().diag(); 39 | m.getPattern().visit(new DebugVisitor(DebugOutput.out)); 40 | boolean found = m.matches(); 41 | DebugOutput.out.print("match "+text+" using "+pat+" => "+found+" "); 42 | if(found) { 43 | DebugOutput.out.print("["+m.getBegin()+","+m.getEnd()+"]"); 44 | m.dumpMatchesXML(); 45 | } 46 | DebugOutput.out.println(); 47 | DebugOutput.out.println(); 48 | if(found) assert(matchsize == m.getEnd() - m.getBegin()); 49 | else assert(matchsize == -1); 50 | } 51 | 52 | static boolean assertOn = false; 53 | 54 | static public boolean turnAssertOn() { 55 | assertOn = true; 56 | return true; 57 | } 58 | 59 | public static void main(String[] args) throws Exception { 60 | assert(turnAssertOn()); 61 | if(!assertOn) 62 | throw new RuntimeException("Assertions are not enabled"); 63 | 64 | test("(\\[[^\\]]*)+","[",1); 65 | test("a(0|1|2|3|4|5|6a|7|8|9)*x","a6a72x",6); 66 | test("a(0|1|2|3|4|5|6a|7|8|9)*x","a6a72",-1); 67 | test("<{d}>|x","<>",5); 68 | test("<{d}>|x","<",-1); // this one should fail, no balancing > 69 | test("[a-z0-9]+","abcd0123",8); 70 | test("(foo|)","bar",0); 71 | test("(?i:foo)","bar",-1); // should fail 72 | test("[^\n]+","foo",3); 73 | test("[\u0000-\uffff]*","foo",3); 74 | test("(?=foo)foo","foo",3); 75 | test("(?!foo)foo","foo",-1); 76 | test("(?i:foo)","FOO",3); 77 | test("(?-i:foo)","foo",3); 78 | test("[^]+","foobar",6); 79 | test("[^](\\b|{d})","foo.bar",3); 80 | test("(?i:[a-z]+)","FOO",3); 81 | test("(?i:[fo]+)","FOO",3); 82 | test("\\t","\t",1); 83 | test("\t","\t",1); 84 | test("[a-c]+","bbca",4); 85 | test("(aaa|aa)aa$","aaaa",-1); 86 | test("a*a","aaaa",-1); 87 | test("\\b(?!apple\\b)[a-z]+\\b","grape",5); 88 | test("(?!foo)","foo",-1); 89 | test("(?=foo)","foo",0); 90 | test("(?=[^\\.]*\\.)","abcd.",0); 91 | 92 | { 93 | System.out.println("CHECK"); 94 | Grammar g = new Grammar(); 95 | g.compile("name","[a-z]+"); 96 | g.compile("plus","{name}\\+"); 97 | g.compile("minus", "{name}-"); 98 | g.compile("all","{plus}|{minus}|{name}"); 99 | Matcher mm = g.matcher("foo*"); 100 | mm.matches(); 101 | assert(mm.substring().equals("foo")); 102 | } 103 | 104 | { 105 | Grammar g = new Grammar(); 106 | g.compile("int","[0-9]+"); 107 | // Packrat allows this expression to match 108 | g.compile("add", "{add}\\+{add}|{int}"); 109 | Matcher mm = g.matcher("9+20"); 110 | mm.matches(); 111 | assert(mm.group().substring().equals("9+20")); 112 | } 113 | 114 | try { 115 | test("a**","a",1); 116 | assert(false); 117 | } catch (ParseException e) { 118 | ; 119 | } 120 | 121 | Grammar g; 122 | Matcher m; 123 | g = new Grammar(); 124 | g.compile("x", "(?i:[xyz])"); 125 | g.compile("y","(?i:{x}a\\1)"); 126 | m = g.matcher("y", "Xax"); 127 | assert(m.find()); 128 | 129 | // Check name matches 130 | // g = new Grammar(); 131 | // g.compile("name", "[a-zA-Z_][a-zA-Z0-9_]*"); 132 | // g.compile("block", "\\{ *((decl +{name} *;|use +{$name} *;|{block}) *)*\\}"); 133 | // g.diag(DebugOutput.out); 134 | // m = g.matcher("block", "{decl b;{ decl a; use a;} {{use a;}}}"); 135 | //assert(m.matches()); 136 | 137 | Grammar xml = new Grammar(); 138 | xml.compileFile(Test.class.getResourceAsStream("xml.peg")); 139 | m = xml.matcher("doc",""); 140 | if(!m.matches()) { 141 | System.out.println(m.near()); 142 | assert(false); 143 | } 144 | 145 | // Test composabality 146 | g = new Grammar(); 147 | g.importGrammar("math", Calc.makeMath()); 148 | g.compile("vector", "{math:expr}(,{math:expr}){0,}"); 149 | m = g.matcher("vector","1+2,(8+3)*9-4,4,9+7"); 150 | assert(m.matches()); 151 | 152 | g.compile("eol", "\n"); 153 | g.compile("line","line"); 154 | g.compile("line_eol","{line}{eol}"); 155 | m = g.matcher("line_eol","line\n"); 156 | assert(m.matches()); 157 | 158 | test("[\\a-c]+","abab",4); 159 | test("[a-\\c]+","abab",4); 160 | test("[\\a-\\c]+","acb",3); 161 | test("[a-]+","a-a-",4); 162 | test("[\\a-]+","a-a-",4); 163 | test("[\\a\\-]+","a-a-",4); 164 | test("[a\\-]+","a-a-",4); 165 | test("[-a]+","a-a-",4); 166 | test("\\[a\\]","[a]",3); 167 | test("(\\[(\\\\[^]|[^\\]\\\\])*\\]|\\\\[^]|[^\b-\n\r ])+","xxx",3); 168 | test("[a-zA-Z0-9\\.\\*]+|\"[^\"]*\"|'[^']*'","\"Failed password\"",17); 169 | test("(b{brk}|.)*","aaabaaa",4); 170 | test("[^ \t\r\n\b]+","abc",3); 171 | test("(?i:ab(c|g)ef)","ABCEF",5); 172 | test("[0-67-9]+","1234",4); 173 | test("{OpenWhite}"," ",0); 174 | test("{OpenWhite}","",-1); 175 | test("{OpenWhite}{BodyWhite}"," \n ",1); 176 | test("{OpenWhite}{BodyWhite}\n{CloseWhite}x[ \t]*"," \nx",4); 177 | test("{OpenWhite}({BodyWhite}y\n{OpenWhite}{BodyWhite}x|{BodyWhite}y)"," y\n z",2); 178 | test("{OpenWhite}({BodyWhite}y\n{OpenWhite})*"," y\n y\n y\n",7); 179 | 180 | g = new Grammar(); 181 | g.compile("import", "import"); 182 | g.compile("pat","((?!a|{import}_).)+"); 183 | m = g.matcher("pat","foo_import_bar"); 184 | m.find(); 185 | System.out.println(m.substring()); 186 | System.out.println("All tests complete"); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/examples/McLachlan_BSSN.kranc: -------------------------------------------------------------------------------- 1 | @THORN MLP_BSSN 2 | 3 | 4 | # Global settings 5 | 6 | @DEFINE derivOrder = 4 7 | @DEFINE useGlobalDerivs = False 8 | @DEFINE evolutionTimelevels = 3 9 | @DEFINE addMatter = 1 10 | 11 | 12 | 13 | # Finite differencing operators 14 | 15 | @DERIVATIVES 16 | PDstandardNth[i_] -> StandardCenteredDifferenceOperator[1,derivOrder/2,i], 17 | PDstandardNth[i_,i_] -> StandardCenteredDifferenceOperator[2,derivOrder/2,i], 18 | PDstandardNth[i_,j_] -> StandardCenteredDifferenceOperator[1,derivOrder/2,i] * 19 | StandardCenteredDifferenceOperator[1,derivOrder/2,j], 20 | PDdissipationNth[i_] -> 21 | spacing[i]^(derivOrder+1) / 2^(derivOrder+2) * 22 | StandardCenteredDifferenceOperator[derivOrder+2,derivOrder/2+1,i], 23 | @END_DERIVATIVES 24 | 25 | @JACOBIAN {PD, FD, J, dJ} 26 | 27 | 28 | 29 | @TENSORS 30 | normal, tangentA, tangentB, dir, xx, rr, th, ph, J, 31 | admalpha, admdtalpha, admbeta, admdtbeta, H, M, 32 | detg, trR, Km, trK, cdphi, 33 | phi, Xt, Xtn, alpha, A, beta, B, Atm, trA, trAts, 34 | dottrK, dotXt, cXt, cS, cA, 35 | e4phi, em4phi, ddetg, detgt, ddetgt, Gtlu, 36 | T00, T0, rho, S, x, y, z, r, epsdiss, 37 | 38 | admg[la,lb], admK[la,lb], 39 | g[la,lb], K[la,lb], R[la,lb], cdphi2[la,lb], 40 | gt[la,lb], At[la,lb], Ats[la,lb], Rt[la,lb], Rphi[la,lb], T[la,lb], 41 | 42 | {dJ[ua,lb,lc], lb, lc}, 43 | {G[ua,lb,lc], lb, lc}, 44 | {Gtl[la,lb,lc], lb, lc}, 45 | {Gt[ua,lb,lc], lb, lc}, 46 | {gK[la,lb,lc], la, lb}, 47 | 48 | gu[ua,ub], gtu[ua,ub], Atu[ua,ub], 49 | {dgtu[ua,ub,lc], {ua, ub}}, 50 | {ddgtu[ua,ub,lc,ld], {ua, ub}, {lc, ld}}, 51 | @END_TENSORS 52 | 53 | 54 | 55 | @CONNECTION {CD, PD, G} 56 | @CONNECTION {CDt, PD, Gt} 57 | 58 | @GROUPS 59 | phi -> log_confac, 60 | gt[la,lb] -> metric, 61 | Xt[ua ] -> Gamma, 62 | @END_GROUPS 63 | 64 | @EXTRA_GROUPS 65 | Grid::coordinates -> {x, y, z, r}, 66 | ADMBase::metric -> {gxx, gxy, gxz, gyy, gyz, gzz}, 67 | ADMBase::curv -> {kxx, kxy, kxz, kyy, kyz, kzz}, 68 | @END_EXTRA_GROUPS 69 | 70 | @DEFINE pi = N[Pi,40] 71 | 72 | @DEFINE KD = KroneckerDelta 73 | 74 | @DEFINE 75 | detgExpr = Det [MatrixOfComponents [g [la,lb]]] 76 | @DEFINE 77 | ddetgExpr[la_] = 78 | Sum [D[Det[MatrixOfComponents[g[la, lb]]], X] PD[X, la], 79 | {X, Union[Flatten[MatrixOfComponents[g[la, lb]]]]}] 80 | 81 | @DEFINE 82 | detgtExpr = Det [MatrixOfComponents [gt[la,lb]]] 83 | @DEFINE 84 | ddetgtExpr[la_] = 85 | Sum [D[Det[MatrixOfComponents[gt[la, lb]]], X] PD[X, la], 86 | {X, Union[Flatten[MatrixOfComponents[gt[la, lb]]]]}] 87 | 88 | 89 | 90 | @CALCULATION Minkowski 91 | @Schedule {"IN ADMBase_InitialData"} 92 | @ConditionalOnKeyword {"my_initial_data", "Minkowski"} 93 | @EQUATIONS 94 | phi -> IfThen[conformalMethod, 1, 0], 95 | gt[la,lb] -> KD[la,lb], 96 | trK -> 0, 97 | At[la,lb] -> 0, 98 | Xt[ua] -> 0, 99 | alpha -> 1, 100 | A -> 0, 101 | beta[ua] -> 0, 102 | B[ua] -> 0, 103 | @END_EQUATIONS 104 | @END_CALCULATION 105 | 106 | 107 | 108 | @CALCULATION convertFromADMBase 109 | @Schedule {"AT initial AFTER ADMBase_PostInitial"} 110 | @ConditionalOnKeyword {"my_initial_data", "ADMBase"} 111 | @Shorthands g[la,lb], detg, gu[ua,ub], em4phi 112 | @EQUATIONS 113 | g[la,lb] -> admg[la,lb], 114 | detg -> detgExpr, 115 | gu[ua,ub] -> 1/detg detgExpr MatrixInverse [g[ua,ub]], 116 | 117 | phi -> IfThen [conformalMethod, detg^(-1/6), Log[detg]/12], 118 | em4phi -> IfThen [conformalMethod, phi^2, Exp[-4 phi]], 119 | gt[la,lb] -> em4phi g[la,lb], 120 | 121 | trK -> gu[ua,ub] admK[la,lb], 122 | At[la,lb] -> em4phi (admK[la,lb] - (1/3) g[la,lb] trK), 123 | 124 | alpha -> admalpha, 125 | 126 | beta[ua] -> admbeta[ua], 127 | @END_EQUATIONS 128 | @END_CALCULATION 129 | 130 | 131 | 132 | @CALCULATION convertFromADMBaseGammaCalc 133 | @Schedule {"AT initial AFTER convertFromADMBase"} 134 | @ConditionalOnKeyword {"my_initial_data", "ADMBase"} 135 | # Do not synchronise right after this routine; instead, synchronise 136 | # after extrapolating 137 | @Where Interior 138 | # Synchronise after this routine, so that the refinement boundaries 139 | # are set correctly before extrapolating. (We will need to 140 | # synchronise again after extrapolating because extrapolation does 141 | # not fill ghost zones, but this is irrelevant here.) 142 | @Shorthands dir[ua], detgt, gtu[ua,ub], Gt[ua,lb,lc], theta 143 | @EQUATIONS 144 | dir[ua] -> Sign[beta[ua]], 145 | 146 | detgt -> 1, (* detgtExpr *) 147 | gtu[ua,ub] -> 1/detgt detgtExpr MatrixInverse [gt[ua,ub]], 148 | Gt[ua,lb,lc] -> 1/2 gtu[ua,ud]* 149 | (PD[gt[lb,ld],lc] + PD[gt[lc,ld],lb] - PD[gt[lb,lc],ld]), 150 | Xt[ua] -> gtu[ub,uc] Gt[ua,lb,lc], 151 | 152 | # If LapseACoeff=0, then A is not evolved, in the sense that it 153 | # does not influence the time evolution of other variables. 154 | A -> IfThen [LapseACoeff != 0, 155 | 1 / (- harmonicF alpha^harmonicN) 156 | (+ admdtalpha 157 | - LapseAdvectionCoeff beta[ua] PDua[alpha,la] 158 | - LapseAdvectionCoeff Abs[beta[ua]] PDus[alpha,la]), 159 | 0], 160 | 161 | theta -> thetaExpr, 162 | 163 | # If ShiftBCoeff=0 or theta ShiftGammaCoeff=0, then B^i is not 164 | # evolved, in the sense that it does not influence the time 165 | # evolution of other variables. 166 | B[ua] -> IfThen [ShiftGammaCoeff ShiftBCoeff != 0, 167 | 1 / (theta ShiftGammaCoeff), 168 | (+ admdtbeta[ua] 169 | - ShiftAdvectionCoeff beta[ub] PDua[beta[ua],lb] 170 | - ShiftAdvectionCoeff Abs[beta[ub]] PDus[beta[ua],lb]), 171 | 0], 172 | @END_EQUATIONS 173 | @END_CALCULATION 174 | 175 | 176 | 177 | @INHERITED_IMPLEMENTATION ADMBase 178 | @INHERITED_IMPLEMENTATION TmunuBase 179 | 180 | 181 | 182 | @KEYWORD_PARAMETER my_initial_data 183 | # @Visibility "restricted" 184 | # @Description "ddd" 185 | @AllowedValues {"ADMBase", "Minkowski"} 186 | @Default "ADMBase" 187 | @END_KEYWORD_PARAMETER 188 | 189 | 190 | 191 | @REAL_PARAMETER LapseACoeff 192 | @Description "Whether to evolve A in time" 193 | @Default 0 194 | @END_REAL_PARAMETER 195 | 196 | 197 | 198 | @END_THORN 199 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Bracket.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | public class Bracket extends Pattern implements Comparable { 8 | boolean neg; 9 | List ranges = new LinkedList(); 10 | 11 | public Bracket() {} 12 | public Bracket(boolean neg) { 13 | this.neg = neg; 14 | } 15 | 16 | void addRange(char lo,char hi,boolean ignCase) { 17 | if(ignCase) { 18 | addRange(Character.toLowerCase(lo), Character.toLowerCase(hi)); 19 | addRange(Character.toUpperCase(lo), Character.toUpperCase(hi)); 20 | } else { 21 | addRange(lo,hi); 22 | } 23 | } 24 | /** 25 | * Adds a range between lo and hi but will not create an overlap. 26 | * @param lo 27 | * @param hi 28 | */ 29 | Bracket addRange(char lo,char hi) { 30 | if(lo > hi) throw new ParseException("Invalid range "+lo+" > "+hi); 31 | Range rr = new Range(lo,hi), over = null; 32 | int i=0; 33 | for(;i= m.text.length()) 67 | return false; 68 | char c = m.text.charAt(m.getTextPos()); 69 | for(Range range : ranges) { 70 | if(range.lo <= c && c <= range.hi) { 71 | if(!neg) 72 | m.incrTextPos(1); 73 | if(!neg^true) { 74 | m.expected(new Expected(this)); 75 | } 76 | return neg^true; 77 | } 78 | } 79 | if(neg) 80 | m.incrTextPos(1); 81 | if(!neg^false) { 82 | m.expected(new Expected(this)); 83 | } 84 | return neg^false; 85 | } 86 | 87 | @Override public void visit(Visitor v) { 88 | Visitor vv = v.startVisit(this); 89 | for(Range range : ranges) { 90 | range.visit(vv); 91 | } 92 | v.finishVisit(this); 93 | } 94 | 95 | @Override 96 | public String decompile() { 97 | StringBuffer sb = new StringBuffer(); 98 | sb.append("["); 99 | if(neg) 100 | sb.append("^"); 101 | for(int i=0;i Character.MIN_VALUE) { 170 | b.addRange(Character.MIN_VALUE,(char) (ranges.get(0).lo-1)); 171 | } 172 | for(int i=1;i expected(int n) { 241 | List ex = new ArrayList(); 242 | ex.add(decompile()); 243 | return ex; 244 | } 245 | 246 | public String toString() { 247 | return decompile(); 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Grammar.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.io.InputStreamReader; 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | import java.util.HashSet; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.Set; 15 | 16 | public class Grammar { 17 | Map patterns = new HashMap(); 18 | Map grammars = null; 19 | String lastCompiled; 20 | 21 | public void importGrammar(String name,Grammar g) { 22 | if(grammars == null) { 23 | grammars = new HashMap(); 24 | } 25 | if(grammars.containsKey(name)) { 26 | throw new ParseException("Grammar '"+name+"' is already defined"); 27 | } 28 | grammars.put(name, g); 29 | } 30 | 31 | public Pattern compile(String name,String pattern) { 32 | if(name.indexOf(':') >= 0) 33 | throw new ParseException("Illegal character ':' in pattern name"); 34 | if(patterns.containsKey(name)) 35 | throw new ParseException("Rule '"+name+"' is already defined."); 36 | Pattern pat = null; 37 | pat = new ReParse().compile(pattern,this); 38 | patterns.put(name, pat); 39 | lastCompiled = name; 40 | return pat; 41 | } 42 | 43 | // TODO: Make import work 44 | // TODO: Make a decompile work 45 | // TODO: Add "Grammar name" sections 46 | // TODO: Make a more accurate rule to match regex's 47 | public void compileFile(File filename) throws IOException { 48 | String contents = readContents(filename).trim(); 49 | compileFile(contents); 50 | } 51 | public void compileFile(InputStream in) throws IOException { 52 | StringBuilder sb = new StringBuilder(); 53 | BufferedInputStream bin = new BufferedInputStream(in); 54 | InputStreamReader isr = new InputStreamReader(bin); 55 | int c=0; 56 | //while((c = isr.read()) != -1) 57 | while(true) { 58 | c = isr.read(); 59 | if(c == -1) 60 | break; 61 | sb.append((char)c); 62 | } 63 | compileFile(sb.toString()); 64 | } 65 | public void compileFile(String contents) throws IOException { 66 | Grammar pegRules = AutoGrammar.fileparser; 67 | Matcher pegMatcher = pegRules.matcher("file",contents); 68 | if(!pegMatcher.matches()) { 69 | throw new ParseException("Syntax error near line "+pegMatcher.near()+" : "+ 70 | pegMatcher.text.substring(0,pegMatcher.maxTextPos)+">|<"+ 71 | pegMatcher.text.substring(pegMatcher.maxTextPos)); 72 | } 73 | for(int i=0;i entry : patterns.entrySet()) { 91 | out.println(entry.getKey()+" => "+entry.getValue().decompile()); 92 | dv.out.indent+=2; 93 | entry.getValue().visit(dv); 94 | dv.out.indent-=2; 95 | } 96 | } 97 | 98 | boolean checked = false; 99 | 100 | private void check() { 101 | CheckVisitor checker = new CheckVisitor(); 102 | checker.retry = false; 103 | for (Map.Entry p : patterns.entrySet()) { 104 | if (checker.patterns.containsKey(p.getKey())) 105 | continue; 106 | checker.checking = p.getKey(); 107 | p.getValue().visit(checker); 108 | } 109 | if(checker.retry) { 110 | checker.defaults = checker.patterns; 111 | checker.patterns = new HashMap(); 112 | for (Map.Entry p : patterns.entrySet()) { 113 | if (checker.patterns.containsKey(p.getKey())) 114 | continue; 115 | checker.checking = p.getKey(); 116 | p.getValue().visit(checker); 117 | } 118 | } 119 | checked = true; 120 | } 121 | 122 | public List extras(String pat) { 123 | List extraPatterns = new ArrayList(); 124 | Map visited = new HashMap(); 125 | visited.put(pat,Boolean.FALSE); 126 | Visitor extraFinder = new ExtraVisitor(visited); 127 | while(true) { 128 | boolean done = true; 129 | Set set = new HashSet(); 130 | for(String p : visited.keySet()) { 131 | if(visited.get(p) == Boolean.FALSE) 132 | set.add(p); 133 | } 134 | for(String p : set) { 135 | //System.out.println("visit "+p+" "+set); 136 | visited.put(p, Boolean.TRUE); 137 | patterns.get(p).visit(extraFinder); 138 | done = false; 139 | } 140 | if(done) 141 | break; 142 | } 143 | for(String p : patterns.keySet()) { 144 | if(!visited.containsKey(p)) 145 | extraPatterns.add(p); 146 | } 147 | return extraPatterns; 148 | } 149 | 150 | public Matcher matcher(String text) { 151 | return matcher(lastCompiled, text); 152 | } 153 | 154 | public Matcher matcher(String patternName, String text) { 155 | if(!checked) 156 | check(); 157 | Matcher m = new Matcher(); 158 | m.text = text; 159 | m.pattern = patterns.get(patternName); 160 | if(m.pattern == null) 161 | throw new ParseException("No such pattern: '"+patternName+"'"); 162 | m.patternName = patternName; 163 | return m; 164 | } 165 | 166 | public static String readContents(File file) throws IOException { 167 | if(!file.exists()) throw new IOException("File not found "+file); 168 | int fileSize = (int)file.length(); 169 | if(fileSize == 0) return ""; 170 | byte[] buf = new byte[fileSize]; 171 | FileInputStream fr = new FileInputStream(file); 172 | int bytesRead = fr.read(buf,0,buf.length); 173 | fr.close(); 174 | if(bytesRead <= 0) 175 | throw new IOException("Could not read entire file: "+file); 176 | return new String(buf,"utf8"); 177 | } 178 | 179 | public Matcher matchFile(String fileName,String rule) throws IOException { 180 | String contents = readContents(new File(fileName)); 181 | Matcher m = matcher(rule,contents); 182 | m.matches(); 183 | return m; 184 | } 185 | 186 | public void addOps(String name, String finalExprPeg, String whitePeg, String[][] ops) { 187 | String prev_name = name; 188 | for(int i=0;i getPatternNames() { 228 | return patterns.keySet(); 229 | } 230 | 231 | public Pattern getPattern(String pat) { 232 | return patterns.get(pat); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /doc/Calculator.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Calculator 8 | 21 | 22 | 23 | 24 |

piraha-peg - Calculator.wiki

25 | 26 |
27 | 28 |

Creating a calculator with Piraha

29 | 30 |

Getting to Know Piraha: A Calculator

31 | 32 |

When building any grammar in Piraha, you start with patterns. We write a pattern for a number as "-?[0-9]+". This will match one or more digits, optionally preceded by a minus sign. We'll name this pattern num.

33 | 34 |
 35 |   Grammar math = new Grammar();
 36 |   math.compile("num","-?[0-9]+");
 37 | 
38 | 39 |

We'll also need patterns to match the basic mathematical operations: add and subtract.

40 | 41 |
 42 |   math.compile("addop", "\\+|-");
 43 | 
44 | 45 |

So far we have only written simple patterns. While they are named and part of an object called a grammar, they aren't really a grammar yet. The next step is 46 | to start combining these patterns together. We 47 | could write

48 | 49 |
 50 |   math.compile("addexpr", "{num}({addop}{num})*");
 51 | 
52 | 53 |

This pattern would match 3 as well as 3+4 as well as 3+4-2 because the group (i.e. the parens) matches a + or - followed by a number, and this group can match zero or more times. When matching 3+4-2 the 3 matches the first instance of {num} the +4 matches the group, and -2 matches the group again.

54 | 55 |

This will allow us to parse a simple sequence of additions and subtractions.

56 | 57 |

Let's take a look at how we'd use that code. 58 |

 59 |   Matcher m = math.matcher("expr", "3+4-2");
 60 |   boolean b = m.matches();
 61 |   if (b) {
 62 |     m.dumpMatches();
 63 |     System.out.println("eval=" + evalExpr(m));
 64 |   } else {
 65 |     System.out.println(m.near()); // report errors
 66 |   }
 67 | 
68 | 69 |

To apply our patterns to actual text, we create a matcher with both the pattern name and the text we want to match. We then ask the Matcher if the pattern matches, and if it does we print the parse tree in a human readable form. If the parse fails, we print a diagnostic showing where the pattern matcher had trouble.

70 | 71 |

In this example, the printed parse tree looks like this: 72 |

 73 |   [0] addexpr:
 74 |     [0] num=(3)
 75 |     [1] addop=(+)
 76 |     [2] num=(4)
 77 |     [3] addop=(-)
 78 |     [4] num=(2)
 79 | 
80 | 81 |

The topmost node is addexpr, it has child nodes that are either num or addop. An equals sign shows us the value each node has.

82 | 83 |

Evaluating this tree is simple. There is only a single class you need to understand in order to make use of it: Group. The top 5 methods in the listing below are the main ones you need to make sense of any parse tree.

84 | 85 |
 86 | public class Group {
 87 |   // position in text where the match began
 88 |   public int getBegin();
 89 |   // position in text where the match ended
 90 |   public int getEnd();
 91 |   // number of child groups
 92 |   public int groupCount();
 93 |   // the nth child group
 94 |   public Group group(int n);
 95 |   // the name of the pattern that matched this text
 96 |   public String getPatternName();
 97 | 
 98 |   // the full input string
 99 |   public String getText();
100 |   // the substring matched by this pattern
101 |   public String toString();
102 |   // show the parse tree
103 |   public void dumpMatches();
104 |   // show the parse tree in XML form
105 |   public void dumpMatchesXML();
106 | 
107 | }
108 | 
109 | 110 |

The Matcher is a subclass of group. This is a convenience that allows you to access the matches more easily.

111 | 112 |

For the case of our calculator, we can use this Group object to compute the sum of all the addends. 113 |

114 |   private static double evalExpr(Group match) {
115 |     double answer = Double.parseDouble(match.group(0).toString());
116 |     for(int i=1;i<match.groupCount();i+=2) {
117 |       String op = match.group(i).toString();
118 |       double addend = Double.parseDouble(match.group(i+1).toString());
119 |       if("+".equals(op))
120 |         answer += addend;
121 |       else
122 |         answer -= addend;
123 |     }
124 |     return answer;
125 |   }
126 | 
127 | 128 |

Adding Multiplication

129 | 130 |

To capture a calculator we want the multiplies and divides to work as well, and we'll want to recognize the order of operations (multiply and divide before add and subtract).

131 | 132 |

To enable this feature, we'll replace {num} above with {mulexp}.

133 | 134 |
135 |   math.compile("addexpr", "{mulexp}({addop}{mulexp})*");
136 |   math.compile("mulexp", "{num}({mulop}{num})*");
137 | 
138 | 139 |

Now when we examine our parse tree, we'll find that {addexp} nodes have {mulexp} nodes underneath them instead of simple numbers.

140 | 141 |
142 | package edu.lsu.cct.piraha.examples;

143 | 144 | import edu.lsu.cct.piraha.Grammar; 145 | import edu.lsu.cct.piraha.Group; 146 | import edu.lsu.cct.piraha.Matcher; 147 | 148 | public class Calc { 149 | public static Grammar makeMath() { 150 | Grammar math = new Grammar(); 151 | // All the various was a double precision number (or integer) can be 152 | // written 153 | math.compile("num","-?[0-9]+(\.[0-9]+)?"); 154 | 155 | // The basic operators 156 | math.compile("addop", "\\+|-"); 157 | math.compile("mulop", "\\*|/"); 158 | math.compile("powop", "\\*\\*"); 159 | 160 | // unary negation 161 | math.compile("neg", "-"); 162 | 163 | // All the different ways we can stick things together, includes 164 | // parenthesis. 165 | // Note: The start of "expr" should not be {expr} 166 | // because that leads to infinite recursion. 167 | math.compile("expr", "{mulexp}({addop}{mulexp})*"); 168 | math.compile("mulexp", "{powexp}({mulop}{powexp})*"); 169 | math.compile("powexp", "{num}({powop}{num})*|\\({expr}\\)"); 170 | 171 | return math; 172 | } 173 | 174 | Grammar math = makeMath(); 175 | 176 | public double eval(String s) { 177 | Matcher m = math.matcher("expr",s.trim()); 178 | if(m.matches()) 179 | return evalExpr(m); 180 | else 181 | return Double.NaN; 182 | } 183 | 184 | public static void main(String[] args) { 185 | Grammar math = makeMath(); 186 | 187 | Matcher m = math.matcher("expr", "1+2*4+4**3**2"); // answer is 262153 188 | 189 | boolean b = m.matches(); 190 | System.out.println("match? " + b); 191 | if (b) { 192 | m.dumpMatchesXML(); 193 | System.out.println("node count=" + count(m)); 194 | System.out.println("eval=" + evalExpr(m)); 195 | } 196 | } 197 | 198 | private static int count(Group m) { 199 | int n = 1; 200 | for (int i = 0; i < m.groupCount(); i++) { 201 | n += count(m.group(i)); 202 | } 203 | return n; 204 | } 205 | 206 | private static double evalExpr(Group match) { 207 | String pn = match.getPatternName(); 208 | if ("num".equals(pn)) { 209 | return Double.parseDouble(match.substring()); 210 | } else if ("expr".equals(pn)) { 211 | double d = evalExpr(match.group(0)); 212 | for(int i=1;i+1<match.groupCount();i+=2) { 213 | String op = match.group(i).substring(); 214 | if("+".equals(op)) 215 | d += evalExpr(match.group(i+1)); 216 | else 217 | d -= evalExpr(match.group(i+1)); 218 | } 219 | return d; 220 | } else if ("mulexp".equals(pn)) { 221 | double d = evalExpr(match.group(0)); 222 | for(int i=1;i+1<match.groupCount();i+=2) { 223 | String op = match.group(i).substring(); 224 | if("*".equals(op)) 225 | d *= evalExpr(match.group(i+1)); 226 | else 227 | d /= evalExpr(match.group(i+1)); 228 | } 229 | return d; 230 | } else if ("powexp".equals(pn)) { 231 | int n = match.groupCount(); 232 | double d = evalExpr(match.group(n-1)); 233 | for(int i=n-2;i>0;i-=2) { 234 | d = Math.pow(evalExpr(match.group(i-1)), d); 235 | } 236 | return d; 237 | } 238 | return evalExpr(match.group(0)); 239 | } 240 | 241 | } 242 |
243 | 244 | 245 | 246 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/Group.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha; 2 | 3 | import java.io.PrintWriter; 4 | import java.util.LinkedList; 5 | import java.util.Stack; 6 | 7 | public class Group implements Cloneable { 8 | String patternName, text; 9 | int begin, end; 10 | Group replacement; 11 | 12 | public void setReplacement(Group replacement) { 13 | if(replacement.getPatternName().equals(patternName)) 14 | this.replacement = replacement; 15 | else 16 | throw new MatchException("replacement group does not have patternName='"+patternName+"'"); 17 | } 18 | 19 | public int getBegin() { 20 | if(replacement != null) 21 | return replacement.getBegin(); 22 | return begin; 23 | } 24 | public int getEnd() { 25 | if(replacement != null) 26 | replacement.getEnd(); 27 | return end; 28 | } 29 | LinkedList subMatches; 30 | Stack> savedMatches; 31 | public int groupCount() { 32 | if(replacement != null) 33 | return replacement.groupCount(); 34 | if(subMatches == null) 35 | return 0; 36 | return subMatches.size(); 37 | } 38 | public Group group(int n) { 39 | if(replacement != null) 40 | return replacement.group(n); 41 | return subMatches.get(n); 42 | } 43 | 44 | Group() {} 45 | 46 | public final static LinkedList emptyList = new LinkedList(); 47 | 48 | public static Group make(String lookup, String value) { 49 | return new Group(lookup,0,value.length(),emptyList,value); 50 | } 51 | 52 | public Group(String lookup, int before, int after, LinkedList matches,String text) { 53 | this.patternName = lookup; 54 | this.begin = before; 55 | this.end = after; 56 | this.subMatches = matches; 57 | this.text = text; 58 | } 59 | public Group(String patternName, Group m) { 60 | this.patternName = patternName; 61 | this.begin = m.begin; 62 | this.end = m.end; 63 | this.subMatches = m.subMatches; 64 | this.text = m.text; 65 | } 66 | 67 | Group(Group g) { 68 | subMatches = new LinkedList(); 69 | this.patternName = "$empty"; 70 | this.text = "$empty"; 71 | subMatches.add(g); 72 | } 73 | 74 | public Group(String lookup, int b, int e, String text) { 75 | this.patternName = lookup; 76 | this.begin = b; 77 | this.end = e; 78 | this.text = text; 79 | } 80 | 81 | public Group group() { 82 | if(replacement != null) 83 | return replacement.group(); 84 | return new Group(patternName,begin,end,subMatches,text); 85 | } 86 | 87 | public String substring(String text) { 88 | if(replacement != null) 89 | return replacement.substring(text); 90 | return text.substring(begin,end); 91 | } 92 | 93 | public Near near() { 94 | Near near = new Near(); 95 | near.text = text; 96 | for(int i=0;i"); 166 | if(groupCount()==0) { 167 | out.print(xmlText(substring())); 168 | } else { 169 | out.println(); 170 | out.indent++; 171 | try { 172 | for(Group match : subMatches) { 173 | match.dumpMatchesXML(out); 174 | } 175 | } finally { 176 | out.indent--; 177 | } 178 | } 179 | if(showText) { 180 | out.print(""); 181 | out.print(xmlText(substring())); 182 | out.println(""); 183 | } 184 | out.print(""); 187 | } 188 | } 189 | private int getLineNum() { 190 | int num = 1; 191 | for(int i=0;i'"); 212 | out.print(getPatternName()); 213 | out.print("', start=>'"); 214 | out.print(begin); 215 | out.print("', end=>'"); 216 | out.print(end); 217 | out.print("', line=>'"); 218 | out.print(getLineNum()); 219 | out.print("'"); 220 | if(groupCount()==0) { 221 | out.print(", text=>\""); 222 | out.print(escText(substring())); 223 | out.print('"'); 224 | } else { 225 | out.print(", children=>["); 226 | out.println(); 227 | out.indent++; 228 | try { 229 | boolean first = true; 230 | for(Group match : subMatches) { 231 | if(first) { 232 | first = false; 233 | } else { 234 | out.println(","); 235 | } 236 | match.dumpMatchesPerl(out); 237 | } 238 | } finally { 239 | out.indent = out.indent-1; 240 | } 241 | out.print("]"); 242 | } 243 | out.print("}"); 244 | } 245 | } 246 | public void dumpMatchesPython(DebugOutput out) { 247 | if(replacement != null) { 248 | replacement.dumpMatches(out); 249 | } else { 250 | out.print("{'name':'"); 251 | out.print(getPatternName()); 252 | out.print("', 'start':'"); 253 | out.print(begin); 254 | out.print("', 'end':'"); 255 | out.print(end); 256 | out.print("', 'line':'"); 257 | out.print(getLineNum()); 258 | out.print("'"); 259 | if(groupCount()==0) { 260 | out.print(", 'text':'"); 261 | out.print(escText(substring())); 262 | out.print("'"); 263 | } else { 264 | out.print(", 'children':["); 265 | out.println(); 266 | out.indent++; 267 | try { 268 | boolean first = true; 269 | for(Group match : subMatches) { 270 | if(first) { 271 | first = false; 272 | } else { 273 | out.println(","); 274 | } 275 | match.dumpMatchesPython(out); 276 | } 277 | } finally { 278 | out.indent = out.indent-1; 279 | } 280 | out.print("]"); 281 | } 282 | out.print("}"); 283 | } 284 | } 285 | 286 | public String escText(String s) { 287 | StringBuffer sb = new StringBuffer(); 288 | for(int i=0;i= ' ' && c <= '~') 318 | return Character.toString(c); 319 | return "\\x"+Integer.toHexString(c); 320 | } 321 | 322 | public void dumpMatches() { 323 | if(replacement != null) { 324 | replacement.dumpMatches(); 325 | } else { 326 | dumpMatches(DebugOutput.out); 327 | DebugOutput.out.pw.flush(); 328 | } 329 | } 330 | public void dumpMatches(DebugOutput out) { 331 | if(replacement != null) { 332 | replacement.dumpMatches(out); 333 | } else { 334 | out.print(getPatternName()); 335 | if(groupCount()==0) { 336 | out.print("=("); 337 | out.outs(substring()); 338 | out.println(")"); 339 | } else { 340 | out.println(":"); 341 | out.indent+=2; 342 | try { 343 | for(int i=0;i') 363 | sb.append(">"); 364 | else if(c == '&') 365 | sb.append("&"); 366 | else if(c == '"') 367 | sb.append("""); 368 | else if(c <= 13 || c > 127) 369 | sb.append("&#"+(int)c+";"); 370 | else 371 | sb.append(c); 372 | } 373 | return sb.toString(); 374 | } 375 | public void generate(PrintWriter pw) { 376 | if(replacement != null) { 377 | replacement.generate(pw); 378 | } else { 379 | int children = groupCount(); 380 | if(children == 0) { 381 | pw.print(substring()); 382 | } else { 383 | pw.print(text.substring(begin,group(0).begin)); 384 | for(int i=0;i= args.length) 397 | return this; 398 | String pn = args[index]; 399 | for(int i=0;ipatterns.put(\""+pat+"\","); 91 | } else if(lang == Lang.perl) { 92 | out.print("$g->{patterns}->{\""+pat+"\"}=("); 93 | } 94 | Pattern p = g.getPattern(pat); 95 | dumpPattern(p,out,lang); 96 | out.println(");"); 97 | } 98 | } 99 | 100 | int indentLevel = 0; 101 | /** 102 | * Helper class for creating the AutoGrammar file. Ensures 103 | * proper indentation and improves readability. 104 | * 105 | * @author sbrandt 106 | * 107 | */ 108 | class IndentWriter extends Writer { 109 | 110 | Writer w; 111 | 112 | IndentWriter(Writer w) { this.w = w; } 113 | 114 | @Override 115 | public void close() throws IOException { 116 | w.close(); 117 | } 118 | 119 | @Override 120 | public void flush() throws IOException { 121 | w.flush(); 122 | } 123 | 124 | @Override 125 | public void write(char[] arg0, int arg1, int arg2) throws IOException { 126 | for(int i=arg1;i1) { 158 | out.print('"'); 159 | out.print(str);; 160 | out.print('"'); 161 | // out.print("xxx"); 162 | } else { 163 | out.print("'"); 164 | out.print(str); 165 | out.print("'"); 166 | } 167 | } 168 | 169 | /** 170 | * Dump a pattern to a string. Used to generate the AutoGrammar. 171 | * @param p 172 | * @param out 173 | * @throws Error 174 | */ 175 | private void dumpPattern(Pattern p,PrintWriter out,Lang lang) throws Error { 176 | if(p instanceof Literal) { 177 | Literal lit = (Literal)p; 178 | out.print("new Literal("); 179 | fmtc(lit.ch,out,lang); 180 | out.print(')'); 181 | } else if(p instanceof Seq) { 182 | Seq s = (Seq)p; 183 | indentLevel += INDENT_INCR; 184 | if(!s.ignCase && !s.igcShow) 185 | out.println("new Seq("); 186 | else 187 | out.println("new Seq("+s.ignCase+","+s.igcShow+","); 188 | for(int i=0;i0) out.println(","); 190 | dumpPattern(s.patternList.get(i),out,lang); 191 | } 192 | indentLevel -= INDENT_INCR; 193 | if(lang == Lang.cxx) { 194 | out.print(",NULL"); 195 | } 196 | out.print(")"); 197 | } else if(p instanceof Multi) { 198 | Multi m = (Multi)p; 199 | out.print("new Multi("); 200 | dumpPattern(m.pattern,out,lang); 201 | out.print(","+m.min+","+m.max+")"); 202 | } else if(p instanceof Bracket) { 203 | Bracket b = (Bracket)p; 204 | indentLevel += INDENT_INCR; 205 | out.println("(new Bracket("+bv(b.neg,lang)+"))"); 206 | for(int i=0;i"); 210 | } else if(lang == Lang.java) { 211 | out.print("."); 212 | } else if(lang == Lang.perl) { 213 | out.print("->"); 214 | } 215 | out.print("addRange("); 216 | fmtc(r.lo,out,lang); 217 | out.print(','); 218 | fmtc(r.hi,out,lang); 219 | out.println(')'); 220 | } 221 | indentLevel -= INDENT_INCR; 222 | } else if(p instanceof Or) { 223 | Or or = (Or)p; 224 | indentLevel += INDENT_INCR; 225 | if(!or.ignCase && !or.igcShow) 226 | out.println("new Or("); 227 | else 228 | out.println("new Or("+or.ignCase+","+or.igcShow+","); 229 | for(int i=0;i0) out.println(','); 231 | dumpPattern(or.patterns.get(i),out,lang); 232 | } 233 | indentLevel -= INDENT_INCR; 234 | if(lang == Lang.cxx){ 235 | out.print(",NULL"); 236 | } 237 | out.print(")"); 238 | } else if(p instanceof Lookup) { 239 | Lookup lk = (Lookup)p; 240 | out.print("new Lookup(\""+(lk.capture ? "" : "-")+lk.lookup+"\"");//+"\",g)"); 241 | if(lang == Lang.perl) { 242 | out.print(",$g)"); 243 | } else { 244 | out.print(",g)"); 245 | } 246 | } else if(p instanceof Start) { 247 | out.print("new Start()"); 248 | } else if(p instanceof End) { 249 | out.print("new End()"); 250 | } else if(p instanceof Nothing) { 251 | out.print("new Nothing()"); 252 | } else if(p instanceof NegLookAhead) { 253 | NegLookAhead neg = (NegLookAhead)p; 254 | out.print("new NegLookAhead("); 255 | dumpPattern(neg.pattern, out,lang); 256 | out.print(")"); 257 | } else if(p instanceof Dot) { 258 | out.print("new Dot()"); 259 | } else { 260 | throw new Error(p.getClass().getName()); 261 | } 262 | } 263 | 264 | /** A quick test of the grammar defined in this file. */ 265 | static ReParse createAndTest() { 266 | ReParse r = new ReParse(); 267 | r.init(false); 268 | test(r,"(?=a)"); 269 | test(r,"[0-3a-d]"); 270 | test(r,"(d|)"); 271 | test(r,"(({a}|{b})|{c})"); 272 | test(r,"(({named}|{literal}|{charclass}|{group})({quant}|)|({start}|{end}|{boundary}))"); 273 | test(r,"<{d}>|x","(<{d}>|x)"); 274 | test(r,"a*"); 275 | test(r,"b+"); 276 | test(r,"c{3}"); 277 | test(r,"d{1,4}"); 278 | test(r,"e{9,}"); 279 | test(r,"hello"); 280 | test(r,"hell{4}o\\n\\t\\r\\u012f\\\\");//|w+o?od*e{3,}n{1,2})"); 281 | test(r,"(?i:abc)"); 282 | test(r,"(?-i:abc)"); 283 | test(r,"(?!abc)"); 284 | test(r,"^a"); 285 | test(r,"a$"); 286 | test(r,"a\\b "); 287 | test(r,"[a-z]"); 288 | test(r,"[z-]","[-z]"); 289 | test(r,"[-a]"); 290 | test(r,"[a-df]"); 291 | test(r,"[\\u0123-\\u0129]"); 292 | test(r,"(\\[[^\\]]*)+"); 293 | return r; 294 | } 295 | static void test(ReParse r,String s) { 296 | test(r,s,null); 297 | } 298 | static void test(ReParse r,String s,String s2) { 299 | Pattern p = r.compile(s,new Grammar()); 300 | System.out.print("decomp="+p.decompile()); 301 | if(p.decompile().equals(s2)) 302 | return; 303 | if(!s.equals(p.decompile())) { 304 | DebugVisitor dv = new DebugVisitor(new DebugOutput()); 305 | dv.startVisit(p); 306 | throw new Error(s+" != "+p.decompile()); 307 | } 308 | } 309 | 310 | enum Lang { cxx, java, perl, python }; 311 | 312 | public static void main(String[] args) throws IOException { 313 | ReParse p = createAndTest(); 314 | for(String arg: args) { 315 | if(arg.endsWith(".cc")||arg.endsWith(".cpp")) 316 | p.generateFile(arg, Lang.cxx); 317 | else if(arg.endsWith(".pl")||arg.endsWith(".pm")) 318 | p.generateFile(arg,Lang.perl); 319 | else if(arg.endsWith(".java")) 320 | p.generateFile(arg, Lang.java); 321 | else 322 | throw new Error("Unknown suffix for file "+arg); 323 | } 324 | } 325 | 326 | void generateFile(String autoFile,Lang lang) throws IOException { 327 | init(false); 328 | 329 | StringWriter sw = new StringWriter(); 330 | indentLevel = 0; 331 | PrintWriter pw = new PrintWriter(new IndentWriter(sw)); 332 | if(lang == Lang.perl) { 333 | pw.print("# "); 334 | } else { 335 | pw.print("// "); 336 | } 337 | pw.println("Autogenerated code, created by ReParse. Do not edit."); 338 | if(lang == Lang.java) { 339 | pw.println("package edu.lsu.cct.piraha;"); 340 | } else if(lang == Lang.cxx) { 341 | pw.println("#include \"Piraha.hpp\""); 342 | } else if(lang == Lang.perl) { 343 | pw.println("use strict;"); 344 | pw.println(); 345 | pw.println("use piraha;"); 346 | } 347 | pw.println(); 348 | if(lang == Lang.java) { 349 | indentLevel += INDENT_INCR; 350 | pw.println("public class AutoGrammar {"); 351 | } 352 | pw.println(); 353 | 354 | init(false); 355 | if(lang == Lang.java) { 356 | pw.println("public final static Grammar reparser = reparserGenerator();"); 357 | } else if(lang == Lang.cxx) { 358 | } 359 | pw.println(); 360 | indentLevel += INDENT_INCR; 361 | if(lang == Lang.java) { 362 | pw.println("private static Grammar reparserGenerator() {"); 363 | pw.println("Grammar g = new Grammar();"); 364 | } else if(lang == Lang.cxx) { 365 | pw.println("smart_ptr AutoGrammar::reparserGenerator() {"); 366 | pw.println("smart_ptr g = new Grammar();"); 367 | } else if(lang == Lang.perl) { 368 | pw.println("sub reparserGenerator() {"); 369 | pw.println("my $g = new Grammar();"); 370 | } 371 | dumpGrammar(pw,lang); 372 | indentLevel -= INDENT_INCR; 373 | if(lang == Lang.perl) { 374 | pw.println("return $g;"); 375 | } else { 376 | pw.println("return g;"); 377 | } 378 | pw.println("}"); 379 | pw.println(); 380 | 381 | init(true); 382 | if(lang == Lang.java) { 383 | pw.println("public final static Grammar fileparser = fileparserGenerator();"); 384 | } else if(lang == Lang.cxx) { 385 | } 386 | pw.println(); 387 | indentLevel += INDENT_INCR; 388 | if(lang == Lang.java) { 389 | pw.println("private static Grammar fileparserGenerator() {"); 390 | pw.println("Grammar g = new Grammar();"); 391 | } else if(lang == Lang.cxx) { 392 | pw.println("smart_ptr AutoGrammar::fileParserGenerator() {"); 393 | pw.println("smart_ptr g = new Grammar();"); 394 | } else if(lang == Lang.perl) { 395 | pw.println("sub fileparserGenerator() {"); 396 | pw.println("my $g = new Grammar();"); 397 | } 398 | dumpGrammar(pw,lang); 399 | indentLevel -= INDENT_INCR; 400 | if(lang == Lang.perl) { 401 | pw.println("return $g;"); 402 | } else { 403 | pw.println("return g;"); 404 | } 405 | indentLevel -= INDENT_INCR; 406 | pw.println("}"); 407 | 408 | if(lang == Lang.java) { 409 | pw.println("}"); // End class def 410 | } else if(lang == Lang.perl) { 411 | pw.println("1;"); 412 | } 413 | pw.close(); 414 | String contents = sw.toString(); 415 | 416 | File f = new File(autoFile); 417 | boolean write = true; 418 | if(f.exists()) { 419 | StringBuilder sb = new StringBuilder((int)f.length()); 420 | FileReader fr = new FileReader(f); 421 | BufferedReader br = new BufferedReader(fr); 422 | while(true) { 423 | int c= br.read(); 424 | if(c < 0) 425 | break; 426 | sb.append((char)c); 427 | } 428 | br.close(); 429 | if(sb.toString().equals(contents)) 430 | write = false; 431 | } 432 | if(write) { 433 | FileWriter fw = new FileWriter(f); 434 | fw.write(contents); 435 | fw.close(); 436 | } 437 | } 438 | 439 | /** Compiles a string into a pattern. */ 440 | public Pattern compile(String s,Grammar gram) { 441 | Matcher m = g.matcher("pattern",s); 442 | if(m.matches()) { 443 | // m.dumpMatches(); 444 | return compile(m.group(),false,gram); 445 | } else { 446 | throw new ParseException(m.near().toString()); 447 | } 448 | } 449 | 450 | /** Compiles a parse tree to a pattern. */ 451 | Pattern compile(Group g,boolean ignCase,Grammar gram) { 452 | String pn = g.getPatternName(); 453 | if("literal".equals(pn)) { 454 | char c = getChar(g); 455 | if(ignCase) 456 | return new ILiteral(c); 457 | else 458 | return new Literal(c); 459 | } else if("pattern".equals(pn)) { 460 | if(g.groupCount()==0) 461 | return new Nothing(); 462 | return compile(g.group(0),ignCase,gram); 463 | } else if("pelem".equals(pn)) { 464 | if(g.groupCount()==2) { 465 | Multi m = mkMulti(g.group(1)); 466 | m.pattern = compile(g.group(0),ignCase,gram); 467 | return m; 468 | } 469 | return compile(g.group(0),ignCase,gram); 470 | } else if("pelems".equals(pn)||"pelems_top".equals(pn)||"pelems_next".equals(pn)) { 471 | List li = new ArrayList(); 472 | for(int i=0;i li = new ArrayList(); 482 | for(int i=0;i0 && g.group(0).getPatternName().equals("neg")) { 524 | i++; 525 | br.neg = true; 526 | } 527 | for(;i0) { 600 | int mx = Integer.parseInt(g.group(1).group(0).substring()); 601 | return new Multi(mn,mx); 602 | } else { 603 | return new Multi(mn,Integer.MAX_VALUE); 604 | } 605 | } 606 | g.dumpMatches(); 607 | throw new Error(); 608 | } 609 | } 610 | -------------------------------------------------------------------------------- /src/edu/lsu/cct/piraha/examples/Kranc.java: -------------------------------------------------------------------------------- 1 | package edu.lsu.cct.piraha.examples; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.File; 5 | import java.io.FileWriter; 6 | import java.io.IOException; 7 | import java.io.PrintWriter; 8 | import java.util.ArrayList; 9 | import java.util.HashSet; 10 | import java.util.LinkedList; 11 | import java.util.List; 12 | import java.util.Set; 13 | 14 | import edu.lsu.cct.piraha.DebugVisitor; 15 | import edu.lsu.cct.piraha.Grammar; 16 | import edu.lsu.cct.piraha.Group; 17 | import edu.lsu.cct.piraha.Matcher; 18 | 19 | public class Kranc { 20 | Grammar g = new Grammar(); 21 | DebugVisitor dv = new DebugVisitor(); 22 | //private int calcNumber; 23 | private String thornName; 24 | private List inhers = new ArrayList(); 25 | private List ikpars = new ArrayList(); 26 | private List ekpars = new ArrayList(); 27 | private List kpars = new ArrayList(); 28 | private List ipars = new ArrayList(); 29 | private List rpars = new ArrayList(); 30 | private List calcs = new ArrayList(); 31 | public Kranc() { 32 | g.compile("w0","([ \t\r\n]|#.*|\\(\\*((?!\\*\\))[^])*\\*\\))*"); 33 | g.compile("w1","([ \t\r\n]|#.*|\\(\\*((?!\\*\\))[^])*\\*\\))+"); 34 | g.compile("name","(?i:[a-z_][a-z0-9_:]*)"); 35 | g.compile("dquote","\"(\\\\[^]|[^\\\\\"])*\""); 36 | g.compile("thorn","{-w0}@THORN{-w1}{name}"); 37 | g.compile("args","({-w0}{expr}({-w0},{-w0}{expr})*|)"); 38 | g.compile("fun","{name}{-w0}\\[{args}{-w0}\\]"); 39 | g.compile("div","{fun}{-w0}->{-w0}{expr}"); 40 | g.compile("deriv","DERIVATIVES{-w1}{div}({-w0},{-w0}{div})*{-w0}(,{-w0}|)@END_DERIVATIVES"); 41 | g.compile("def","DEFINE{-w1}({fun}|{name}){-w0}={-w0}{expr}"); 42 | g.compile("num","[0-9]+"); 43 | g.compile("term","{fun}|{name}|{num}|{list}|{dquote}|\\({-w0}{@expr}\\)|[+-][ \t]*{-term}"); 44 | g.compile("end_thorn","@END_THORN"); 45 | g.compile("kranc", "{thorn}*({-w0}@({deriv}|{jac}|{tens}|{sym}|{conn}|{group}|{extra}|"+ 46 | "{def}|{calc}|{inher}|{kpar}|{rpar}|{ipar}))*{-w0}{end_thorn}{-w0}$"); 47 | g.addOps("expr", "{-term}", "{-w0}", new String[][]{ 48 | new String[]{"comp","<=|>=|==|!=|<|>"}, 49 | new String[]{"add","([+-]|<>)"}, 50 | new String[]{"mul","[*/]|"}, 51 | new String[]{"pow","\\^"}, 52 | }); 53 | g.compile("jac","JACOBIAN{-w0}\\{{-w0}{name}({-w0},{-w0}{name})*{-w0}\\}"); 54 | g.compile("conn","CONNECTION{-w0}\\{{-w0}{name}({-w0},{-w0}{name})*{-w0}\\}"); 55 | g.compile("tens","TENSORS{-w0}({list}|{fun}|{name})({-w0},{-w0}({list}|{fun}|{name}))*{-w0}(,{-w0}|)@END_TENSORS"); 56 | g.compile("symexp","{fun}|\\{{-w0}{fun}({-w0},{-w0}{name})+\\}"); 57 | g.compile("sym","SYMMETRIC{-w0}{symexp}({-w0},{-w0}{symexp})*"); 58 | g.compile("group","GROUPS{-w0}(({fun}|{name}){-w0}->{-w0}{name}{-w0}(,{-w0}|))*@END_GROUPS"); 59 | g.compile("extra_list","\\{{-w0}{name}{-w0}(,{-w0}{name}{-w0})*\\}"); 60 | g.compile("extra","EXTRA_GROUPS{-w0}"+ 61 | "({name}{-w0}->{-w0}{extra_list}{-w0}(,{-w0}|))*{-w0}@END_EXTRA_GROUPS"); 62 | 63 | g.compile("list","\\{({@expr}({-w0},{-w0}{@expr})*|){-w0}\\}"); 64 | g.compile("short","({fun}|{name}){-w0}"); 65 | g.compile("shorts","@SHORTHANDS{-w1}{short}({-w0},{-w0}{short})*{-w0}(,{-w0}|)@END_SHORTHANDS"); 66 | g.compile("eqn","({fun}|{name}){-w0}->{-w0}{expr}"); 67 | g.compile("eqns","@EQUATIONS{-w1}{eqn}({-w0},{-w0}{eqn})*{-w0}(,{-w0}|)@END_EQUATIONS"); 68 | g.compile("calc_par","@{name}{-w1}{expr}({-w0},{-w0}{expr})*"); 69 | g.compile("calc","CALCULATION{-w1}{name}{-w0}"+ 70 | "({shorts}{-w0}|{eqns}{-w0}|{calc_par}{-w0})*"+ 71 | "@END_CALCULATION"); 72 | g.compile("inher","INHERITED_IMPLEMENTATION{-w1}{name}"); 73 | 74 | g.compile("ikpar","INHERITED_KEYWORD_PARAMETER{-w1}{name}{-w0}(@{name}{-w1}{expr}({-w0},{-w0}{expr})*{-w0})*@END_INHERITED_KEYWORD_PARAMETER"); 75 | g.compile("ekpar","EXTENDED_KEYWORD_PARAMETER{-w1}{name}{-w0}(@{name}{-w1}{expr}({-w0},{-w0}{expr})*{-w0})*@END_EXTENDED_KEYWORD_PARAMETER"); 76 | g.compile("kpar","KEYWORD_PARAMETER{-w1}{name}{-w0}(@{name}{-w1}{expr}({-w0},{-w0}{expr})*{-w0})*@END_KEYWORD_PARAMETER"); 77 | g.compile("allowed","{dquote}{-w0}->{-w0}{dquote}"); 78 | g.compile("alloweds","@ALLOWED_VALUES{-w0}({allowed}({-w0},{-w0}{allowed})*{-w0})*({-w0},{-w0})*@END_ALLOWED_VALUES"); 79 | g.compile("ipar","INT_PARAMETER{-w1}{name}{-w0}({alloweds}{-w0}|{calc_par}{-w0})*@END_INT_PARAMETER"); 80 | g.compile("rpar","REAL_PARAMETER{-w1}{name}{-w0}(@{name}{-w1}{expr}({-w0},{-w0}{expr})*{-w0})*@END_REAL_PARAMETER"); 81 | //g.diag(DebugOutput.out); 82 | } 83 | public void doFile(String inputfile, String outputfile) throws IOException { 84 | File f = new File(inputfile); 85 | String c = Grammar.readContents(f); 86 | Matcher m = g.matcher("kranc",c); 87 | boolean b = m.matches(); 88 | //trim(m).dumpMatches(); 89 | //m.dumpMatches(); 90 | System.out.println(b); 91 | if(!b) 92 | System.err.println(m.near().toString()); 93 | formatOutput(outputfile,m); 94 | } 95 | public void formatOutput(String file,Group g) throws IOException { 96 | FileWriter fw = new FileWriter(file); 97 | BufferedWriter bw = new BufferedWriter(fw); 98 | PrintWriter pw = new PrintWriter(bw); 99 | pw.println("$Path = Join[$Path, {\"../../../repos/Kranc/Tools/CodeGen\","); 100 | pw.println(" \"../../../repos/Kranc/Tools/MathematicaMisc\"}];"); 101 | pw.println("Get[\"KrancThorn`\"];"); 102 | pw.println("SetEnhancedTimes[False];"); 103 | pw.println("SetSourceLanguage[\"C\"];"); 104 | pw.println(); 105 | pw.println("evolutionTimelevels = 3;"); 106 | formatOutput(pw,g); 107 | pw.flush(); 108 | } 109 | public void formatOutput(PrintWriter pw,Group g) { 110 | String m = g.getPatternName(); 111 | if("kranc".equals(m)) { 112 | for(int i=0;i 0) pw.print(", "); 132 | pw.print(g.group(i).substring()); 133 | } 134 | pw.println("];"); 135 | } else if("tens".equals(m)) { 136 | Set tensors = new HashSet(); 137 | for(int i=0;i1 && "list".equals(gg.group(1).getPatternName())) { 154 | for(int j=1;j 0) pw.print(", "); 167 | pw.print(gg.group(j).substring()); 168 | } 169 | pw.println("];"); 170 | } 171 | } else if("fun".equals(mm)) { 172 | name = g.group(i).group(0).substring(); 173 | declareTensor(pw, g.group(i), tensors, name); 174 | pw.print("AssertSymmetricIncreasing["); 175 | pw.print(g.group(i).substring()); 176 | pw.println("];"); 177 | } 178 | } 179 | } else if("conn".equals(m)) { 180 | pw.print("DefineConnection["); 181 | for(int i=0;i 0) pw.print(", "); 183 | pw.print(g.group(i).substring()); 184 | } 185 | pw.println("];"); 186 | } else if("group".equals(m)) { 187 | pw.println(); 188 | pw.print("declaredGroups = {"); 189 | for(int i=0;i+1 0) pw.print(", "); 231 | pw.print("calc"); 232 | pw.print(i); 233 | } 234 | pw.println("};");*/ 235 | } else if("calc".equals(m)) { 236 | /*pw.print("calc"); 237 | pw.print(calcNumber); 238 | pw.println(" ="); 239 | pw.println("{"); 240 | for(int i=1;i "); 246 | pw.print(g.group(i).group(1).substring()); 247 | pw.println(","); 248 | } else if("eqns".equals(mm)) { 249 | formatOutput(pw, g.group(i)); 250 | } else { 251 | throw new Error("bad calc near "+g.group(i).near()); 252 | } 253 | } 254 | pw.print(" Name -> \""); 255 | pw.print(thornName); 256 | pw.print("_\" <> "); 257 | pw.print(g.group(0).substring()); 258 | pw.println(); 259 | pw.println("};");*/ 260 | calcs.add(g); 261 | } else if("shorts".equals(m)) { 262 | pw.println(" Shorthands -> {"); 263 | for(int i=0;i0) pw.println(","); 265 | pw.print(" "); 266 | pw.print(g.group(i).substring()); 267 | } 268 | pw.println(); 269 | pw.println(" },"); 270 | } else if("eqns".equals(m)) { 271 | pw.println(" Equations -> {"); 272 | for(int i=0;i0) pw.println(","); 274 | pw.print(" "); 275 | pw.print(g.group(i).group(0).substring()); 276 | pw.print(" -> "); 277 | pw.print(g.group(i).group(1).substring()); 278 | } 279 | pw.println(); 280 | pw.println(" },"); 281 | } else if("inher".equals(m)) { 282 | inhers.add(g); 283 | } else if("ikpar".equals(m)) { 284 | ikpars.add(g); 285 | } else if("ekpar".equals(m)) { 286 | ekpars.add(g); 287 | } else if("kpar".equals(m)) { 288 | kpars .add(g); 289 | } else if("ipar".equals(m)) { 290 | ipars .add(g); 291 | } else if("rpar".equals(m)) { 292 | rpars .add(g); 293 | } else if("alloweds".equals(m)) { 294 | pw.println(" AllowedValues -> {"); 295 | for(int i=0;i0) pw.println(","); 297 | pw.print(" { Value -> "); 298 | pw.print(g.group(i).group(0).substring()); 299 | pw.print(", Description -> "); 300 | pw.print(g.group(i).group(1).substring()); 301 | pw.print(" }"); 302 | } 303 | pw.println(); 304 | pw.println(" },"); 305 | 306 | } else if("end_thorn".equals(m)) { 307 | for(int calcNum=0;calcNum < calcs.size();calcNum++) { 308 | Group calc = calcs.get(calcNum); 309 | pw.println(); 310 | pw.print("calc"); 311 | pw.print(calcNum); 312 | pw.println(" = {"); 313 | for(int i=1;i "); 319 | pw.print(calc.group(i).group(1).substring()); 320 | pw.println(","); 321 | } else if("shorts".equals(mm)) { 322 | formatOutput(pw, calc.group(i)); 323 | } else if("eqns".equals(mm)) { 324 | formatOutput(pw, calc.group(i)); 325 | } else { 326 | throw new Error("bad calc near "+calc.group(i).near()); 327 | } 328 | } 329 | pw.print(" Name -> \""); 330 | pw.print(calc.group(0).substring()); 331 | pw.println("\""); 332 | pw.println("};"); 333 | } 334 | pw.println(); 335 | pw.print("calculations = {"); 336 | for(int i=0;i 0) pw.print(", "); 338 | pw.print("calc"); 339 | pw.print(i); 340 | } 341 | pw.println("};"); 342 | calcs = new ArrayList(); 343 | 344 | pw.println(); 345 | pw.print("inheritedImplementations = {"); 346 | for(int inum=0; inum 0) pw.print(", "); 349 | pw.print('"'); 350 | pw.print(inher.group(0).substring()); 351 | pw.print('"'); 352 | } 353 | pw.println("};"); 354 | inhers = new ArrayList(); 355 | 356 | pw.println(); 357 | pw.println("inheritedKeywordParameters = {"); 358 | for(int knum = 0;knum < ikpars.size();knum++) { 359 | Group ikpar = ikpars.get(knum); 360 | pw.println(" {"); 361 | for(int i=1;i+1 "); 365 | pw.print(ikpar.group(i+1).substring()); 366 | pw.println(","); 367 | } 368 | pw.print(" Name -> "); 369 | pw.println(ikpar.group(0).substring()); 370 | if(knum+1 == ikpars.size()) 371 | pw.println(" }"); 372 | else 373 | pw.println(" },"); 374 | } 375 | pw.println("};"); 376 | ikpars = new ArrayList(); 377 | 378 | pw.println(); 379 | pw.println("extendedKeywordParameters = {"); 380 | for(int knum = 0;knum < ekpars.size();knum++) { 381 | Group ekpar = ekpars.get(knum); 382 | pw.println(" {"); 383 | for(int i=1;i+1 "); 387 | pw.print(ekpar.group(i+1).substring()); 388 | pw.println(","); 389 | } 390 | pw.print(" Name -> "); 391 | pw.println(ekpar.group(0).substring()); 392 | if(knum+1 == ekpars.size()) 393 | pw.println(" }"); 394 | else 395 | pw.println(" },"); 396 | } 397 | pw.println("};"); 398 | ekpars = new ArrayList(); 399 | 400 | pw.println(); 401 | pw.println("keywordParameters = {"); 402 | for(int knum = 0;knum < kpars.size();knum++) { 403 | Group kpar = kpars.get(knum); 404 | pw.println(" {"); 405 | for(int i=1;i+1 "); 409 | pw.print(kpar.group(i+1).substring()); 410 | pw.println(","); 411 | } 412 | pw.print(" Name -> "); 413 | pw.println(kpar.group(0).substring()); 414 | if(knum+1 == kpars.size()) 415 | pw.println(" }"); 416 | else 417 | pw.println(" },"); 418 | } 419 | pw.println("};"); 420 | kpars = new ArrayList(); 421 | 422 | pw.println(); 423 | pw.println("realParameters = {"); 424 | for(int rnum = 0;rnum < rpars.size();rnum++) { 425 | Group rpar = rpars.get(rnum); 426 | pw.println(" {"); 427 | for(int i=1;i+1 "); 431 | pw.print(rpar.group(i+1).substring()); 432 | pw.println(","); 433 | } 434 | pw.print(" Name -> "); 435 | pw.println(rpar.group(0).substring()); 436 | if(rnum+1 == rpars.size()) 437 | pw.println(" }"); 438 | else 439 | pw.println(" }"); 440 | } 441 | pw.println("};"); 442 | rpars = new ArrayList(); 443 | 444 | pw.println(); 445 | pw.println("intParameters = {"); 446 | for(int inum = 0;inum < ipars.size();inum++) { 447 | Group ipar = ipars.get(inum); 448 | pw.println(" {"); 449 | for(int i=1;i "); 457 | pw.print(ipar.group(i).group(1).substring()); 458 | pw.println(","); 459 | } else { 460 | throw new Error("intParameters"); 461 | } 462 | } 463 | pw.print(" Name -> "); 464 | pw.println(ipar.group(0).substring()); 465 | if(inum+1 == ipars.size()) 466 | pw.println(" }"); 467 | else 468 | pw.println(" }"); 469 | } 470 | pw.println("};"); 471 | ipars = new ArrayList(); 472 | 473 | pw.println(); 474 | pw.println("CreateKrancThornTT[allGroups, \".\", \""+thornName+"\","); 475 | pw.println(" Calculations -> calculations,"); 476 | pw.println(" DeclaredGroups -> declaredGroupNames,"); 477 | pw.println(" PartialDerivatives -> partialDerivatives,"); 478 | pw.println(" EvolutionTimelevels -> evolutionTimelevels,"); 479 | pw.println(" DefaultEvolutionTimelevels -> 3,"); 480 | pw.println(" UseLoopControl -> True,"); 481 | pw.println(" UseVectors -> True,"); 482 | pw.println(" InheritedImplementations -> inheritedImplementations,"); 483 | pw.println(" InheritedKeywordParameters -> inheritedKeywordParameters,"); 484 | pw.println(" ExtendedKeywordParameters -> extendedKeywordParameters,"); 485 | pw.println(" KeywordParameters -> keywordParameters,"); 486 | pw.println(" IntParameters -> intParameters,"); 487 | pw.println(" RealParameters -> realParameters"); 488 | pw.println("];"); 489 | } else { 490 | pw.flush(); 491 | throw new Error(m+": "+g.near()); 492 | } 493 | } 494 | private void declareTensor(PrintWriter pw, Group g, Set tensors, String name) throws Error { 495 | if(name == null) throw new NullPointerException(g.near().toString()); 496 | pw.println(); 497 | pw.print("DefineTensor["); 498 | pw.print(name); 499 | pw.println("];"); 500 | if(tensors.contains(name)) { 501 | throw new Error("multiply defined tensor "+name+" "+g.near()); 502 | } 503 | tensors.add(name); 504 | } 505 | public static void main(String[] args) throws IOException { 506 | Kranc k = new Kranc(); 507 | for(String inputfile : args) { 508 | String outputfile = inputfile.replaceFirst("\\.kranc$", ".m"); 509 | System.out.println(inputfile); 510 | System.out.println(" --> "+outputfile); 511 | k.doFile(inputfile, outputfile); 512 | } 513 | } 514 | Group trim(Group g) { 515 | if(g.groupCount()==1) { 516 | return trim(g.group(0)); 517 | } else if(g.groupCount()==0) { 518 | return new Group(g.getPatternName(),g.getBegin(),g.getEnd(),Group.emptyList,g.getText()); 519 | } else { 520 | LinkedList groups = new LinkedList(); 521 | for(int i=0;i 2 | 4 | 5 | 6 | 7 | CSscript: Build a Language in Two Hours 8 | 21 | 22 | 23 |

piraha-peg - CScriptLanguage.wiki

24 | 25 |
26 | 27 |

Build A Language In Two Hours

28 | 29 |

The Grammar

30 | 31 |

One of the nice things at parsing expression grammars is how expressive they are, and how easy it is to build fairly complex results in a small space.

32 | 33 |

The first step in creating our language will be to create the "skipper". The skipper is a pattern which matches things like white space and comments. A typical C-like skipper

34 | 35 |
 36 | skipper = \b([ \t\r\n]|//[^\n]*|/\*(\*[^/]|[^*])*\*/)*
 37 | 
38 | 39 |

Let's consider what we have here. The "\b" pattern means we want this pattern to include a word boundary. Word boundaries are things like transitions between letters and symbols.

40 | 41 |

The next types of things in the skipper are white space, [ \t\r\n]; a comment that goes until the end of the line, //[^\n]*; or a multi-line comment /\*(\*[^/]|[^*])*\*/. This last consists of the literal start and end sequences, /\* and \*/. In between it matches a sequence which consists of characters that either match [^*] or \*[^/].

42 | 43 |

Now we consider various types of basic pattern elements. The double quote:

44 | 45 |
 46 | dquote = "(\\.|[^"\\])*"
 47 | 
48 | 49 |

The integer, with optional leading minus sign:

50 | 51 |
 52 | int = -?[0-9]+
 53 | 
54 | 55 |

A name (which is really a C-Identifier):

56 | 57 |
 58 | name = [a-zA-Z_][a-zA-Z0-9_]*
 59 | 
60 | 61 |

Next we'll define a mathematical or logical expression, basing it loosely on the C-language precedence of operators. This is similar to what we already did with the calculator, except we'll include comparison and logical operators.

62 | 63 |

operators

64 | 65 |
mulop = *|/|%
 66 | addop = +|-
 67 | ltgt = <=?|>=?
 68 | eq = [!=]=
 69 | andor = &&|\|\|
70 | 71 |

expressions built from operators

72 | 73 |
e1 = {fcall}|{index}|{name}|{int}|{dquote}|( {expr} )
 74 | e2 = {e1}( {mulop} {e1})*
 75 | e3 = {e2}( {addop} {e2})*
 76 | e4 = {e3}( {ltgt} {e3})*
 77 | e5 = {e4}( {eq} {e4})*
 78 | expr = {e5}( {andor} {e5})*
 79 | 
 80 | index = {name} [ {expr} ]
 81 | 
 82 | fcall = {name} \( ({expr}( , {expr})*|) \)
 83 | 
84 | 85 |

The index defines syntax for indexing an array.

86 | 87 |

Wherever a blank space occurs in any pattern in the peg, the skipper is implicit. Thus, I could have written the index definition as follows:

88 | 89 |
 90 | index = {name}{skipper}\[{skipper}{expr}{skipper}\]
 91 | 
92 | 93 |

But it would be much harder to read.

94 | 95 |

Note we've also included a function call definition (fcall) which consists of a name followed by parenthesis containing 0 or more arguments delimited by commas.

96 | 97 |

At this point we begin the meat of the language. This pattern, quite similar to our function call pattern above, matches a function definition:

98 | 99 |
100 | func = {name} \( ({name}( , {name})*|) \) {block}
101 | 
102 | 103 |

Everything here, except the definition of block is defined in terms of things we already know. Nothing in the peg syntax requires us to build our pattern definitions in order of use. We're attempting to build a minimal, but reasonably complete language. For this reason we'll define our block to consist of one of 5 types of statements. Control flow, including an if (with optional else), and a while. We'll also want the ability to assign to local variables, to call functions (e.g. printf("Hello, world"); and the ability to return from functions.

104 | 105 |

All of this can be expressed in a straightforward way in just a few lines.

106 | 107 |
108 | block = { ({statement} )*}
109 | 
110 | statement = {if}|{while}|{return}|{assign}|{call}|{block}
111 | 
112 | assign = ({index}|{name}) = {expr} ;
113 | call = {name} ( ({expr}( , {expr})*|) ) ;
114 | if = if ( {expr} ) {block} (else {block})?
115 | while = while ( {expr} ) {block}
116 | return = return {expr} \;
117 | 
118 | 119 |

Finally, we complete our peg by defining the program pattern. It matches any number of variable assignments and function definitions.

120 | 121 |
122 | program = ^ ({assign} |{func} )*$
123 | 
124 | 125 |

Complete Grammar

126 | 127 |
128 | skipper = \b([ \t\r\n]|//[^\n]|/*(*[^/]|[^])*/)
129 | 
130 | dquote = "(\.|[^"\])*"
131 | 
132 | int = -?[0-9]+
133 | 
134 | name = [a-zA-Z_][a-zA-Z0-9_]*
135 | 
136 | func = {name} ( ({name}( , {name})*|) ) {block}
137 | 
138 | block = { ({statement} )*}
139 | 
140 | statement = {if}|{while}|{return}|{assign}|{call}|{block}
141 | 
142 | index = {name} [ {expr} ]
143 | 
144 | assign = ({index}|{name}) = {expr} ;
145 | call = {name} ( ({expr}( , {expr})*|) ) ;
146 | if = if ( {expr} ) {block} (else {block})?
147 | while = while ( {expr} ) {block}
148 | return = return {expr} \;
149 | 150 |

operators

151 | 152 |
mulop = *|/|%
153 | addop = +|-
154 | ltgt = <=?|>=?
155 | eq = [!=]=
156 | andor = &&|\|\|
157 | 158 |

expressions built from operators

159 | 160 |
e1 = {fcall}|{index}|{name}|{int}|{dquote}|( {expr} )
161 | e2 = {e1}( {mulop} {e1})*
162 | e3 = {e2}( {addop} {e2})*
163 | e4 = {e3}( {ltgt} {e3})*
164 | e5 = {e4}( {eq} {e4})*
165 | expr = {e5}( {andor} {e5})*
166 | 
167 | fcall = {name} ( ({expr}( , {expr})*|) )
168 | 
169 | program = ^ ({assign} |{func} )*$
170 | 
171 | 172 |

The Interpreter

173 | 174 |

Reading and Compiling our Language

175 | 176 |

The first step is to read in the grammar we've defined above 177 | and compile it. We use the compileFile() member function on 178 | the Grammar class to accomplish this:

179 |
180 | Grammar g = new Grammar();
181 | 
182 | static String[] cmdArgs;
183 | public Cexample(String[] args) throws IOException {
184 |     cmdArgs = args;
185 |     g.compileFile(new File("Cexample.peg"));
186 | }
187 | 
188 | 189 |

Next, we provide a method within our code to compile files 190 | in our new language and report syntax errors. The near() 191 | function does the latter task.

192 | 193 | 194 |
195 | 
196 | public void compile(File fname) throws IOException {
197 |     String s = Grammar.readContents(fname);
198 |     Matcher m = g.matcher("program",s);
199 |     if(m.matches()) {
200 |         program = m.group();
201 |     } else {
202 |         System.out.println(m.near());
203 |     }
204 | }
205 | 
206 | 207 |

Our main method will do both of these things for us. It will also store our command

208 | 209 |
210 |     public static void main(String[] args) throws Exception {
211 |         Cexample c = new Cexample(args);
212 |         c.compile(new File(args[0]));
213 |         c.exec();
214 |     }
215 | 
216 | 217 |

The bulk of the work is implementing the interpreter logic. We'll create a VarMap class to map variable names to values. Each time we invoke a function we'll create a new one of these. The VarMap has a prev field to point to definitions of variables that live farther down in the call stack.

218 | 219 |

Once our exec(Group,VarMap) function completes, we'll know the definitions of all functions and can locate and invoke main.

220 | 221 |
222 |     class VarMap {
223 |         VarMap prev;
224 |         Map vars = new HashMap();
225 |     }
226 | 
227 |     public void exec() {
228 |       exec(program);
229 |   }
230 | 
231 |   public void exec(Group g) {
232 |     VarMap root = new VarMap();
233 |     exec(g,root);
234 |     if(funcMap.containsKey("main")) {
235 |         List<Object> args = new ArrayList<Object>();
236 |         for(int i=0;i<cmdArgs.length;i++)
237 |             args.add(cmdArgs[i]);
238 |         List<Object> arg = new ArrayList<Object>();
239 |         arg.add(args);
240 |         fcall("main",arg,root);
241 |     }
242 | }
243 | 
244 | 245 |

The next thing to look at is the exec(Group,VarMap) function itself.

246 | 247 |

Two types of patterns are important for the exec function to understand the program. It must understand assignment statements for root or global variables, and it must understand function definitions.

248 | 249 |

The "func" definition, if found, will contain a "name" as its first (0th) child. This is the name of the function, and the key we'll use to look up the function in our function map. The call to g.group(0).substring() retrieves this name from the parse tree.

250 | 251 |

Assignments to indexes are a little more complex. If the first child of the assignment statement is an index, it's an element of an array we are trying to assign. If not, it's a name. This latter case is simpler. We retrieve the name with g.group(0).substring(), and the expression being assigned with g.group(1). This latter needs to be evaluated before being assigned to the variables table, and eval(g.group(1),vm) performs this task.

252 | 253 |

For indexes, we have to look up the name of the array being indexed, the List<Object> used to represent the index, evaluate the index, the value, and finally set the element.

254 | 255 |
256 |     Map funcMap = new HashMap();
257 | 
258 |     void exec(Group g,VarMap vm) {
259 |     if("assign".equals(g.getPatternName())) {
260 |         if(g.group(0).getPatternName().equals("index")) {
261 |             List<Object> li = (List<Object>)eval(g.group(0).group(0),vm);
262 |             Integer index = (Integer)eval(g.group(0).group(1),vm);
263 |             Object value = eval(g.group(1),vm);
264 |             li.set(index.intValue(),value);
265 |         } else {
266 |             vm.vars.put(g.group(0).substring(),eval(g.group(1),vm));
267 |         }   
268 |     } else if("func".equals(g.getPatternName())) {
269 |         funcMap.put(g.group(0).substring(),g);
270 |     } else if( ....
271 |         ....
272 | 
273 | 274 |

The eval() function is going to convert our mathematical expressions and values into objects. Let's take a brief look at that.

275 | 276 |

If eval sees an "int" or a "dquote", it simply stores the value into an appropriate form and returns it. For an int, that involves calling the Integer constructor. For a string, the initial and trailing quotes in g.substring() have to be removed, and the basic escape sequences processed ('\n' maps to line feed, '\r' to carriage return, etc.).

277 | 278 |

If we find an expression, then we have to process logical operators. If we have a single child element, we just evaluate it. If we have more, there will be an odd number, as the pattern matches values joined by logical "and" or "or" operators. We process these left to right and store each new sub result in val. This same basic logic will be used to construct all the different kinds of operators.

279 | 280 |
281 |     Object eval(Group g,VarMap vm) {
282 |         if("int".equals(g.getPatternName())) {
283 |             return new Integer(g.substring());
284 |         } else if("dquote".equals(g.getPatternName())) {
285 |             String s = g.substring();
286 |             return remap(s);
287 |         } else if("expr".equals(g.getPatternName())) {
288 |             Object val = eval(g.group(0),vm);
289 |             for(int i=1;i<g.groupCount();i+=2) {
290 |                 String op = g.group(i).substring();
291 |                 Object val2 = eval(g.group(i+1),vm);
292 |                 boolean b1 = mkBoolean(val);
293 |                 boolean b2 = mkBoolean(val2);
294 |                 if("&&".equals(op))
295 |                     val = (b1 & b1) ?  1 : 0;
296 |                 else
297 |                     val = (b1 | b2) ? 1 : 0;
298 |             }
299 |             return val;
300 |          ...
301 | 
302 | 303 |

Function calls are handled by the fcall() function. It takes the function name, the value of all arguments (evaluated by eval() and finally the current variable map.

304 | 305 |

Variable names are looked up by starting with the current variable map and checking for the variable name. If found, the value is returned. If not, the prev pointer is checked for additional definitions.

306 | 307 |
308 |            ...
309 |         } else if("fcall".equals(g.getPatternName())) {
310 |             List<Object> args = new ArrayList<Object>();
311 |             for(int i=1;i<g.groupCount();i++) {
312 |                 args.add(eval(g.group(i),vm));
313 |             }
314 |             return fcall(g.group(0).substring(),args,vm);
315 |         } else if("name".equals(g.getPatternName())) {
316 |             VarMap x = vm;
317 |             String nm = g.substring();
318 |             while(x != null) {
319 |                 if(vm.vars.containsKey(nm))
320 |                     return vm.vars.get(nm);
321 |                 else if(vm.prev == null)
322 |                     throw new Error("Undefined variable: '"+nm+"'");
323 |                 vm = vm.prev;
324 |             }
325 |             return null;
326 |         ....
327 | 
328 | 329 |

Evaluating function calls is our next step. The fcall method 330 | starts by providing logic for "builtin" functions such as printf(). If we don't have a builtin function, we create a new VarMap, populate it with variable names.

331 | 332 |

The variable names are found inside the function definition. The first child of the "func" definition is the function name, the last is the function body. All the children in between are argument names. We fetch each of these in turn, assign values to the variables in the new VarMap and finally invoke the function body with our exec(Group,VarMap) function.

333 | 334 |
335 |     Object fcall(String fn,List<Object> args,VarMap vm) {
336 |         try {
337 |             if("printf".equals(fn)) {
338 |                 Object[] pars = new Object[args.size()-1];
339 |                 for(int i=0;i<pars.length;i++)
340 |                     pars[i] = args.get(i+1);
341 |                 System.out.printf(args.get(0).toString(),pars);
342 |                 return 1;
343 |             } else if("len".equals(fn)) {
344 |                 ....
345 |             } 
346 |             VarMap local = new VarMap();
347 |             local.prev = vm; 
348 |             Group func = funcMap.get(fn);
349 |             if(func == null)
350 |                 throw new Error("unknown function "+fn);
351 |             for(int i=1;i+1<func.groupCount();i++) {
352 |                 local.vars.put(func.group(i).substring(),args.get(i-1));
353 |             } 
354 | exec(func.group(func.groupCount()-1),local); 355 | } catch(ReturnException re) { 356 | return re.o; 357 | }
358 | return null; 359 | }
360 |
361 | 362 |

Hopefully, this explains enough of the code that you can understand the complete code listing below. Here is a sample program written in our C-Script language.

363 | 364 |
365 | fib(n) {
366 |     if(n < 2) {
367 |         return n;
368 |     }
369 |     return fib(n-1)+fib(n-2);
370 | }
371 | 
372 | main(args) {
373 |     n = int(args[1]);
374 |     printf("fib(%d)=%d\n",n,fib(n));
375 | }
376 | 
377 | 378 |

It is invoked as follows:

379 | 380 |

381 | $ java -cp piraha.peg:. Cexample fib.cs 15 382 | fib(15)=610 383 |

384 | 385 |

Possibly a more interesting example is this sort program:

386 | 387 |
388 | // Quicksort variant
389 | sort2(arr,start,end) {
390 |   if(start >= end) {
391 |     return 1;
392 |   }
393 |   lo = start;
394 |   hi = end;
395 | 
396 |     // find the pivot
397 |     r = rand();
398 |   if(r < 0) {
399 |     r = 0-r;
400 |   }
401 |   pivot = r % (hi - lo + 1) + lo;
402 | 
403 |   pv = arr[pivot];
404 |   while(lo < hi) {
405 |     lov = arr[lo];
406 |     hiv = arr[hi];
407 |     if(lov > hiv) {
408 |       arr[lo]=hiv;
409 |       arr[hi]=lov;
410 |     } else {
411 |       if(lov <= pv) {
412 |         lo = lo + 1;
413 |       } else {
414 |         hi = hi - 1;
415 |       }
416 |     }
417 |   }
418 |   if(lo == end) {
419 |     lo = lo - 1;
420 |   }
421 |   sort2(arr,start,lo);
422 |   sort2(arr,lo+1,end);
423 | 
424 | }
425 | 
426 | sort(arr) {
427 |   n = len(arr);
428 |   sort2(arr,0,n-1);
429 | }
430 | 
431 | main() {
432 |   arr = mkarray();
433 |   // make a random array
434 |   i = 0;
435 |   while(i < 100) {
436 |     append(arr,rand() % 1000);
437 |     i = i + 1;
438 |   }
439 |   sort(arr);
440 |   printf("%s\n",arr);
441 | }
442 | 
443 | 444 |

Complete listing

445 | 446 |
447 | import edu.lsu.cct.piraha.*;
448 | import java.io.*;
449 | import java.util.*;
450 | 
451 | public class Cexample {
452 |   static class ReturnException extends RuntimeException {
453 |     Object o;
454 |     ReturnException(Object o) {
455 |       this.o = o;
456 |     }
457 |   }
458 | 
459 |   Grammar g = new Grammar();
460 | 
461 |   static String[] cmdArgs;
462 |   public Cexample(String[] args) throws IOException {
463 |     cmdArgs = args;
464 |     g.compileFile(new File("Cexample.peg"));
465 |   }
466 | 
467 |   Group program;
468 | 
469 |   public void compile(File fname) throws IOException {
470 |     String s = Grammar.readContents(fname);
471 |     Matcher m = g.matcher("program",s);
472 |     if(m.matches()) {
473 |       program = m.group();
474 |     } else {
475 |       System.out.println(m.near());
476 |     }
477 |   }
478 | 
479 |   public static void main(String[] args) throws Exception {
480 |     Cexample c = new Cexample(args);
481 |     c.compile(new File(args[0]));
482 |     c.exec();
483 |   }
484 | 
485 |   class VarMap {
486 |     VarMap prev;
487 |     Map<String,Object> vars = new HashMap<String,Object>();
488 |   }
489 | 
490 |   public void exec() {
491 |     exec(program);
492 |   }
493 | 
494 |   public void exec(Group g) {
495 |     VarMap root = new VarMap();
496 |     exec(g,root);
497 |     if(funcMap.containsKey("main")) {
498 |       List<Object> args = new ArrayList<Object>();
499 |       for(int i=0;i<cmdArgs.length;i++)
500 |         args.add(cmdArgs[i]);
501 |       List<Object> arg = new ArrayList<Object>();
502 |       arg.add(args);
503 |       fcall("main",arg,root);
504 |     }
505 |   }
506 | 
507 |   Map<String,Group> funcMap = new HashMap<String,Group>();
508 | 
509 |   void exec(Group g,VarMap vm) {
510 |     if("assign".equals(g.getPatternName())) {
511 |       if(g.group(0).getPatternName().equals("index")) {
512 |         List<Object> li = (List<Object>)eval(g.group(0).group(0),vm);
513 |         Integer index = (Integer)eval(g.group(0).group(1),vm);
514 |         Object value = eval(g.group(1),vm);
515 |         li.set(index.intValue(),value);
516 |       } else {
517 |         vm.vars.put(g.group(0).substring(),eval(g.group(1),vm));
518 |       }
519 |     } else if("func".equals(g.getPatternName())) {
520 |       funcMap.put(g.group(0).substring(),g);
521 |     } else if("program".equals(g.getPatternName())) {
522 |       for(int i=0;i<g.groupCount();i++)
523 |         exec(g.group(i),vm);
524 |     } else if("block".equals(g.getPatternName())) {
525 |       for(int i=0;i<g.groupCount();i++)
526 |         exec(g.group(i),vm);
527 |     } else if("statement".equals(g.getPatternName())) {
528 |       for(int i=0;i<g.groupCount();i++)
529 |         exec(g.group(i),vm);
530 |     } else if("return".equals(g.getPatternName())) {
531 |       //vm.vars.put("$ret",eval(g.group(0),vm));
532 |       throw new ReturnException(eval(g.group(0),vm));
533 |     } else if("while".equals(g.getPatternName())) {
534 |       while(true) {
535 |         Object o = eval(g.group(0),vm);
536 |         boolean b = mkBoolean(o);
537 |         if(b)
538 |           exec(g.group(1),vm);
539 |         else
540 |           break;
541 |       }
542 |     } else if("if".equals(g.getPatternName())) {
543 |       Object o = eval(g.group(0),vm);
544 |       boolean b = mkBoolean(o);
545 |       if(b)
546 |         exec(g.group(1),vm);
547 |       else if(g.groupCount() > 2)
548 |         exec(g.group(2),vm);
549 |     } else if("call".equals(g.getPatternName())) {
550 |       List<Object> args = new ArrayList<Object>();
551 |       for(int i=1;i<g.groupCount();i++) {
552 |         args.add(eval(g.group(i),vm));
553 |       }
554 |       fcall(g.group(0).substring(),args,vm);
555 |     } else {
556 |       throw new Error(g.getPatternName());
557 |     }
558 |   }
559 | 
560 |   boolean mkBoolean(Object o) {
561 |     if(o instanceof String) {
562 |       String s = (String)o;
563 |       if(s.length()==0)
564 |         return false;
565 |     } else if(o instanceof Integer) {
566 |       Integer i = (Integer)o;
567 |       if(i.intValue()==0)
568 |         return false;
569 |     }
570 |     return true;
571 |   }
572 | 
573 |   int cmp(Object o1,Object o2) {
574 |     if(o1 instanceof Integer && o2 instanceof Integer) {
575 |       Integer i1 = (Integer)o1;
576 |       Integer i2 = (Integer)o2;
577 |       return i1.compareTo(i2);
578 |     } else if(o1 instanceof String && o2 instanceof String) {
579 |       String s1 = (String)o1;
580 |       String s2 = (String)o2;
581 |       return s1.compareTo(s2);
582 |     } else {
583 |       throw new Error("cannot compare "+o1+" and "+o2);
584 |     }
585 |   }
586 | 
587 |   Object add(Object o1,Object o2) {
588 |     if(o1 instanceof Integer && o2 instanceof Integer) {
589 |       Integer i1 = (Integer)o1;
590 |       Integer i2 = (Integer)o2;
591 |       return i1 + i2;
592 |     } else if(o1 instanceof String && o2 instanceof String) {
593 |       String s1 = (String)o1;
594 |       String s2 = (String)o2;
595 |       return s1 + s2;
596 |     } else {
597 |       throw new Error("cannot add "+o1+" and "+o2);
598 |     }
599 |   }
600 | 
601 |   Object mul(Object o1,Object o2) {
602 |     if(o1 instanceof Integer && o2 instanceof Integer) {
603 |       Integer i1 = (Integer)o1;
604 |       Integer i2 = (Integer)o2;
605 |       return i1 * i2;
606 |     } else if(o1 instanceof String && o2 instanceof Integer) {
607 |       String s1 = (String)o1;
608 |       Integer i2 = (Integer)o2;
609 |       StringBuffer sb = new StringBuffer();
610 |       for(int i=0;i<i2;i++)
611 |         sb.append(s1);
612 |       return sb.toString();
613 |     } else {
614 |       throw new Error("cannot mul "+o1+" and "+o2);
615 |     }
616 |   }
617 | 
618 |   Object sub(Object o1,Object o2) {
619 |     if(o1 instanceof Integer && o2 instanceof Integer) {
620 |       Integer i1 = (Integer)o1;
621 |       Integer i2 = (Integer)o2;
622 |       return i1 - i2;
623 |     } else {
624 |       throw new Error("cannot subtract "+o1+" and "+o2);
625 |     }
626 |   }
627 | 
628 |   Object rem(Object o1,Object o2) {
629 |     if(o1 instanceof Integer && o2 instanceof Integer) {
630 |       Integer i1 = (Integer)o1;
631 |       Integer i2 = (Integer)o2;
632 |       return i1 % i2;
633 |     } else {
634 |       throw new Error("cannot remainder "+o1+" and "+o2);
635 |     }
636 |   }
637 | 
638 |   Object div(Object o1,Object o2) {
639 |     if(o1 instanceof Integer && o2 instanceof Integer) {
640 |       Integer i1 = (Integer)o1;
641 |       Integer i2 = (Integer)o2;
642 |       return i1 / i2;
643 |     } else {
644 |       throw new Error("cannot divide "+o1+" and "+o2);
645 |     }
646 |   }
647 | 
648 |   final static java.util.Random rand = new java.util.Random();
649 | 
650 |   Object fcall(String fn,List<Object> args,VarMap vm) {
651 |     try {
652 |       if("printf".equals(fn)) {
653 |         Object[] pars = new Object[args.size()-1];
654 |         for(int i=0;i<pars.length;i++)
655 |           pars[i] = args.get(i+1);
656 |         System.out.printf(args.get(0).toString(),pars);
657 |         return 1;
658 |       } else if("len".equals(fn)) {
659 |         List<Object> li = (List<Object>)args.get(0);
660 |         return li.size();
661 |       } else if("append".equals(fn)) {
662 |         List<Object> li = (List<Object>)args.get(0);
663 |         for(int i=1;i<args.size();i++)
664 |           li.add(args.get(i));
665 |         return 1;
666 |       } else if("mkarray".equals(fn)) {
667 |         return new ArrayList<Object>();
668 |       } else if("int".equals(fn)) {
669 |         return new Integer((String)args.get(0));
670 |       } else if("rand".equals(fn)) {
671 |         return new Integer(rand.nextInt());
672 |       } else if("str".equals(fn)) {
673 |         return args.get(0).toString();
674 |       }
675 |       VarMap local = new VarMap();
676 |       local.prev = vm;
677 |       Group func = funcMap.get(fn);
678 |       if(func == null)
679 |         throw new Error("unknown function "+fn);
680 |       for(int i=1;i+1<func.groupCount();i++) {
681 |         local.vars.put(func.group(i).substring(),args.get(i-1));
682 |       }
683 |       exec(func.group(func.groupCount()-1),local);
684 |     } catch(ReturnException re) {
685 |       return re.o;
686 |     }
687 |     return null;
688 |   }
689 | 
690 |   Object eval(Group g,VarMap vm) {
691 |     if("int".equals(g.getPatternName())) {
692 |       return new Integer(g.substring());
693 |     } else if("dquote".equals(g.getPatternName())) {
694 |       String s = g.substring();
695 |       return remap(s);
696 |     } else if("expr".equals(g.getPatternName())) {
697 |       Object val = eval(g.group(0),vm);
698 |       for(int i=1;i<g.groupCount();i+=2) {
699 |         String op = g.group(i).substring();
700 |         Object val2 = eval(g.group(i+1),vm);
701 |         boolean b1 = mkBoolean(val);
702 |         boolean b2 = mkBoolean(val2);
703 |         if("&&".equals(op))
704 |           val = (b1 & b1) ?  1 : 0;
705 |         else
706 |           val = (b1 | b2) ? 1 : 0;
707 |       }
708 |       return val;
709 |     } else if("e5".equals(g.getPatternName())) {
710 |       Object val = eval(g.group(0),vm);
711 |       for(int i=1;i<g.groupCount();i+=2) {
712 |         String op = g.group(i).substring();
713 |         Object val2 = eval(g.group(i+1),vm);
714 |         if("==".equals(op))
715 |           val = cmp(val,val2)==0 ?  1 : 0;
716 |         else
717 |           val = cmp(val,val2)==0 ? 0 : 1;
718 |       }
719 |       return val;
720 |     } else if("e4".equals(g.getPatternName())) {
721 |       Object val = eval(g.group(0),vm);
722 |       for(int i=1;i<g.groupCount();i+=2) {
723 |         String op = g.group(i).substring();
724 |         Object val2 = eval(g.group(i+1),vm);
725 |         if("<".equals(op))
726 |           val = cmp(val,val2)< 0 ?  1 : 0;
727 |         else if("<=".equals(op))
728 |           val = cmp(val,val2)<=0 ?  1 : 0;
729 |         else if(">".equals(op))
730 |           val = cmp(val,val2)> 0 ?  1 : 0;
731 |         else if(">=".equals(op))
732 |           val = cmp(val,val2)>=0 ?  1 : 0;
733 |       }
734 |       return val;
735 |     } else if("e3".equals(g.getPatternName())) {
736 |       Object val = eval(g.group(0),vm);
737 |       for(int i=1;i<g.groupCount();i+=2) {
738 |         String op = g.group(i).substring();
739 |         Object val2 = eval(g.group(i+1),vm);
740 |         if("+".equals(op)) {
741 |           val = add(val,val2);
742 |         } else {
743 |           val = sub(val,val2);
744 |         }
745 |       }
746 |       return val;
747 |     } else if("e2".equals(g.getPatternName())) {
748 |       Object val = eval(g.group(0),vm);
749 |       for(int i=1;i<g.groupCount();i+=2) {
750 |         String op = g.group(i).substring();
751 |         Object val2 = eval(g.group(i+1),vm);
752 |         if("*".equals(op)) {
753 |           val = mul(val,val2);
754 |         } else if("%".equals(op)) {
755 |           val = rem(val,val2);
756 |         } else {
757 |           val = div(val,val2);
758 |         }
759 |       }
760 |       return val;
761 |     } else if("e1".equals(g.getPatternName())) {
762 |       return eval(g.group(0),vm);
763 |     } else if("fcall".equals(g.getPatternName())) {
764 |       List<Object> args = new ArrayList<Object>();
765 |       for(int i=1;i<g.groupCount();i++) {
766 |         args.add(eval(g.group(i),vm));
767 |       }
768 |       return fcall(g.group(0).substring(),args,vm);
769 |     } else if("name".equals(g.getPatternName())) {
770 |       VarMap x = vm;
771 |       String nm = g.substring();
772 |       while(x != null) {
773 |         if(vm.vars.containsKey(nm))
774 |           return vm.vars.get(nm);
775 |         else if(vm.prev == null)
776 |           throw new Error("Undefined variable: '"+nm+"'");
777 |         vm = vm.prev;
778 |       }
779 |       return null;
780 |     } else if("index".equals(g.getPatternName())) {
781 |       VarMap x = vm;
782 |       String nm = g.group(0).substring();
783 |       Integer index = (Integer)eval(g.group(1),vm);
784 |       while(x != null) {
785 |         if(vm.vars.containsKey(nm)) {
786 |           List<Object> li = (List<Object>)vm.vars.get(nm);
787 |           return li.get(index);
788 |         } else if(vm.prev == null)
789 |           throw new Error("Undefined variable: '"+nm+"'");
790 |         vm = vm.prev;
791 |       }
792 |       return null;
793 |     } else {
794 |       throw new Error(g.getPatternName());
795 |     }
796 |   }
797 |   String remap(String s) {
798 |     StringBuffer sb = new StringBuffer();
799 |     for(int i=1;i+1<s.length();i++) {
800 |       char c = s.charAt(i);
801 |       if(c == '\\') {
802 |         char c2 = s.charAt(++i);
803 |         if(c2 == 'n')
804 |           sb.append('\n');
805 |         else if(c2 == 'r')
806 |           sb.append('\r');
807 |         else if(c2 == 't')
808 |           sb.append('\t');
809 |         else
810 |           sb.append(c2);
811 |       } else {
812 |         sb.append(c);
813 |       }
814 |     }
815 |     return sb.toString();
816 |   }
817 | 
818 | }
819 | 
820 | 821 | 822 | 823 | --------------------------------------------------------------------------------